単に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
}
}
}