https://velog.io/@sustainable-git/SwiftUI에서-불필요한-렌더링-제거하기
https://velog.io/@sustainable-git/SwiftUI에서-불필요한-렌더링-제거하기2
앞선 포스팅에서 아래와 같은 것들을 확인하였습니다.
오늘은 실제로 일어날만한(저에게 실제로 일어났던) 일을 소개해드리겠습니다.
시, 분, 초를 나타내야 하는 Picker가 있고, 각 Picker는 독립적이게 움직여야 합니다.
하지만 영상을 자세히 보면 분이 움직이고 있는 와중 초가 변경되고 있습니다.
14분이던 분 Picker를 가속해서 돌리는 와중 초를 변경하면 분 Picker가 초기화되어 다시 14분으로 돌아갑니다.
해당 코드는 시, 분 초 중 하나라도 값이 정해지면 body 전체를 다시 그리면서 정해지지 않은(움직이는 Picker) 값은 초기화하여 그리게 됩니다.
import SwiftUI
struct ContentView: View {
@State private var hours: Int = 0
@State private var minutes: Int = 0
@State private var seconds: Int = 0
var body: some View {
HStack {
Picker("시", selection: $hours) {
ForEach(0...5, id: \.self) { hour in
Text("\(hour)시")
}
}
Picker("분", selection: $minutes) {
ForEach(0...60, id: \.self) { minute in
Text("\(minute)분")
}
}
Picker("초", selection: $seconds) {
ForEach(0...60, id: \.self) { second in
Text("\(second)초")
}
}
}
.pickerStyle(.wheel)
}
}
앞선 포스팅들을 살펴보면 이 이슈의 원인을 쉽게 이해할 수 있습니다.
하나라도 @State 값이 정해지면 body를 다시 그려야 합니다.
모듈화가 되어 있다면 해당 @State에 영향을 받는 모듈만 업데이트 되지만, 그렇지 않다면 전부 다시 그립니다.
때문에 각 Picker를 모듈화 하면 이를 쉽게 해결할 수 있을 것입니다.
import SwiftUI
struct ContentView: View {
@State private var hours: Int = 0
@State private var minutes: Int = 0
@State private var seconds: Int = 0
var body: some View {
HStack {
MyPickerView(unit: "시", range: 0...5, bindedTime: $hours)
MyPickerView(unit: "분", range: 0...60, bindedTime: $minutes)
MyPickerView(unit: "초", range: 0...60, bindedTime: $seconds)
}
.pickerStyle(.wheel)
}
}
fileprivate struct MyPickerView: View {
let unit: String
let range: ClosedRange<Int>
@Binding var bindedTime: Int
var body: some View {
Picker(unit, selection: $bindedTime) {
ForEach(range, id: \.self) { time in
Text("\(time)\(unit)")
}
}
}
}
위 현상은 SwiftUI를 조금만 쓰다 보면 여러분도 겪게될 일입니다.
하지만, 이를 모르고 지나친다면 영원히 발견하지 못할 수도 있습니다.
특히 개발 편의를 위해 하나의 ViewModel을 여러 View에서 참조하도록 개발하는 경우가 많은데, 이 때 View가 다시 그려지는 것에 많은 주의가 필요합니다.