1
votes

I've come to SwiftUI from UIKit and I'm having trouble with a NavigationLink not animating when presenting a new View.

I've set up the view structure to include the NavigationLink when the following property is non-nil:

@State private var retrievedDeviceIdentity: Proteus.DeviceIdentity?

The Proteus.DeviceIdentity type is a basic data struct. This property is populated by a successful asynchronous closure, rather than a direct user interaction. Hence, the view structure is set up like so, using NavigationLink's destination:isActive:label: initialiser:

var body: some View {
    NavigationView {
        VStack {
            Form {
                // Form building
            }
            
            if let deviceIdentity = retrievedDeviceIdentity {
                NavigationLink(
                    destination: AddDeviceLinkDeviceForm(deviceIdentity: deviceIdentity),
                    isActive: .constant(retrievedDeviceIdentity != nil),
                    label: {
                        EmptyView()
                    }
                )
                .onDisappear() {
                    updateSyncButtonEnabledState()
                }
            }
        }
    }
}

When retrievedDeviceIdentity is populated to be non-nil the new View is indeed presented. However, there is no slide transition to that View; it just changes immediately. When in that new view, tapping on the back button does do the slide transition back to this view.

Any ideas how to fix this? As I'm pretty new to SwiftUI if I've set the new structure up wrong then I'd welcome feedback on that too.

(I'm using Xcode 12.3 on macOS Big Sur 11.0.1.)

2

2 Answers

1
votes

I think it is due to conditional injection, try instead to have it persistently included in view hierarchy (and so be registered in NavigationView), like

VStack {
    Form {
        // Form building
    }
}
.background(
    NavigationLink(
        destination: AddDeviceLinkDeviceForm(deviceIdentity: retrievedDeviceIdentity),
        isActive: .constant(retrievedDeviceIdentity != nil),
        label: {
            EmptyView()
        }
    )
    .onDisappear() {
        updateSyncButtonEnabledState()
    }
)

Note: I'm not sure about your expectation for .onDisappear and why do you need it, probably it will be needed to move in some other place or under different modifier.

0
votes

@Asperi got close, but moving the NavigationLink led to the view not presenting at all.

What did work was removing the if brace unwrapping retrievedDeviceIdentity:

var body: some View {
NavigationView {
    VStack {
        Form {
            // Form building
        }
        
        NavigationLink(
            destination: AddDeviceLinkDeviceForm(deviceIdentity: deviceIdentity),
            isActive: .constant(retrievedDeviceIdentity != nil),
            label: {
                EmptyView()
            }
        )
        .onDisappear() {
            updateSyncButtonEnabledState()
        }
    }
}

This required AddDeviceLinkDeviceForm's deviceIdentity property to be made optional to accept the wrapped value.