【问题标题】:SwiftUI and the three-finger undo gestureSwiftUI 和三指撤消手势
【发布时间】:2020-01-15 19:34:54
【问题描述】:

我正在尝试在 iOS 的 SwiftUI 应用程序中实现撤消,但我无法让撤消手势起作用。这是一个演示问题的示例:

class Model: ObservableObject {
    @Published var active = false

    func registerUndo(_ newValue: Bool, in undoManager: UndoManager?) {
        let oldValue = active
        undoManager?.registerUndo(withTarget: self) { target in
            target.active = oldValue
        }
        active = newValue
    }
}

struct UndoTest: View {
    @ObservedObject private var model = Model()
    @Environment(\.undoManager) var undoManager

    var body: some View {
        VStack {
            Toggle(isOn: Binding<Bool>(
                get: { self.model.active },
                set: { self.model.registerUndo($0, in: self.undoManager) }
            )) {
                Text("Toggle")
            }
            .frame(width: 120)
            Button(action: {
                self.undoManager?.undo()
            }, label: {
                Text("Undo")
                    .foregroundColor(.white)
                    .padding()
                    .background(self.undoManager?.canUndo == true ? Color.blue : Color.gray)
            })
        }
    }
}

切换开关然后点击撤消按钮可以正常工作。使用三指撤消手势或摇晃撤消不会执行任何操作。你如何绑定系统手势?

【问题讨论】:

    标签: ios swiftui undo


    【解决方案1】:

    编辑手势似乎要求窗口有第一响应者,而 SwiftUI 没有设置任何 UIWindow 默认选择作为第一响应者的东西。

    如果您将UIHostingController 子类化,并且在您的子类中覆盖canBecomeFirstResponder 以返回true,那么UIWindow 将默认将您的控制器设置为第一响应者,这似乎足以启用编辑手势。

    我在运行 iPadOS 13.1 beta 2 (17A5831c) 的 iPad Pro 上测试了以下代码。它主要工作。我相信有一个 iOS 错误,可能在较新的测试版中修复:当撤消堆栈为空时,手势有时不起作用(即使重做操作是可能的)。切换到主屏幕,然后返回测试应用程序(不终止测试应用程序)似乎使编辑手势再次起作用。

    import UIKit
    import SwiftUI
    
    class MyHostingController<Content: View>: UIHostingController<Content> {
        override var canBecomeFirstResponder: Bool { true }
    }
    
    class Model: ObservableObject {
        init(undoManager: UndoManager) {
            self.undoManager = undoManager
        }
    
        let undoManager: UndoManager
    
        @Published var active = false {
            willSet {
                let oldValue = active
                undoManager.registerUndo(withTarget: self) { me in
                    me.active = oldValue
                }
            }
        }
    }
    
    struct ContentView: View {
        @ObservedObject var model: Model
        @Environment(\.undoManager) var undoManager
    
        var body: some View {
            VStack {
                Toggle("Active", isOn: $model.active)
                    .frame(width: 120)
                HStack {
                    Button("Undo") {
                        withAnimation {
                            self.undoManager?.undo()
                        }
                    }.disabled(!(undoManager?.canUndo ?? false))
                    Button("Redo") {
                        withAnimation {
                            self.undoManager?.redo()
                        }
                    }.disabled(!(undoManager?.canRedo ?? false))
                }
            }
        }
    }
    
    class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    
        var window: UIWindow?
    
        func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
            guard let scene = scene as? UIWindowScene else { return }
    
            let window = UIWindow(windowScene: scene)
            let model = Model(undoManager: window.undoManager!)
            let contentView = ContentView(model: model)
    
            window.rootViewController = MyHostingController(rootView: contentView)
            self.window = window
            window.makeKeyAndVisible()
        }
    }
    

    【讨论】:

    • 这太棒了!我验证了通过将宿主控制器设置为第一响应者,它的撤消管理器现在成为环境中的一个。非常便利。还确认了撤消堆栈清空时重做不起作用的错误(报告:FB7269279)。请注意,即使三指手势不起作用,摇动撤消也能继续工作。
    猜你喜欢
    • 2021-10-23
    • 2012-12-08
    • 1970-01-01
    • 2020-06-06
    • 1970-01-01
    • 2020-02-21
    • 1970-01-01
    • 2012-02-20
    • 1970-01-01
    相关资源
    最近更新 更多