如何在 PencilKit 中通过 PKInkingTool 设置恒定的笔画宽度

How to set constant width of strokes from PKInkingTool in PencilKit

当使用 finger/pencil 和 PencilKit API 在屏幕上绘图时,我想将笔画的宽度设置为常量。目前,say PKInkingTool 中的宽度设置只设置了用手指或铅笔绘图时的基线宽度,并且如果用他们的 finger/pencil.

进行慢速或快速笔划,宽度会有所不同

我不确定如何设置最小示例,有很多代码可以让 PencilKit 视图正常工作。您可以使用 Apple 的 this example 来设置一个简单的绘图应用程序。

这是我选择默认绘图工具的代码:

canvas.tool = PKInkingTool(.pen, color: .white, width: 10)

其中 canvas 是一个 PKCanvasView 对象。每个 InkType (link to docs) 中都有一个 validWidthRange 属性,但我不确定这是否可以帮助我实现我想要的。

我找到了解决方案(感谢 Will Bishop), however I'm not sure it's the best for everyone. I solve it by redrawing the stroke after its been completed with a constant width. Apple released new APIs for PKStroke and PKStrokePoint 在 WWDC2020 上的 iOS 14。这是我代码的相关部分(newDrawing 是具有恒定笔划宽度的新绘图,而 canvasView 指的是我 PKCanvas 上的当前绘图):

var newDrawingStrokes = [PKStroke]()
    for stroke in canvasView.drawing.strokes {
        var newPoints = [PKStrokePoint]()
        stroke.path.forEach { (point) in
        let newPoint = PKStrokePoint(location: point.location, 
                                     timeOffset: point.timeOffset, 
                                     size: CGSize(width: 5,height: 5), 
                                     opacity: CGFloat(1), force: point.force,
                                     azimuth: point.azimuth, altitude: point.altitude)
        newPoints.append(newPoint)
        }
        let newPath = PKStrokePath(controlPoints: newPoints, creationDate: Date())
        newDrawingStrokes.append(PKStroke(ink: PKInk(.pen, color: UIColor.white), path: newPath))
     }
let newDrawing = PKDrawing(strokes: newDrawingStrokes)

这是我拼凑的一些初步代码,所以如果您找到任何 bugs/mistakes 请告诉我!

有一种方法可以通过 swizzling UITouch forcetimestamp 方法修复它。

您可以在您的项目中添加这个 Objective-C 类别:

#import <objc/runtime.h>

@implementation UITouch (UITouch_Overrides)

+ (void)load {
    [super load];

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [self swizzleForce];
        [self swizzleTimestamp];
    });
}

+ (void)swizzleForce {
    [self swizzle:@selector(force) with:@selector(ed_force)];
}

+ (void)swizzleTimestamp {
    [self swizzle:@selector(timestamp) with:@selector(ed_timestamp)];
}

+ (void)swizzle:(SEL)originalSelector with:(SEL)swizzledSelector {
    Class class = [self class];
    
    Method originalMethod = class_getInstanceMethod(class, originalSelector);
    Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
    BOOL didAddMethod =
        class_addMethod(class,
            originalSelector,
            method_getImplementation(swizzledMethod),
            method_getTypeEncoding(swizzledMethod));
    if (didAddMethod) {
        class_replaceMethod(class,
            swizzledSelector,
            method_getImplementation(originalMethod),
            method_getTypeEncoding(originalMethod));
    } else {
        method_exchangeImplementations(originalMethod, swizzledMethod);
    }
}

- (CGFloat)ed_force {
    return 1.0;
}

- (NSTimeInterval)ed_timestamp {
    return 0;
}

@end