본 포스팅은 Swift 5.2.4 기준으로 작성되었습니다.

이번 포스팅에서는 Button 과 List (UIKit 의 UITableView 와 동일) 를 생성하는 방법을 공부해보도록 할게요. SwiftUI 에서는 Button 과 List 를 구현하는 코드도 단 몇 줄이면 충분합니다. 이게 다 오토레이아웃이 자동 적용되서 그런것 같아요.


Button

이전에 공부할 때 Text 가 View 라고했었으니까 Button 도 당연히 View 의 일종일거라 생각하고 따로 찾아보진 않으려고 했거든요? 근데 그래도 공식문서를 한번쯤 보는게 예의가 아닐까 싶어 찾아보니 View 가 아니였습니다. 안찾아봤으면 어쩔뻔!

Button 자체는 View 가 아니라 Control 이더라고요. 다만 Button 내의 Label 이 Generic 타입으로 구현되어 있고 이 Label 이 View 로 인식이 되는 형태입니다. 뭐 그래서 결국 Button 이 View 의 일종이 되는거긴 하지만 공식문서의 구분에서 control 로 구분하고 있어요.

Apple 공식문서의 Button

버튼 생성은 Text 처럼 parenthesis(괄호) 안에 원하는 타이틀을 입력하고 brace(중괄호) 안에 버튼이 눌렸을 때 동작하기 원하는 코드를 작성하면 됩니다. 일단 우리는 버튼이 잘 동작하는지만 확인하면 되니까 버튼이 눌리면 “Button Pressed” 가 print 되는 코드를 구현해보록 하겠습니다.

var body: some View {
    Button("Button") {
        print("Button Pressed")
    }
}

아래 결과를 보면 이번에도 코드 단 몇 줄만으로 Button 이 중앙에 잘 표시되고 있죠?

버튼이 정상적으로 표시되고 있는 화면

Preview 에서는 단순히 Button 의 위치를 확인하는 것 뿐 아니라 어떤 입력을 받을 때 특정 코드가 실행되는 것을 확인할 수 있습니다.

그렇다고 바로 누르면 되는건 아니고 Preview 를 기준으로 우측 하단에 위치한 두 개의 버튼 중 첫번째 버튼인 Live Preview 를 우클릭하면 좌측 이미지와 같은 창이 하나 뜨는데 이 중 두번째 메뉴인 Debug Preview 를 클릭해 봅시다.

잠시 로딩을 기다린 뒤 정상적으로 Debug Preview 가 활성화 된 상태에서 버튼을 눌러보면!! Button 액션으로 구현된 print 까지 console 에 printing 되는 것을 확인할 수 있습니다.

여기서 추가적으로 궁금증이 생겼는데 Preview 에서 Animation 까지 확인해 볼 수 있는 건지 궁금해졌어요. 이렇게 button action 까지 실행되는 것 보면 가능할 것 같기도 하고요 ㅎㅎㅎ 하지만 오늘은 기초적인 것들만 알아볼거니까 추가로 확인해보진 않을거에요 ㅎ

“Button Pressd” 가 print 되는 모습

대신 버튼에 텍스트말고 이미지와 SF Symbol 을 넣을 수 있는 방법을 알아보도록 할게요.

HStack 을 사용해 Button 2개를 가로로 배치하고 첫번째 버튼에는 SwiftUI 이미지를, 두번째 버튼에는 SF Symbol “play.circle” 아이콘을 넣었어요.

주의해야할 점 하나가 있는데 SwiftUI 에서는 Button 이미지의 renderingMode 기본 값이 .template 으로 되어 있어 다운로드 받은 이미지의 경우 .original 로 변경을 해주어야 이미지가 정상적으로 보이게됩니다. SFSymbol 을 삽입한 경우에는 오히려 기본값이 .template 이라 별 다른 추가작업 없이도 색상이 변경가능하지만요!

var body: some View {
        HStack(spacing: 20) {
            Button(action: { print("Button 1") }) {
                Image("SwiftUI")
                    .renderingMode(.original)
                    .resizable()
                    .frame(width: 120, height: 120)
            }
            Button(action: { print("Button 1") }) {
                Image(systemName: "play.circle")
                    .imageScale(.large)
                    .font(.largeTitle)
            }
            .accentColor(.green)
        }
    }

위 코드를 입력하면 다음과 같은 결과를 확인해 볼 수 있습니다. 참고로 .accentColor 는 UIKit 의 tintColor 의 역할을 대신하며 색상을 바꿔주는 코드에요. 지금은 .green 을 적용해서 오른쪽 play.circle 이미지가 초록색으로 변했습니다.

Button 에 이미지를 삽입


List

List 가 뭘까요? UIKit 에서는 없던 이름이에요. UITableView 가 SwiftUI 에서 List 라는 이름으로 완전히 바뀌었습니다. 왜 굳이 Table 이 아니라 List 라고 했는지는 조금 의아하지만 아무튼 List 도 직관적이고 좋은 것 같아요 ㅎㅎ 근데 이러면 아직 SwiftUI 로는 지원되지 않는 UICollectionView 이름은 어떻게 정해질지 궁금해지네요.

위에서 Button 공식 문서를 봤더니 버튼 자체는 control 이지만 버튼 내부의 Label 이 View 타입이었던 것 기억하시죠? 이처럼 List 자체는 다른 타입이지만 내부의 어떤 것 때문에 View 로 인정되는 것 수 있을 것 같다는 의심이 들어 공식문서를 먼저 살펴보았습니다.

Apple 공식문서의 List

역시나!! List 는 View 가 아니라 data 를 row 의 형태로 나타낼 수 있는 container라고 하네요. 그럼 body 가 어떻게 View 로 받을 수 있느냐… 위 스크린샷에서 Generic 타입으로 SelectionValue 랑 Content 2가지가 있는걸 확인할 수 있는데요. 짤려서 보이지 않지만 이 중 content 가 View 타입입니다.

그럼 List 를 생성해볼게요.

var body: some View {
    List{
        Text("1")
    }
}

네 이게 끝입니다. 정말로 List 가 생성이 되었다구요! UITableView 를 만들어보신 분들은 이게 얼마나 놀랍도록 간단한건지 느낄거에요!!

List 가 생성된 모습

이전에 없던 옅은 선들이 보이시죠? 이게 바로 List 입니다. List 역시 Button 과 같은 방법으로 Preview 에서 스크롤을 실험해 볼 수 있습니다.

그런데 그냥 이렇게 빈 칸으로 두면 허전하니까 각 줄에 데이터를 몇가지 넣어보도록 할게요. List brace 안쪽으로 원하는 View 를 구현하면 각 instance 가 순서대로 List 의 row 에 추가되게 됩니다. 한번 확인해볼까요?

var body: some View {
    List {
        Text("List").font(.largeTitle)
        Image("SwiftUI")
        Circle().frame(width: 100, height: 100)
        Color(.red).frame(width: 100, height: 100)
    }
}

위 코드를 입력해보세요.

Result

정말 그냥 적힌 순서대로 각 instance 가 List 의 row 에 추가됩니다. 다만 최대로 입력할 수 있는 instance 의 갯수는 10개라는거 기억하세요

당연히 데이터가 10개를 넘더라도 정상적으로 표시할 수 있는 방법이 있습니다.

var body: some View {
    List(0..<100) {
        Text("\($0)")
    }
}

위 코드를 입력해보세요. 이런식으로 원하는 데이터를 마음껏 넣어줄 수 있습니다.

Result

마지막으로 UITableView 하면 빼놓을 수 없는 것 중 Header 와 Footer 가 있었죠? 얘네를 List 에서 추가할 수 있는 방법을 알아보겠습니다.

List {
        Section (
            header: Text("Header1"),
            footer: Text("Footer1")
        ) {
            Text("1")
            Text("2")
            Text("3")
            Text("SwiftUI")
        }
        Section (
            header: Text("Header2"),
            footer: HStack { Spacer(); Text("Footer2") }
        ) {
            Text("Section2")
            Text("SwiftUI")
        }
    }
    .listStyle(GroupedListStyle())
}

List 내부에 Section 항목을 먼저 추가하고 header 와 footer 를 입력합니다. 그 다음 brace 를 열어 해당 Section 에 속하는 데이터들을 입력해주면 끝입니다. header 와 footer 는 기본적으로 좌정렬 되어 있으니까 우측에 표시하고 싶을 때는 HStack 을 사용해 Spacer 를 추가한 뒤 Text 를 입력해주면 됩니다.

추가적으로 .listStyle 이라는 코드가 보이죠? 얘는 GroupedListStyle 또는 DefualtListStyle 중에 선택할 수 있고, 결과에 따라 header 나 footer 가 List 가 스크롤 될 때 함께 스크롤 될지 아니면 남아있을지 설정할 수 있는 옵션입니다. 직접 한번 실험 해보시면 확실히 이해가 되실거에요.

Result

이렇게 기본적인 List 의 생성 방법을 알아보았어요. UIKit 과 많이 달라진 방식이 아직은 서툴지만 또 쓰다보면 금방 익숙해질거에요. 그러니까 SwiftUI 도 미리미리 같이 공부해보자구요~~