0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

SwiftUIで画面遷移をViewから剥がして独立させる

Last updated at Posted at 2024-02-25

単にNavigationStackを使いやすくカスタマイズしただけです

注意

NavigationStackを使用するためiOS16以上をターゲットにしたプロジェクトが対象です。

課題

Viewに画面遷移の責務を持たせたくない

理由

・Viewに画面遷移の役割を持たせるのが責務として肥大化してしまうから
・筆者はUIKitを使用しての画面遷移の際にRouterクラスを作成してそこに画面遷移の責務を持たせる形に慣れ親しんでいたため

対応

使い方

// Stackさせるベースのところ
var body: some View {
    CustomNavigationStack(.home) {
        Text("Aの画面ですよ")
    }
}

class ARouter {
    // 遷移の仕方
    func navigateToB() {
        RouterPath.shared.addToHome(path: .home2)
    }
}

中身

struct CustomNavigationStack<Content>: View where Content: View {
    let pathType: RouterPathType
    @ViewBuilder let root: () -> Content
    
    @ObservedObject private var sharedRouterPath = RouterPath.shared
    
    var body: some View {
        switch pathType {
        case .home:
            navigationStack(path: $sharedRouterPath.homeRouterPath)

        case .profile:
            navigationStack(path: $sharedRouterPath.profileRouterPath)
        }
    }
    
    @ViewBuilder private func navigationStack<Data>(
        path: Binding<Data>
    ) -> some View
    where Data : MutableCollection,
          Data : RandomAccessCollection,
          Data : RangeReplaceableCollection,
          Data.Element : RouterPathProtocol
    {
        NavigationStack(path: path) {
            root()
                .navigationDestination(for: Data.Element.self)
        }
    }
}
class RouterPath: ObservableObject {
    static var shared = RouterPath()

    // このプロパティと下のRouterPathTypeが1対1になる
    @Published var homeRouterPath = [HomeRouterPath]()
    @Published var profileRouterPath = [ProfileRouterPath]()
    
    func addToProfile(newView: ProfileRouterPath) {
        profileRouterPath.append(newView)
    }
    
    func addToProfile(path: ProfileRouterPath) {
        profileRouterPath.append(path)
    }
}
enum RouterPathType {
    // ホームタブ
    case home
    // プロフィールタブ
    case profile
}
protocol RouterPathProtocol: Hashable {
    associatedtype Content: View
    var destinationView: Content { get }
}

enum HomeRouterPath: RouterPathProtocol {
    case home2
    case home3
    
    var destinationView: some View {
        switch self {
        case .home2:
            Text("home2編集")
        case .home3:
            Text("home3作成")
        }
    }
}
extension View {
    func navigationDestination<D>(
        for data: D.Type
    ) -> some View where D : RouterPathProtocol {
        return navigationDestination(for: data) { path in
            return path.destinationView
        }
    }
}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?