본문 바로가기
Apple/SwiftUI

[iOS] Apple의 Introducing SwiftUI 공부하기 2

by 어멘드 2023. 7. 26.
반응형

1편은 여기에서 확인할 수 있다.

 

[iOS] Apple의 Introducing SwiftUI 공부하기 1

애플 개발자 문서에서 제공하는 튜토리얼인 Introducing SwiftUI를 읽으면서 SwiftUI를 공부해보자. Introducing SwiftUI | Apple Developer Documentation SwiftUI is a modern way to declare user interfaces for any Apple platform. Create

please-amend.tistory.com

 오늘은 두번째 소챕터 Building Lists and Navigation을 공부해보자.

1. SwiftUI Essentials
    a. Creating and Combining Views
    b. Building Lists and Navigation
    c. Handling User Input
2. Drawing and Animation
    a. Drawing Paths and Shapes
    b. Animating Views and Transitions
3. App Design and Layout
    a. Composing Complex Interfaces
    b. Working with UI Controls
4. Framework Integration
    a. Interfacing with UIKit
    b. Creating a watchOS App
    c. Creating a macOS App

 

Section 1

이전 편에서는 MapView에서 region 정보를 하드코딩했다. 이제 모델을 만들어 뷰에 전달하도록 개선해보자.

먼저 다운받은 리소스에서 landmarkData.json 파일을 프로젝트에 추가한다. 그리고 Landmark.swift 파일을 생성한다. 아래와 같이 Landmark 구조체를 선언한다. json 파일을 모델로 변환하기 위해 Codable 프로토콜을 채택한다.

다음으로 리소스의 모든 이미지들을 에셋으로 추가해준다. 그리고 Landmark 구조체로 다시 돌아와서, ImageName 프로퍼티와 image라는 연산 프로퍼티를 추가한다. ImageName은 이름 그대로 랜드마크에 해당하는 이미지의 이름을 저장하고, image는 이미지 이름을 가지고 해당하는 이미지를 에셋에서 로드하여 반환한다.

이제 좌표 정보를 담는 프로퍼티 coordinates를 추가해주자. 경도와 위도를 담는 구조체 Coordinates를 Landmark 안에 중첩타입으로 선언해준다. 그리고 private 프로퍼티로 Coordinates 타입의 coordinates를 선언한다.

MapKit과 상호작용하기 위한 CLLocationCoordinate2D 타입의 locationCoordinate 프로퍼티를 추가하자. coordinates 프로퍼티를 사용하여 CLLocationCoordinate2D를 생성하여 반환하도록 연산 프로퍼티로 구현한다. CoreLocation을 임포트해주는 것을 잊지 말자.

이제 json 데이터를 Landmark 모델로 변환할 시간이다. ModelData.swift라는 새 파일을 생성하여 load(_:) 메소드를 만들자. load(_:)메소드는 앱의 메인 번들에서 주어진 파일명의 json 데이터를 찾아 지정한 Decodable 타입으로 디코드하는 메소드이다. 그리고 해당 메소드를 사용하여 landmarkData.json 파일로부터 Landmark 배열을 만들자.

 

Section 2

랜드마크 목록에서 각 행에 해당하는 뷰를 만들어보자.

LandmarkRow.swift 라는 이름의 SwiftUI 뷰 파일을 생성한다. 해당 행에 표시할 랜드마크 모델을 landmark라는 프로퍼티로 선언해준다. 그리고 랜드마크 이미지와 이름이 가로방향으로 나란히 나오도록 HStack에 담아준다. 왼쪽 정렬을 위해 Spacer를 마지막에 추가해준다.

 여기까지 완료했으면 프리뷰 프로바이더에서 에러가 표시될 것이다. LandmarkRow 인스턴스를 만들기 위해서는 landmark 프로퍼티를 초기화해주어야 하기 때문이다. 첫번째 랜드마크인 landmarks[0]로 초기화해서 프리뷰를 확인해보면, 이렇게 의도한 레이아웃이 잘 잡힌 것을 볼 수 있다.

 

Section 3

프리뷰 캔버스에서 뷰를 원하는 사이즈로 지정하여 확인할 수도 있다. .previewLayout(_:) 이라는 modifier를 사용한다. 300x70 사이즈로 확인하려면 다음과 같이 작성해주면 된다. 만약 캔버스가 여전히 전체 디바이스를 보여준다면, 왼쪽 하단의 두번째 버튼을 클릭하여 Selectable 모드로 변경해보자.

또한 Group을 사용하면 여러 개의 프리뷰를 확인할 수 있다. Group은 여러 개의 뷰 또는 씬 또는 명령들을 단일 유닛으로 만들어준다. 여러 뷰들을 묶을 때 HStack이나 VStack을 사용해보았다. 하지만 스택은 뷰의 레이아웃에 영향을 미친다. 레이아웃에 영향을 미치지 않으면서 여러 뷰들을 하나의 인스턴스로 만들고 싶은 경우에는 그룹을 사용하면 된다. 이후 그룹에 적용하는 모디파이어들은 그룹 내 모든 요소에 적용된다.

다음과 같이 .previewLayout(_:) 코드를 Group에 한번에 적용하도록 바꾸어 코드 중복을 줄일 수 있다.

 

Section 4

LandmarkRow들로 이루어진 LandmarkList를 만들자.

LandmakrList.swift 라는 이름의 SwiftUI View 파일을 새로 생성한다. 그리고 첫번째와 두번째 Landmark를 List 안에 담는다. List는 단일 열에 배열된 데이터 행을 표시하는 컨테이너이고, 하나 이상의 요소를 선택할 수 있는 기능을 제공한다. 프리뷰를 확인해보면, 두 개의 LandmarkRow가 일렬로 배치된 것을 볼 수 있다. 

 

Section 5

두 개의 랜드마크로 이루어진 List를 하드코딩해보았다. landmarks에 대해 동적인 List를 생성하도록 개선해보자.

List 생성자에 landmarks를 전달해준다. 그런데 List는 식별 가능한 데이터에 대해서만 동작한다. 각 원소를 식별가능하게 만드는 방법은 두 가지가 있다. 첫번째는 각 원소를 고유하게 식별할 수 있는 프로퍼티의 key path를 같이 전달하는 것이고, 두번째는 각 원소 타입이 Identifiable 프로토콜을 준수하는 것이다. Landmark는 Identifiable을 준수하지 않으므로 각 랜드마크를 식별하게 하는 id 프로퍼티의 key path를 같이 전달해주는 방법을 사용하자. 프리뷰를 확인해보면 landmarks의 모든 랜드마크에 대해 LandmarkRow가 생성된 것을 볼 수 있다.

그런데 어차피 Landmark 구조체가 id라는 프로퍼티를 가지고 있으므로 Identifiable 프로토콜을 채택해도 문제없다. 다시 Landmark.swift로 이동하여 Identifiable 프로토콜을 추가로 채택해주자.

이제 List 생성자의 id key path를 전달하지 않아도 된다.

 

Section 6

목록에서 랜드마크를 터치하면, 랜드마크의 세부 정보 페이지로 이동하도록 만들어보자.

LandmarkDetail.swift라는 이름의 SwiftUI View 파일을 생성한다. 그리고 원래 ContentView의 body에 해당하는 코드를 전부 LandmarkDetail의 body 안으로 옮긴다.

지워진 ContentView.body는 LandmarkList()로 대체한다.

LandmarkList.swift로 이동해서, List를 NavigationView에 임베드한다. NavigationView는 UIKit의 UINavigationViewController와 같은 역할을 하는 것으로 보인다. (그런데 문서를 확인해보니 이것은 이제 Deprecated 되었고 대신 NavigationStack이나 NavigationSplitView를 사용하라고 한다. 일단 튜토리얼을 따라 NavigationView를 사용해보자.)

그리고 Navigation Title을 지정해주자. Navigation Title은 NavigationView에 설정해주는 것이 아니라, NavigationView에 들어있는 List에 설정해주어야 한다. Navigation View 내의 View가 변경될 때마다 해당 View에 해당하는 타이틀이 표시되기 때문이다. 타이틀 설정도 modifier를 사용하면 된다. navigationTitle(_:)로 제목을 "Landmarks"로 설정해주자.

List 생성 클로저를 다음과 같이 변경하자. LandmarkDetail 뷰를 destination으로 하고, label을 LandmarkRow로 하는 Navigation Link를 추가한다. NavigationLink는 내비게이션 프레젠테이션을 제어하는 뷰이다. destination은 말 그대로 도착지 뷰이고, label은 링크를 시각적으로 표시하기 위한 뷰에 해당한다. 따라서 생성한 링크는 LandmarkRow 뷰를 터치하면 LandmarkDetail 뷰로 이동하는 링크가 된다.

아무 행이나 터치해보면, 이렇게 디테일뷰로 이동되는 것을 볼 수 있다.

 

Section 7

그런데 지금 디테일 뷰는 어떤 행을 선택하더라도 Turtle Rock만 보여주고 있다. LandmarkDetail 뷰를 하드코딩해두었기 때문이다. 동적인 정보를 표시할 수 있도록 개선해보자.

CircleImage.swift로 이동하여 image라는 Image 프로퍼티를 추가해준다. PreviewProvider도 수정해주는 것을 잊지 말자. 프리뷰를 수정해주어도 프리뷰 로드에 실패할텐데, CircleImage를 사용하는 다른 부분에서 빌드 에러가 나기 때문이다.

MapView.swift로 이동하여 CLLocationCoordinate2D 타입의 coordinate 프로퍼티를 추가한다.

그리고 주어진 coordinate로 region을 업데이트하는 setRegion(_:) 메소드를 추가한다.

이제 Map 뷰가 나타나면 region을 coordinate 값에 따라 업데이트 하도록 .onAppear(perform:)라는 modifier를 추가하자. .onAppear(perform:)는 뷰가 나타나기 전에 수행할 작업을 추가하는 모디파이어이다.

LandmarkDetail.swift로 이동하여 landmark 프로퍼티를 추가한다. 그리고 LandmarkList의 NavigationLink의 desitination 뷰인 LandmarkDetail의 생성자 부분을 수정해주자. landmark를 전달하도록 수정해야 한다.

LandmarkDetail.swift로 돌아와 프로퍼티 추가로 인해 변경된 생성자들을 모두 수정해주자. MapView, CircleImage 부분을 수정해야 한다.

그리고 하드코딩 되어있던 부분도 모두 landmark 프로퍼티를 사용하여 수정해준다.

그런데 설명이 너무 길어서 잘린다. 스크롤 가능하게 만들어 잘리는 부분이 없도록 수정해주자. VStack을 ScrollView 타입으로 변경해주면 간단하게 해결할 수 있다. ScrollView는 이름 그대로 스크롤이 가능한 뷰이다. ScrollView는 수평, 수직 또는 둘 다 스크롤할 수 있지만, 확대/축소 기능을 제공하지 않는다. ScrollView 이니셜라이저의 showsIndicators 매개 변수로 비활성화할 수 있습니다.마지막에 추가해주었던 Spacer는 더이상 필요하지 않으므로 지워준다.

마지막으로 네비게이션 타이틀을 설정해주자.

ContentView.swift로 이동하여 라이브모드로 프리뷰를 확인해보면 이렇게 타이틀까지 잘 적용되어있다.

 

Section 8

iOS 기기마다 화면 사이즈가 다르다. 다른 화면 사이즈에서도 잘 보이는지 확인해보자.

LandmarkList.swift로 이동하여 프리뷰에 previewDevice(_:)라는 modifier를 적용하여 아이폰 SE3로 변경해보자.

ForEach를 사용하여 여러 개의 디바이스 프리뷰를 한번에 생성할 수도 있다. ForEach는 주어진 데이터컬렉션을 기반으로 뷰들을 계산한다. ForEach 또한 List와 같이 식별 가능한 원소에 대해서만 동작하기 때문에 문자열 그대로를 id로 사용하도록 지정해주었다.

previewDisplayName(_:)이라는 modifier를 추가하면 캔버스에 디바이스 명이 표시된다.

 

다음 챕터는 3편에서 확인.

 

[iOS] Apple의 Introducing SwiftUI 공부하기 3

2편은 여기에서 확인할 수 있다. [iOS] Apple의 Introducing SwiftUI 공부하기 2 1편은 여기에서 확인할 수 있다. [iOS] Apple의 SwiftUI Tutorials 공부하기 1 애플 개발자 문서에서 제공하는 SwiftUI Tutorials를 읽으

please-amend.tistory.com

 

반응형

댓글