Devlog
article thumbnail

위 글은 유튜브 정대리님의 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)
        
        
    }
}

좌) 아이폰12 미니, 우) 아이폰12  프로 맥스

다른 핸드폰 기종으로 화면의 크기가 달라 같은 코드지만 다르게 보여줌

모든 디바이스에 호환이 되지 않음

 

 

 

 

 

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)
        
    }
}

좌) 아이폰12 미니, 우)아이폰12 프로 맥스
아이패드 프로 12.9인치

 

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))

 

 

 

아이폰 12미니 -> 아이폰 12 프로 맥스

profile

Devlog

@덩이

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!

검색 태그