Calayer绘制在视图之外

发布于 2025-02-07 16:33:12 字数 5018 浏览 0 评论 0原文

我有使用此代码使用此代码:

import UIKit

class Page: UIView {
    
    var bezierMemory = [BezierRecord]()
    var currentBezier: UIBezierPath = UIBezierPath()
    
    var firstPoint: CGPoint = CGPoint()
    var previousPoint: CGPoint = CGPoint()
    var morePreviousPoint: CGPoint = CGPoint()
    var previousCALayer: CALayer = CALayer()
    
    var pointCounter = 0
    
    var selectedPen: Pen = Pen(width: 3.0, strokeOpacity: 1, strokeColor: .red, fillColor: .init(gray: 0, alpha: 0.5), isPencil: true, connectsToStart: true, fillPencil: true)
    
    enum StandardPageSizes {
        case A4, LEGAL, LETTER
    }
    
    var firstCALayer = true
    var pointsTotal = 0
    
    override init(frame: CGRect) {
        super.init(frame: .zero)
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
    }
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesBegan(touches, with: event)
        guard let touch = touches.first else { return }
        let point = touch.location(in: self)
        firstPoint = point
        
        pointCounter = 1
        
        currentBezier = UIBezierPath()
        currentBezier.lineWidth = selectedPen.width
        selectedPen.getStroke().setStroke()
        currentBezier.move(to: point)
        
        previousPoint = point
    }
    
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesMoved(touches, with: event)
        guard let touch = touches.first else { return }
        let point = touch.location(in: self)
        pointCounter += 1
        
        if (pointCounter == 3) {
            let midpoint = CGPoint(x: (morePreviousPoint.x + point.x)/2.0, y: (morePreviousPoint.y + point.y)/2.0)
            currentBezier.addQuadCurve(to: midpoint, controlPoint: morePreviousPoint)
            let updatedCALayer = CAShapeLayer()
            updatedCALayer.path = currentBezier.cgPath
            updatedCALayer.lineWidth = selectedPen.width
            updatedCALayer.opacity = selectedPen.strokeOpacity
            updatedCALayer.strokeColor = selectedPen.getStroke().cgColor
            updatedCALayer.fillColor = selectedPen.getFill()
            if (firstCALayer) {
                layer.addSublayer(updatedCALayer)
                firstCALayer = false
            } else {
                layer.replaceSublayer(previousCALayer, with: updatedCALayer)
            }
            previousCALayer = updatedCALayer
            pointCounter = 1
        }
        
        morePreviousPoint = previousPoint
        previousPoint = point
    }
    
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesEnded(touches, with: event)
        guard let touch = touches.first else { return }
        let point = touch.location(in: self)
        
        if (pointCounter != 3) {
            if (selectedPen.connectsToStart) {
                currentBezier.addQuadCurve(to: firstPoint, controlPoint: previousPoint)
            } else {
                currentBezier.addQuadCurve(to: point, controlPoint: previousPoint)
            }
            let updatedCALayer = CAShapeLayer()
            updatedCALayer.path = currentBezier.cgPath
            updatedCALayer.lineWidth = selectedPen.width
            updatedCALayer.opacity = selectedPen.strokeOpacity
            updatedCALayer.strokeColor = selectedPen.getStroke().cgColor
            updatedCALayer.fillColor = selectedPen.getFill()
            if (firstCALayer) {
                layer.addSublayer(updatedCALayer)
                firstCALayer = false
            } else {
                // layer.setNeedsDisplay()
                layer.replaceSublayer(previousCALayer, with: updatedCALayer)
            }
        }
        
        firstCALayer = true
        let bezierRecord = BezierRecord(bezier: currentBezier, strokeColor: selectedPen.getStroke(), fillColor: selectedPen.getFill(), strokeWidth: selectedPen.width)
        bezierMemory.append(bezierRecord)
    }
    
    private func normPoint(point: CGPoint) -> CGPoint {
        return CGPoint(x: point.x/frame.width, y: point.y/frame.height)
    }
    
    public class BezierRecord {
        var bezier: UIBezierPath
        var strokeColor: UIColor
        var strokeWidth: CGFloat
        var fillColor: CGColor
        
        init(bezier: UIBezierPath, strokeColor: UIColor, fillColor: CGColor, strokeWidth: CGFloat) {
            self.bezier = bezier
            self.strokeColor = strokeColor
            self.strokeWidth = strokeWidth
            self.fillColor = fillColor
        }
    }
    
}

真的,唯一的相关部分是触摸并触摸的,在Calayer's的地方处理。从GIF中可以看到,只要我开始在边界内绘制,我就可以在页面(Uiview)的边界之外绘制。我不想要这个 - 我想要的是您可以在页面范围之外保持中风(只要您在页面上启动),但是中风不会出现在页面外。有什么想法吗?

编辑:我应该补充一点,对于这些UibezierCurves,(0,0)被认为是页面左上角(白色),而不是整个视图。因此,例如,从页面上开始并继续开启的Beziers是负面的。

I have this behaviour with this code:

import UIKit

class Page: UIView {
    
    var bezierMemory = [BezierRecord]()
    var currentBezier: UIBezierPath = UIBezierPath()
    
    var firstPoint: CGPoint = CGPoint()
    var previousPoint: CGPoint = CGPoint()
    var morePreviousPoint: CGPoint = CGPoint()
    var previousCALayer: CALayer = CALayer()
    
    var pointCounter = 0
    
    var selectedPen: Pen = Pen(width: 3.0, strokeOpacity: 1, strokeColor: .red, fillColor: .init(gray: 0, alpha: 0.5), isPencil: true, connectsToStart: true, fillPencil: true)
    
    enum StandardPageSizes {
        case A4, LEGAL, LETTER
    }
    
    var firstCALayer = true
    var pointsTotal = 0
    
    override init(frame: CGRect) {
        super.init(frame: .zero)
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
    }
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesBegan(touches, with: event)
        guard let touch = touches.first else { return }
        let point = touch.location(in: self)
        firstPoint = point
        
        pointCounter = 1
        
        currentBezier = UIBezierPath()
        currentBezier.lineWidth = selectedPen.width
        selectedPen.getStroke().setStroke()
        currentBezier.move(to: point)
        
        previousPoint = point
    }
    
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesMoved(touches, with: event)
        guard let touch = touches.first else { return }
        let point = touch.location(in: self)
        pointCounter += 1
        
        if (pointCounter == 3) {
            let midpoint = CGPoint(x: (morePreviousPoint.x + point.x)/2.0, y: (morePreviousPoint.y + point.y)/2.0)
            currentBezier.addQuadCurve(to: midpoint, controlPoint: morePreviousPoint)
            let updatedCALayer = CAShapeLayer()
            updatedCALayer.path = currentBezier.cgPath
            updatedCALayer.lineWidth = selectedPen.width
            updatedCALayer.opacity = selectedPen.strokeOpacity
            updatedCALayer.strokeColor = selectedPen.getStroke().cgColor
            updatedCALayer.fillColor = selectedPen.getFill()
            if (firstCALayer) {
                layer.addSublayer(updatedCALayer)
                firstCALayer = false
            } else {
                layer.replaceSublayer(previousCALayer, with: updatedCALayer)
            }
            previousCALayer = updatedCALayer
            pointCounter = 1
        }
        
        morePreviousPoint = previousPoint
        previousPoint = point
    }
    
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesEnded(touches, with: event)
        guard let touch = touches.first else { return }
        let point = touch.location(in: self)
        
        if (pointCounter != 3) {
            if (selectedPen.connectsToStart) {
                currentBezier.addQuadCurve(to: firstPoint, controlPoint: previousPoint)
            } else {
                currentBezier.addQuadCurve(to: point, controlPoint: previousPoint)
            }
            let updatedCALayer = CAShapeLayer()
            updatedCALayer.path = currentBezier.cgPath
            updatedCALayer.lineWidth = selectedPen.width
            updatedCALayer.opacity = selectedPen.strokeOpacity
            updatedCALayer.strokeColor = selectedPen.getStroke().cgColor
            updatedCALayer.fillColor = selectedPen.getFill()
            if (firstCALayer) {
                layer.addSublayer(updatedCALayer)
                firstCALayer = false
            } else {
                // layer.setNeedsDisplay()
                layer.replaceSublayer(previousCALayer, with: updatedCALayer)
            }
        }
        
        firstCALayer = true
        let bezierRecord = BezierRecord(bezier: currentBezier, strokeColor: selectedPen.getStroke(), fillColor: selectedPen.getFill(), strokeWidth: selectedPen.width)
        bezierMemory.append(bezierRecord)
    }
    
    private func normPoint(point: CGPoint) -> CGPoint {
        return CGPoint(x: point.x/frame.width, y: point.y/frame.height)
    }
    
    public class BezierRecord {
        var bezier: UIBezierPath
        var strokeColor: UIColor
        var strokeWidth: CGFloat
        var fillColor: CGColor
        
        init(bezier: UIBezierPath, strokeColor: UIColor, fillColor: CGColor, strokeWidth: CGFloat) {
            self.bezier = bezier
            self.strokeColor = strokeColor
            self.strokeWidth = strokeWidth
            self.fillColor = fillColor
        }
    }
    
}

Really the only relevant parts are touchesMoved and touchesEnded, where CALayer's are dealt with. As you can see from the gif, I can draw outside the bounds of the Page (UIView) as long as I start drawing inside the bounds. I do not want this - what I would like is something where you can maintain a stroke outside of the bounds of the Page (as long as you start it on the Page), but the stroke wont appear outside of the Page. Any ideas?

EDIT: I should add that for these UIBezierCurves, (0, 0) is considered to be the top left of the Page (white), and not the entire view. Thus, for example, beziers that start on the page and continue on, are negative.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

以酷 2025-02-14 16:33:12

您需要做的就是在视图上设置.clipstobounds = true

一种方法:

override init(frame: CGRect) {
    super.init(frame: .zero)
    commonInit()
}
required init?(coder: NSCoder) {
    super.init(coder: coder)
    commonInit()
}
func commonInit() {
    self.clipsToBounds = true
}

可以 put self.clipstobounds = true在两个init funcs中添加这样的“常见初始化”功能(无论如何,它都可以命名为...这就是我的做法)。通常,我们有其他需要调用的“初始设置”代码,这避免了复制代码。

All you should need to do is set .clipsToBounds = true on the view.

One approach:

override init(frame: CGRect) {
    super.init(frame: .zero)
    commonInit()
}
required init?(coder: NSCoder) {
    super.init(coder: coder)
    commonInit()
}
func commonInit() {
    self.clipsToBounds = true
}

You could put self.clipsToBounds = true in both of the init funcs, but it is common practice (no pun intended) to add a "common init" func like this (it can be named whatever... this is how I do it). Frequently we have other "initial setup" code that we want called, and this avoids duplicating the code.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文