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)")
}
- 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의 진가가 보이지 않는 것이다.
전기차 튜토리얼! 책에 있는 내용으로 공부했지만 이걸로 더 확장할 수 있겠다.. ㅎㅎㅎㅎ
'외부활동 > 멋사 앱스쿨 1기' 카테고리의 다른 글
[SwiftUI] Graphic Drawing... 스유에서 도형을 그려보시겠습니까? (0) | 2022.12.09 |
---|---|
[SwiftUI] TabView, Context Menu를 사용해보자. (2) | 2022.12.09 |
[SwiftUI] Observable Object와 Environment Object에 대해 알아보자. (0) | 2022.12.09 |
[TIL] 22-10-21: SwiftUI 기초(룰렛 게임) (2) | 2022.10.25 |
[TIL] 22-10-20: Typography, SwiftUI 기초(숫자 게임) (0) | 2022.10.25 |