I have a question about an app using PencilKit.
I would like to add a UIImageView that is addSubviewed on top of PKCanvasView to PKDrawing and retrieve it as data along with other drawing information using dataRepresentation().
Please tell me how.
PencilKit
RSS for tagCapture touch input as an opaque drawing and turn it into high-quality images that can be displayed on iOS and macOS using PencilKit.
Posts under PencilKit tag
36 Posts
Sort by:
Post
Replies
Boosts
Views
Activity
I have a question about an app using PencilKit.
I would like to add a UIImageView that is addSubviewed on top of PKCanvasView to PKDrawing and retrieve it as data along with other drawing information using dataRepresentation().
Please tell me how.
Hi,
I encounter various problems with inserting PKDrawing into a PDFAnnotation :
First : After "page.addAnnotation(myCustomAnnotation)", saved document seems corrupted (affected pages are displayed with a "X" watermark covering the whole page),
Second : The only way to extract PKDrawing from the annotation is unarchiveTopLevelObjectWithData: which is deprecated,
Final : I'm not able to re-read PKDrawings to restore PKCanvasView undoManager.
Does anyone have an idea on a correct way to do this?
Thank you in advance and happy new year everyone!
I'm trying to add rotation functionality to the PKCanvasView, so that rotation gesture rotates the drawing, I want it to be working together with zoom gesture too, which is already implemented in the PKCanvasView.
Rotation works reasonably fine when rotation and zoom can't be performed simultaneously. The moment I enable simultaneous zoom+rotation performance of rotation gets noticably bad. Visually it looks like rotation only happens on noticable angles like 10,20,30.. degrees which looks choppy. Zoom at the same time looks smooth as before. I'm trying to understand why and how to fix this.
The rotation gesture handler is here:
@objc
func handleRotation(_ gesture: UIRotationGestureRecognizer) {
guard let curView = gesture.view as? PKCanvasView else {return}
if gesture.state == .began || gesture.state == .changed {
let rotation = gesture.rotation
let gestureCenter = gesture.location(in: curView)
let center = CGPoint(x: gestureCenter.x / curView.zoomScale, y: gestureCenter.y / curView.zoomScale)
let finalTransform = CGAffineTransform(translationX: -center.x, y: -center.y)
.concatenating(CGAffineTransform(rotationAngle: rotation))
.concatenating(CGAffineTransform(translationX: center.x, y: center.y))
curView.drawing.transform(using: finalTransform)
gesture.rotation = 0
}
}
which just constructs rotation matrix around rotation gesture center and calls curView.drawing.transform(using: finalTransform). Rotation matrix is correct I think because without simultaneous zoom+rotation it rotates everything as intended and smoothly under any zoom. (Just for clarity: in the rotation handler the matrix does only the rotation, zoom is done in pinch gesture handler implemented by the PKCanvasView. The zoomScale is used in estimations only to figure out the content coodrinate).
The handleRotation method is set as
let rotationGestureRecognizer = UIRotationGestureRecognizer(target: self, action: #selector(handleRotation))
rotationGestureRecognizer.delegate = self
canvasView.addGestureRecognizer(rotationGestureRecognizer)
The simultaneous handling of zoom+rotation I enable with this code:
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
What I've tried:
Tried to nable simultaneous recognition only for zoom+rotation (without the pan gesture and possible others), the effect is the same. For example rotation+pan work smoothly together.
I've verified that the gesture handler is called very often, every 8-16 milliseconds (rotation matrix creation and curView.drawing.transform(using: finalTransform) is included in this time period) which should be enough for visually smooth rotation.
Other observations:
When zoom and rotation both are enabled the rotation sometimes gets completely stuck, zoom and pinch is responsive but rotation is not happening visually. Only when I release fingers from the screen the rotation unstucks and content is rotated as if it didn't stuck at all. During the "stuck" period the rotation handler is called with the same frequency it's just not being visually reflected.
Other thoughts:
It kind of looks like the tranformation I set in the rotation handler gets overwritten by transformation from the zoom handler. But pan+rotation works fine together which confuses this theory.
Hello. I am developing an app that helps people with language issues communicate and for that I thought I could do a custom keyboard for them.
I was wondering if there is a way of doing a 3rd party keyboard using SwiftUI (little to no UIKit) and if so, where can I find documentation/tutorials.
I have looked with no success.
Additionally, can we use a PKCanvasView inside of keyboards (ie, is there any restriction/policy)? I intend to use it so that users can draw the words they're looking for (I already have the model and know how to come up with the suggestions and everything)
Thanks a lot!
Since updating to iOS17, lasso no longer works in Pencilkit with SwiftUI support using UIViewRepresentable. It works in PKToolPicker, but the programmatically created PKLassoTool does not work. How can I get lasso to work?
I am showing PDF file in QLPreview Controller, in iOS17, after copy and paste the page of pdf and edit the original page using Pencil kit, will affect the duplicate page also. How to restrict?
Thanks to all
It seems PKCanvasView overrides the property UIScrollViewDelegate which inherits from the UIScrollView to PKCanvasViewDelegate. And does not provide access to UIScrollViewDelegate.
In order to implement zooming, so I added a PKCanvasView into my own UIScrollView. And implemented delegate method viewForZooming in which return PKCanvasView.
But all drawing in PKCanvasView was blurred when zooming or scale. How to re-render drawing after zoom to make it has reasonable stroke width and clear ?
Some related code:
let canvasView = PKCanvasView()
let scrollView = UIScrollView()
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(scrollView)
scrollView.addSubview(canvasView)
scrollView.delegate = self
scrollView.minimumZoomScale = 0.5
scrollView.maximumZoomScale = 2.5
}
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return canvasView
}
Some solutions I had tried:
1: Reset PKCanvasView contentScaleFactor
func scrollViewDidEndZooming(_ scrollView: UIScrollView, with view: UIView?, atScale scale: CGFloat) {
if let canvas = view {
let contentScale = scale * UIScreen.main.scale
canvas.contentScaleFactor = contentScale
}
}
Not worked!
2: Re-render PKStroke:
func reRender(_ scale: CGFloat) {
let newStrokeWidth = strokeWidth * scale
var newDrawingStrokes: [PKStroke] = []
for stroke in canvasView.drawing.strokes {
canvasView.tool = PKInkingTool(.pen, color: .red, width: newStrokeWidth)
var newPoints = [PKStrokePoint]()
stroke.path.forEach { (point) in
let newPoint = PKStrokePoint(location: point.location,
timeOffset: point.timeOffset,
size: CGSize(width: newStrokeWidth, height: newStrokeWidth),
opacity: CGFloat(1), force: point.force,
azimuth: point.azimuth, altitude: point.altitude)
newPoints.append(newPoint)
}
let newPath = PKStrokePath(controlPoints: newPoints, creationDate: Date())
let newStroke = PKStroke(ink: PKInk(.pen, color: UIColor.red), path: newPath)
newDrawingStrokes.append(newStroke)
}
let newDrawing = PKDrawing(strokes: newDrawingStrokes)
canvasView.drawing = newDrawing
}
Not worked! Still blurred, just changed strokeWidth by multiply scale.
3: I try to reset PKDrawing or PKStroke transform by using scrollView scale. Then PKDrawing position disordered and it was still blurred.
Please help me.
In iOS whenever you use the PKLassoTool on a set of strokes if you click on the selected region a menu will pop up as you can see here
Is there a way to add more buttons to this menu?
I tried looking through the Swift docs but I haven't seen this mentioned anywhere.
We've been using PencilKit in our app for a couple of years now, but have run into a significant slowdown recently. It coincided with a shift to iPadOS 17, but possibly that's a coincidence.
It seems to occur when we add PKStroke elements to a PKDrawing (programatically). Previously this has refreshed on-screen instantly, but now it's so slow we can sometime see the new stroke progressively drawing on-screen. I profiled the running app and the lag seems to entirely occur within the following call sequence:
2563 -[PKDrawingConcrete addNewStroke:]
2494 -[PKDrawing setNeedsRecognitionUpdate]
2434 -[NSUserDefaults(NSUserDefaults) boolForKey:]
2419 _CFPreferencesGetAppBooleanValueWithContainer
2417 _CFPreferencesCopyAppValueWithContainerAndConfiguration
2399 -[_CFXPreferences copyAppValueForKey:identifier:container:configurationURL:]
2395 -[_CFXPreferences withSearchListForIdentifier:container:cloudConfigurationURL:perform:]
2394 normalizeQuintuplet
2367 __108-[_CFXPreferences(SearchListAdditions) withSearchListForIdentifier:container:cloudConfigurationURL:perform:]_block_invoke
2338 __76-[_CFXPreferences copyAppValueForKey:identifier:container:configurationURL:]_block_invoke
2336 -[CFPrefsSource copyValueForKey:]
2332 -[CFPrefsSearchListSource alreadylocked_copyValueForKey:]
1924 -[CFPrefsSearchListSource alreadylocked_copyValueForKey:].cold.1
1921 _os_log_debug_impl
1918 _os_log
1914 _os_log_impl_flatten_and_send
1829 _os_log_impl_stream
1811 _os_activity_stream_reflect
1205 dispatch_block_perform
1199 _dispatch_block_invoke_direct
1195 _dispatch_client_callout
1194 ___os_activity_stream_reflect_block_invoke
1188 _xpc_pipe_simpleroutine
882 _xpc_send_serializer
867 _xpc_pipe_mach_msg
859 mach_msg
859 mach_msg_overwrite
855 mach_msg2_internal
853 0x1cf7701d8
Up to 'addNewStroke' this makes sense. But where is it going with 'PKDrawing setNeedsRecognitionUpdate'? This ultimately seems to be hitting some kind of lock, which I assume is the cause of the lag.
Any suggestions for why this is happening or a means of avoiding it would be much appreciated.
I'm using PkCanvasView one of my app for drawing purpose. When I use lasso tool for selection and tap on selected area, menu controller appear with options "Cut, Copy, Delete, Duplicate, Insert Space Above". As per my app requirement, I want to remove "Insert Space Above" from menu controller.
Is it doable?
You used to be able to select a drawing, long press to show the edit menu (copy, duplicate, delete, ...), copy the item and paste it after another long press on the canvas. However, since iOS / iPadOS 16.1 long pressing the canvas does not show any menu.
The action still works if you connect an external mouse to your iPad, activate the secondary click functionality and use the right mouse button to click on the canvas. This shows the menu and you can paste the copied drawing.
It seems to be broken in all PencilKit-based apps, including Apple's sample apps. Is there any workaround? This is a major problem for my app and used to work fine since the introduction of PencilKit with iOS 13.
I’m building a drawing app and I want to add more things I can do with the strokes selected by the PKLassoTool. Is there a way to edit the menu of buttons that appears when you make a selection with the lasso tool? And then is there away to pass the selected strokes into a function?
I’ve looked at the Apple documentation but there doesn’t seem to be a lot of support to doing anything with the lasso tool.
I am using Xcode 15 beta 5 with iOS 17 beta 4 SDK. By building with the iOS 17 SDK my app got all the new inks which work great. Saving/encoding the PKDrawing to Data also works fine. However if I want to load the same PKDrawing again, on the same simulator or device (i.e., same iOS version) it fails with "Apple Drawing Format is from a future version that is too new.".
From my understanding, reading https://developer.apple.com/documentation/pencilkit/supporting_backward_compatibility_for_ink_types?changes=_2 this is the expected behaviour when trying to load such a PKDrawing on an older iOS version which doesn't support the new ink types.
Here is a short example, which prints the error:
var drawing = PKDrawing()
let strokePoints = [
PKStrokePoint(location: CGPoint(x: 0, y: 0), timeOffset: 0, size: CGSize(width: 3.0, height: 3.0), opacity: 2, force: 1, azimuth: 1, altitude: 1)
]
let strokePath = PKStrokePath(controlPoints: strokePoints, creationDate: Date())
drawing.strokes.append(PKStroke(ink: .init(.watercolor), path: strokePath))
do {
let data = drawing.dataRepresentation()
let drawing2 = try PKDrawing(data: data)
print("success")
} catch {
print(error)
}
}
Saving & loading a PKDrawing which does not use any of the new ink types works fine.
Hello, I have a issue that's PKCanvasView's delegate method (canvasViewDrawingDidChange) is automatically called when the view present with .popover setting. In my case, I want to show some popup view on PKCanvasView and that's happening.
viewController.modalPresentationStyle = .popover
When I comment out this line the delegate method not getting called and everything is fine.
The biggest problem was all previous ink on PKCanvas deleted somehow if this delegate being called automatically. Is this PencilKit bugs?
Note: this trigger only with Apple Pencil, finger touch is fine.
Hi,
I'm trying to use PencilKit over PDFKit as described in https://developer.apple.com/videos/play/wwdc2022/10089/. The thing is I open my custom UIDocument and initialize all its content to feed PDFView. Everything seems to work, I Input sketches in the canvas, PDFPageOverlayViewProvider's overlayView(for:) generates canvas correctly (it seems) but when editing finishes : willEndDisplayingOverlayView never gets called, and when I save the UIDocument (I use document.close(completionHandler:)) contents(forType:) never sees my custom PDFPages and I get no content for sketches.
Does anyone of you have an idea of the lifecycle we should follow to get the methods called ?
Sincerely yours