🎨
【SwiftUI】ToolbarItem(placement: .principal)にNavigationLinkを配置する際の注意点
NavigationView
を使用している場合、ToolbarItem(placement: .principal)
にNavigationLink
を配置すると画面遷移が発火しないケースがありました。
画面遷移しないパターン
以下のように、ToolbarItem(placement: .principal)
内のNavigationLink
を
条件によって表示・非表示を切り替えられるようにすると、画面遷移が全く発火しなくなりました。
import SwiftUI
struct NavigationViewSampleView: View {
@State private var showItem = false
var body: some View {
NavigationView {
VStack {
Button(showItem ? "hide" : "show") {
showItem.toggle()
}
}
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .principal) {
if showItem {
NavigationLink("principal") {
Text("principal tapped")
}
}
}
ToolbarItem(placement: .primaryAction) {
if showItem {
NavigationLink("menu") {
Text("menu tapped")
}
}
}
}
}
}
}
条件分岐がなければ正しく画面遷移されますし、.primaryAction
の方は条件分岐があっても遷移するため、
特定条件下でのみ発生するバグかと思われます。
画面遷移するように修正する
ToolbarItem(placement: .principal)
内でNavigationLink
を使わず、
isActive
で遷移を管理するNavigationLink
を使えばこの問題は発生しなくなりました。
import SwiftUI
struct NavigationViewSampleView: View {
@State private var showItem = false
@State private var principalTapped = false
var body: some View {
NavigationView {
VStack {
Button(showItem ? "hide" : "show") {
showItem.toggle()
}
}
.background {
NavigationLink(isActive: $principalTapped) {
Text("principal tapped")
} label: {
EmptyView()
}
}
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .principal) {
if showItem {
Button("principal") {
principalTapped = true
}
}
}
ToolbarItem(placement: .primaryAction) {
if showItem {
NavigationLink("menu") {
Text("menu tapped")
}
}
}
}
}
}
}
また、NavigationStack
を使っていれば、ToolbarItem(placement: .principal)
内でNavigationLink
の表示制御をしていても問題なく画面遷移ができました。
import SwiftUI
struct NavigationStackSampleView: View {
@State private var showToolbar = false
var body: some View {
NavigationStack {
VStack {
Text("NavigationStackSampleView")
Button(showToolbar ? "hide" : "show") {
showToolbar.toggle()
}
}
.toolbar {
ToolbarItem(placement: .principal) {
if showToolbar {
NavigationLink("principal", value: "principal")
}
}
}
.navigationDestination(for: String.self) {
Text($0 + " tapped")
}
}
}
}
isActive
を用いたNavigationLink
は現在deprecatedになっているため、
iOS 16以降をターゲットとしたアプリであれば、素直にNavigationStack
を使った方が良いと思います。
Discussion