본문 바로가기
2023년 이전/iOS

SwiftUI - View

by JeongUPark 2020. 10. 4.
반응형

다음 내용들은 [핵심만 골라배우는 SwiftUI 기반 iOS 프로그래밍] 을 공부하면서 정리한 내용 입니다.

 

사이드프로젝트를 진행하면서 iOS를 담당하게 되었고, iOS14가 나오면서 SwiftUI를 메인으로 해주는 느낌이 들어 SwiftUI로 작업을 하게 되었습니다.

 

그래서 SwiftUI에 대해 AppleDeveloper에 있는 tutorial로는 부족한 듯하여 책을 사서 따로 공부를 하게 되었고 그 내용을 정리하였습니다.

 

SwiftUI로 프로젝트르 만들면 LifeCycle 선택에 UIKit App Delegate와 SwfitUI App을 선택할 수 있는데 (Xcode12) 저는 우선 책과 똑같이하기 위해 UIKit App Delegate를 선택 하였습니다.

 

프로젝트 생성시 생성되는 파일들에 대해서는 다음에 정리하기로 하겠습니다.

 

1. 뷰(View)

SwitfUI에서 가장 중요하가 가장 먼저 이해해야 할 단계는 뷰(View)라는 용어 입니다. SwiftUI에서 뷰는 View 프로토콜을 따르는 구조체로 선언되고, 그 View 프로토콜을 따르기 위해서는 body 프로퍼티를 가지고 있어야 하며, 그 안에 뷰가 선언되어야 합니다.

 

프로젝트를 생성하면 다양한 파일이 생성되는되 그 중에서 ContentView라는 파일이 있습니다.

 

그 파일을 보면

import SwiftUI

struct ContentView: View {
    var body: some View {
        Text("Hello, world!")
            .padding()
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

이렇게 되어있고, body 안에 Text가 있는 것을 볼 수 있습니다. 그래서 xcode 우측에 preview를 resume 을 누르면 

이렇게 미리 보기를 볼 수 있는데, 그 이유는 ContentView의 하단에 있는 PreviewProvider가 설정되어 프리뷰를 볼 수 있습니다.

 

2. 뷰 추가

위 ContentView에 Text를 추가해보겠습니다.

struct ContentView: View {
    var body: some View {
        Text("Hello, world!")
            .padding()
        Text("Hello, world!")
            .padding()
    }
}

이렇게 추가 할 경우

이렇게 프리뷰에 2개의 폰이 만들어지고, 프리뷰를 실행하여도 Hellow, world! 가 1줄만 보입니다.

그 이유는 body 프로퍼티는 단 1개의 뷰를 반환하도록 구성되어 있기 때문입니다.

그렇기 때문에 새로운 뷰를 추가하기 위해서는 VStack , HStack, ZStack같은 Stack이나 From 같은 컨테이너 뷰들을 배치해야 합니다.

(사실 body는 이렇게도 작성 할 수 있기 때문입니다.

struct ContentView: View {
    var body: some View {
        return
            Text("Hello, world!")
    }
}

 그래서 1개의 view만 반환 하는 것입니다.)

그래서 Textview를 추가하기 위해서

struct ContentView: View {
    var body: some View {
        VStack{
        Text("Hello, world!")
            .padding()
        Text("Hello, world!")
            .padding()
        }
    }
}

이렇게 하면

세로로 2줄 Hello, world!를 확인 할 수 있습니다.

 

위 방법 이외에도

struct ContentView: View {
    var body: some View {
        Text("Hello, ") + Text("world!") + Text(" Hello, world!")
            
    }
}

이렇게도 작업 할 수 있습니다.

3. 하위 뷰

만일 컨테이너 안에 들어가야할 내용들이 너무 크고 복잡하다면 다음과 같이 하위 뷰로 나눌수 있는 부분을 찾아 나누면 좋습니다

아래와 같이 뷰가 생성되었고 

struct ContentView: View {
    var body: some View {
        VStack{
            Text("Hello, world! - 1")
            Text("Hello, world! - 2")
            HStack{
                Text("Hello, world! - 3")
                Text("Hello, world! - 4")
            }
        }
    }
}

HStack 부분을 하위뷰로 나눈다면

struct ContentView: View {
    var body: some View {
        VStack{
            Text("Hello, world! - 1")
            Text("Hello, world! - 2")
            SubView()
        }
    }
}

struct SubView : View{
    var body: some View{
        HStack{
            Text("Hello, world! - 3")
            Text("Hello, world! - 4")
        }
    }
}

이렇게 작업을 할 수 있습니다.

또한, 위 작업을 수작업말고도 다음과 같이 진행 할 수 있습니다.

HStack 부분을 command 클릭하면 위와 같은 메뉴가 나타나고 Extract Subview를 선택하면

위와 같이  SubView 가 생성되고 이름을 지정해 주면 됩니다.

4. 프로퍼티로서 뷰

하위뷰 뿐만 아니라 프로퍼티로 뷰를 만들수 있습니다.

import SwiftUI

struct ContentView: View {
    
    let subview = HStack{
        Text("Hello, world! - 3")
        Text("Hello, world! - 4")
    }
    var body: some View {
        VStack{
            Text("Hello, world! - 1")
            Text("Hello, world! - 2")
            subview
        }
    }
}

 

5.뷰 변경하기

SwiftUI에서 제공하는 모든 뷰는 수정자(Modifer)를 사용하여 뷰의 모양과 동작을 변경할 수 있습니다.

이들 수정자는 뷰의 인스턴스에서 호출되는 메서드 형태를 취하며 원래 뷰를 다른 뷰로 깜싸는 방식으로 필요한 변경을 합니다. 

예를 들어 

Text("Hello, world! - 1")

 Text("Hello, world! - 1").font(.headline).foregroundColor(.red)

이렇게 변경하거나

Image(systemName: "car.fill")

이 Image를

Image(systemName: "car.fill").resizable().aspectRatio(contentMode: .fit)

이렇게 변경 할 수 있습니다.

그리고 원하는 Text 타입이 없을 경우 커스텀 폰트를 적요하는 수정자도 있습니다. 예를 들어

Text("Simple Text")

Text("Simple Text").font(.custom("Copperplate", size: 50))

이렇게 간지나개 수정 할 수 있습니다.

 

거기다 위에서 설명한 하위 뷰나 프로퍼티 뷰 역시 수정자로 변경 가능 합니다.

 

6. 수정자 순서

수정자를 적용할 때 그 적용 순서가 중요합니다.  다음 예를 보면서 이해해 보겠습니다.

Text("Simple Text").border(Color.black).padding()

텍스트에 외각선을 만들고 padding을 주면 외각선 밖으로 padding이 생성됩니다.

반면, 다음과 같이 padding을 먼저 주면

Text("Simple Text").padding().border(Color.black)

이렇게 텍스트 주변으로 padding이 생기고 그 다음에 외곽선이 발생합니다.

 

7. 커스텀 수정자

            Text("Simple Text")
                .font(.largeTitle)
                .background(Color.yellow)
                .border(Color.black)
                .shadow(color: .black, radius: 5, x: 5, y: 5)

위와 같이 Text에 다양한 수정자를 줘서 Text를 만들 었습니다.

위와 같은 형태를 여러개 만들어야할 때 커스텀 수정자로 위 수정자들을 묶어서 필요할 때마다 참조하는 방법이 있습니다.

import SwiftUI

struct ContentView: View {
    
    var body: some View {
        VStack{
            Text("Simple Text")
                .modifier(customModifer()).padding()
            
            Text("Simple Text - 2 ")
                .modifier(customModifer())
        }
    }
}
struct customModifer : ViewModifier {
    func body(content : Content) -> some View{
        content.font(.largeTitle)
            .background(Color.yellow)
            .border(Color.black)
            .shadow(color: .black, radius: 5, x: 5, y: 5)
    }
}

ViewModifier를 만들어서 공통으로 적용할 수정자를 만들고, .modifier(생성한 커스텀 수정자())를 적용하면 여러개의 뷰에 동일한 수정자를 적용할 수 있습니다.

8. Button 이벤트 처리

기본적으로 SwiftUI에서 Button 이벤트 처리는

            Button(action: {
                // 동작 코드 혹은 함수
            }, label: {
                Text("Button")
            })

이렇게 하여 버튼을 눌렸을 때 action의 동작이 발생 합니다.

 

9.onAppear 메서드 와 onDisappear 메서드

onApper 메서드와 onDisapper 메서드를 사용하여 뷰가 나타날때 혹은 뷰가 사라질 때에 수행을 적용할 수 있습니다.

            Text("Simple Text")
                .onAppear(perform: {
                    // 뷰가 나타날떄 수행 할 코드
                }).onDisappear(perform: {
                    // 뷰가 사라질 때 수행 할 코드
                })

 

10. 커스텀 컨테이너 뷰 만들기

하위 뷰는 뷰 선언부를 작고 가벼우며 재사용할 수 있는 블록으로 나누는 유용한 방법을 제공합니다. 하지만 하위 뷰가 레이아웃에 포함되는 시점에 하위 뷰에 포함될 뷰를 동적으로 지정할 수 없다는 한계가 있습니다. 하위 뷰에 포함되는 뷰들은 최초 선어부에지정된 하위 뷰들 뿐입니다.

예를 들어

struct MyVStack : View{
    var body: some View{
        VStack(spacing: 10){
            Text("Text Item 1")
            Text("Text Item 2")
            Text("Text Item 3")
        }.font(.largeTitle)
    }
}

MyStack 뷰가 있고, 이를 MyStack()으로 적용하게 될 것입니다.

그런데 간격이 10이며, 폰트가 largeTitle인 VStack이 프로젝트 내에서 자주 사용되는데, 사용되는 곳마다 서로 다른 뷰드이 여기에 담겨야 한다고 가정하며, 하위 뷰들은 처음 선언으로 고정이 되기 때문에 사용이 될 수 없습니다. 그래서 이때 커스텀  컨테이너 뷰를 만들어서 그 조건을 만족시킬 수 있습니다.

ViewBuilder 속성을 사용하여 커스텀 컨테이너 뷰를 만들수 있습니다. ViewBuilder는 스위프트 클로저 형태를 취하며 여러 하위 뷰로 구성된 커스텀 뷰를 만드는데 사용되며, 이 뷰가 레이아웃 선언부 내에 사용 될 때 까지 내용을 선언할 필요가 없습니다.

 

import SwiftUI

struct ContentView: View {
    
    var body: some View {
        MyVStack{
            Text("Itemt 1")
            Text("Itemt 2")
            HStack{
                Image(systemName: "star.fill")
                Image(systemName: "star.fill")
                Image(systemName: "star")
            }
        }
    }
}
struct MyVStack<Content : View> : View{
    let content: () -> Content
    init(@ViewBuilder content : @escaping () -> Content){
        self.content = content
    }
    var body: some View{
        VStack(spacing: 10){
            content()
        }.font(.largeTitle)
    }
}

반응형