3

IOS 16 just came out and I was trying to update my app. While doing this, I stumbled upon a problem:

In IOS 15, I had a list where you could delete items. But this was only possible when the isEditing variable is true. The problem is that .deleteDisabled() does not do what I expect it to do.

I’ll explain what happens:

.deleteDisabled(isEditing ? true : false) -> Is Editing = Does not let me edit. Is Not Editing = Lets me edit

.deleteDisabled(isEditing ? false : true) -> Is Editing = Does not let me edit. Is Not Editing = Does not let me edit

What I want is to be able to ONLY edit when isEditing = true. This worked fine in IOS 15 but not in IOS 16

Is this a bug? Is there a workaround?

Under is the complete code where the problem occurs:

import SwiftUI

struct ContentView: View {
    @State var isEditing = false
    @Environment(\.editMode) var mode
    
    @State private var fruits = [
        "Apple",
        "Banana",
        "Papaya",
        "Mango"
    ]
    
    var body: some View {
        NavigationView {
            VStack{
                List {
                    ForEach(fruits, id: \.self) { fruit in
                        Text(fruit)
                    }
                    .onDelete { fruits.remove(atOffsets: $0) }
                    .deleteDisabled(isEditing ? false : true) // Does not work as expected
                    //.deleteDisabled(isEditing ? true : false) // Works as expected
                }
                
            }
            .environment(\.editMode, .constant(self.isEditing ? EditMode.active : EditMode.inactive))
            .animation(Animation.spring(), value: isEditing)
            .navigationTitle("Fruits")
            .toolbar {
                ToolbarItem(placement: .navigationBarTrailing){
                    Button(action: {
                        isEditing.toggle()
                    }, label: {
                        if(isEditing == true){
                            Text("Done")
                        }
                        else{
                            Text("Edit")
                        }
                    })
                }
                
                //EditButton()
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
  • `Bool`s can be tricky. You are dealing with `deleteDisabled` (a negative) you are expecting it to work as `deleteEnabled` (a positive) if it was working before it was like a bug. – lorem ipsum Sep 13 '22 at 15:22
  • Huh, thought I found it through a tutorial but that is a bummer. Is there a way to do what I want to without using this bug? – Even Finnøy Oct 04 '22 at 12:06
  • Workaround in this answer here: https://stackoverflow.com/a/74425946/20496253 – Matt Nov 14 '22 at 00:57

1 Answers1

1

I found a workaround after a while. I'm not really sure why this abomination works... but it does:

var isEditing: Bool {
    editMode?.wrappedValue.isEditing == true
}

@ViewBuilder
var someSection: some View { Section { ... } } 

var body: some View {
    if !isEditing {
        someSection
        .deleteDisabled(true)
    } else {
        someSection
        .deleteDisabled(false)
    }
}

This doesn't (as of iOS 16.2):

...

var body: some View {
    someSection.deleteDisabled(!isEditing)
}
David
  • 2,109
  • 1
  • 22
  • 27
  • Maybe instead of the `if` you can tag with an ID (`.id(isEditing)`)? Still, changing the view hierarchy is a terrible solution (but that's Apple's fault). – George Mar 09 '23 at 21:27
  • 1
    I don't know if it'll work in this case, but I've found writing something like `let _ = isEditing` in the body works to fix some of my problems. I presume this works as SwiftUI sees that a variable is used in that part of the `body` so it needs to get the updated value. – George Mar 09 '23 at 21:28
  • This sort of works for me, however I had to also add a `.id(isEditing)` to my list per @George's suggestion above. Now, everything works for me EXCEPT when I enter/exit edit mode, the list scrolls to the top (since the id is changing, swiftui sees it as a whole new list). This is not ideal, as you lose context and have to scroll back to the rows you want to select. Rant: I am amazed at how frustrating it has been to get a selectable list that also supports swipe to delete in swiftui. I never imagined I would encounter so many hurdles trying to do such a simple thing. – finiteloop Aug 30 '23 at 19:17