14

In SwiftUI how can I avoid having row content being indenting in my List when I move into Edit Mode, but not using onDelete? That is currently my row content is indented as this happens, as if the "delete" button will be shown on the left, however I'm not using onDelete so there is no button there.

Animated GIF

enter image description here

Code extract here:

var body: some View {
    VStack{
        List() {
            ForEach(gcTasks) { gcTask in
                HStack {
                    GCTaskRow(withGcTask: gcTask, haha: "")
                }
            }
            // .onDelete(perform: self.deleteTask)   // NOTE: HAVE REMOVED
            .onMove(perform: self.move)
        }
    }
    .environment(\.editMode, listInEditMode ? .constant(.active) : .constant(.inactive))
}

Background - Actually want to move to being in EDIT mode always, i.e. so always have the option of dragging row up/down, however will never use Delete hence want to review all traces of onDelete, in this case being the automatic indentation..

UPDATE: Another example (playgrounds) of how the unwanted indenting is occuring:

import SwiftUI
import PlaygroundSupport

let modelData: [String] = ["Eggs", "Milk", "Bread"]

struct ListTestNoDelete: View {
    private func move(from uiStartIndexSet: IndexSet, to uiDestIndex: Int) {
        print("On Move")
    }
    var body: some View {
        NavigationView {
            VStack {
                List {
                    ForEach(modelData, id: \.self) { str in
                        Text(str)
                    }
                    .onMove(perform: self.move)
                }
                .environment(\.editMode, .constant(.active))
                .navigationBarTitle( Text("Test") )
            }
        }
    }
}


let listTest = ListTestNoDelete()
PlaygroundPage.current.liveView = UIHostingController(rootView: listTest)

enter image description here

3 Answers 3

12

Maybe worth submitting request to Apple, but it is default behaviour. The following workaround is possible for now. Tested with Xcode 11.4 / iOS 13.4.

demo

struct DemoListEditMode: View {

    @State var items = ["a","b","c","d","e"]

    var body: some View {
        List {
            ForEach(items, id: \.self) { z in
                HStack {
                    Image(systemName: "checkmark.square") // just for demo
                    Text("\(z)")
                }
            }.onMove(perform: self.move)
            .listRowInsets(EdgeInsets(top: 0, leading: -24, // workaround !!
                bottom: 0, trailing: 0))                    
        }
        .environment(\.editMode, .constant(.active)) // persistent edit mode
    }

    func move(source: IndexSet, destination: Int) {
        self.items.move(fromOffsets: source, toOffset: destination)
    }
}
Sign up to request clarification or add additional context in comments.

4 Comments

actually I'm finding with this approach in edit mode that my Image onTapGesture is not picking up when the checkbox is pressed? Image(systemName: gcTask.completed ? "checkmark.square" : "square") .onTapGesture { print("Check box Tapped") self.gcTask.completed.toggle() GCCoreData.save() }
it seems like once the "leading" value goes more negative than about -8, then the checkbox stops picking up tap gestures...any ideas? (very close re work around)
@Greg did you ever find a fix for this? I'm having the same issue. It looks like a hidden UIView overlays that spot.
In my case I had to make .environment(\.editMode, .constant(.transient))
3

Maybe not a very elegant fix but you could extend the List to the left.

List {
    // ...
}
// .padding(.leading, self.isEditing == .inactive ? 0 : -39)
.padding(.leading, self.isEditing ? -45 : 0)

Update:

I created a ListSelectionBoxWidthReader to find the exact selection box width since it seems to be different per device and iOS version. You can find it in this gist: https://gist.github.com/Amzd/2e1c56675109a005f11c77a7f2dbe224

3 Comments

thanks - just saw this - this seems to work Casper (with slight adjustment)
@Greg I think this might scale differently per screen size, in my testing I found 39 to be the closest.
@CasperZandbergen, same here. I found 44 to be perfect for iPhone 11.
2
struct ContentView: View  {
    
    /// this is the padding that the list adds in left for delete button when in the edit mode.
    /// I calculated the constant and works fine on iPhone11 but you will need to check for each device separately for accurate offset for every device.
    let listEditModeOffsetX = -UIScreen.main.bounds.width * 0.10647343
    
    @State var modelData: [String] = ["Eggs", "Milk", "Bread","Cake"]
    @State var editingList = false
    
    private func move(from source: IndexSet, to destination: Int) {
        modelData.move(fromOffsets: source, toOffset: destination)
        editingList = false
        print("On Move")
    }
    
    var body: some View {
        NavigationView {
            VStack {
                List {
                    ForEach(modelData, id: \.self) { str in
                        Text(str)
                            .offset(x: self.editingList ? listEditModeOffsetX : 0)
                    }
                    .onMove(perform: self.move)
                    .onLongPressGesture{
                        self.editingList = true
                    }
                    
                }
                .environment(\.editMode, editingList ? .constant(.active):.constant(.inactive))
                .navigationBarTitle( Text("Test") )
            }
        }
    }
}
No more space on left side of list

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.