GeometryReader를 이용한 반응형 UI를 적용하면서 겪었던 시련

문다연·2023년 4월 12일
1

ios.moon

목록 보기
25/26

GeometryReader

@frozen struct GeometryReader<Content> where Content : View

A container view that defines its content as a function of its own size and coordinate space.

GeometryReader도 view의 일종. 컨테이너 안의 스스로 view 크기와 위치를 함수로 정의한다.

GeometryProxy

A proxy for access to the size and coordinate space (for anchor resolution) of the container view.

GeometryReader(Container View)의 크기와 위치에 접근할 수 있게 해준다.

public var size: CGSize { get }
public var safeAreaInsets: EdgeInsets { get }
public func frame(in coordinateSpace: CoordinateSpace) -> CGRect

겪었던 문제점

playgrounds에서 실행시 윈도우 사이즈를 조정할 때 geometry reader가 반응형으로 뷰 사이즈를 못읽어온다고 생각했던 부분! (iPhone, iPad는 기기 사이즈가 바뀌는 것이 아니므로 고려 대상은 playgrounds 앱으로 실행시였다)

→ 알고보니 geometry reader로 테스트해봤을 때 안됐다고 생각했던 점이, @Binding과 @State의 역할을 헷갈리면서 적용되지 않았던 것이었다.

  • 기존 코드
    struct TutorialView: View {
    	**@State private var size: CGSize = .zero**
    	var body: some View {
    		**GeometryReader { geo in**
    					**Gauge(size: $size)**
    						.onAppear {
    							**size = geo.size**
    						}
    				}
        }
    }
    struct Gauge: View {
    	**@Binding var size: CGSize**
    	var body: some View {
            ZStack (alignment: .leading) {
                Capsule()
                    .frame(width: **size.width * 0.8**, height: 50)
                    .foregroundColor(.light)
            }
        }
    }

이 코드는 TutorialView가 처음 나타날 때 (onAppear) 읽어온 뷰 사이즈를 size라는 @State 변수에 값으로 초기화해주고 그 사이즈를 Gauge라는 뷰에 @Binding으로 넘겨준다.

이렇게 했을 때의 가장 큰 문제점은 뷰가 처음 나타날 때 GeometryReader가 읽어온 값을 전달한다. (내가 onAppear할 때만 값이 초기화되도록 했으니까 .. ㅋㅋ) 즉, 현재 뷰가 navigation이 없다면 size 변수의 값이 바뀌지 않는다! 그렇기 때문에 윈도우 사이즈를 좁히면 반응형이 아니라 게이지가 터지는 것처럼 보였다.

  • 해결 방법
    struct TutorialView: View {
    	var body: some View {
    		GeometryReader { **geo** in
    					**Gauge(size: geo.size)**
    				}
        }
    }
    struct Gauge: View {
    	**@State var size: CGSize**
    	var body: some View {
            ZStack (alignment: .leading) {
                Capsule()
                    .frame(width: **size.width * 0.8**, height: 50)
                    .foregroundColor(.light)
            }
        }
    }

놀랍게도 TutorialView에서 GeometryReader의 클로저 인자인 geo를 Guage에 바로 전달해주면 해결된다. 코드도 간결해지고 onAppear가 아닌 어떤 상황에서 size 변수를 초기화해야 반응형 UI를 제공할 수 있을지에 대한 고민도 필요없어진다.

이런 문제가 발생했던 원인: 앞에서 언급했듯 GeometryReader에 대한 개념이 부족했던 것도 있지만, @State와 @Binding의 역할을 헷갈린 이유가 크다. 다른 뷰로 유동적인 변수의 값을 전달할 때는 반드시 @State 변수로 읽어 @Binding 변수로 전달해줘야 한다고 생각했는데, 그렇지 않았다. property wrapper type들에 대해 정의를 다시 확인해야 할 필요가 있다.


Ref.

https://developer.apple.com/documentation/swiftui/geometryreader

https://swiftwithmajid.com/2020/11/04/how-to-use-geometryreader-without-breaking-swiftui-layout/

https://medium.com/hcleedev/swift-geometryreader는-무엇일까-564896c6d6e0

profile
ios-moon.tistory.com 이전했어요 🚛

0개의 댓글