GeometryReader
https://developer.apple.com/documentation/swiftui/geometryreader
GeometryReader는 자신의 크기와 좌표 공간을 함수로 사용여 컨텐츠를 정의하는 컨테이너 뷰다.
이를 통해 자신이 배치된 부모 뷰의 크기와 좌표 공간에 대한 정보를 얻을 수 있고,
이 정보를 기반으로 레이아웃을 동적으로 정의할 수 있다.
import SwiftUI
struct ContentView: View {
var body: some View {
GeometryReader { proxy in
VStack(spacing: 50) {
Text("Width: \(proxy.size.width)")
.frame(width: proxy.size.width)
.border(.red)
Text("Height: \(proxy.size.height / 10)")
.frame(height: proxy.size.height / 10)
.border(.red)
Text("X: \(proxy.frame(in: .local).minX)")
Text("Y: \(proxy.frame(in: .local).minY)")
}
.frame(
width: proxy.size.width,
height: proxy.size.height
)
}
}
}
GeometryReader를 사용하여 뷰를 그릴 때 다음의 스텝을 거치게 된다.
1. 부모가 자식에게 크기 제안
부모 뷰가 자식 뷰에게 크기를 제안한다. 이 크기는 부모의 레이아웃 요구사항을 반영한 것이다. (frame 모디파이어 등)
2. 자식은 자신의 크기 결정
자식 뷰는 부모로부터 받은 크기 제안을 바탕으로 자자신의 크기를 결정한다. 이 과정에서 자식 뷰는 필요에 따라 부모가 제안한 크기를 무시하고 자신이 필요로 하는 크기를 반환할 수 있다.
3. 부모가 자식을 포지셔닝
부모 뷰는 자식 뷰가 결정한 크기를 사용하여 자식 뷰를 적절하게 배치한다.
[주의]
GeometryReader는 가능한 모든 공간을 사용하려는 Greedy한 속성을 갖고 있다. 이 때문에 종종 자식 뷰가 예상보다 큰 크기를 가질 수 있는데, 이를 방지하려면 frame 모디파이러를 사용하여 크기를 제한해야 한다.
GeometryReaderd의 Greedy한 속성을 확인해보자.
import SwiftUI
struct GreedyGeometryReaderExample: View {
var body: some View {
VStack {
Text("평범 텍스트")
.background(Color.green)
GeometryReader { proxy in
Text("GeometryReader 안의 텍스트")
.frame(width: proxy.size.width)
.background(Color.blue)
}
.background(
Color.red
.overlay(Text("공간을 다 먹어버림"))
)
Text("평범 텍스트")
.background(Color.yellow)
}
.background(Color.gray)
}
}
#Preview {
GreedyGeometryReaderExample()
}
GeomentryProxy
GeometryReader에서 전달되는 `proxy` 매개변수는 GeomentryProxy 라는 타입의 인스턴스다.
이 GeomentryProxy는 아래 정보들을 제공한다.
1. 부모 뷰로부터 제안된 크기
부모 뷰가 자식 뷰에게 제안한 크기 반환
let proposedWidth = proxy.size.width
let proposedHeight = proxy.size.height
2. 적용된 safe area inset
부모 뷰로부터 적용된 safeareainset 값 반환
let safeAreaInsets = proxy.safeAreaInsets
let topInset = safeAreaInsets.top
let bottomInset = safeAreaInsets.bottom
3. frame 값을 읽을 수 있는 메서드
특정 좌표 공간 내에서 뷰의 프레임 값을 읽어 반환
어떤 좌표 공간에서 프레임 값을 읽을 것인지 명확히 지정
let localFrame = proxy.frame(in: .local)
let globalFrame = proxy.frame(in: .global)
let specificLayerFrame = proxy.frame(in: .named("layer"))
CoordinateSpace
CoordinateSpace의 정의와 static 프로퍼티에 대해 알아보자
https://developer.apple.com/documentation/swiftui/coordinatespace
A resolved coordinate space created by the coordinate space protocol.
Coordinatoe Space 프로토콜에 의해 해석되어 생성된 좌표 공간.
CoordinatoeSpace를 직접 사용하는 경우는 드물다.
대신 .global, local, .named(_:)와 같은 CoordinateSpaceProtocol의 static 프로퍼티와 함수를 사용하는 것이 좋다.
1. .global : 전체 화면 또는 앱의 좌표계
뷰의 프레임을 화면 전체를 기준으로 측정한다. 이 경우 X와 Y 좌표는 화면의 좌측 상단이 (0, 0)으로 설정된다.
2. .local: 현재 뷰의 좌표계
뷰의 부모 뷰를 기준으로 프레임을 측정한다. 이 경우 부모 뷰의 좌측 상단이 (0, 0)으로 설정된다.
3. .named(_:): 특정 이름으로 지정된 커스텀 좌표계 참조
커스텀 좌표 공간을 지정할 수 있다. coordinatoeSpace() 모디파이어를 사용하여 뷰에 특정 좌표 공간 이름을 부여할 수 있다.
그런 다음 해당 뷰의 자식 뷰들은 그 좌표 공간을 기준으로 프레임을 측정할 수 있다.
참고
'iOS > SwiftUI' 카테고리의 다른 글
SwiftUI - TabView 내부 컨텐츠의 OffsetX 구하기 (0) | 2024.08.20 |
---|---|
SwiftUI 뷰가 다시 그려지는 조건 탐구 (Redraw) (1) | 2022.11.20 |