🙌
[SwiftUI]Routerを実装
SwiftUI
で遷移先をまとめるクラスRouter
という考え方で実装方法をまとめます。
2種類の遷移方法
画面遷移方法としては主に以下の2つになります。
-
sheet
を使ってモーダルで表示 -
NavigationView
とNavigationLink
を使ってプッシュ表示
動的にモーダル表示
sheet
を使う場合、基本的には1つのView
のみ表示することが多いですが、複数表示したい場合があります。
動的にモーダル表示する際は、以下を使用します。
public func sheet<Item, Content>(item: Binding<Item?>, onDismiss: (() -> Void)? = nil, @ViewBuilder content: @escaping (Item) -> Content) -> some View where Item : Identifiable, Content : View
ちなみに、iOS14
以下では.sheet
は1つのView要素に1つしか追加できません。
Routerの実装
extension Identifiable where Self: Hashable {
typealias ID = Self
var id: Self { self }
}
enum ContentRouter: View, Hashable, Identifiable {
case first
case second(text: String)
var body: some View {
switch self {
case .first: return AnyView(FirstView())
case .second(let text): return AnyView(SecondView(text: text))
}
}
}
ContentRouter
はView
、Hashable
、Identifiable
を継承しています。そうすることにより上記のsheet
が使えます。
また、enum
とすることにより、1つのRouterで複数の画面遷移先を定義することができます。SecondView
については、値の受け渡しもできるようにしました。
ContentViewの実装
struct ContentView: View {
@State var router: ContentRouter?
var body: some View {
NavigationView {
VStack(spacing: 16) {
Button.init("First") {
self.router = .first
}
Button.init("Second") {
self.router = .second(text: "Second")
}
}
}.sheet(item: self.$router) { $0 }
}
}
router: ContentRouter?
に値を入れることで、.sheet
が呼ばれます。さらに、ContentRouter
はView
を継承しているので、そのまま$0
として返却します。
結果
NavigationLinkを使って表示
動的に遷移先を変えることはできそうですが、実装が複雑になってしまうのでNavigationLink
を複数用意することが簡単です。
NavigationLinkを使って動的に表示したい場合。
動的に変更する場合の参考はこちらになります。
NavigationLinkとRouterを使って実装
上記のContentRouter
はそのまま使います。
struct ContentView: View {
@State var router: ContentRouter?
var body: some View {
NavigationView {
VStack(spacing: 16) {
Button.init("First") {
self.router = .first
}
Button.init("Second") {
self.router = .second(text: "Second")
}
NavigationLink("First Navigation", destination: ContentRouter.first)
NavigationLink(
destination: ContentRouter.second(text: "Second from Navi"),
label: {
Label.init("Second Navigation", systemImage: "moon")
})
}
}.sheet(item: self.$router) { $0 }
}
}
NavigationLink
のdestination
として、ContentRouter.first
などとしました。ContentRouter
として遷移先を定義しているので、実装がスッキリしています。
結果
サンプル
今回作ったサンプルはこちらです。
参考
Discussion