Reputation: 1993
I'm seeing inconsistent behavior when using NavigationStack inside a TabView. On my device (iOS 17.6.1) with Xcode 16.0, the "drilling down" stops working after navigating back to the root and tapping the button again. It works fine on the Simulator, and also on my device when building using Xcode 15.
If I remove the TabView
, everything functions as expected.
Any ideas if this is a bug with Xcode, the compiler, SwiftUI, or TCA? Or am I missing something here?
import ComposableArchitecture
import SwiftUI
@main
struct DemoApp: App {
var body: some Scene {
WindowGroup {
TabView {
NavigationStack {
FeatureView(store: .init(initialState: .init(), reducer: {
Feature()
}))
}
}
}
}
}
@Reducer
struct Feature: Equatable {
@Reducer(state: .equatable)
public enum Destination {
case drillDown(Feature)
}
@ObservableState
struct State: Equatable {
@Presents var destination: Destination.State?
}
enum Action {
case destination(PresentationAction<Destination.Action>)
case drillDownButtonTapped
}
public var body: some Reducer<State, Action> {
Reduce { state, action in
switch action {
case .destination:
return .none
case .drillDownButtonTapped:
state.destination = .drillDown(Feature.State())
return .none
}
}
.ifLet(\.$destination, action: \.destination)
}
}
struct FeatureView: View {
@Bindable var store: StoreOf<Feature>
var body: some View {
Button("Drill Down") {
store.send(.drillDownButtonTapped)
}
.navigationDestination(
item: $store.scope(state: \.destination?.drillDown, action: \.destination.drillDown)
) { store in
FeatureView(store: store)
}
}
}
Upvotes: 2
Views: 324
Reputation: 29
I've noticed this issue as well, it only happens on XCode 16.
I can confirm the issue is also related to having a Button
inside a TabView
. I noticed if you long press on the button it will register the tap.
A workaround is to add a long press gesture with a minimum duration of 0 to your Button
, in your body you can do this:
Note: make sure to remove the button action closure otherwise it may lead to your action being registered twice.
Button("Drill Down") {
// touch up
// no longer needed
}
.onLongPressGesture(minimumDuration: 0, perform: {}) { pressing in
if pressing {
// touch down
store.send(.drillDownButtonTapped)
}
}
.navigationDestination(
item: $store.scope(state: \.destination?.drillDown, action: \.destination.drillDown)
) { store in
FeatureView(store: store)
}
Upvotes: 0