DADAHAE's Log
비싼 장난감 가지고 노는 중 (❁´▽`❁)*✲゚*
[SwiftUI] List, Navigation을 이용해서 간단한 앱 구현하기 (EVCar Demo)
22-10-25 화요일
7주차

 

 

잡담.

WWDC Platforms State of the Union

Platforms State of the Union - WWDC22 - Videos - Apple Developer

 

Platforms State of the Union - WWDC22 - Videos - Apple Developer

Take a deeper dive into the latest tools, technologies, and advances across Apple platforms to help you create even better apps.

developer.apple.com

 

 


 

 

🦢 SwiftUI

오늘도 스유 고고!

List와 Navigation에 대해서 알아보자.

 

 

✔️ List, Navigation

이제 화면을 넘어서는 수준의 항목이 필요해진다! 그럼 어떻게 해야 할까?

 

List 정적 데이터동적 데이터를 모두 표현할 수 있다.

  • 추가, 삭제, 항목 순서 재지정

NavigationView, NavigationLink 화면을 이동할 수 있다.

  • 물론 지금은 NavigationStack을 더 권장한다.
  • 그러나 지금은 NavigationView로 하고, NavigationStack으로 새로운 내용을 포스팅할 예정이다.

 

 

1️⃣ List

ListDemo를 만들어보면서 List를 사용해보자.

  • List안에 Text를 써보자.
  • Embed HStack(VStack)도 가능하다.

 

  • Identifiable protocol
    • id는 id라고 써야한다.
    • UUID를 할당해서 id 값을 부여하기도 한다.
    • UUID().uuidString을 쓰면 string 형태의 UUID를 받을 수 있다.

 

  • ContentView.swift
// 해당 프로토콜을 따라주어야 한다!
struct ToDoItem: Identifiable {
    var id = UUID() // 처음 생성될 때 자동으로 만들어지게 한다.
    var task: String
    var imageName: String
}

var listData: [ToDoItem] = [
    ToDoItem(task: "Wash the car", imageName: "car.fill"),
    ToDoItem(task: "Vaccum house", imageName: "house.fill"),
    ToDoItem(task: "Pick up kids from school bus @ 3pm", imageName: "bus.fill"),
    ToDoItem(task: "Auction the kids on ebay", imageName: "cart.fill"),
    ToDoItem(task: "Order Pizza for dinner", imageName: "fork.knife")
]
    
struct ContentView: View {
    @State private var toggleStatus = true
    
    var body: some View {
        List {
            Section(header: Text("Settings")) {
                Toggle(isOn: $toggleStatus) {
                    Text("Allow Notification")
                }
            }
            Section(header: Text("To Do Tasks")) {
                ForEach(listData) { item in
                    HStack {
                        Image(systemName: item.imageName)
                        Text(item.task)
                    }
                }
            }
        }
    }
}

 

좌잔

 

 

 

2️⃣ NavigationView, NavigationLink

List에 있는 항목을 탭하여 뷰를 이동하게 만들자!

  • NavigationView 안에 List 넣기

 

  • ContentView.swift
import SwiftUI

// 해당 프로토콜을 따라주어야 한다!
struct ToDoItem: Identifiable {
    var id = UUID() // 처음 생성될 때 자동으로 만들어지게 한다.
    var task: String
    var imageName: String
}

var listData: [ToDoItem] = [
    ToDoItem(task: "Wash the car", imageName: "car.fill"),
    ToDoItem(task: "Vaccum house", imageName: "house.fill"),
    ToDoItem(task: "Pick up kids from school bus @ 3pm", imageName: "bus.fill"),
    ToDoItem(task: "Auction the kids on ebay", imageName: "cart.fill"),
    ToDoItem(task: "Order Pizza for dinner", imageName: "fork.knife")
]
    
struct ContentView: View {
    @State private var toggleStatus = true
    
    var body: some View {
        NavigationView {
            List {
                Section(header: Text("Settings")) {
                    Toggle(isOn: $toggleStatus) {
                        Text("Allow Notification")
                    }
                }
                Section(header: Text("To Do Tasks")) {
                    ForEach(listData) { item in
                        NavigationLink(destination: Text(item.task)) {
                            HStack {
                                Image(systemName: item.imageName)
                                Text(item.task)
                            }
                        }
                    }
                    .onDelete(perform: deleteItem)
                    .onMove(perform: moveItem)
                }
            }
            .navigationTitle(Text("To Do List"))
            .navigationBarItems(trailing: EditButton())
        }
        Button (action: {
            print("hh")
        }) {
            Text("hi")
        }
    }

}

func deleteItem(at offset: IndexSet) {
    print("indexSet: \(offset)")
}

func moveItem(from source: IndexSet, to destination: Int) {
    print("source: \(source)")
    print("destination: \(destination)")
}

 

 

좌잔 2

  • navigationBarItem으로 Edit 버튼을 추가하면 오른쪽에 버튼이 생긴다. (근데 이제 toolbar로 사용하는 것을 권장함)

 

 


 

 

📒 전기 자동차 앱 Tutorial

전기 자동차 앱 만들어보기
팀원들과 함께 튜토리얼을 진행해보자.

 

  • 사용하는 기능
    • ObservableObject
    • NavigationView, NavigationLink

 

📌  꿀팁

  • CarData
    • Target Membership의 체크가 풀려있는지 확인하자.
    • Target Membership이 빠져있으면 프로젝트에서 해당 파일을 제외해버린다.
  • Car와 CarStore의 차이?
    • Car : 계란
    • CarStore: 계란판

 

폴더 구조

📁 ListNavDemo
	∟⎼ ListNaveDemoApp.swift
	∟⎼ 📁ViewModel
		∟⎼ CarData.swift
		∟⎼ CarStore.swift
	∟⎼ 📁 Model
		∟⎼ carData.json
		∟⎼ Car.swift
	∟⎼ 📁 View
		∟⎼ ContentView.swift
		∟⎼ AddNewCar.swift
		∟⎼ CarDetail.swift

 

 

실행 순서

  • ContentView에서 List 중 하나 선택
  • 선택된 아이템의 데이터를 CarDetail에 넘겨줌
  • CatDetail에서는 Car 타입의 selectedCar변수에 넘겨받은 데이터를 담는다.
    • 이때, 따로 ObservedObject로 래핑하지 않는다. CarDetail에서 해당 객체 값을 변경할 일이 없기 때문이다.
    • 변경해야 한다면 ObservedObject로 래핑하자.

 

주요 코드

  • carData.json
[
    {
        "id": "aa32jj887hhg55",
        "name": "Tesla Model 3",
        "description": "Luxury 4-door all-electic car. Range of 310 miles. 0-60mph in 3.2 seconds",
        "isHybrid": false,
        "imageName": "tesla_model_3"
    },
    {
        "id": "bb32jj887hhg77",
        "name": "Tesla Model S",
        "description": "5-door lift-back electric car. Performance model has a three-phase, four-pole AC induction 416 hp (310 kW) and 443 ft⋅lb (601 N⋅m) rear-mounted electric motor",
        "isHybrid": false,
        "imageName": "tesla_model_s"
    },
    {
        "id": "cc32jj887hhg66",
        "name": "Toyota Prius",
        "description": "5-door liftback full hybrid electric automobile developed by Toyota.",
        "isHybrid": true,
        "imageName": "toyota_prius"
    },
    {
        "id": "dd32jj887hhg88",
        "name": "Nissan Leaf",
        "description": "A compact five-door hatchback all-electric car manufactured by Nissan",
        "isHybrid": false,
        "imageName": "nissan_leaf"
    },
    {
        "id": "ee32jj887hhg99",
        "name": "Chevrolet Volt",
        "description": "5-door hatchback plug-in hybrid car manufactured by General Motors.",
        "isHybrid": true,
        "imageName": "chevrolet_volt"
    }
    
]

우리가 필요로하는 데이터이다. 실제 데이터 값이므로, Model 폴더에 넣었다.

데이터가 JSON형식이므로, Swift로 건드릴 수 있도록 디코딩해야 한다.

JSON 디코딩을 수행하는 코드는 CarData의 loadJson 함수이다.

 

 

  • CarData.swift
import SwiftUI

// JSON 파일을 디코딩하는 함수
var carData: [Car] = loadJson("carData.json")

func loadJson<T: Decodable>(_ filename: String) -> T {
    let data: Data
    
    guard let file = Bundle.main.url(forResource: filename, withExtension: nil) else {
        fatalError("\\(filename) not found")
    }
    
    // 예외처리: 파일이 있는가?
    do {
        data = try Data(contentsOf: file)
    } catch {
        fatalError("Could not load \\(filename) : \\(error)")
    }
    
    // 예외처리: 디코딩이 잘 완료되었는가?
    do {
        return try JSONDecoder().decode(T.self, from: data)
    } catch {
        fatalError("Unable to prase \\(filename) : \\(error)")
    }
}

데이터를 직접 만지는 코드이므로, ViewModel 폴더에 넣었다.

JSON을 [Car]타입으로 디코딩하여 carData 변수에 할당한다. 자동차 데이터는 carData에 담겨있고, 이제 이 변수를 사용하면 된다.

 

 

  • Car.swift
import Foundation

struct Car: Codable, Identifiable {
    var id: String
    var name: String
    var description: String
    var isHybrid: Bool
    var imageName: String
}

JSON 디코딩시 사용되는 타입이다.

JSON 형식에 맞춰서 구조체를 설계해야 한다.

JSON의 key값은 구조체의 프로퍼티 이름과 같다. 타입도 맞춰줘야 한다.

 

  • CarStore.swift
import SwiftUI
import Combine

class CarStore: ObservableObject {
    @Published var cars: [Car]
    
    init(cars: [Car] = []) { 
        self.cars = cars
    }
}

ObservableObject 클래스이다.

JSON으로 디코딩한 carData 값을 CarStore 인스턴스의 cars 프로퍼티로 초기화해주고.. 이를 마음껏 사용한다.

  • 아니 그냥 carData 쓰면 안되나요? 왜 귀찮게 CarStore 만들어요오
    • 지금 프로젝트에서는 그냥 carData를 전역으로 사용하고, CarDetail로 넘어갈 때 해당 데이터만 넘겨주면 동일하게 앱이 동작한다.
    • 그럼에도 불구하고 ObservableObject 클래스를 만들어서 진행하는 이유는… 중간에 Store을 만들어두면, DB가 바뀌어도 뷰에 관련된 코드는 따로 건드릴 필요 없이 Store 쪽 코드만 만져주면 된다. Store의 객체를 뷰에서 가져다 쓰니까! 
    • 그래서 지금은 딱히 Store의 진가가 보이지 않는 것이다. 

 

 

 

전기차 튜토리얼! 책에 있는 내용으로 공부했지만 이걸로 더 확장할 수 있겠다.. ㅎㅎㅎㅎ

  Comments,     Trackbacks