[iOS] NavigationLink의 특성

유인호·2024년 5월 11일
0

iOS

목록 보기
45/73
post-custom-banner

0. 서론

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이 전혀 실행이 안될것 같아야 정상적일 것이다.

1. 그러나,,,

0번째 뷰 init
0번째 뷰 init
1번째 뷰 init
1번째 뷰 init
2번째 뷰 init
2번째 뷰 init
...
17번째 뷰 init
17번째 뷰 init
18번째 뷰 init
18번째 뷰 init

무수한 init의 요청이 들어오는걸 알 수 있었다.

이로서 NavigationLink의 특징은 Naviagtion을 사용하지 않더라도 초기화 하여 값을 가지고 있다는 것이다.

심지어 메모리 사용량도 조금이나마 증가하고 있다는걸 알 수 있었다. 만약 init시점마다 특정 행동을 취하도록 만들고 싶다고 한다면, 뷰가 띄워지기도 전, 원하지 않는 시점에 딱 한번만 실행이 될 것이다.

2. 어떻게 해결할 수 있을까?

  1. 네비게이션 링크안에 있는 뷰를 한번 더 감싼다.
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이 되지 않는 상태일 것이다.

  1. 조금 더 쉽게 사용하기 위해 네비게이션 링크를 커스텀한다.
    아까와 같은 아이디어인데, 이번엔 NavigationLink자체를 커스텀 하는 방식이다.
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
		}
	}

}

위와 같은 방법으로 처리 하면 콘솔에는 아무것도 찍히지 않는다는걸 알 수 있다.

profile
🍎Apple Developer Academy @ POSTECH 2nd, 🌱SeSAC iOS 4th
post-custom-banner

0개의 댓글