문제상황
struct BlueRectangle: View {
@State var blueRectSize = CGSize(width: 20, height: 40)
var body: some View {
VStack {
Rectangle()
.frame(width: blueRectSize.width, height: blueRectSize.height)
.foregroundStyle(.blue)
BlackRectangle()
.frame(height: 60)
}
}
}
struct BlackRectangle: View {
var body: some View {
HStack {
Rectangle()
Rectangle()
Rectangle()
Rectangle()
}
}
}

BlueRectangle 1개와 BlackRectangle 3개로 구성되어있다.
여기서 BlackRectangle의 사이즈는 동적으로 결정되었다.
이 상황에서 BlueRectangle 객체의 사이즈를 BlackRectangle 한 개의 사이즈과 똑같이 만들고 싶다.
따라서 이 장에서는 다음을 해결해야한다.
- BlackRectangle 한 개의 사이즈를 구한다.
- 구한 사이즈를 상위뷰로 전달한다.
BlackRectangle Size 구하기
GeometryReader를 이용해서 구할 수 있다.
뷰를 GeometryReader로 감싸면 GeometryProxy 타입의 변수를 통해 Size를 알 수 있다.
ForEach structure에 감싸져 있던 Rectangle을 GeometryReader로 감싸서,
overlay로 width값을 확인해 보자.
GeometryReader { proxy in
Rectangle()
.overlay {
Text("\(proxy.size.width)")
.foregroundStyle(.white)
}
}

이제 오른쪽 화면을 보면 BlackRectangle이 101.5의 width값을 가짐을 확인했다.
즉, GeometryReader를 통해 뷰의 Size를 구할 수 있음을 알았다.
이제 사이즈를 값을 분리된 상위뷰인 BlueRectangle에 전달해보자.
상위뷰로 데이터 전달하기
PreferenceKey 프로토콜를 통해 데이터를 상위뷰로 전달할 수 있다.
먼저, PreferenceKey를 준수하는 struct를 하나 정의한다.
struct GeometryPrefereneKey: PreferenceKey {
typealias Value = CGSize
static var defaultValue: CGSize = .zero
static func reduce(value: inout CGSize, nextValue: () -> CGSize) {
value = nextValue()
}
}
preference modifier를 통해 데이터를 송신시키자.
GeometryReader { proxy in
Rectangle()
.overlay {
Text("\(proxy.size.width)")
.foregroundStyle(.white)
}
.preference(key: GeometryPrefereneKey.self, value: proxy.size)
}
이제 onPreferenceChange modifier를 통해 데이터를 수신할 수 있다.
onPreferenceChange는 blueRectangle 변수에 값을 넣을 수 있는 뷰에 적당히 넣자. 나는 VStack을 선택했다.
그리고 수신된 데이터를 blueRectSize에 저장하면 문제가 해결된다.
(추가로 blueRectangle의 width값의 확인을 위해 overlay로 화면에 width를 렌더링했다)
VStack {
Rectangle()
.frame(width: blueRectSize.width, height: blueRectSize.height)
.foregroundStyle(.blue)
.overlay {
Text("\(blueRectSize.width)")
}
BlackRectangle()
.frame(height: 60)
}
.onPreferenceChange(GeometryPrefereneKey.self) { blackRectangleSize in
blueRectSize = blackRectangleSize
}

대충 순서를 보면 다음과 같다 :
GeometryReader를 통해 Size 찾기
-> BlackRectangle에서 데이터 송신
-> BlueRectangle에서 데이터 수신
-> 수신된 데이터를 blueRectSize에 저장
-> @State에 의해 뷰 reRendering
-> blueRectangle의 사이즈 변경됨
'SwiftUI' 카테고리의 다른 글
[SwiftUI] ForEach and ID (0) | 2023.11.06 |
---|