💡 iOS 16 이상을 기준으로 작성된 코드입니다
이전 포스팅 기본편과 큰 차이는 없습니다
import SwiftUI
protocol CoordinatorProtocol {
associatedtype T: Hashable
func push(_ path: T)
func pop()
func popToView(_ to: T)
func popToRoot()
}
final class Coordinator<T: Hashable>: ObservableObject {
@Published var paths: [T] = []
}
extension Coordinator: CoordinatorProtocol {
func push(_ path: T) {
print("PATH: ", paths)
print("추가할 PATH: ", path)
paths.append(path)
print("Result PATH: ", paths)
}
func pop() {
print("PATH: ", paths)
paths.removeLast()
print("Result PATH: ", paths.count)
}
func popToView(_ to: T) {
print("PATH: ", paths)
guard let found = paths.firstIndex(where: { $0 == to }) else {
return
}
let numToPop = (found..<paths.endIndex).count - 1
paths.removeLast(numToPop)
print("Result PATH: ", paths)
}
func popToRoot() {
print("PATH: ", paths)
paths.removeAll()
print("Result PATH: ", paths)
}
}
import SwiftUI
struct RootView: View {
init() {
UITabBar.appearance().backgroundColor = UIColor.white
}
var body: some View {
TabView {
ViewTwo()
.tabItem {
VStack {
Image(systemName: "1.square.fill")
Text("Views")
}
}
ProductView()
.tabItem {
VStack {
Image(systemName: "2.square.fill")
Text("Products")
}
}
}
.background(Color.white)
}
}
enum ViewsDestination: Hashable {
case view3
case view4
case view5
case reuseable(type: DestinationType) //재사용할 화면
}
import SwiftUI
struct ViewTwo: View {
@ObservedObject private var coordinator = Coordinator<ViewsDestination>()
var body: some View {
NavigationStack(path: $coordinator.paths) {
VStack {
Text("View Two")
Button {
coordinator.push(.view3)
} label: {
Text("To View Three")
}
.buttonStyle(.borderedProminent)
}
.navigationDestination(for: ViewsDestination.self) { destination in
switch destination {
case .view3:
ViewThree()
case .view4:
ViewFour()
case .view5:
ViewFive()
case .reuseable:
ReuseableView(type: .views, text: "Views", coordinator: coordinator)
}
}
}
.environmentObject(coordinator)
}
}
import SwiftUI
struct ViewThree: View {
@EnvironmentObject private var coordinator: Coordinator<ViewsDestination>
var body: some View {
VStack {
Text("View Three")
Button {
coordinator.push(.view4)
} label: {
Text("To View Four")
}
.buttonStyle(.borderedProminent)
}
}
}
/*
ViewThree와 ViewFour는 동일합니다
*/
struct ViewFive: View {
@EnvironmentObject private var coordinator: Coordinator<ViewsDestination>
var body: some View {
ZStack {
Color.white.ignoresSafeArea()
VStack {
Text("ViewFive")
Button {
coordinator.push(.reuseable(type: .views))
} label: {
Text("Reuseable View로 이동")
}
.buttonStyle(.borderedProminent)
}
}
}
}
import SwiftUI
struct ReuseableView<T>: View where T: Hashable {
var type: DestinationType
var text: String
var coordinator: Coordinator<T>
init(type: DestinationType, text: String, coordinator: Coordinator<T>) {
self.type = type
self.text = text
self.coordinator = coordinator
}
var body: some View {
VStack {
Text(text)
Button {
coordinator.popToRoot() //Flow에 맞는 popToRoot 함수를 실행합니다
} label: {
Text("PopToRoot")
}
.buttonStyle(.borderedProminent)
}
}
}
struct Product: Hashable {
let name: String
let price: Int
}
enum ProductsDestination: Hashable {
// MARK: Product
case detail(data: Product)
case reuseable(type: DestinationType) //재사용할 뷰
}
import SwiftUI
struct ProductView: View {
@ObservedObject private var coordinator = Coordinator<ProductsDestination>()
@StateObject private var viewModel = ProductViewModel()
var body: some View {
NavigationStack(path: $coordinator.paths) {
ZStack {
Color.white.ignoresSafeArea()
VStack {
Button {
coordinator.push(.reuseable(type: .product))
} label: {
Text("Reuseable View로 이동")
}
List {
ForEach(viewModel.productData, id: \.self) { data in
ProductCell(product: Product(name: data.name, price: data.price))
.background(Color.white)
.onTapGesture {
coordinator.push(.detail(data: data))
}
}
}
}
}
.navigationDestination(for: ProductsDestination.self) { destination in
switch destination {
case .detail(let data):
ProductDetail(product: data)
case .reuseable:
ReuseableView(type: .product, text: "Products", coordinator: coordinator)
}
}
}
.environmentObject(coordinator)
}
}
import Foundation
final class ProductViewModel: ObservableObject {
@Published var productData: [Product] = [
Product(name: "레고", price: 12000),
Product(name: "카메라", price: 112000),
Product(name: "핸드폰", price: 1512000),
Product(name: "거치대", price: 2000),
Product(name: "테블릿", price: 1312000),
Product(name: "노트북", price: 3212000),
Product(name: "음료수", price: 2000),
Product(name: "냉장고", price: 5112000),
Product(name: "책상", price: 612000),
Product(name: "선반", price: 112000),
Product(name: "쓰레기봉투", price: 500),
Product(name: "선풍기", price: 52000),
Product(name: "텀블러", price: 42000),
Product(name: "키보드", price: 212000),
Product(name: "마우스", price: 112000),
]
}
import SwiftUI
struct ProductDetail: View {
@EnvironmentObject private var coordinator: Coordinator<ProductsDestination>
var product: Product
var body: some View {
VStack {
Text(product.name)
.font(.largeTitle)
Text("\(product.price)₩")
}
}
}
import SwiftUI
struct ProductCell: View {
var product: Product
var body: some View {
VStack(alignment: .leading) {
Text(product.name)
.frame(maxWidth: .infinity, alignment: .leading)
Text("가격: \(product.price) 원")
.frame(maxWidth: .infinity, alignment: .leading)
}
.padding(.all, 4)
}
}
[SwiftUI] SwiftUI + Coordinator Pattern 기본편 (0) | 2024.07.24 |
---|---|
[SwiftUI] AsyncImage - 이미지를 비동기 처리로 불러와보자 (1) | 2023.06.11 |