FREE TRIAL: Accelerate your app development career with Hacking with Swift+! >>

Split View depending on State: text updates, other view does not

Forums > macOS

Hi everyone,

Hope someone knows the answer to this issue, I have a NavigationView with on the left a list of vehicles build by using a ScrollView with 4 DisclosureGroups in it. Each DisclosureGroup has a list of vehicles belonging to that group. When I want to edit a vehicle I select it in the list and expect the details on the right (which is a separate view), which works the first time I click a vehicle. However, when I click the next vehicle the right pane doesn't update anymore. Putting just a text instead of a separate custom view does update. So in code I have this (stripped of the rest):

@State private var selectedVehicle: Vehicle?

NavigationView {
    ScrollView {
        if viewModel.cars.isEmpty == false {
            DisclosureGroup(isExpanded: $isExpanded, content: {
                ForEach(viewModel.cars) { vehicle in
                    Button(action: { selectedVehicle = vehicle }) {
                        VehicleRowView(vehicle: vehicle)
                    }
                     .buttonStyle(BorderlessButtonStyle())
                }
            }, label: { Text("Auto's") })
        }

        // 3 more DisclosureGroups here
    }

    if let vehicle = selectedVehicle {
        Text("\(vehicle.getLicensePlate())") // <- this update when pressing above button
        EditVehicleView(vehicle: vehicle) // <- this only works the 1st time
            .padding()
            .onDisappear { selectedVehicle = nil }
    } else {
        Text("Selecteer een voertuig")
    }
}

I tried creating a separate view for the bottom part, setting and reading from a Published property on the ViewModel and also using a Binding in EditVehicleView but it all results to the same. Works the first time, then only the text updates and not the EditVehicleView.

It does work when I add a toolbaritem in which I set selectedVehicle to nil first, but that isn't really a pleasant user experience as it still does not fix the issue above.

Does anyone have a clue if what I want is possible and if so, how to achive it?

   

So the code at the bottom

 if let vehicle = selectedVehicle {
        Text("\(vehicle.getLicensePlate())") // <- this update when pressing above button
        EditVehicleView(vehicle: vehicle) // <- this only works the 1st time
            .padding()
            .onDisappear { selectedVehicle = nil }
    } else {
        Text("Selecteer een voertuig")
    }

Does that appear at the bottom of the NavigationView view, or a different view?

Its not clear to me where vehicle is defined for that piece of code. I can see that selectedVehicle is set to nil when the UI disappers so unless selectedVehicle is updated again in some fashion, this could cause your problem.

It's worth printing out both vehicle and selectedVehicle just before this comparison test.

   

@eoinnorris the EditVehicleView will be displayed to the right of the list. Here are 2 screenshots: After first selection Changed selection

As you can see on the second image the license plate did change, that's the text you see in the if let part. Everything below that license plate text is the EditVehicleView which did not update. I added a print statement to the init of EditVehicleView and that does get triggered, only the view itself doesn't update somehow.

The selectedVehicle is updated in the Button you see in my first code, it's the 4th line after ScrollView. Somehow the UI for EditVehicleView is not redrawn when I click that button on a different element in the list.

When I put a breakpoint on the if let line and try to print the selectedVehicle in the console I get an error: error: Couldn't lookup symbols: FuelScanR.VehiclesView.selectedVehicle.getter : Swift.Optional<FuelScanR.Vehicle>

At that point vehicle is still <uninitialized> Stepping into the if let and printing the vehicle does give the correct vehicle object, which is displayed by the Text with licensePlate.

   

This is just a guess, but perhaps the views inside the "if let" will recognize the change in selectedVehicle if you reference selectedVehicle rather than the if let variable in your views, i.e.:

if selectedVehicle != nil {
        Text("\(selectedVehicle!.getLicensePlate())") // <- this update when pressing above button
        EditVehicleView(vehicle: selectedVehicle!) // <- this only works the 1st time
            .padding()
            .onDisappear { selectedVehicle = nil }
    } else {
        Text("Selecteer een voertuig")
    }

   

@bobstern that unfortunately leads to the exact same behaviour. The Text will update, the EditVehicleView does not.

So far I found that 2 things work:

1) Using a sheet, but that's not the desired UX.

2) Using a second @State variable called "selectedVehicle2" or whatever and make these changes:

Update the button in the ForEach to check which is nil and set the other @State variable:

Button(action: {
    if selectedVehicle == nil {
        selectedVehicle = vehicle
        selectedVehicle2 = nil
    } else {
        selectedVehicle2 = vehicle
        selectedVehicle = nil
    }
}) 

Check which "selectedVehicle" is active and show the EditVehicleView for it:

if selectedVehicle != nil {
    Text("\(selectedVehicle!.getLicensePlate())")
    EditVehicleView(vehicle: selectedVehicle!)
        .padding()
        .onDisappear { selectedVehicle = nil }
 } else if selectedVehicle2 != nil {
    Text("\(selectedVehicle2!.getLicensePlate())")
    EditVehicleView(vehicle: selectedVehicle2!)
        .padding()
        .onDisappear { selectedVehicle2 = nil }
} else {
    Text("Selecteer een voertuig")
}

It works but it really is a hack in my opinion which shouldn't be necessary. And I still think I did something wrong / did not understand something. So if someone has the correct / an other answer instead of this hack that would be great!

   

Hacking with Swift is sponsored by Sentry

SPONSORED With Sentry’s error and performance monitoring for iOS you see mobile vitals that actually matter, can solve any latency issues quickly, and learn how each release is performing over time.

Get started

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.