【问题标题】:Draw app is Lagging when adding shapeLayer into views layer in Swift / IOS在 Swift / IOS 中将 shapeLayer 添加到视图层时,绘图应用程序滞后
【发布时间】:2018-11-15 10:40:56
【问题描述】:

我正在创建一个绘图应用程序。这个应用程序允许用户在视图上绘制。我在下面给出的代码中将该视图命名为绘图视图。为了创建这个应用程序,我使用了 BeizerPathShapeLayer,每当用户触摸屏幕时,UIBeizerPathCAShapeLayer 都会被初始化.这个应用程序的问题是在某些绘图视图被滞后之后。我无法弄清楚发生了什么。有没有更好的方法来优化这个应用?

CanvasViewController: UIViewController {

    // MARK: Properties

    @IBOutlet weak var verticalScrollView: UIScrollView!
    var lastPoint:CGPoint?   // var mutablePath:CGMutablePath?
    var drawLine:UIBezierPath?
    var shapeLayer:CAShapeLayer?
    let dropDown = DropDown()
    var shape:CAShapeLayer?
    let listOfDropDownMenu:[String] = ["Create New Canvas","Save","Change Color"]
    var presenter: CanvasModuleInterface?

    // MARK: IBOutlets
     @IBOutlet weak var drawingView: UIView!
    @IBOutlet weak var showColorViewControllerDropDownButton: UIBarButtonItem!

    // MARK: VC's Life cycle

    override func viewDidLoad() {
        super.viewDidLoad()
        self.setup()
        self.verticalScrollView.isUserInteractionEnabled = false
    }

    // MARK: IBActions

    // MARK: Other Functions

    @IBAction func ShowColorViewControllerAction(_ sender: Any) {
         self.dropDown.anchorView = self.showColorViewControllerDropDownButton
         self.dropDown.dataSource = self.listOfDropDownMenu
        self.dropDown.selectionAction = { [unowned self] (index: Int, item: String) in
            switch index{
            case 0:
              self.createNewView()
            case 1:
              self.saveTheCanvas()
            case 2:
             self.presentToColorViewController()

            //self.presentColorController()
            default:
                print("out of order !!!")
            }

        }
        self.dropDown.show()
    }

    private func createNewView(){
        //Mark: - create new canvas
        self.drawingView.layer.sublayers!.forEach { (layers) in
            layers.removeFromSuperlayer()
        }
    }
    private func presentToColorViewController(){
        // Mark : - naviagate to color view controller.


    }
    private func saveTheCanvas(){
        // Mark: - save the drawing

    }

    private func setup() {
        // all setup should be done here
    }
    private func presentColorController(){
        let colorSelectionController = EFColorSelectionViewController()
        let colorNavigationController = UINavigationController(rootViewController: colorSelectionController)
        colorNavigationController.navigationBar.backgroundColor = UIColor.white
        colorNavigationController.navigationBar.isTranslucent = false
        colorNavigationController.modalPresentationStyle = UIModalPresentationStyle.popover
        colorSelectionController.delegate = self
        colorSelectionController.color = self.view.backgroundColor ?? UIColor.white

        if UIUserInterfaceSizeClass.compact == self.traitCollection.horizontalSizeClass {
            let doneButton: UIBarButtonItem = UIBarButtonItem(
                title: NSLocalizedString("Done", comment: ""),
                style: UIBarButtonItem.Style.done,
                target: self,
                action: #selector(dismissViewController)
            )
            colorSelectionController.navigationItem.rightBarButtonItem = doneButton
        }
        self.present(colorNavigationController, animated: true, completion: nil)
    }
    @objc func dismissViewController(){
        //##ask confusion.....
        self.dismiss(animated: true, completion: nil)
    }

}

// MARK: CanvasViewInterface extension CanvasViewController: CanvasViewInterface {
     } extension CanvasViewController: EFColorSelectionViewControllerDelegate{
    func colorViewController(_ colorViewCntroller: EFColorSelectionViewController, didChangeColor color: UIColor) {

    } } extension CanvasViewController{

    //Mark: - this extension contains function for drawing the circle.
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        print("touches begin")

        self.drawLine = UIBezierPath()
        self.shapeLayer = CAShapeLayer()
        //       self.mutablePath = CGMutablePath()

        if let touchesPoint = touches.first{
            self.lastPoint = touchesPoint.location(in: self.drawingView)
         }

    }
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
         //        self.drawingView.layer.sublayers!.forEach { (layer) in //            print(layer) //        }
      //  print(self.drawingView.layer)

    }
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?){ //        print("\(self.drawingView)") //        print("\(self.drawingView.layer.sublayers)")

        var nextPoint:CGPoint?
        if let touchesPoint = touches.first{
            nextPoint = touchesPoint.location(in: self.drawingView)
            guard let lastLinePoint = self.lastPoint , let nextLinePoint = nextPoint else{return}
            self.drawLineInDrawingView(from: lastLinePoint, to: nextLinePoint)
        }
        if let newPoint = nextPoint{
        self.lastPoint  = newPoint
        }
    }
    func drawLineInDrawingView(from:CGPoint,to:CGPoint){
        drawLine!.move(to: CGPoint(x: from.x, y: from.y))
        drawLine!.addLine(to: CGPoint(x: to.x, y:  to.y))
        shapeLayer!.path = drawLine!.cgPath
        shapeLayer!.strokeColor = UIColor.black.cgColor
        shapeLayer!.lineCap = .round
        shapeLayer!.lineWidth = 100
        self.drawingView.layer.addSublayer(shapeLayer!)    //     print(self.drawingView.layer.sublayers)
    }
     }

【问题讨论】:

    标签: ios swift draw


    【解决方案1】:

    在不知道您使用的是什么CanvasViewController 的情况下,无法完全正确回答您的问题,但我会回答,假设它的工作原理类似于 iOS 中的任何常规 UIView。

    我可以指出几点来提高性能:

    首先,不要在drawLineInDrawingView(from:CGPoint,to:CGPoint) 中一遍又一遍地将相同的ShapeLayer 添加到绘图视图中,而是在touchesBegan(_ touches: Set&lt;UITouch&gt;, with event: UIEvent?) 中只添加一次并存储对它的引用。

    其次,您的drawLineInDrawingView(from:CGPoint,to:CGPoint) 应如下所示:

    func drawLineInDrawingView(from:CGPoint,to:CGPoint){
        CATransaction.begin()
        CATransaction.setDisableActions(true)
    
        drawLine!.move(to: CGPoint(x: from.x, y: from.y))
        drawLine!.addLine(to: CGPoint(x: to.x, y:  to.y))
        shapeLayer!.path = drawLine!.cgPath
        shapeLayer!.strokeColor = UIColor.black.cgColor
        shapeLayer!.lineCap = .round
        shapeLayer!.lineWidth = 100
        shapeLayer!.setNeedsDisplay()
    
        CATransaction.commit()
    }
    

    总之:在 touchesBegan() 之后,您添加 shapeLayer 并存储对它的引用。在 touchesMoved 内部,您调用 drawLineInDrawingsFunction。 您的 drawLineInDrawingsFunction,仅使用 shapeLayer.setNeedsDisplay() 更新您的 Shapelayer。

    把你的转换放进去

    CATransaction.begin()
    CATransaction.setDisableActions(true)
    /**Your transformation here**/
    CATransaction.commit()
    

    仅用于阻止您的视图自动为更改设置动画,而是立即显示它们。

    我希望这会有所帮助。

    【讨论】:

    • 我应该在哪里写这个 self.drawingView.layer.addSublayer(shapeLayer!) 方法?
    • @junky 你可以在touchesBegan 方法中编写那行代码,在self.shapeLayer = CAShapeLayer() 之后
    • 是否有更多选项可以优化此代码并使我的绘图应用程序更加流畅和快速?
    • @junky 您正在使用的画布视图可能有一些细节。但是由于我不知道目前性能如何或何时下降,您可以为每种颜色只创建一个 ShapeLayer,然后将相同颜色的每个新路径连接到一个路径中。此外,您可以想出一种巧妙的方法来最小化用于创建线的点的数量。例如,当你画完线后,让一个任务删除不必要的点并重画前一条线。例如,可以仅使用一个起点和终点来删除直线段。
    猜你喜欢
    • 2017-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多