SwiftUI - 如何在编辑模式下避免列表中的行缩进,但不使用 onDelete? (附code/video)
SwiftUI - how to avoid row indenting in List when in Edit Mode, but not using onDelete? (code/video attached)
在 SwiftUI 中,当我进入编辑模式但不使用 onDelete 时,如何避免行内容在我的列表中缩进?这是当前我的行内容在发生这种情况时缩进,就好像 "delete" 按钮将显示在左侧,但我没有使用 onDelete,所以那里没有按钮。
GIF 动画
代码摘录在这里:
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))
}
背景 - 实际上想要一直处于编辑模式,即总是可以选择拖动行 up/down,但是永远不会使用删除因此想要查看 onDelete 的所有痕迹,在这种情况下作为自动缩进..
更新:关于如何发生不需要的缩进的另一个例子(游乐场):
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)
也许值得向 Apple 提交请求,但这是默认行为。目前可以使用以下解决方法。使用 Xcode 11.4 / iOS 13.4.
测试
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)
}
}
也许不是一个非常优雅的修复,但您可以将列表扩展到左侧。
List {
// ...
}
// .padding(.leading, self.isEditing == .inactive ? 0 : -39)
.padding(.leading, self.isEditing ? -45 : 0)
更新:
我创建了一个 ListSelectionBoxWidthReader 来查找确切的选择框宽度,因为它似乎因设备和 iOS 版本而异。你可以在这个要点中找到它:https://gist.github.com/Amzd/2e1c56675109a005f11c77a7f2dbe224
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") )
}
}
}
}
在 SwiftUI 中,当我进入编辑模式但不使用 onDelete 时,如何避免行内容在我的列表中缩进?这是当前我的行内容在发生这种情况时缩进,就好像 "delete" 按钮将显示在左侧,但我没有使用 onDelete,所以那里没有按钮。
GIF 动画
代码摘录在这里:
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))
}
背景 - 实际上想要一直处于编辑模式,即总是可以选择拖动行 up/down,但是永远不会使用删除因此想要查看 onDelete 的所有痕迹,在这种情况下作为自动缩进..
更新:关于如何发生不需要的缩进的另一个例子(游乐场):
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)
也许值得向 Apple 提交请求,但这是默认行为。目前可以使用以下解决方法。使用 Xcode 11.4 / iOS 13.4.
测试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)
}
}
也许不是一个非常优雅的修复,但您可以将列表扩展到左侧。
List {
// ...
}
// .padding(.leading, self.isEditing == .inactive ? 0 : -39)
.padding(.leading, self.isEditing ? -45 : 0)
更新:
我创建了一个 ListSelectionBoxWidthReader 来查找确切的选择框宽度,因为它似乎因设备和 iOS 版本而异。你可以在这个要点中找到它:https://gist.github.com/Amzd/2e1c56675109a005f11c77a7f2dbe224
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") )
}
}
}
}