Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS14 introducing errors with @State bindings

The below swiftUI code was working fine with iOS13, but on testing with iOS14, I'm getting fatal errors caused by the force-unwrapped optional when trying to display the modal sheet. As far as I can tell, the sheet should never try to present with a nil value for selectedModel, as showingDetails is only ever made true after assigning selectedModel?

struct SpeakerBrandMenu: View {
    var filteredSpeakers: [Speaker] {
        // An array of Speaker objects
        }
        
    @State var selectedModel: Speaker?
    @State private var showingDetails = false

    var body: some View {
        List{       
        ForEach(filteredSpeakers) { speaker in
            HStack {
                Button(action: {
                    self.selectedModel = speaker
                    self.showingDetails = true
                }) {
                    SpeakerModelRow(speaker: speaker).contentShape(Rectangle())
                }
                .buttonStyle(PlainButtonStyle())

                Spacer()
                
                Button(
                //unrelated
                ).padding(5)
              }
            }
        } .sheet(isPresented: self.$showingDetails) { SpeakerDetailView(speaker: self.selectedModel!, showSheet: self.$showingDetails).environmentObject(self.favoriteSpeakers).environmentObject(self.settings)}
               
          .navigationBarTitle(Text(brand), displayMode: .inline)
    }
}

Interestingly, if I unwrap it as speaker: self.selectedModel ?? filteredSpeakers[0]it behaves exactly as expected: The first time pressing any of the menu items, the first item is passed to the sheet, but on dismissing the sheet and selecting another item it then shows the correct item every time. So it's as though the button to assign selectedModel is trying to display the sheet before it has had time assign it.

like image 435
Neil Avatar asked Sep 16 '20 22:09

Neil


1 Answers

It looks like in iOS 14 the sheet(isPresented:content:) is now created beforehand, so any changes made to selectedModel are ignored.

Try using sheet(item:content:) instead:

var body: some View {
    List {
        ...
    }
    .sheet(item: self.$selectedModel) {
        SpeakerDetailView(speaker: $0)
    }
}

and dismiss the sheet using @Environment(\.presentationMode):

struct SpeakerDetailView: View {
    @Environment(\.presentationMode) private var presentationMode
    var speaker: Speaker

    var body: some View {
        Text("Speaker view")
            .onTapGesture {
                presentationMode.wrappedValue.dismiss()
            }
    }
}
like image 150
pawello2222 Avatar answered Nov 13 '22 09:11

pawello2222