struct ContentView: View {
var body: some View {
List(0..<100) { item in
NavigationLink {
TestView(num: item)
} label: {
Text("\(item)")
}
}
}
}
struct TestView: View {
let num: Int
init(num: Int) {
self.num = num
print("\(num)번째 뷰 init")
}
var body: some View {
Text("\(num)")
}
}
이런 뷰를 만들고, ContentView를 실행하면 init은 어떻게 될까..? 상식적으로 생각했을때는, NavigationLink안에 들어있는 뷰를 들어가는거고, 사용자의 눈에서도 눌러야 나오는것처럼 보이니, init이 전혀 실행이 안될것 같아야 정상적일 것이다.
0번째 뷰 init
0번째 뷰 init
1번째 뷰 init
1번째 뷰 init
2번째 뷰 init
2번째 뷰 init
...
17번째 뷰 init
17번째 뷰 init
18번째 뷰 init
18번째 뷰 init
무수한 init의 요청이 들어오는걸 알 수 있었다.
이로서 NavigationLink의 특징은 Naviagtion을 사용하지 않더라도 초기화 하여 값을 가지고 있다는 것이다.
심지어 메모리 사용량도 조금이나마 증가하고 있다는걸 알 수 있었다. 만약 init시점마다 특정 행동을 취하도록 만들고 싶다고 한다면, 뷰가 띄워지기도 전, 원하지 않는 시점에 딱 한번만 실행이 될 것이다.
struct NavigationLazyView<T: View>: View {
let build: () -> T
init(_ build: @autoclosure @escaping () -> T) {
self.build = build
}
var body: some View {
build()
}
}
NavigationLink를 사용할때 직접적으로 뷰를 명시하는것이 아닌, NavigationLazyView라는 또 하나의 뷰로 감싸서 가지고 있게 한다. SwiftUI에서 NavigationLink는 최상위의 뷰만 초기화해서 갖고 있기 때문에, 한번 감싸는 형태로 보내게 된다면 실질적인 뷰는 init이 되지 않는 상태일 것이다.
struct LazyNavigationLink<Label, Destination> : View where Label : View, Destination : View {
private let label: Label
private let destination: Destination
public init(@ViewBuilder destination: () -> Destination, @ViewBuilder label: () -> Label) {
self.label = label()
self.destination = destination()
}
@MainActor var body: some View {
NavigationLink {
NavigationLazyView(destination)
} label: {
label
}
}
}
위와 같은 방법으로 처리 하면 콘솔에는 아무것도 찍히지 않는다는걸 알 수 있다.