ユニットテストを書き始めたところ、テストがしやすいコードとしにくいコードの差を強く意識するようになり、以前から非推奨の警告が出ていたNavigationViewを新しいNavigationStackに書き換えることにしました。単純な置き換えで済むと思っていたのですが、想像以上に手こずりました。
1. NavigationLinkをそのまま使ったら画面遷移が二重になった
最初はNavigationViewをNavigationStackに書き換えただけで、中身のNavigationLinkはそのまま残していました。
NavigationStack {
List(items) { item in
NavigationLink(item.title, destination: DetailView(item: item))
}
}
これ自体はほぼ問題なく動いたのですが、別の画面でプログラムから画面遷移を制御しようとした際に、従来のやり方が通用しないことに気づきました。NavigationStackは画面遷移の履歴を「パス」として管理する仕組みに変わっており、単純な置き換えでは済まない設計変更だったと理解しました。
2. プログラムによる画面遷移をどう書けばいいか分からなかった
ボタンを押した処理の中から明示的に次の画面へ遷移させたかったのですが、NavigationView時代のやり方が通用せず、書き方を一から調べ直すことになりました。
@State private var path = NavigationPath()
NavigationStack(path: $path) {
// ...
}
// 遷移させたい処理の中で
path.append(item)
pathという配列のようなものに値を追加するだけで画面遷移が起きる、という考え方に最初は戸惑いました。「遷移先のビューを直接指定する」のではなく、「遷移の履歴(パス)を操作する」という発想に変わったのだと理解してから、書き方に納得がいくようになりました。
3. 型の異なる遷移先が混在する画面で詰まった
一覧画面から複数の異なる種類の詳細画面(記事詳細と、設定画面など)に遷移させたい場面で、NavigationPathに色々な型を混ぜて追加しようとしたところ、遷移先の判定でエラーが出ました。
.navigationDestination(for: Item.self) { item in
DetailView(item: item)
}
.navigationDestination(for: Settings.self) { settings in
SettingsView(settings: settings)
}
navigationDestinationを遷移先の型ごとに複数用意することで、異なる種類の画面を1つのNavigationStackで扱えると分かりました。型ごとに「この型が来たらこの画面を表示する」というルールを登録しておく、という発想に慣れるまで少し時間がかかりました。
4. 一部の画面だけ書き換えて中途半端に混在させ、余計に混乱した
時間の都合で一部の画面だけNavigationStackに書き換え、残りはNavigationViewのまま放置していたところ、async/awaitの書き換えのときと同じように、中途半端な混在がかえって理解を難しくしました。
新旧の仕組みが並行して存在すると、画面遷移まわりの不具合が起きたときにどちらの仕組みの問題か切り分けにくくなります。書き換えるなら一気に全画面を移行してしまう方が、結果的に迷いなく進められると学びました。
まとめ
NavigationStackは画面遷移を「パス(履歴の配列)」として管理する仕組みで、単純な置き換えでは済まない- プログラムによる画面遷移は、パスに値を追加・削除する形で行う
- 異なる型の遷移先は
navigationDestinationを型ごとに複数用意することで扱える - 新旧の仕組みを中途半端に混在させると、不具合の切り分けが難しくなる。書き換えるなら一気に進める方がよい
見た目の変更が少ない分、油断していましたが、画面遷移の管理方法そのものが変わっているのだと理解してからは、落ち着いて対応できるようになりました。
次は、クラッシュレポートの確認・分析でやっていることをまとめます。
本記事は生成AI(Claude)を活用して執筆・編集しています。内容は実体験に基づき、執筆者本人が確認・監修しています。


コメント