Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI EditButton() bug

Tags:

ios

swift

swiftui

I'm presenting a List inside a modal. If I'm inside a NavigationView the EditButton its totally broken.

enter image description here

struct ContentView: View {
@State var showSheetView = false

var body: some View {
    NavigationView {
        Button(action: {
            self.showSheetView.toggle()
        }) {
            Image(systemName: "bell.circle.fill")
                .font(Font.system(.title))
        }
        .sheet(isPresented: $showSheetView) {
            SheetView()
        }
    }
}
}

struct SheetView: View {
@State private var myArray: [String] = ["One", "Two", "Three"]
var body: some View {
    NavigationView {
        VStack {
            List {
                ForEach(myArray, id: \.self) { item in
                    Text(item)
                }.onDelete(perform: { indexSet in
                })
            }
        }
        .navigationBarItems(trailing: EditButton())
    }
}
}

If I remove the NavigationView where i present from, then at first it seems to work, the second time i present it gets broken again.

struct ContentView: View {
@State var showSheetView = false

var body: some View {
        Button(action: {
            self.showSheetView.toggle()
        }) {
            Image(systemName: "bell.circle.fill")
                .font(Font.system(.title))
        }
        .sheet(isPresented: $showSheetView) {
            SheetView()
        }
}
}

enter image description here

like image 484
Godfather Avatar asked Jan 24 '23 16:01

Godfather


2 Answers

Manually handling the editMode works for me on macOS Big Sur with Xcode 12.1 / iOS 14.1.

I also had a problem of EditButton showing "Edit" again in an edit mode when I rotate the simulator, and the below solution solves it as well.

The following solution uses a custom EditButton struct that handles a manual editMode binding. First the custom EditButton:

struct EditButton: View {
    @Binding var editMode: EditMode

    var body: some View {
        Button {
            switch editMode {
            case .active: editMode = .inactive
            case .inactive: editMode = .active
            default: break
            }
        } label: {
            if let isEditing = editMode.isEditing, isEditing {
                Text("Done")
            } else {
                Text("Edit")
            }
        }
    }
}

Using the above EditButton is straightforward:

struct SheetView: View {
    @State private var myArray: [String] = ["One", "Two", "Three"]
    @State private var editMode = EditMode.inactive

    var body: some View {
        NavigationView {
            VStack {
                List {
                    ForEach(myArray, id: \.self) { item in
                        Text(item)
                    }.onDelete(perform: { indexSet in
                    })
                }
            }
            .navigationBarItems(trailing: EditButton(editMode: $editMode))
            .environment(\.editMode, $editMode)
            .animation(.spring(response: 0))
        }
    }
}

The EditButton in the trailing navigation bar item handles the @State private var editMode kept in SheetView. This editMode is then injected into the inner views using the environment .environment(\.editMode, $editMode). For the animation effect of the edit mode transition, I found .spring(response: 0) most appropriate.

like image 179
Jay Lee Avatar answered Feb 10 '23 05:02

Jay Lee


Instead of

.navigationBarItems(trailing: EditButton())

you could try:

.toolbar { EditButton() }

I had the same problem and this worked fine for me.

like image 43
Roger Avatar answered Feb 10 '23 05:02

Roger