[SwiftUI] ScrollView와 GeometryReader를 사용하여 화면 안에 공간 구현하기

Laav·2022년 3월 31일
1

SwiftUI

목록 보기
9/9
post-thumbnail

앱 화면은 생각보다 크지 않고, 모든 내용을 한 화면에 담을 수 없기에 우리는 '스크롤' 이라는 것을 사용한다. 우리에게 친숙한 것은 위아래, 즉 수직 스크롤일 것이다. 거의 대부분의 앱이나 웹 페이지가 채택하고 있는 방식이기도 하다.

그렇다면, 수직과 수평 스크롤을 사용해서 앱 내에 공간을 만들 수는 없을까?

사실 이게 정말 실용적일지는 모르겠지만, ScrollView와 GeometryReader에 대해 알아보고, 이를 적절히 적용하여 앱 내에서 한 번 구현해보자.

📌 ScrollView & GeometryReader

ScrollView

ScrollView 란, 스마트폰을 써본 사람이라면 모두가 아는 스크롤 화면을 구현하는 친구이다.

사용법은 아주아주 간단하다.

스크롤이 필요할 것 같은 View를, ScrollView로 감싸주면 끝이다.

예를 들어, 다음과 같은 코드와 화면을 보자.

struct ContentView: View {
    
    var body: some View {
        
        VStack {

            Rectangle().frame(height: 500).foregroundColor(.red)
            
            Rectangle().frame(height: 500).foregroundColor(.blue)
            
            Rectangle().frame(height: 500).foregroundColor(.green)
            
        } // VStack
    }
}

코드에서 정의된 3가지 사각형이 화면에 모두 표시되지 않는 것을 볼 수 있다.

그럼, ScrollView로 묶은 뒤에 결과를 보자.

struct ContentView: View {
    
    var body: some View {
        
        // ScrollView 사용
        ScrollView {
        
            VStack {

                Rectangle().frame(height: 500).foregroundColor(.red)
                
                Rectangle().frame(height: 500).foregroundColor(.blue)
                
                Rectangle().frame(height: 500).foregroundColor(.green)
                
            } // VStack
        } // ScrollView
    }
}

만약 수평 스크롤을 사용하고 싶다면, 아래와 같이 기본값이 .vertical로 설정되어 있는 방향값을 .horizontal로 주면 된다.

(살짝 예습을 해보면, 방향값을 [.vertical, .horizontal] 로 준다면 수직과 수평 스크롤을 모두 사용할 수 있을 것이다.)

struct ContentView: View {
    
    var body: some View {
        
        // 수평 스크롤
        ScrollView(.horizontal) {
        
            HStack {

                Rectangle().frame(width: 500).foregroundColor(.red)
                
                Rectangle().frame(width: 500).foregroundColor(.blue)
                
                Rectangle().frame(width: 500).foregroundColor(.green)
                
            } // VStack
        } // ScrollView
    }
}


GeometryReader

이번에는 GeometryReader 에 대해 간단하게 알아보자.

SwiftUI에서는, HStack/VStack/ZStack을 적절히 사용하여 특정 View를 원하는 위치에 놓을 수 있다.

하지만, 어떠한 상황에서는 직접 놓을 자리를 설정해야 하는 경우도 있는데, 이때 GeometryReader 가 사용된다.

GeometryReader는 상위 View에서 자신에게 넘어온 크기의 View 안에서 GeometryReader안의 View를 특정한 위치에 놓을 수 있다.

아래의 예시를 보자.

struct ContentView: View {
    
    var body: some View {
        
        VStack {
            
            Spacer().frame(height: 300)
            
            Divider()
            
            GeometryReader { geo in
                
                Rectangle().frame(width: geo.size.width / 4, height: geo.size.height / 4)
                    .foregroundColor(.blue)
                    .position(x: geo.size.width / 2, y: geo.size.height / 2)
                
            }
            
        } // VStack
    }
}

코드를 보면, 화면 위쪽에는 높이가 300인 빈 공간이 있고 Divider 선 밑의 공간을 GeometryReader로 전달하고 있다.

GeometryReader 안에서는, 전달받은 View에서 View의 크기의 4분의 1만한 파란 정사각형을 만들고, 이를 View의 정중앙에 위치시키고 있다.

결과는 아래의 화면과 같다.

이렇게, GeometryReader는 그 자체로 View이며 Parent View에게 할당받은 크기 내에서 자신의 Child View들을 원하는 위치에 놓을 수 있다.


💻 화면 안의 공간

그럼, 이제 ScrollView와 GeometryReader를 응용하여 화면 안에 커다란 공간을 만들어보자.

ScrollView 에는 위에서 잠깐 언급한 것처럼 방향값을 [.vertical, .horizontal] 로 설정하면 될 것이고, GeometryReader 에서는 View의 크기를 화면보다 큰 (1000, 1000) 으로 설정할 것이다.

코드에 적용하기

우선, 크기가 (100, 100)인 카드를 하나 정의해준다.

str은 카드 위에 쓰일 문구이고, xy는 각각 GeometryReader 안에서 위치를 나타낼 x좌표와 y좌표이다.

struct Card: View {

    var str: String
    var x: Int
    var y: Int

    var body: some View {

        ZStack {

            Rectangle().frame(width: 100, height: 100).foregroundColor(.orange).position(CGPoint(x: x, y: y))

            Text(str)
                .font(.system(size: 40, weight: .bold))
                .position(CGPoint(x: x, y: y))

        } // ZStack
    }
}

이제, 배경색을 설정한 화면에서 방향값을 [.vertical, .horizontal] 로 설정한 ScrollView 안에 프레임 크기가 (1000, 1000)인 GeometryReader를 만들어준다.

추가로, ScrollView에서 "showsIndicators" 값을 false로 설정하면 스크롤 바를 없앨 수도 있다.

카드는 숫자에 따라,

  1. 왼쪽 위
  2. 오른쪽 위
  3. 정중앙
  4. 왼쪽 아래
  5. 오른쪽 아래

에 위치시켜주겠다.

코드와 결과를 보자.

struct ContentView: View {
    
    var body: some View {
        
        ZStack {

            Color(.lightGray)
            .edgesIgnoringSafeArea(.all)

            VStack {

                ScrollView([.vertical, .horizontal], showsIndicators: false) {

                    GeometryReader { geo in

                        Card(str: "1", x: 100, y: 100)

                        Card(str: "2", x: 900, y: 100)

                        Card(str: "3", x: Int(geo.size.width) / 2, y: Int(geo.size.height) / 2)

                        Card(str: "4", x: 100, y: 900)

                        Card(str: "5", x: 900, y: 900)


                    } // GeometryReader
                    .frame(width: 1000, height: 1000)

                } // ScrollView
            } // VStack
        } // ZStack
    }
}

전체 코드는 다음과 같다.

import SwiftUI

struct ContentView: View {
    
    var body: some View {
        
        ZStack {

            Color(.lightGray)
            .edgesIgnoringSafeArea(.all)

            VStack {

                ScrollView([.vertical, .horizontal], showsIndicators: false) {

                    GeometryReader { Geo in

                        Card(str: "1", x: 100, y: 100)

                        Card(str: "2", x: 900, y: 100)

                        Card(str: "3", x: Int(Geo.size.width) / 2, y: Int(Geo.size.height) / 2)

                        Card(str: "4", x: 100, y: 900)

                        Card(str: "5", x: 900, y: 900)


                    } // GeometryReader
                    .frame(width: 1000, height: 1000)

                } // ScrollView

            } // VStack

        } // ZStack
    }
}

struct Card: View {

    var str: String
    var x: Int
    var y: Int

    var body: some View {

        ZStack {

            Rectangle().frame(width: 100, height: 100).foregroundColor(.orange).position(CGPoint(x: x, y: y))

            Text(str)
                .font(.system(size: 40, weight: .bold))
                .position(CGPoint(x: x, y: y))

        } // ZStack
    }
}

📌 마치며

GeometryReader는 이렇게만 보면 매우 강력한 도구 같지만, 사실은 탈이 많은 친구이다. 그렇기 때문에 상위 View에서 꼭 필요할 때만 사용하도록 하자.

ScrollView와 Geometryreader를 알아보고 응용해보았다.
어떻게 보면 화면을 구성하는데 있어서 중요한 View들이니 잘 알아두면 좋을 것 같다.

printf("Thank You!\n");
printf("Posted by Thirsty Developer\n");
profile
iOS 왕초보

0개의 댓글