NavigationStack を使った動的な画面遷移の実装手順
概要
本記事では、NavigationStackのpathを活用し、動的に画面遷移を管理する方法を解説します。
アプリ全体で遷移管理を一元化するために、AppRouterというクラスを作成し、@Environment
を使って各Viewからアクセスできるようにしました。
サンプルコードは GitHub に公開していますので、ぜひご活用ください!🚀
実装手順
1. Destinationの作成
まず、NavigationStackで使用する遷移先の画面(Destination)を管理するenumを作成します。
Destinationは、Hashableに準拠する必要があります。
また、画面によっては パラメータを渡す必要があるため、連想値を持たせることが可能です。
さらに、makeView()を定義することで、Destinationに応じたViewを動的に返す仕組みを作ります。
enum Destination: Hashable {
case home
case activity
case profile(User)
case settings
@ViewBuilder
func makeView() -> some View {
switch self {
case .home: HomeView()
case .activity: ActivityView()
case .profile(let user): ProfileView(user: user)
case .settings: SettingsView()
}
}
}
2. AppRouterの作成
次に、アプリ全体の画面遷移を管理するAppRouterを作成します。
NavigationStackのpathに、配列操作のようにDestinationを追加・削除するだけで画面遷移が可能です。
@MainActor
@Observable
final class AppRouter {
var path: [Destination] = []
func push(_ destination: Destination) {
path.append(destination)
}
// ルート画面に戻り、指定したdestinationに遷移
func pushAndRemoveUntil(_ destination: Destination) {
path.removeAll()
path.append(destination)
}
// 現在のViewをdestinationに置き換えて遷移
func pushReplacement(_ destination: Destination) {
if !path.isEmpty {
path.removeLast()
}
path.append(destination)
}
func pop() {
if !path.isEmpty {
path.removeLast()
}
}
func popToRoot() {
path.removeAll()
}
// 指定したdestinationまで戻る
func popUntil(_ target: Destination) {
while path.last != target && !path.isEmpty {
path.removeLast()
}
}
}
3. View側でNavigationStackの実装
AppRouterを@Environment
に登録することで、アプリ全体で遷移管理が可能になります。
まず、App内で@State
を使ってAppRouterを保持し、environmentモディファイアで子View(RootView)にrouterを渡します。
@main
struct NavigationPracticeApp: App {
@State private var router: AppRouter = .init()
var body: some Scene {
WindowGroup {
RootView()
.environment(router)
}
}
}
struct RootView: View {
@Environment(AppRouter.self) var router
var body: some View {
@Bindable var router = router
NavigationStack(path: $router.path) {
VStack(spacing: 40) {
CustomButton("Home") {
router.push(.home)
}
CustomButton("Settings") {
router.push(.settings)
}
}
.padding()
.navigationDestination(for: Destination.self) { destination in
destination.makeView()
}
}
}
}
#Preview {
RootView()
.environment(AppRouter())
}
@Environment(AppRouter.self)を使えば、
どのViewでもrouter.push(.xxx))
のように簡単に遷移できます!
struct HomeView: View {
@Environment(AppRouter.self) var router
var body: some View {
VStack {
CustomButton("Profile") {
router.push(.profile(User(id: 1, name: "tomo")))
}
}
}
}
#Preview {
HomeView()
.environment(AppRouter())
}
この方法を活用すれば、アプリの画面遷移がシンプルかつ柔軟になります!
サンプルコードでは、RootViewや特定の画面戻る実装もしてありますので、参考にしていただければと思います。
他にもいい画面遷移の実装があれば、ご指摘いただければ幸いです。
参考記事
Discussion