You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用PKCanvasView实现类似iOS Freeform的无限画布功能?

实现PKCanvasView无限画布(类似Freeform自动扩展)的思路与方案

针对你想要实现的「靠近画布边缘绘图时自动扩展空间」的需求,结合你提供的现有代码,以下是具体的实现思路和代码修改方案:

1. 调整PKCanvasView基础配置

首先需要修改画布的缩放和回弹设置,为无限扩展提供基础支持:

private(set) var canvasView: PKCanvasView = {
    let canvasView = PKCanvasView()
    canvasView.translatesAutoresizingMaskIntoConstraints = false
    canvasView.alwaysBounceVertical = true
    canvasView.alwaysBounceHorizontal = true // 开启水平回弹,支持左右方向扩展
    canvasView.minimumZoomScale = 0.25 // 允许缩小画布,方便查看整体内容
    canvasView.maximumZoomScale = 4.0 // 允许放大画布,精细绘图
    canvasView.drawingPolicy = .anyInput // 确保所有触控输入都能触发绘图逻辑
    return canvasView
}()

2. 动态扩展画布核心逻辑

可以通过两种方式实现自动扩展:

方式一:绘图结束后根据内容边界扩展

通过PKCanvasViewDelegate监听绘图完成事件,根据当前绘图的实际边界调整画布大小:
首先在视图初始化时设置代理:

override func viewDidLoad() {
    super.viewDidLoad()
    canvasView.delegate = self
    buildCanvasDraw()
}

然后实现代理方法:

extension YourViewController: PKCanvasViewDelegate {
    func canvasViewDrawingDidChange(_ canvasView: PKCanvasView) {
        let drawingBounds = canvasView.drawing.bounds
        let currentSize = canvasView.contentSize
        // 预留200pt的额外空间,避免紧贴边缘
        let newWidth = max(currentSize.width, drawingBounds.maxX + 200)
        let newHeight = max(currentSize.height, drawingBounds.maxY + 200)
        
        // 处理左/上边缘的绘图情况,确保画布向左/上扩展后内容不偏移
        let adjustOffsetX = min(canvasView.contentOffset.x, drawingBounds.minX - 200)
        let adjustOffsetY = min(canvasView.contentOffset.y, drawingBounds.minY - 200)
        
        // 仅在需要时更新画布大小,避免不必要的重绘
        if newWidth != currentSize.width || newHeight != currentSize.height {
            canvasView.contentSize = CGSize(width: newWidth, height: newHeight)
        }
        canvasView.setContentOffset(CGPoint(x: adjustOffsetX, y: adjustOffsetY), animated: false)
    }
}

方式二:绘图过程中实时扩展画布

如果需要更即时的扩展效果(比如绘图时靠近边缘就立刻扩展),可以监听触摸事件:

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    super.touchesMoved(touches, with: event)
    guard let touch = touches.first else { return }
    let touchPoint = touch.location(in: canvasView)
    
    let edgeThreshold: CGFloat = 50 // 距离边缘50pt时触发扩展
    let visibleBounds = canvasView.bounds.insetBy(dx: -edgeThreshold, dy: -edgeThreshold)
    var newSize = canvasView.contentSize
    var needUpdate = false

    // 右边缘扩展
    if touchPoint.x > visibleBounds.maxX {
        newSize.width = max(newSize.width, touchPoint.x + 200)
        needUpdate = true
    }
    // 左边缘扩展,同时调整偏移量避免内容移位
    if touchPoint.x < visibleBounds.minX {
        let extraWidth = visibleBounds.minX - touchPoint.x + 200
        newSize.width += extraWidth
        canvasView.contentOffset.x += extraWidth
        needUpdate = true
    }
    // 下边缘扩展
    if touchPoint.y > visibleBounds.maxY {
        newSize.height = max(newSize.height, touchPoint.y + 200)
        needUpdate = true
    }
    // 上边缘扩展,调整偏移量
    if touchPoint.y < visibleBounds.minY {
        let extraHeight = visibleBounds.minY - touchPoint.y + 200
        newSize.height += extraHeight
        canvasView.contentOffset.y += extraHeight
        needUpdate = true
    }

    if needUpdate {
        canvasView.contentSize = newSize
    }
}

3. 初始化画布初始大小

修改buildCanvasDraw方法,设置一个比屏幕大的初始画布,避免一开始就紧贴边缘:

private func buildCanvasDraw() {
    canvasView.drawing = PKDrawing()
    let screenSize = UIScreen.main.bounds.size
    // 初始画布设为屏幕的2倍大小,并居中显示
    let initialSize = CGSize(width: screenSize.width * 2, height: screenSize.height * 2)
    canvasView.contentSize = initialSize
    canvasView.setContentOffset(
        CGPoint(x: initialSize.width/2 - screenSize.width/2, 
                y: initialSize.height/2 - screenSize.height/2), 
        animated: false
    )
}

关键注意事项

  • 必须同时开启水平和垂直回弹,否则无法触发边缘扩展的交互逻辑
  • 扩展画布时要同步调整contentOffset,避免用户正在绘制的区域被移出可视范围
  • 可以根据需求调整边缘阈值和额外空间的数值,平衡扩展灵敏度和空间利用率

内容的提问来源于stack exchange,提问作者HoangTam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 09:51:14