簡體   English   中英

我如何從單個 CAShapeLayer 中的觸摸中繪制線條以便能夠 select 並移動 UIBezierPaths?

[英]How can i draw lines from touches in single CAShapeLayer to be able to select and move UIBezierPaths?

我正在嘗試用觸摸畫線,然后能夠移動它。 我沒有使用帶有 draw(_ rect: CGRect) 的 UIContext 方法進行繪圖,因為我無法獲得筆畫的大小及其一些屬性,所以我使用 CAShapeLayer 來使用如下觸摸方法進行繪圖:

    let shapeLayer: CAShapeLayer = {
    let layer = CAShapeLayer()
    layer.lineWidth = 1
    layer.strokeColor = UIColor.black.cgColor
    layer.fillColor = UIColor.clear.cgColor
    layer.lineCap = .round
    layer.lineJoin = .round
    layer.lineDashPattern = [10, 10]
    layer.name = "ShapeLayer"
    return layer
}()

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        let shapeLayer = CAShapeLayer()
        shapeLayer.lineWidth = 1
        shapeLayer.strokeColor = UIColor.black.cgColor
        shapeLayer.fillColor = UIColor.clear.cgColor
        shapeLayer.lineCap = .round
        shapeLayer.lineJoin = .round
        shapeLayer.lineDashPattern = [10, 10]
        shapeLayer.name = "ShapeLayer"
        self.canvas.layer.addSublayer(shapeLayer)

        path = MyBezierPath()
        if let location = touches.first?.location(in: self.canvas) { previousTouchPoint = location }
}

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    
    guard let touch = touches.first?.location(in: self.canvas) else { return }
    
        if let location = touches.first?.location(in: self.canvas) {
            path.move(to: location)
            path.addLine(to: previousTouchPoint)
            previousTouchPoint = location

            if canvas.layer.sublayers != nil && canvas.layer.sublayers?.last?.name == "ShapeLayer" {
                guard let layer = canvas.layer.sublayers?.last as? CAShapeLayer else { return }
                print("Here \(layer.path?.boundingBoxOfPath)")
                layer.path = path.cgPath
            }
        }
 }

我試圖將所有 UIBezierpath 添加到單個 CAShapeLayer 以便我可以 select 特定路徑並移動它。 但是在這種方法中,它為每一行創建新的 CAShapeLayer,所以我嘗試定義全局 CAShapeLayer 變量並為所有行附加 UIBezierPath 並將其添加到全局 CAShapeLayer 變量,但它非常緩慢且滯后。 有什么辦法我可以只用一個 CAShapeLayer 繪制然后能夠改變它的 BezierPath 的位置?

這是將多個“線段”添加到單個UIBezierPath的簡單示例,然后能夠拖動/移動該路徑。

UIView子類中,我們:

  • 創建一個UIBezierPath我們稱之為thePath
  • 創建、分配屬性並添加CAShapeLayer -- shapeLayer
  • 如果我們處於“繪圖”模式
    • 在 touchesBegan 上, thePath.move(to: point)
    • 在 touchesMoved 上, thePath.addLine(to: point)shapeLayer.path = thePath.cgPath
  • 如果我們處於“移動”模式
    • 在 touchesBegan 時,保存要點
    • 在 touchesMoved 上,變換路徑

在啟動時,它看起來像這樣:

在此處輸入圖像描述

我們觸摸並拖動以添加到路徑:

在此處輸入圖像描述

更多的觸摸和拖動以添加更多“段”:

在此處輸入圖像描述

現在我們切換到“移動”並向下和向右拖動:

在此處輸入圖像描述

切換回“Draw”並添加更多片段:

在此處輸入圖像描述

切換回“移動”並向上和向左拖動:

在此處輸入圖像描述

示例 controller

class DrawMoveLayerTestVC: UIViewController {

    let testView = DrawMoveLayerView()

    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = .systemBackground
        
        testView.backgroundColor = UIColor(white: 0.95, alpha: 1.0)
        testView.translatesAutoresizingMaskIntoConstraints = false
        
        // segmented control to switch between drawing / moving
        let segControl = UISegmentedControl(items: ["Draw", "Move"])
        segControl.translatesAutoresizingMaskIntoConstraints = false
        segControl.selectedSegmentIndex = 0
        segControl.addTarget(self, action: #selector(segChanged(_:)), for: .valueChanged)
        
        view.addSubview(testView)
        view.addSubview(segControl)
        
        let g = view.safeAreaLayoutGuide
        
        NSLayoutConstraint.activate([
            
            // put segmented control at bottom
            segControl.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
            segControl.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
            segControl.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -20.0),

            // constrain test view to all top/leading/trailing with 20-points "padding"
            testView.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
            testView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
            testView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
            // bottom to segmented control top with 20-points "padding"
            testView.bottomAnchor.constraint(equalTo: segControl.topAnchor, constant: -20.0),
            
        ])
        
    }
    @objc func segChanged(_ sender: UISegmentedControl) {
        // set test view to "drawing" or "moving"
        testView.isDrawing = sender.selectedSegmentIndex == 0
    }
}

示例視圖子類

class DrawMoveLayerView: UIView {
    
    public var isDrawing: Bool = true
    
    private let shapeLayer = CAShapeLayer()
    private let thePath = UIBezierPath()
    private var startPoint: CGPoint = .zero

    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    private func commonInit() {
        shapeLayer.lineWidth = 1
        shapeLayer.strokeColor = UIColor.black.cgColor
        shapeLayer.fillColor = UIColor.clear.cgColor
        shapeLayer.lineCap = .round
        shapeLayer.lineJoin = .round
        shapeLayer.lineDashPattern = [5, 10]
        shapeLayer.name = "ShapeLayer"
        layer.addSublayer(shapeLayer)
        self.clipsToBounds = true
    }
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let t = touches.first else { return }
        let point = t.location(in: self)
        if isDrawing {
            thePath.move(to: point)
        } else {
            self.startPoint = point
        }
    }
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let t = touches.first else { return }
        let point = t.location(in: self)
        if isDrawing {
            thePath.addLine(to: point)
        } else {
            // move the path by the distance the touch moved
            let tr = CGAffineTransform(translationX: point.x - startPoint.x, y: point.y - startPoint.y)
            thePath.apply(tr)
            startPoint = point
        }
        // update the path of the shape layer
        shapeLayer.path = thePath.cgPath
    }
    
}

在做了一些研究后,我發現不可能在單個 CAShapeLayer 中繪制多條線,您可以繪制單條線但不能繪制多條線。 您可以通過向UIBezierPath添加新線來繪制多條線,但它會滯后很多。

對於繪制多條線,主要有兩種已知方法:

  1. 使用CGContext
  2. 通過為您繪制的每條線添加單獨的CAShapeLayer

CAShapeLayer 方法更容易,並且對性能和新手都有好處。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM