【问题标题】:How to dismiss PKToolPicker when view disappears in SwiftUI?当视图在 SwiftUI 中消失时如何关闭 PKToolPicker?
【发布时间】:2021-03-20 07:49:11
【问题描述】:

我正在尝试将 PKCanvasView 包装为名为 CanvasView 的 SwiftUI 视图。我希望能够在另一个视图之上切换整个画布。当CanvasView 出现时,我希望PKToolPicker 出现。当它消失时,我希望 PKToolPicker 消失。

我在这里找到了一些类似的方法,但它们只涉及显示选择器或使用按钮切换选择器;我希望选择器可见性与视图可见性相关联。

在下面的示例中,您可以看到可以切换画布,但是一旦工具选择器可见,它就会保持可见。

这是我的CanvasView

import SwiftUI
import PencilKit

struct CanvasView: UIViewRepresentable {

    class Coordinator: NSObject, PKCanvasViewDelegate {
        var canvasView: Binding<PKCanvasView>
        let onChange: () -> Void

        init(canvasView: Binding<PKCanvasView>, onChange: @escaping () -> Void) {
            self.canvasView = canvasView
            self.onChange = onChange
        }

        func canvasViewDrawingDidChange(_ canvasView: PKCanvasView) {
            if canvasView.drawing.bounds.isEmpty == false {
                onChange()
            }
        }
    }

    @Binding var canvasView: PKCanvasView
    @Binding var toolPickerIsActive: Bool
    private let toolPicker = PKToolPicker()

    let onChange: () -> Void

    func makeUIView(context: Context) -> PKCanvasView {
        canvasView.backgroundColor = .clear
        canvasView.isOpaque = true
        canvasView.delegate = context.coordinator
        showToolPicker()

        return canvasView
    }

    func updateUIView(_ uiView: PKCanvasView, context: Context) {
        toolPicker.setVisible(toolPickerIsActive, forFirstResponder: uiView)
    }

    func showToolPicker() {
      toolPicker.setVisible(true, forFirstResponder: canvasView)
      toolPicker.addObserver(canvasView)
      canvasView.becomeFirstResponder()
    }

    func makeCoordinator() -> Coordinator {
        Coordinator(canvasView: $canvasView, onChange: onChange)
    }
}

还有一个例子ContentView

struct ContentView: View {
    @State private var canvasView = PKCanvasView()
    @State private var toolPickerIsActive = false
    @State private var canvasIsVisible = false

    var body: some View {
        ZStack {
            if canvasIsVisible {
                CanvasView(canvasView: $canvasView,
                           toolPickerIsActive: $toolPickerIsActive,
                           onChange: canvasDidChange)
                    .onAppear { toolPickerIsActive = true }
                    .onDisappear { toolPickerIsActive = false }
            }

            Button(action: {
                canvasIsVisible.toggle()
            }, label: {
                Text("Toggle canvas view")
            })
        }
    }

    private func canvasDidChange() {
        // Do something with updated canvas.
    }
}

任何指导将不胜感激!

【问题讨论】:

    标签: swiftui pencilkit


    【解决方案1】:

    在您的场景中,CanvasView 在消失时被销毁,因此 SwiftUI 渲染引擎不会在任何状态更改时更新它(因为它认为不需要这样做)。

    此用例的可能解决方案是在协调器deinit 上隐藏选择器(因为它在所有者视图中被销毁)。

    这是一个演示。使用 Xcode 12.4 / iOS 14.4 测试

    struct CanvasView: UIViewRepresentable {
    
        class Coordinator: NSObject, PKCanvasViewDelegate {
            var canvasView: Binding<PKCanvasView>
            let onChange: () -> Void
            private let toolPicker: PKToolPicker
    
            deinit {       // << here !!
                toolPicker.setVisible(false, forFirstResponder: canvasView.wrappedValue)
                toolPicker.removeObserver(canvasView.wrappedValue)
            }
    
            init(canvasView: Binding<PKCanvasView>, toolPicker: PKToolPicker, onChange: @escaping () -> Void) {
                self.canvasView = canvasView
                self.onChange = onChange
                self.toolPicker = toolPicker
            }
    
            func canvasViewDrawingDidChange(_ canvasView: PKCanvasView) {
                if canvasView.drawing.bounds.isEmpty == false {
                    onChange()
                }
            }
        }
    
        @Binding var canvasView: PKCanvasView
        @Binding var toolPickerIsActive: Bool
        private let toolPicker = PKToolPicker()
    
        let onChange: () -> Void
    
        func makeUIView(context: Context) -> PKCanvasView {
            canvasView.backgroundColor = .clear
            canvasView.isOpaque = true
            canvasView.delegate = context.coordinator
            showToolPicker()
    
            return canvasView
        }
    
        func updateUIView(_ uiView: PKCanvasView, context: Context) {
            toolPicker.setVisible(toolPickerIsActive, forFirstResponder: uiView)
        }
    
        func showToolPicker() {
            toolPicker.setVisible(true, forFirstResponder: canvasView)
            toolPicker.addObserver(canvasView)
            canvasView.becomeFirstResponder()
        }
    
        func makeCoordinator() -> Coordinator {
            Coordinator(canvasView: $canvasView, toolPicker: toolPicker, onChange: onChange)
        }
    }
    
    struct ContentView: View {
        @State private var canvasView = PKCanvasView()
        @State private var toolPickerIsActive = false
        @State private var canvasIsVisible = false
    
        var body: some View {
            ZStack {
                if canvasIsVisible {
                    CanvasView(canvasView: $canvasView,
                                  toolPickerIsActive: $toolPickerIsActive,
                                  onChange: canvasDidChange)
                        .onAppear { toolPickerIsActive = true }
    //                    .onDisappear { toolPickerIsActive = false }
                }
    
                Button(action: {
                    canvasIsVisible.toggle()
                }, label: {
                    Text("Toggle canvas view")
                })
            }
        }
    
        private func canvasDidChange() {
            // Do something with updated canvas.
        }
    }
    

    注意:所有权可能会重新设计,因此toolPicker 将只存在于协调者内部,但不会改变想法,取决于您。

    【讨论】:

    • 像你一样使用 PencilKit 的唯一方法是通过 UIViewRepresentable 吗?我们可以只使用纯 SwiftUI 而不使用 UIViewRepresentable 来做同样的事情吗?
    • @swiftPunk,PencilKit 是基于 UIKit 的框架
    • 非常感谢@Asperi。顺便说一句,我最近做了很多 SwiftUI 谷歌搜索,你的答案经常出现,而且总是非常有价值和有用,所以谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-15
    • 2020-06-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多