2

I have a view controller that wants to show a PKCanvasView and a PKToolPicker from PencilKit. Here's the code:

import UIKit
import PencilKit

class ViewController: UIViewController {
    @IBOutlet weak var canvas: PKCanvasView!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        canvas.backgroundColor = .lightGray
        canvas.allowsFingerDrawing = true
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

//      self.showPicker()
        self.perform(#selector(showPicker), with: self, afterDelay: 0)
    }

    @objc private func showPicker() {
        guard let window = UIApplication.shared.windows.filter({ $0.windowScene?.activationState == .foregroundActive }).first else {
            return
        }

        let picker = PKToolPicker.shared(for: window)
        if let picker = picker, picker.isVisible {
            return
        }

        print("Picker not visible")
        picker?.setVisible(true, forFirstResponder: canvas)
        picker?.addObserver(canvas)
        picker?.addObserver(self)

        canvas.becomeFirstResponder()

        self.perform(#selector(showPicker), with: self, afterDelay: 0.5)
    }
}


extension ViewController: PKToolPickerObserver {

}

My problem is, when I programmatically launch ViewController as the initial ViewController in the storyboard, the PKToolPicker shows itself just fine at the bottom of the screen. Here's a screenshot of a working DrawingViewController

enter image description here

However, if i programmatically launch ViewController with a new UIWindow. then the PKToolPicker does not show.

enter image description here

So I'm assuming that there isn't anything wrong with my ViewController code. That it might be an issue with how I create a new UIWindow.

I create the window with the following code (note I'm working with iOS 13) from SceneDelegate.swift:

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
        // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
        // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
        guard let windowScene = (scene as? UIWindowScene) else { return }

        let window = UIWindow(frame: windowScene.coordinateSpace.bounds)
        window.windowScene = windowScene
        window.rootViewController = UIStoryboard(name: "Main", bundle: .main).instantiateViewController(withIdentifier: "ViewController")

        self.window = window
    }

I'm pretty stumped. PencilKit doesn't give much in the way of warnings or errors as to why it fails to show the PKToolPicker. Any input is appreciated.

Edit 1

Just adding more information. I looked at the hierarchy viewer, and It looks like The PKToolPicker is there, but none of the tools are drawn to screen.

enter image description here enter image description here

Edit 2

I added a timer in the ViewController, and I noticed that when the PKToolPicker is actually shown, the PKCanvasView actually becomes the first responder. Not immediately, but after a few seconds. When I use a programmatically created UIWindow, PKCanvasView never becomes the first responder. So maybe it has to do with the fact that my window isn't asking to be the first responder in some way.

Edit 3

I've managed to simplify the problem and put it into a sample app which you can checkout here. If you look in SceneDelegate.swift, there's code in there to create my own window. If I comment this code out, I see my tool picker without issue. When I uncommented the code, it doesn't show the tool picker.

  • In my program, the palette does not appear before a user uses a keyboard. Once a keyboard appears, the palette can show up anytime after. (i.e. any input field must become a first responder and resign before PKToolPicker is used.) – user5865651 Jan 08 '21 at 05:37

1 Answers1

1

I was just playing around with this same API today and ran into the same issue. I think the problem is in your window creation in the scene delegate, you are missing: window.makeKeyAndVisible()

That plus removing the storyboard reference in your Info.plist since you are manually loading it in the scene delegate. Just remove the key UISceneStoryboardFile inside UIApplicationSceneManifest

I think the problem is the OS is creating your window for you (based on the Info.plist) but then you are overriding that window with your own manually created one. The window that is actually visible (The Info.plist created one) can no longer be referenced.

Jason Clardy
  • 239
  • 3
  • 6