위 글은 유튜브 정대리님의 SwiftUI fundamental Tutorial 강좌를 보고 작성한 정리글로
자세한 내용은 유튜브를 통해 확인하시길 권장합니다.
이전까지 VStack, HStack을 활용해서 View를 구성하였음
이는 핸드폰 디바이스 크기에 따라 깨질 수도 있음
이때 GeometryReader를 이용하여 전체 View에 대한 비율을 정할 수 있음
VStack이나 HStack을 GeometryReader로 감싸주면 됨
이전 같이 하드코딩 작성
import SwiftUI
struct MyGeometryReader: View{
var body: some View{
HStack(spacing: 0){
Text("1")
.font(.largeTitle)
.fontWeight(.black)
.frame(width: 100)
.foregroundColor(.white)
.background(Color.red)
Text("2")
.font(.largeTitle)
.fontWeight(.black)
.frame(width: 100)
.foregroundColor(.white)
.background(Color.blue)
Text("3")
.font(.largeTitle)
.fontWeight(.black)
.frame(width: 100)
.foregroundColor(.white)
.background(Color.green)
Text("4")
.font(.largeTitle)
.fontWeight(.black)
.frame(width: 100)
.foregroundColor(.white)
.background(Color.purple)
}
.background(Color.yellow)
}
}
다른 핸드폰 기종으로 화면의 크기가 달라 같은 코드지만 다르게 보여줌
모든 디바이스에 호환이 되지 않음
GeometryReader 적용하기
import SwiftUI
struct MyGeometryReader: View{
var body: some View{
GeometryReader{ geometryReader in
HStack(spacing: 0){
Text("1")
.font(.largeTitle)
.fontWeight(.black)
.frame(width: geometryReader.size.width/4)
.foregroundColor(.white)
.background(Color.red)
Text("2")
.font(.largeTitle)
.fontWeight(.black)
.frame(width: geometryReader.size.width/4)
.foregroundColor(.white)
.background(Color.blue)
Text("3")
.font(.largeTitle)
.fontWeight(.black)
.frame(width: geometryReader.size.width/4)
.foregroundColor(.white)
.background(Color.green)
Text("4")
.font(.largeTitle)
.fontWeight(.black)
.frame(width: geometryReader.size.width/4)
.foregroundColor(.white)
.background(Color.purple)
}.background(Color.yellow)
}.background(Color.black)
}
}
GeometryReader{ 변수명 in
내용
}
아이패드를 포함한 모든 디바이스에서 비율적으로 계산해서 넣을 수 있음
강의랑 코드 똑같은데 왜 가운데가 아니라 상단에 Stack이 생기는지 모르겠음 ㅠ
📌 정렬의 차이가 나는 이유 iOS 13버전과 14버전의 GeometryReader의 동작 방식의 차이
13.x 버전에서는 GeometryReader의 Text가 기본적으로 가운데 위치시키지만 14.x버전에서는 Text를 왼쪽 상단 모서리에 위치시켜줌
그 이유는 Xcode 12 beta 3 release note에서 찾을 수 있었다.
Resolved in Xcode 12 beta
Rebuilding against the iOS 14, macOS 11, watchOS 7, and tvOS 14 SDKs changes uses of GeometryReader to reliably top-leading align the views inside the GeometryReader. This was the previous behavior, except when it wasn’t possible to detect a single static view inside the GeometryReader. (59722992) (FB7597816)
즉 top-leading 정렬이 정상 동작이며, GeometryReader가 단일 정적 뷰를 내부 뷰로 가져갈 경우 이를 감지 못하던 이슈(가운데 정렬시키던)가 해결되었다고 언급하고 있다.
출처: https://protocorn93.github.io/2020/07/26/GeometryReader-in-SwiftUI/
GeometryReader in SwiftUI
SwiftUI를 공부하다 보면 자연스레 등장하는 개념이 바로 GeometryReader다. 어렴풋이 이해하고 있던 개념을 포스팅을 통해 정리해보려 한다. What is GeometryReaderGeometryReader란 무엇인가? SwiftUI에선 UIKit으
protocorn93.github.io
각기 다른 비율로 정해줄 수도 있음
import SwiftUI
struct MyGeometryReader: View{
var body: some View{
GeometryReader{ geometryReader in
HStack(spacing: 0){
Text("1")
.font(.largeTitle)
.fontWeight(.black)
.frame(width: geometryReader.size.width/4)
.foregroundColor(.white)
.background(Color.red)
Text("2")
.font(.largeTitle)
.fontWeight(.black)
.frame(width: geometryReader.size.width/2)
.foregroundColor(.white)
.background(Color.blue)
}.background(Color.yellow)
}.background(Color.black)
}
}
SwiftUI의 GeometryReader는 안드로이드의 WeightSum이랑 같다고 보면 됨
GeometryReader 응용하기
처음 작성한 코드
import SwiftUI
struct MyGeometryReader: View{
@State
private var isActivated: Bool = false
var body: some View{
GeometryReader{ geometryReader in
VStack(spacing: 0){
Text("1")
.font(.largeTitle)
.fontWeight(.black)
.frame(width: isActivated ? geometryReader.size.width/3 : geometryReader.size.width/10, height: geometryReader.size.height/3)
.foregroundColor(.white)
.background(Color.red)
// .onTapGesture {
// self.isActivated.toggle()
// }
Text("2")
.font(.largeTitle)
.fontWeight(.black)
.frame(width: isActivated ? geometryReader.size.width/3 : geometryReader.size.width/10, height: geometryReader.size.height/3)
.foregroundColor(.white)
.background(Color.blue)
// .onTapGesture {
// self.isActivated.toggle()
// }
Text("3")
.font(.largeTitle)
.fontWeight(.black)
.frame(width: isActivated ? geometryReader.size.width/3 : geometryReader.size.width/10, height: geometryReader.size.height/3)
.foregroundColor(.white)
.background(Color.green)
// .onTapGesture {
// self.isActivated.toggle()
// }
}
.background(Color.yellow)
//가운데 정렬을 위한 구문
.frame(width: geometryReader.size.width, height: geometryReader.size.height, alignment: .center)
}
.background(Color.yellow)
.onTapGesture {
withAnimation{
self.isActivated.toggle()
}
}
}
}
각기 다르게 적용되야 하는데 통째로 적용됨 ㅜㅜ
수정한 코드
import SwiftUI
enum Index{
case one, two, three
}
struct MyGeometryReaderStack: View{
@State
private var index: Index = .one
var body: some View{
GeometryReader{ geometry in
VStack(spacing: 0){
Button(action: {
print("1번")
withAnimation{
self.index = .one
}
}){
Text("1")
.font(.largeTitle)
.fontWeight(.black)
.frame(width: 100, height: geometry.size.height/3)
.padding(.horizontal, self.index == .one ? 50 : 0)
.foregroundColor(.white)
.background(Color.red)
}
Button(action: {
print("2번")
withAnimation{
self.index = .two
}
}){
Text("2")
.font(.largeTitle)
.fontWeight(.black)
.frame(width: 100, height: geometry.size.height/3)
.padding(.horizontal, self.index == .two ? 50 : 0)
.foregroundColor(.white)
.background(Color.blue)
}
Button(action: {
print("3번")
withAnimation{
self.index = .three
}
}){
Text("3")
.font(.largeTitle)
.fontWeight(.black)
.frame(width: 100, height: geometry.size.height/3)
.padding(.horizontal, self.index == .three ? 50 : 0)
.foregroundColor(.white)
.background(Color.green)
}
}
.frame(width: geometry.size.width, height: geometry.size.height, alignment: .center)
}
.background(Color.yellow)
.edgesIgnoringSafeArea(.all)
}
}
enum 타입의 Index 변수 생성, case로 구분
디폴트: private var index: Index = .one
버튼 클릭을 하면 index 값이 바뀌고 해당 버튼의 padding을 눌려주도록 구현
번외: GeometryProxy
iOS 14.x 버전 이후 포지션을 따로 지정해줘야 함
import SwiftUI
enum index{
case one, two, three
}
struct MyGeometryReader: View{
@State
private var index: index = .one
var body: some View{
GeometryReader{ proxy in
VStack(spacing: 0){
Button(action: {
print("1번")
withAnimation{
self.index = .one
}
}){
Text("1")
.font(.largeTitle)
.fontWeight(.black)
.frame(width: 100, height: proxy.size.height/3)
.padding(.horizontal, self.index == .one ? 50 : 0)
.foregroundColor(.white)
.background(Color.red)
}
Button(action: {
print("2번")
withAnimation{
self.index = .two
}
}){
Text("2")
.font(.largeTitle)
.fontWeight(.black)
.frame(width: 100, height: proxy.size.height/3)
.padding(.horizontal, self.index == .two ? 50 : 0)
.foregroundColor(.white)
.background(Color.blue)
}
Button(action: {
print("3번")
withAnimation{
self.index = .three
}
}){
Text("3")
.font(.largeTitle)
.fontWeight(.black)
.frame(width: 100, height: proxy.size.height/3)
.padding(.horizontal, self.index == .three ? 50 : 0)
.foregroundColor(.white)
.background(Color.green)
}
}.position(CGPoint(x: proxy.frame(in: .local).midX, y: proxy.frame(in: .local).midY))
}
.background(Color.yellow)
.edgesIgnoringSafeArea(.all)
}
}
코드를 좀 더 간결하게 작성하는 방법
import SwiftUI
enum index{
case one, two, three
}
struct MyGeometryReader: View{
@State
private var index: index = .one
let centerPosition : (GeometryProxy) -> CGPoint = { proxy in
return CGPoint(x: proxy.frame(in: .local).midX,
y: proxy.frame(in: .local).midY)
}
var body: some View{
GeometryReader{ proxy in
VStack{
Button(action: {
print("1번")
withAnimation{
self.index = .one
}
}){
Text("1")
.font(.largeTitle)
.fontWeight(.black)
.frame(width: 100, height: proxy.size.height/3)
.padding(.horizontal, self.index == .one ? 50 : 0)
.foregroundColor(.white)
.background(Color.red)
}
Button(action: {
print("2번")
withAnimation{
self.index = .two
}
}){
Text("2")
.font(.largeTitle)
.fontWeight(.black)
.frame(width: 100, height: proxy.size.height/3)
.padding(.horizontal, self.index == .two ? 50 : 0)
.foregroundColor(.white)
.background(Color.blue)
}
Button(action: {
print("3번")
withAnimation{
self.index = .three
}
}){
Text("3")
.font(.largeTitle)
.fontWeight(.black)
.frame(width: 100, height: proxy.size.height/3)
.padding(.horizontal, self.index == .three ? 50 : 0)
.foregroundColor(.white)
.background(Color.green)
}
}
.position(centerPosition(proxy))
}
.background(Color.yellow)
.edgesIgnoringSafeArea(.all)
}
}
GeometryProxy를 매개변수로 가지고CGPoint를 반환하는 클로저 사용
let centerPosition : (GeometryProxy) -> CGPoint = { proxy in
return CGPoint(x: proxy.frame(in: .local).midX,
y: proxy.frame(in: .local).midY)
}
.position()부분에 centerPosition을 대입하고 proxy를 매개변수로 사용하면 됨: .position(centerPosition(proxy))
'강의 > etc' 카테고리의 다른 글
[SwiftUI fundamental Tutorial] Custom TabView (0) | 2021.09.01 |
---|---|
[SwiftUI fundamental Tutorial] TabView (0) | 2021.08.31 |
[SwiftUI fundamental Tutorial] Navigation View (0) | 2021.08.25 |
[SwiftUI fundamental Tutorial] List (0) | 2021.08.23 |
[SwiftUI fundamental Tutorial] Layout (0) | 2021.08.23 |