【问题标题】:SwiftUI detect edit modeSwiftUI 检测编辑模式
【发布时间】:2023-01-23 08:07:36
【问题描述】:

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

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

主要方法似乎是通过检测编辑模式的 if 语句来处理它。 Apple 文档在此开发人员页面上为这种方法提供了以下 sn-p: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()
    }
}

但是,这不起作用(我已经将 sn-p 嵌入到 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")))
            }
        }
    }

预览按预期工作,但这在我的真实应用程序中部分有效。当我实现这个时,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
                }
            )
         }
    }
}

更新3:

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

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

更新4:

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

更新5:

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

Asperi 的回答确实涵盖了为什么检测不起作用。我确实发现当手动设置编辑模式而不使用默认的 EditButton 时,编辑模式检测确实工作得更好,有关详细信息,请参见上面的答案。

【问题讨论】:

  • @Asperi 给出了一个很好的解决方法。但是因为它与文档不符,我已经向 Apple 提交了错误报告:FB10429307

标签: swift swiftui


【解决方案1】:

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

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

    Form {
        InternalView()
    }
    .toolbar {
        EditButton()
    }

使用 Xcode 13.4 / iOS 15.5 测试

Test module on GitHub

【讨论】:

    【解决方案2】:

    @Asperi 的回答对我很有效。但是我希望仍然能够访问同一层次结构中的 editMode。作为解决方法,我创建了以下内容:

    用法

    struct ContentView: View {
        @State
        private var editMode: EditMode = .inactive
    
        var body: some View {
            NavigationView {
                Form {
                    if editMode.isEditing == true {
                        Color.red
                    } else {
                        Color.blue
                    }
                }
                .editModeFix($editMode)
                .toolbar {
                    EditButton()
                }
            }
        }
    }
    

    执行

    extension View {
        func editModeFix(_ editMode: Binding<EditMode>) -> some View {
            modifier(EditModeFixViewModifier(editMode: editMode))
        }
    }
    
    private struct EditModeFixView: View {
        @Environment(.editMode)
        private var editModeEnvironment
        
        @Binding
        var editMode: EditMode
        
        var body: some View {
            Color.clear
                .onChange(of: editModeEnvironment?.wrappedValue) { editModeEnvironment in
                    if let editModeEnvironment = editModeEnvironment {
                        editMode = editModeEnvironment
                    }
                }
                .onChange(of: editMode) {
                    editModeEnvironment?.wrappedValue = $0
                }
        }
    }
    
    private struct EditModeFixViewModifier: ViewModifier {
        @Binding
        var editMode: EditMode
        
        func body(content: Content) -> some View {
            content
                .overlay {
                    EditModeFixView(editMode: $editMode)
                }
        }
    }
    

    【讨论】:

    • 这适用于 NavigationLink 吗?
    • 没试过,试一试,让我们知道
    【解决方案3】:

    我通过在 EditButton 上使用 .simultaneousGesture 并使用 @State 包装器来让它工作。

    struct EditingFix: View {
    @Environment(.editMode) var editMode
    @State var showDeleteButton = false
    
    var body: some View {
        Text("hello")
            .toolbar(content: {
                if showDeleteButton {
                    ToolbarItem(placement: .navigationBarLeading, content: {
                        Label("Remove selected", systemImage: "trash")
                            .foregroundColor(.red)
                    })
                }
                ToolbarItem(placement: .navigationBarTrailing, content: {
                    EditButton()
                        .simultaneousGesture(TapGesture().onEnded({
                            showDeleteButton.toggle()
                        }))
                })
            })
            .onChange(of: showDeleteButton, perform: { isEditing in
                editMode?.wrappedValue = isEditing ? .active : .inactive
            })
            .animation(.default, value: editMode?.wrappedValue) // Restore the default smooth animation for list selection and others
    }
    

    我可以肯定地说 EditButton 没有使用与调用 @Environment(.editMode) var editMode 时相同的 EditMode 环境。因此,如果我们想获得 EditButton 的好处,我们必须自己做所有事情。主要是它在我的案例中显示的本地化编辑文本。

    【讨论】:

      猜你喜欢
      • 2012-04-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-09-26
      • 1970-01-01
      • 2017-09-07
      • 1970-01-01
      相关资源
      最近更新 更多