블로그

하위뷰에서 상위뷰로 데이터 전달 (PreferenceKey, GeometryReader) [SwiftUI]

게시 4분

문제상황

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의 사이즈 변경됨

댓글

댓글을 남기면 닉네임(또는 GitHub 계정 정보)이 저장됩니다. 개인정보처리방침

GitHub