문제상황
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의 사이즈 변경됨