티스토리 뷰

https://developer.apple.com/tutorials/swiftui/building-lists-and-navigation

 

Building Lists and Navigation | Apple Developer Documentation

With the basic landmark detail view set up, you need to provide a way for users to see the full list of landmarks, and to view the details about each location.

developer.apple.com

튜토리얼 두 번째, 리스트를 만들고 네비게이션을 지정하는 것을 해볼 것 같습니다. 시작하기 전에 이미지 파일은 위 링크 들어가셔서 Project files 누르고 다운받으시면 됩니다!

시~작~

 


Section1.

랜드마크 모델 생성하기

첫 번째 튜토리얼에서, 우리는 커스텀 뷰의 모든 정보를 하드코딩하여 집어 넣었습니다.
이번에는 그 뷰로 전달할 수 있는 데이터를 저장하는 모델을 만들 것입니다.

 

Step1. 위에서 다운로드한 프로젝트 파일안에 리소스 폴더에서 landmarkData.json 파일을 프로젝트의 네비게이션 영역으로 드래그 하세요. 드래그 후 나타나는 창에서 Copy items if needed를 체크해 주셔야 합니다!!

 

Step2. 상단 메뉴에서 File > New > File 을 통해 Landmark.swift라는 이름의 새로운 Swift파일을 생성해주세요.
SwiftUI View 아닙니당

 

Step3. landmarkData.json 데이터 파일의 일부 키 이름과 일치하는 몇 가지 속성으로 랜드마크 구조체를 정의하십시오.

이게 무슨 말이냐면 JSON은 Key : Value 구조로 되어 있어요. 아까 Resource파일에서 가져온 landmarkData.json의 일부를 보면

    {
        "name": "Turtle Rock",
        "category": "Rivers",
        "city": "Twentynine Palms",
        "state": "California",
        "id": 1001,
        "isFeatured": true,
        "isFavorite": true,
        "park": "Joshua Tree National Park",
        "coordinates": {
            "longitude": -116.166868,
            "latitude": 34.011286
        },
        "description": "Suscipit inceptos est felis purus aenean aliquet adipiscing diam venenatis, augue nibh duis neque aliquam tellus condimentum sagittis vivamus, cras ante etiam sit conubia elit tempus accumsan libero, mattis per erat habitasse cubilia ligula penatibus curae. Sagittis lorem augue arcu blandit libero molestie non ullamcorper, finibus imperdiet iaculis ad quam per luctus neque, ligula curae mauris parturient diam auctor eleifend laoreet ridiculus, hendrerit adipiscing sociosqu pretium nec velit aliquam. Inceptos egestas maecenas imperdiet eget id donec nisl curae congue, massa tortor vivamus ridiculus integer porta ultrices venenatis aliquet, curabitur et posuere blandit magnis dictum auctor lacinia, eleifend dolor in ornare vulputate ipsum morbi felis. Faucibus cursus malesuada orci ultrices diam nisl taciti torquent, tempor eros suspendisse euismod condimentum dis velit mi tristique, a quis etiam dignissim dictum porttitor lobortis ad fermentum, sapien consectetur dui dolor purus elit pharetra. Interdum mattis sapien ac orci vestibulum vulputate laoreet proin hac, maecenas mollis ridiculus morbi praesent cubilia vitae ligula vel, sem semper volutpat curae mauris justo nisl luctus, non eros primis ultrices nascetur erat varius integer.",
        "imageName": "turtlerock"
    },

name이라는 Key 에는 Turtle Rock 이라는 Value가 들어있고

category라는 Key에는 Rivers라는 Value가 들어있는 형식입니다!

이 중에 사용할 일부 키 이름들의 속성을 가진 Landmark 구조체를 정의하라는 말입니다. 예제에서는 id, name, park, state, description만 가져다 쓰는 것 같으므로 저도 똑같이 가져다 생성할게요!

struct Landmark: Hashable, Codable {
    var id: Int
    var name: String
    var park: String
    var state: String
    var description: String
}

Hashable과 Codable에 대한 설명은 좀 길어질 것 같으니 아래에서 정리해볼게요.

간단히 말해서 Hashable은 hash를 사용하기위한 프로토콜 Codable은 Json을 파싱하기 위한 애플에서 제공해주는 프로토콜 이라고 알아두면 될 것 같아요!

 

Step4. 또 한번 Resoruce파일에 가셔서 Images 폴더 안에 있는 이미지들을 모두 Xcode Assets의 이미지로 추가해주세요.

Json 파일에 imageName이라는 key가 존재하는데 이 key의 value와 이미지 asset의 이름이 같아야 합니다

 

Step5. Step3에서 생성한 구조체에 Image이름을 받아올 imageName 속성과 Assets에 있는 이미지를 로드해 저장할 image 속성을 추가해주세요.

struct Landmark: Hashable, Codable {
    var id: Int
    var name: String
    var park: String
    var state: String
    var description: String
    
    private var imageName: String
    var image: Image {
        Image(imageName)
    }
}

private는 접근 지정자 또는 접근 제어자 라고 불리는데 지금은 붙여도 안붙여도 동작에는 큰 문제가 없습니다!

외부에서 imageName에 접근할 필요가 없어서 접근 못하게 막아논 거에요!  image에만 접근할 테니까요

참고로 image는 연산프로퍼티로 선언되어 있는 형태입니다!  get 연산 프로퍼티로 값을 반환만 해주고 쓰기 작업은 불가능해요!
Image 타입은 SwiftUI를 import 해주어야합니다!!

 

다음으로 랜드마크의 위치 정보를 관리하는 단계입니다!

 

Step6. Json을 보시면 이런 좌표를 나타내는 coordinates Key가 있고 value가 구조체로 되어있습니다.

        "coordinates": {
            "longitude": -116.166868,
            "latitude": 34.011286
        },

 이 value를 codable로 파싱하기 위해서 Coordinates라는 구조체를 정의 해줍니다.

    struct Coordinates: Hashable, Codable {
        var latitude: Double
        var longitude: Double
    }

그리고 이런 구조체 타입을 가진 coordinates 속성을 추가해주세요~!

private var coordinates: Coordinates

 

Step7. 위에서 선언한 coordinates의 타입은 Coordinates로 사용자가 커스텀한 구조체 타입입니다. 이를 Mapkit에서 상호작용할 수 있는 CLLocationCoorinate2D타입으로 저장하는 변수를 하나 더 선언해줍니다. CLLOcationCoorinate2D타입은  CoreLocation에 속해 있으므로 import CoreLocation 또한 잊지 말고 해주어야 합니다.

    var locationCoordinate: CLLocationCoordinate2D {
        CLLocationCoordinate2D(
            latitude: coordinates.latitude,
            longitude: coordinates.longitude)
    }

-- 최종 코드

import Foundation
import SwiftUI
import CoreLocation

struct Landmark: Hashable, Codable {
    var id: Int
    var name: String
    var park: String
    var state: String
    var description: String
    
    private var imageName: String
    
    var image: Image {
        Image(imageName)
    }
    
    private var coordinates: Coordinates
    
    var locationCoordinate: CLLocationCoordinate2D {
        CLLocationCoordinate2D(
            latitude: coordinates.latitude,
            longitude: coordinates.longitude)
    }

    struct Coordinates: Hashable, Codable {
        var latitude: Double
        var longitude: Double
    }
}

 

Step8. 새로운 Swift File을 만들어주세요. 이름은 ModelData.swift입니다.

 

Step9. 앱의 메인 번들에서 주어진 이름으로 JSON 데이터를 가져오는 load(_:) 메소드를 만드세요.

func load<T: Decodable>(_ filename: String) -> T {
    let data: Data
    
    guard let file = Bundle.main.url(forResource: filename, withExtension: nil)
    else { fatalError("Couldn't find \(filename) in main bundle." }
                      
    do {
        data = try Data(contentsOf: file)
    } catch {
        fatalError("Couldn't load \(filename) from main bundle:\n\(error)")
    }
                      
    do {
        let decoder = JSONDecoder()
        return try decoder.decode(T.self, from: data)
    } catch {
        fatalError("Couldn't parse \(filename) as \(T.self):\n\(error)")
    }
}

애플 공식문서에 따르면 번들은 실행가능한 코드와 관련 리소스를 한 공간에 묶는 파일시스템에 있는 디렉토리라고 합니다!

위 함수는 이 번들을 이용해 우리가 추가했던 landmarkData의 경로 url을 가져오고 그 url에서 Data객체를 통해 Data를 가져오고 그 데이터를 JSONDecoder를 통해 Decoding하는 과정입니다. 제너릭도 사용되고 Codable도 사용되었지만 다 설명하면 너무 길어지니 이런 함수구나 까지만 말하고 넘어갈게요..

 

Step10. landmarkData.json 파일로부터 landmarks 배열을 생성해줍니다! 

var landmarks: [Landmark] = load("landmarkData.json")

Step11. 앞으로 추가적인 파일 관리를 위해 관련된 것들끼리 같은 폴더로 묶는 작업을 합시다!!

ContentView.swift / CircleImage.swift / MapView.swift 는 View group으로

landmarkData.json은 Resource group으로

Landmark.swift / ModelData.swift는 Model group으로 묶겠습니다.

 

 

Section2.

Row View를 생성하라!!

각 랜드마크에 대한 세부 사항을 표시하기 위한 row View 입니다.

나중에 여러 행을 랜드마크 목록으로 결합할 것입니다.

 

Step1. View Group에서 LandmarkRow.swift 이름으로 SwiftUI View 파일을 생성하세요.

 

Step2. Canvas를 우측에 띄우세요! Editor > Canvas 클릭 후 Resume 클릭

 

Step3. LandmarkRow 안에 landmark 저장 프로퍼티를 추가하세요. (Landmark 타입)

var landmark: Landmark

Step4. 위에서 landmark의 초기값을 지정해주지 않았기 때문에 canvas preview가 멈추게 됩니다. 이를 해결하기 위해

previews에서 LandmarkRow 생성자에서 landmark 파라미터의 인수로 landmarks 배열의 첫 번째 요소를 정적으로 추가해주세요.

struct LandmarkRow_Previews: PreviewProvider {
    static var previews: some View {
        LandmarkRow(landmark: landmarks[0])
    }
}

 

Step5. 이제 row View를 만들어 볼텐데! 기존에 존재하는 Textview를 HStack에 넣어주세요.

기억하시죠!? Command-좌Click

    HStack {
        Text("Hello, World!")
    }

 

Step6. 이 TextView의 Text를 lnadmark.name으로 바꿔 주세요.

 

Step7. TextView의 위쪽에 landmark의 속성 중 image를 추가해주고 frame은 width: 50 height: 50 으로 설정하고 마지막에 Spacer를 추가해 왼쪽으로 밀어주세요.

    var body: some View {
        HStack {
            landmark.image
                .resizable()
                .frame(width: 50, height: 50)
            Text(landmark.name)
            Spacer()
        }
    }

resizable()은 공간에 맞게 이미지 사이즈를 조절해주는 메소드입니다.

 

 

Section3.

행 미리보기 커스터마이징하기

Xcode Canvas는 PreviewProvider 프로토콜을 준수하는 현재 Editor의 모든 유형을 자동으로 인식하고 표시해줍니다.
PreviewProvider는 크기와 장치를 구성하는 옵션과 함께 하나 이상의 뷰를 반환합니다.
PreviewProvider에서 반환된 콘텐츠를 커스터마이징하여 가장 도움이 되는 미리보기를 정확하게 렌더링할 수 있습니다.

오.. 해봅시다!

이번 Section3 예시

Step1. LandmarkRow_Previews에서 LandmarkRow 생성자 매개변수에 landmarks[0]이 아닌 2번째 요소 landmarks[1]을 넘겨주세요. 미리보기가 바로 두 번째요소로 업데이트 되어 나타나는 것을 볼 수 있습니다.

 

Step2. previewLayout(_:) modifier를 사용하여 목록의 행에 가까운 사이즈로 설정하세요. ( Live모드 일 땐 안되는 것 같아요!)

struct LandmarkRow_Previews: PreviewProvider {
    static var previews: some View {
        LandmarkRow(landmark: landmarks[1])
            .previewLayout(.fixed(width: 300, height: 70))
    }
}

Step3.  Group을 사용하여 여러가지 미리보기를 제공받을 수 있습니다.

    static var previews: some View {
        Group {
            LandmarkRow(landmark: landmarks[0])
                .previewLayout(.fixed(width: 300, height: 70))
            LandmarkRow(landmark: landmarks[1])
                .previewLayout(.fixed(width: 300, height: 70))
        }
    }

 

 

Section4.

랜드마크 목록 생성하기

SwiftUI의 List타입을 사용하면 목록 뷰를 나타낼 수 있습니다. UIKit의 테이블 뷰 느낌인 것 같네요!

List의 요소는 정적이거나 동적으로 생성될 수 있으며 동적 및 정적으로 생성된 뷰를 혼합할 수도 있습니다.

 

Step1. SwiftUI View 파일을 생성하세요. 파일명 : LandmarkList.swift

 

Step2. LandmarkList 의 body 안에 TextView를 지우고 List를 추가하고 두 개의 LandmarkRow 인스턴스를 추가해주세요.

struct LandmarkList: View {
    var body: some View {
        List {
            LandmarkRow(landmark: landmarks[0])
            LandmarkRow(landmark: landmarks[1])
        }
    }
}

 

 

Section 5.

동적인 리스트 만들기

List의 요소를 Section4 에서 처럼 개별적으로 지정하는 대신 Collection에서 직접 행을 생성할 수 있습니다.

데이터 컬렉션과 컬렉션의 각 요소에 대한 View를 제공하는 클로저를 전달해 컬렉션의 요소를 표시하는 목록을 만들 수 있습니다.

 

 

데이터 컬렉션과 컬렉션의 각 요소에 대한 보기를 제공하는 클로저를 전달하여 컬렉션의 요소를 표시하는 목록을 만들 수 있습니다. 이 목록은 제공된 클로저를 사용하여 컬렉션의 각 요소를 자식 보기로 변환합니다.

 

Step1. Section4에서 정적으로 추가해주었던 landmark인스턴스 2개를 제거하고 ModelData의 landmarks배열을 List 생성자로 전달하세요.

struct LandmarkList: View {
    var body: some View {
        List(landmarks, id: \.id) { landmark in
            
        }
    }
}

List는 식별 가능한 데이터로 작동합니다. 아래 2가지 방법 중 한가지로 데이터를 식별할 수 있습니다.

1. 데이터와 함께 각 요소를 고유하게 식별하는 속성의 키 경로를 전달한다.

2. 데이터 타입이 Identifiable 프로토콜을 준수하도록 한다.

 

Step2. 이제 클로저 구문 안에서 LandmarkRow 인스턴스를 반환하여 동적으로 생성된 목록을 완성해주세요.

각 landmarks 배열의 요소에 맞게 LandmarksRow가 생성됩니다.

struct LandmarkList: View {
    var body: some View {
        List(landmarks, id: \.id) { landmark in
            LandmarkRow(landmark: landmark)
        }
    }
}

위 코드는 데이터를 식별하는 방법 중 첫 번째 방법으로 데이터와 함께 속성의 키 경로를 전달하는 방법임

 

 

Step3. 이번엔 2 번째 방법인 Identifiable 프로토콜을 준수하는 데이터모델을 만들어서 식별하도록 합니다!!

struct Landmark: Hashable, Codable, Identifiable {
    var id: Int
    var name: String
    var park: String
    var state: String
    var description: String
    
    private var imageName: String
    
    var image: Image {
        Image(imageName)
    }
    
    private var coordinates: Coordinates
    
    var locationCoordinate: CLLocationCoordinate2D {
        CLLocationCoordinate2D(
            latitude: coordinates.latitude,
            longitude: coordinates.longitude)
    }

    struct Coordinates: Hashable, Codable {
        var latitude: Double
        var longitude: Double
    }
}

 

Landmark 구조체가 Identifiable 프로토콜을 준수하도록 채택만 추가해주었습니다.

 

Step4. 그럼 List 생성자에서 매개변수로 Id 키를 넘겨줄 필요가 없으므로 제거해줍니다.

struct LandmarkList: View {
    var body: some View {
        List(landmarks) { landmark in
            LandmarkRow(landmark: landmark)
        }
    }
}

 

 

Section6.

List와 detal사이 navigation 설정하기

Section5 과정을 통해 랜드마크 List는 제대로 렌더링 되지만 아직 요소를 탭했을 때 랜드마크의 세부 정보 페이지를 볼 수 없습니다.

Section6에서는 NavigationView를 삽입하여 List에 Navigation기능을 추가한 다음 탭 했을때 세부 정보 페이지로 view를 전환해줄 것입니다. 이 과정은 튜토리얼[1]에서 만든 상세페이지뷰(ContentView.swift)가 필요합니다.

 

Step1. SwiftUI View 파일 생성 파일명: LandmarkDetail.swift

 

Step2. CotentView의 body의 LandmarkDetail body로 복사하세요.

 

Step3. ContentView의 body는 LandmarkList를 나타내주세요.

struct ContentView: View {
    var body: some View {
        LandmarkList()
    }
}

Step2 와 Step3의 이유는

@main
struct SwiftUI_Tutorial1_LandmarksApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
//                .background(Color.brown)
//            TestView()
//                .background(Color.yellow)
        }
    }
}
위에 코드를 통해 앱의 첫 시작이 ContentView이기 때문에 ContentView에서 랜드마크 리스트를 띄워주는 것이라고 보시면 됩니다!!
물론 저 위에 코드에 ContentView()대신 LandmarkList()를 해줘도 되겠지만 튜토리얼에선 이렇게 진행하네요!

 

다음으로, 네비게이션 기능을 추가하겠습니다!!
 
Step4. LandmarkList body에 존재하는 List를 NavigationView로 감싸줍니다.
struct LandmarkList: View {
    var body: some View {
        NavigationView {
            List(landmarks) { landmark in
                LandmarkRow(landmark: landmark)
            }
        }
    }
}

 

Step5. navigationTitle(_:) modifier 메소드를 이용해 navigation bar의 title을 설정합니다.

        NavigationView {
            List(landmarks) { landmark in
                LandmarkRow(landmark: landmark)
            }
        }
        .navigationTitle("Landmarks")

 

Step6. List의 클로저 안에서, 반환된 행을 NavigationLink로 감싸고 LandmarkDetail 뷰를 대상으로 지정하세요.

struct LandmarkList: View {
    var body: some View {
        NavigationView {
            List(landmarks) { landmark in
                NavigationLink {
                    LandmarkDetail()
                } label: {
                    LandmarkRow(landmark: landmark)
                }
            }
        }
        .navigationTitle("Landmarks")
    }
}

 

Step7. 미리보기를 라이브 모드로 전환하여 직접 네비게이션을 사용해 볼 수 있습니다! 이건 직접 해보시면 될 것 같아요 잘 작동하는지!

 

 

Section7.

자식뷰로 데이터 전달하기

LandmarkDetail View는 여전히 하드코딩된 세부사항 내용을 보여줍니다.

LandmarkRow와 마찬가지로 랜드마크 속성을 Data Soruce로 사용해야합니다.

 

자식 뷰 부터 시작해 CircleImage , MapView, LandmarkDetial 변환해 하드코딩 대신 전달된 데이터를 표시하도록 바꾸어 보겠습니다.

 

Step1. CircleImage.swift에서 저장프로퍼티 Image를 선언하고 고정된 이미지인 Image("turtlerock") 대신 image로 바꾸어주세요.

이 작업을 통해 고정된 image가 아닌 생성자로 들어온 image가 표출 될 수 있습니다.이런 작업은 SwiftUI에서 흔한 패턴이라고 합니다!!

struct CircleImage: View {
    var image: Image
    
    var body: some View {
        image
            .clipShape(Circle())
            .overlay { Circle().stroke(.white, lineWidth: 4) }
            .shadow(radius: 7)
    }
}

 

Step2. Turtle Rock의 이미지를 전달하기 위해 PreviewProvider를 업데이트하세요. image가 초기값이 없어서 미리보기가 동작하지 않는 문제를 해결해줍시다!

struct CircleImage_Previews: PreviewProvider {
    static var previews: some View {
        CircleImage(image: Image("turtlerock")
    }
}

 

 

Step3. MapView.swift 또한 마찬가지 입니다. coordinate 속성을 선언하고 PreviewProvider에는 고정 좌표를 전달해주세요.

import SwiftUI
import MapKit

struct MapView: View {
    var coordinate: CLLocationCoordinate2D
    
    @State private var region = MKCoordinateRegion(
        center: CLLocationCoordinate2D(latitude: 34.011_286, longitude: -116.166_868),
        span: MKCoordinateSpan(latitudeDelta: 0.2, longitudeDelta: 0.2)
        )
    
    var body: some View {
        Map(coordinateRegion: $region)
    }
}

struct MapView_Previews: PreviewProvider {
    static var previews: some View {
        MapView(coordinate: CLLocationCoordinate2D(latitude: 34.011_286, longitude: -116.166_868))
    }
}

 

 

Step4. coordinate값을 기반으로 region을 업데이트 하는 함수를 구현해주세요.

    private func setRegion(_ coordinate: CLLocationCoordinate2D) {
        region = MKCoordinateRegion(
            center: coordinate,
            span: MKCoordinateSpan(latitudeDelta: 0.2, longitudeDelta: 0.2))
    }

 

Step5. OnAppear 뷰 modifier를 사용하여 MapView가 나타날때 현재 coordinate를 기반으로 region을 변경해주세요.

import SwiftUI
import MapKit

struct MapView: View {
    var coordinate: CLLocationCoordinate2D
    
    @State private var region = MKCoordinateRegion() // 단순 초기값
    
    var body: some View {
        Map(coordinateRegion: $region)
            .onAppear() {
                setRegion(coordinate)
            }
    }
    
    private func setRegion(_ coordinate: CLLocationCoordinate2D) {
        region = MKCoordinateRegion(
            center: coordinate,
            span: MKCoordinateSpan(latitudeDelta: 0.2, longitudeDelta: 0.2))
    }
}

struct MapView_Previews: PreviewProvider {
    static var previews: some View {
        MapView(coordinate: CLLocationCoordinate2D(latitude: 34.011_286, longitude: -116.166_868))
    }
}

 

Step6. LandmarkDetail.swift에서 LandmarkDetail에 랜드마크 프로퍼티를 추가하세요.

struct LandmarkDetail: View {
    var landmark: Landmark

    ...
}

 

Step7. LandmarkList에서 LandmarkDetail을 생성자에 landmark를 넘겨주세요.

            List(landmarks) { landmark in
                NavigationLink {
                    LandmarkDetail(landmark: landmark)
                } label: {
                    LandmarkRow(landmark: landmark)
                }
            }

 

Step8. LandmarkDetail 파일에서 각자 필요한 타입을 넘겨주면 됩니다. CircleImage, MapView 등 이 부분을 해결하고 나면 멈췄던 미리보기가 다시 작동할 것입니다!

import SwiftUI

struct LandmarkDetail: View {
    var landmark: Landmark
    
    var body: some View {
        VStack {
            MapView(coordinate: landmark.locationCoordinate)
                .ignoresSafeArea(edges: .top)
                .frame(height: 300)
            
            CircleImage(image: landmark.image)
                .offset(y: -130)
                .padding(.bottom, -130)
            
            VStack(alignment: .leading) {
                Text(landmark.name)
                    .font(.title)
                HStack {
                    Text(landmark.park)
                    Spacer()
                    Text(landmark.state)
                }
                .font(.subheadline)
                .foregroundColor(.secondary)
                
                Divider()
                
                Text("About \(landmark.name)")
                    .font(.title2)
                Text(landmark.description)
            }
            .padding()
            
            Spacer()
        }

    }
}

struct LandmarkDetail_Previews: PreviewProvider {
    static var previews: some View {
        LandmarkDetail(landmark: landmarks[0])
    }
}

내용이 짤리는 현상

위와 같이 내용이 모두 나오지 못하고 짤리는 현상이 발생합니다.

 

Step9. VStack을 제거하고 ScrollView로 변경해주어 짤리는 현상을 해결해 봅시다. 그리고 필요 없어진 Spacer()는 제거해줍니다.

 

Step10. NavigationTitle을 랜드마크 이름으로 설정해주고 inline모드로 설정해줍니다.

struct LandmarkDetail: View {
    var landmark: Landmark
    
    var body: some View {
        ScrollView {
            MapView(coordinate: landmark.locationCoordinate)
                .frame(height: 300)
                .ignoresSafeArea(edges: .top)
            
            CircleImage(image: landmark.image)
                .offset(y: -130)
                .padding(.bottom, -130)
            
            VStack(alignment: .leading) {
                Text(landmark.name)
                    .font(.title)
                HStack {
                    Text(landmark.park)
                    Spacer()
                    Text(landmark.state)
                }
                .font(.subheadline)
                .foregroundColor(.secondary)
                
                Divider()
                
                Text("About \(landmark.name)")
                    .font(.title2)
                Text(landmark.description)
            }
            .padding()
        }
        .navigationBarTitle(landmark.name)
        .navigationBarTitleDisplayMode(.inline)
    }
}

struct LandmarkDetail_Previews: PreviewProvider {
    static var previews: some View {
        LandmarkDetail(landmark: landmarks[0])
    }
}

 

 

Section8.

마지막으로! 기기별로 미리보기를 보는 방법까지만 알아봅시다!!

 

Step1. iPhone SE 사이즈로 미리보기 렌더링을 해봅시다!

struct LandmarkList_Previews: PreviewProvider {
    static var previews: some View {
        LandmarkList()
            .previewDevice(PreviewDevice(rawValue: "iPhone SE (2nd generation)"))
    }
}
// 예제는 위처럼 나와있는데 2nd generation을 지원하지 않는 것 같아서 3rd로 바꾸니 잘 작동 되었고 아래 처럼 작성해도 되었다.
// .previewDevice("iPhone SE (3rd generation)")
// 지원기기를 잘 확인 해보는게 좋을 것 같다

 

Step2. List 미리보기 내에서 장치 이름 배열을 데이터로 사용하여 ForEach 인스턴스에 LandmarkList를 삽입하십시오.

struct LandmarkList_Previews: PreviewProvider {
    static var previews: some View {
        ForEach(["iPhone SE (3rd generation)", "iPhone 14"], id: \.self) { deviceName in
            LandmarkList()
                .previewDevice(PreviewDevice(rawValue: deviceName))
                .previewDisplayName(deviceName)
        }
    }
}

 

 

미리보기로 지원되는 장치 이름들은 터미널 앱에 아래 명령어를 통해 확인할 수 있습니다.

또는 오른쪽 인스펙터 영역을 확인해도 됩니다.

% xcrun simctl list devicetypes

 

Step3. previewDisplayName(_:) 를 사용해 미리보기 이름 또한 바꿀 수 있습니다.

 

음 여기까지네요 이번 튜토리얼은! 블로그를 쓰면서 하니까 엄청 오래 걸렸어요.. 간단한건데

이번 튜토리얼을 통해 배운 것 정리를 해볼까요

 

- List를 정적 또는 동적으로 나타내는 방법과 터치시 Navigaton 기능을 추가하는 방법을 배웠습니다.

- Device별로 미리보기를 렌더링 하는 방법과 렌더링 크기를 지정하는 방법을 배웠습니다.

 

대부분이 List관련인데 List는 알아야할게 더 많아 보입니다.. 튜토리얼을 마치고 List에 대해서는 따로 정리하는 글을 작성해봐야겠어요..

«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
공지사항
링크
Total
Today
Yesterday