-
[SwiftUI] - GeometryReaderiOS/SWIFTUI 2024. 7. 26. 20:57728x90반응형
오늘은 어려운 GeometryReader를 가져와봤다.
솔직히 GeometryReader는 거의 모르는 상태로 왜 쓰는지도 모르고 사용해 왔던 것 같다. 하지만 이번 기회에 개념을 확실히 잡아 내 마음대로 자유자재로 사용하고자 하는 취지로 포스팅하게 되었다.
GeometryReader
OverView를 해석하자면 부모뷰의 레이아웃 정보를 불러오는 데 사용된다고 한다.
GeometryReader는 GeometryProxy를 파아미터로 받는 클로져를 반환하는데, 여기서 GeometryProxy는 GeometryReader 내부에서 사용되는 뷰의 기하학적 정보를 포함하고 있다.
여기서 말하는 정보는 GeometryProxy의 attribute를 살펴보면 되는데,
- size : 뷰의 크기 정보
- safeAreaInsets : 뷰의 안전 영역 정보
- frame(in:) : 지정된 좌표 공간에서의 뷰의 프레임
전체적으로 GeometryReader로 읽어 들인 내용을 바탕으로 이벤트를 처리하거나 화면에 출력하기 위함으로 쓰이는 느낌이다.
자세히 하나씩 알아보자.
size & safeAreaInsets
import SwiftUI struct GeomertyReaderView: View { var body: some View { GeometryReader{ geometry in VStack{ Text("\(geometry.safeAreaInsets)") Text("\(geometry.size)") } } } } #Preview { GeomertyReaderView() }
위의 코드를 실행 시키면 이런 화면이 나온다.
위에서도 말했듯이 Geometry는 상위뷰의 레이아웃 정보를 불러오는 놈이다.
현재 상위뷰는 최상위 뷰임으로 이 기종으로 보여줄 수 있는 최대 값의 안전영역을 제외한 값이 size가 되는 것이다. (참고로 기종은 iPhone 15 pro max)
size는 width, height를 각각 사용할 수 있다.
geometry.size.width // 상위 뷰의 가로길이 geometry.size.height // 상위 뷰의 세로길이
safeAreaInsets 같은 경우는 안전 영역, 말 그대로 Safe Area를 뜻한다. 즉 이 영역을 무시하지 않는 한 상위 뷰 레이아웃에서 표시할 수 있는 전체 영역을 뜻한다.
geometry.safeAreaInsets.top // 최상단 으로 부터의 거리 geometry.safeAreaInsets.bottom //최히단 으로 부터의 거리 geometry.safeAreaInsets.leading //좌측 으로 부터의 거리 geometry.safeAreaInsets.trailing //우측 으로 부터의 거리
그래서 지금은 top - 59.0, bottom - 34.0이 띄워져 있지만 만약. ignoresSafeArea()을 해당 컨테이너에 적용할 경우
이렇게 나온다. 안전영역을 무시했으니 자동으로 상위뷰의 시작점이 디바이스 기준으로 설정되어 height size값이 759 -> 852로 바뀐 것을 볼 수 있다.
그럼 만약
var body: some View { ZStack{ Color.yellow GeometryReader{ geometry in VStack{ Text("\(geometry.safeAreaInsets)") Text("\(geometry.size)") GeometryReader{ proxy in Color.cyan VStack{ Text("\(proxy.safeAreaInsets)") Text("\(proxy.size)") } } } } } }
이렇게 GeometryReader안에 GeometryReader가 있으면 어떻게 될까?
만약 부모 GeometryReader컨테이너를 A, 자식 GeometryReader 컨테이너를 B라고 한다면
여기서 A,B의 상위뷰인 Zstack의 크기만큼의 size와 Safe Area가 각각 A, B 내부 Text들에 출력된다.
frame(in:)
frame같은 경우는 3가지 옵션이 각각 존재한다.
- global : (기본적으로 Safe Area를 제외) 전체 스크린 전체에서의 좌표
- local : 상위 컨테이너에서의 좌표
- name : 이름을 붙인 컨테이너에서의 좌표
말로 들으면 이해가 가지 않으니 예제로 살펴보자
var body: some View { ZStack{ VStack(spacing:0){ Circle() .frame(width: 100,height: 100) GeometryReader{ geometry in let globalFrame = geometry.frame(in: .global) let localFrame = geometry.frame(in: .local) let nameFrame = geometry.frame(in: .named("GEO")) VStack(alignment:.leading){ Text("글로벌 : \(globalFrame.midX) \(globalFrame.minY)") Text("로컬 : \(localFrame.midX) \(localFrame.minY)") Text("네임 : \(nameFrame.midX) \(nameFrame.minY)") Text("디바이스 : \(mainWidth) \(mainHeight)") } //mainWidth = UIScreen.main.bounds.width //mainWidth = UIScreen.main.bounds.height } } } .coordinateSpace(name: "GEO") }
하나씩 살펴보자
global
GeometryReader안에 존재한다 하더라도 전체 스크린(디바이스 크기) 기준으로부터의 좌표를 나타내고 싶을 때 이 옵션을 선택한다.
눈치 빠른 사람들은 알겠지만 위에 코드와 결과에 따르면 safeArea top의 길이는 총 59였다.
그리고 Circle의 frame 중 height의 길이는 100
그렇다면 전체 스크린에서 safe area 59 + Circle() height 100 = 총 159의 거리만큼이 현제 global 좌표값이다.
local
GeometryReader안을 기준으로 한 좌표를 나타내고 싶을 때 이 옵션을 선택한다.
예제에서 컨테이너는 총 4개의 TextView를 포함하고 있고, 첫 번째 TextView의 시작 좌표가 바로 이 local 좌표값이다. 그러므로 당연히 0부터 시작.
이 옵션의 좌표를 얻는 것은 GeometryReader를 사용하는 이유 중 가장 많은 이유일 것이고, 위의 내용을 이해했다면 당연한 내용임으로 그냥 넘어가겠다.
name
GeometryReader안에 존재한다 하더라도 특정 컨테이너를 정해서 그 컨테이너를 기준으로의 좌표를 나타내고 싶을 때 이 옵션을 선택한다.
예제에서 .coordinateSpace(name: "GEO")라는 옵션은 최 상위뷰의 Zstack에 붙어있다.
그럼 Zstack을 기준으로의 좌표가 되는 것이다.
global 같은 경우 safe area를 포함한 좌표였지만, 기준점을 Zstack부터로 잡았기 때문에 그 컨테이너 안 Circle()의 height 즉, 100의 거리만큼이 현재 "GEO"라는 이름의 커스텀 옵션 좌표값인 것이다.
생각보다 Geometry가 설명하기가 힘들었다. 예제를 직접 만들어보는 과정에서 착오가 많았지만, 이런 식으로 원리부터 이해하고 학습하면 자유자재로 사용할 수 있을 것 같다. 캐러셀이나 스크롤 애니메이션 등 Geometry는 활용도가 굉장히 높은 편이니 꼭 확실히 공부해보자
'iOS > SWIFTUI' 카테고리의 다른 글
[SwiftUI] - @ViewBuilder (0) 2024.07.16 [SwiftUI] #문법 - @ObservedObject (0) 2023.04.14 [SWIFTUI] #문법 - @Binding (1) 2023.01.04 [SWIFTUI] #문법 - @State (0) 2022.10.09