0

It seems like the EditButton() in SwiftUI (Xcode 12.5 beta 3) has various issues.

In my code, everything was working fine until I replaced the List with a ScrollView and added a LazyVGrid. Now, when a user taps on the EditButton, EditMode is not activated.

Any ideas for a workaround? Having 2 columns is a requirement of the UI, and while I could work with a list I prefer the look of ScrollView. I've tried numerous things... putting the ForEach in a Section and putting the EditButton in the header, replacing it with a manual button... unfortunately none of them seem to work :-(

Many thanks for any thoughts or anything anyone else has done to get round this.

struct Home: View {
    @Environment(\.managedObjectContext) private var viewContext
    @FetchRequest(entity: Cars.entity(), sortDescriptors: []) var cars: FetchedResults<Cars>
    
    private var columns: [GridItem] = [
            GridItem(.flexible()),
            GridItem(.flexible())
        ]

    var body: some View {
  
        NavigationView {
            ScrollView {

                if cars.count > 0 {
                    LazyVGrid(
                        columns: columns) {
                
                ForEach(cars) { n in
                    Text("hello")
                }
                .onDelete(perform: deleteCars)
                    }    
                }
                
                else {
                    Text("You have no cars.")
                }   
            } 
            .navigationBarItems(leading: EditButton())       
        }   
    }
    
    func deleteCars(at offsets: IndexSet) {
        for offset in offsets {
            let cars = cars[offset]
            viewContext.delete(cars)
        }
        try? viewContext.save()
    }
    
}

Attempt 1

After reading Asperi's comments below, I have added the following (below) to the ScrollView to manually create the button, trigger EditMode and remove items. Now I am getting a new error on the line deleteCars: "Initializer 'init(_:)' requires that 'FetchedResults.Element' (aka 'Cars') conform to 'Sequence'".

It seems like I am really close, but still struggling - can anyone help me with the final piece of this? Many thanks!

@State var isEditing = false

...

ScrollView {

                if cars.count > 0 {
                    LazyVGrid(
                        columns: columns) {
                        ForEach(cars) { n in
                    Text("hello")
                    Button(action : {
                        deleteCars(at: IndexSet(n))
                        print("item deleted")
                    })
                    
                    {Text("\(isEditing ? "Delete me" : "not editing")")}
                }
                .onDelete(perform: deleteCars)
                .environment(\.editMode, .constant(self.isEditing ? EditMode.active : EditMode.inactive)).animation(Animation.spring())
                    }
                }
                else {
                    Text("You have no cars.")
                }
                Button(action: {
                                    self.isEditing.toggle()
                                }) {
                                    Text(isEditing ? "Done" : "Edit")
                                        .frame(width: 80, height: 40)
                                }
            
            }
1
  • 4
    EditMode is activate, just ScrollView does not have such built-in functionality as List has for EditMode. If you want some your need to handle it manually. Commented Apr 21, 2021 at 17:13

1 Answer 1

0

TLDR: manually create an EditButton and add it directly inside the ForEach loop. Make sure the ForEach array has indices specified.

Ok, finally found an answer following progress in Attempt 1 above... worked by adding .indices to the array in the ForEach loop. Here goes in full:

struct Home: View {
    @Environment(\.managedObjectContext) private var viewContext
    @FetchRequest(entity: Cars.entity(), sortDescriptors: []) var cars: FetchedResults<Cars>

    @State var isEditing = false
    
    private var columns: [GridItem] = [
            GridItem(.flexible()),
            GridItem(.flexible())
        ]

    var body: some View {
  
        NavigationView {
            VStack{   
            ScrollView {

                if cars.count > 0 {
                    LazyVGrid(
                        columns: columns) {
                    ForEach(cars.indices, id: \.self) { n in
                    Text("hello")
                    Button(action : {
                        deleteCars(at: [n])
                        print("item deleted")
                    })
                    
                    {Text("\(isEditing ? "Delete me" : "not editing")")}
                        }
                }
                .environment(\.editMode, .constant(self.isEditing ? EditMode.active : EditMode.inactive)).animation(Animation.spring())
                    }
                }
                else {
                    Text("You have no cars.")
                }        
            }
            .navigationBarItems(leading: Button(action: {
                self.isEditing.toggle()
            }) {
                Text(isEditing ? "Done" : "Edit")
                    .frame(width: 80, height: 40)
            }
            ) 
        }   
    }
    
    func deleteCars(at offsets: IndexSet) {
        for offset in offsets {
            let cars = cars[offset]
            viewContext.delete(cars)
        }
        try? viewContext.save()
    }
    
}

I am sure there are many ways to optimise this (both the code and the way the question/answer are written, so do feel free to suggest them.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.