본문 바로가기
SwiftUI

@State값을 ModalView(FullScreen, sheet..) 에 주입시 유의점

by SeoB-P 2024. 1. 6.

배경

  • State값을 이용하여 특정 뷰를 보여주는 View를 보여주려고함.
  • 하지만, State 값이 변하였음에도 불구하고 의도했던대로 동작하지 않음.

문제 재현 코드

struct ContentView: View {
    @State private var n = 1
    @State private var show = false

    var body: some View {
        VStack {
            Text("\(n)")
            // Text("n = \(n)") // Comment out this line, and the n value displayed in the sheet will be 1 (not the expected 2)
            Button("Set n = 2") {
                n = 2
                show = true
            }
            .buttonStyle(.bordered)
        }
        .sheet(isPresented: $show) {
            VStack {
                Text("n = \(n)")
                Button("Close") {
                    show = false
                    print("n in fullScreenCover is", n)
                }
                .buttonStyle(.bordered)
            }
        }
    }
}

 

샘플 코드 출처

 

위 코드는 State값을 이용해 n값과 show값을 바꿀수 있다

show값이 true일때 현재 n값을 기반으로 sheetView를 보여주는 코드이다.

하지만, 실제로 코드를 실행시켜보면 n값이 바뀌지 않음을 알 수 있다.

원인 분석

위 현상이 일어나는 이유는 뭘까?

관련 현상을 정리한 글에서 참고할 수 있었다

위 글에 따르면 ..State의 highly optimized mechanism때문

글에서 소개하는 State의 highly optimized mechanism은 View를 업데이트 하는 방식에서 나타난다.

View를 업데이트하는 방식에는 크게 두가지가 있다.

  • @StateObject, @ObservedObject, @EnvironmentObject.. 이용
    • ObjectWillChange.send() 실행
    • 값을 사용하는지 안하는지 유무에 상관없이 관련된 뷰 재생성, 업데이트
  • @State값 변경
    • State값이 바뀜에 따라 View가 재생성, 업데이트
    • ❗️Body안에서 값을 사용하는지 안하는지에 따라 뷰 생성 로직이 다름❗️

StateTest SampleCode

struct StateTest: View {
    @State var n = 10
    var body: some View {
        VStack {
            let _ = print("update")
            Text("Hello")
//            Text("\(n)")   // 이 부분을 풀면 n값이 변경될때 마다 update가 불린다.

//            if n > 15 {
//                Text("over 15!")
//            }

            Button("n = n + 1") {
                n += 1
                print(n)
            }

            Button("Dump State") {
                dump(_n)
            }
        }
    }
}

위 코드를 실행시켜보면 Text나 if 조건문을 주석해제하지 않는 이상 update는 불리지 않는다.

body안에 조건문이든 View든 n이 포함이 되어야 n값이 바뀌어야 update가 되는 것.

Dump를 찍으면 더 자세히 알수 있다.

n값을 22까지 올렸음에도 불구하고 value는 아직 초기값인 10임을 알수 있고

wasRead값은 false로 되어있음을 확인 할 수 있다.

 

반면에, body에 n값을 넣어서 View를 만들경우

value가 21로 되어있고 wasRead값이 true로 변경되어 있음을 확인 할 수 있었다.

조건문을 추가했을때도 마찬가지였다.

 

sheet, Fullmodal?

처음 문제 코드를 보면 sheet안에 Text("n = \(n)") 이라는 코드가 있는데 왜 업데이트가 안된걸까?

 
 

다른 ViewModifier와 다르게 sheet나 fullmodal은 호출될 때만 내부 클로저가 계산되고 사용된다.

따라서, modal이 띄워지기 전까지는 wasRead값은 false로 유지되고, 업데이트된 n값은 사용되지 않는다.

마찬가지로 show값이 바뀌었다고해서 body가 recomputed되지는 않는다.

하지만, body에 앞서 했던대로 n에 대한 Text나 조건문등을 주게 된다면 wasRead값은 true로 바뀌고 값을 가져온다.

시도 해볼수 있는 것들

위와 같은 문제를 해결하기위해 시도해볼 수 있는 해결책들.

  • StateObject
  • Binding
  • onChange, onReceive
  • Data update시 Delay를 주기

참고

'SwiftUI' 카테고리의 다른 글

@StateObject vs @ObservedObject  (0) 2024.01.02
Building Custom View with SwiftUI(WWDC19) - 2  (0) 2022.11.12
Building Custom View with SwiftUI(WWDC19) - 1  (0) 2022.11.12

댓글