상세 컨텐츠

본문 제목

[SwiftUI] SwiftUI + Coordinator Pattern 기본편

IOS/SwiftUI

by 카키IOS 2024. 7. 24. 13:30

본문

타겟


준비

  • 화면 전환 기능을 담당하게 될 Coordinator 함수를 Protocol로 작성합니다 (Coordinator.swift)
  • 전환할 모든 화면을 enum 타입으로 작성합니다 (Destination.swift)
  • 전환할 View를 만듭니다

CoordinatorProtocol.swift

protocol CoordinatorProtocol {
    associatedtype T: Hashable

    func push(_ path: T)
    func pop()
    func popToView(_ to: T)
    func popToRoot()
}

Coordinator.swift

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)
    }
}

Destination.swift

// push로 화면전환할 case
enum Destination: Hashable {
    case view2
    case view3
    case view4
    case view5
}

RootView.swift

import SwiftUI

struct RootView: View {
    @ObservedObject private var coordinator = Coordinator<Destination>()

    var body: some View {
        NavigationStack(path: $coordinator.paths) {
            VStack {
                Text("Root View")

                Button {
                    coordinator.push(.view2)
                } label: {
                    Text("Go to second view")
                }
                .buttonStyle(.borderedProminent)
            }
            .navigationDestination(for: Destination.self) { destination in
                switch destination { // 화면 전환할 모든 화면
                case .view2:
                    ViewTwo()
                case .view3:
                    ViewThree()
                case .view4:
                    ViewFour()
                case .view5:
                    ViewFive()
                }
            }
        }
        .environmentObject(coordinator) // coordinator 환경변수 전달
    }
}

ViewTwo.swift … ViewFour.swift

import SwiftUI

struct ViewTwo: View {
    @EnvironmentObject private var coordinator: Coordinator<Destination>

    var body: some View {
        VStack {
            Text("View Two")

            Button {
                coordinator.push(.view3)
            } label: {
                Text("Go to third view")
            }
            .buttonStyle(.borderedProminent)
        }
    }
}

ViewFive.swift (LastView)

import SwiftUI

struct ViewFive: View {
    @EnvironmentObject private var coordinator: Coordinator<Destination>
    var body: some View {
        ZStack {
            Color.white.ignoresSafeArea()
            VStack {
                Text("Last View")

                Button {
                    coordinator.popToRoot() // 루트뷰로 이동
                } label: {
                    Text("Root View로 이동")
                }
                .buttonStyle(.borderedProminent)
            }
        }
    }
}
728x90
반응형

관련글 더보기