SwiftUI 检测编辑模式

Ded*_*oel 6 swift swiftui

一段时间后,我回到了 iOS 开发,并在 SwiftUI 中从头开始重建我的 Objective-C 应用程序。

我想做的一件事是使用默认的编辑模式来允许列表中的条目(由 CloudKit 上的核心数据支持)在 NavigationLink 到详细视图和编辑视图之间切换。

主要方法似乎是通过检测编辑模式的 if 语句来处理它。Apple 文档在此开发人员页面上提供了此方法的以下代码片段:https ://developer.apple.com/documentation/swiftui/editmode

@Environment(\.editMode) private var editMode
@State private var name = "Maria Ruiz"

var body: some View {
    Form {
        if editMode?.wrappedValue.isEditing == true {
            TextField("Name", text: $name)
        } else {
            Text(name)
        }
    }
    .animation(nil, value: editMode?.wrappedValue)
    .toolbar { // Assumes embedding this view in a NavigationView.
        EditButton()
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,这不起作用(我已按照假设将代码片段嵌入到 NavigationView 中)。这是 Xcode 13.4.1 中的错误吗?iOS 15.5?或者我做错了什么?

更新1:

根据 Asperi 的回答,我想出了以下通用视图来处理我的情况:

import SwiftUI

struct EditableRow: View {
#if os(iOS)
    @Environment(\.editMode) private var editMode
#endif
    
    @State var rowView: AnyView
    @State var detailView: AnyView
    @State var editView: AnyView

    var body: some View {
        NavigationLink{
            if(editMode?.wrappedValue.isEditing == true){
                editView
            }
            else{
                detailView
            }
            
        }label: {
            rowView
        }
    }
}

struct EditableRow_Previews: PreviewProvider {
    static var previews: some View {
        NavigationView {
            VStack {
                EditButton()
                EditableRow(rowView: AnyView(Text("Row")), detailView: AnyView(Text("Detail")), editView: AnyView(Text("Edit")))
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

预览按预期工作,但这在我的真实应用程序中部分工作。当我实现此功能时,NavigationLink 在不处于编辑模式时可以工作,但在编辑模式时不会执行任何操作。我还尝试将整个 NavigationLink 放入 if 语句中,但结果相同。知道为什么这不起作用吗?

更新2:

当它位于列表中时会发生一些事情。当我将预览更改为这显示了我得到的行为:

struct EditableRow_Previews: PreviewProvider {
    static var previews: some View {
        NavigationView {
            List {
                EditableRow(rowView: AnyView(GroupRow(title: "Title", subTitle: "Subtitle", type: GroupType.personal)), detailView: AnyView(EntryList()), editView: AnyView(Text("Edit")))
            }
            .navigationBarItems(trailing:
                HStack{
#if os(iOS)
                    EditButton()
#endif
                }
            )
         }
    }
}
Run Code Online (Sandbox Code Playgroud)

更新3:

找到这个答案:SwiftUI - EditMode 和PresentationMode Environment

这声称默认的 EditButton 已损坏,这似乎是真的。用自定义按钮替换默认按钮是可行的(请务必添加一个 withAnimation{} 块以从库存按钮获取所有行为。但它仍然不适用于我的 NavigationLink...

更新4:

好的,尝试将“isEditing”Bool 传递给上面的视图,而不是依赖于可用的环境变量。只要视图(在我的例子中是列表中的 ForEach)不处于“编辑模式”,无论此时发生什么都会破坏任何似乎的 NavigationLink,这都有效。

更新5:

基本上我的结论是,默认编辑模式旨在将“列表对象”作为一个整体进行编辑,从而可以移动和删除行。在这种模式下,Apple 认为编辑行本身并不是您想要做的事情。我可以看到这个观点。但是,如果您仍然想在编辑模式下从行启用 NavigationLink,这个答案应该会有所帮助: How to make SwiftUI NavigationLink work in edit mode?

Asperi 的答案确实涵盖了检测不起作用的原因。我确实发现,在手动设置编辑模式而不使用默认的 EditButton 时,编辑模式检测确实效果更好,请参阅上面的答案以了解详细信息。

Asp*_*eri 3

它位于同一级别,因此环境不可见,因为它已针对子视图激活。

一个可能的解决方案是将依赖部分分离到独立视图中,例如

    Form {
        InternalView()
    }
    .toolbar {
        EditButton()
    }
Run Code Online (Sandbox Code Playgroud)

使用 Xcode 13.4 / iOS 15.5 进行测试

演示

GitHub 上的测试模块