在Swift 4中移动UIBezierPath描边的最佳方式是什么?

4

我正在尝试使用UIBezierPath stroke实现在Swift 4中可以更改位置的光标。目前,我有一个函数,它具有包含“光标”新x和y位置的参数位置。我想使用此位置参数作为UIView中光标的新位置。我的当前实现每次调用该函数时都会呈现另一个光标。是否有一种方法可以更改UIBezierPath的一个实例的位置?请参考下面的示例代码。

private var cursor:UIBezierPath = UIBezierPath()

public func changeCursorLocation(location: ScreenCoordinates) {
    self.cursor = UIBezierPath()

    UIColor.black.setStroke()
    self.cursor.lineWidth = 2

    self.cursor.move(to: CGPoint(x: location.x, y: location.y))
    self.cursor.addLine(to: CGPoint(x: location.x + 100, y: location.y)) // change if staff space changes
    self.cursor.stroke()
}
3个回答

2

将光标绘制为CAShapeLayer对象。这样可以移动光标而无需重新绘制它。

class MyView: UIView {
    let cursor = CAShapeLayer()

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setup()
    }

    func setup() {
        // Draw cursor and add it to this view's layer
        let path = UIBezierPath()
        path.move(to: .zero)
        path.addLine(to: CGPoint(x: 100, y: 0))

        cursor.path = path.cgPath
        cursor.strokeColor = UIColor.black.cgColor
        cursor.lineWidth = 2

        layer.addSublayer(cursor)

        changeCursorLocation(location: CGPoint(x: 50, y: 100))
    }

    func changeCursorLocation(location: CGPoint) {
        cursor.position = location
    }
}

enter image description here


你好,我刚刚尝试实现了这个,但是每当我改变CAShapeLayer的位置时,光标就会消失。我忘了提到这是在一个扩展UIView的类内实现的。我正在UIView的draw()函数中调用setup()函数。 - Vince Gonzales
setup() 应该只被调用一次 - 用于绘制光标形状。从此以后,您只需要更改光标的位置即可。您不需要 draw() 函数,并且绝对不应在其中调用 setup()。什么时候需要更改光标位置? - invisible squirrel
我只调用了一次setup()函数,我也尝试将其放在init函数中,但是在调用changeCursorLocation()函数后形状仍然消失。每次单击按钮更改位置时,光标都会更改位置。 - Vince Gonzales
需要init?(coder aDecoder: NSCoder)来初始化吗?{ super.init(coder: aDecoder) let loc = ScreenCoordinates(x: 100, y: 100) setupCursor(location: loc) changeCursorLocation(location: CGPoint(x: 120, y: 100)) } - Vince Gonzales
使用draw()是有效的方法。创建图层只是另一种方法,对我来说似乎更面向对象。 - invisible squirrel
显示剩余2条评论

0

您可以从0,0开始创建贝塞尔路径,然后在绘制之前对绘图上下文应用x/y平移变换。具体细节取决于您如何进行绘制(您发布的代码是否是从视图的draw(rect:)方法中调用的?)


0
首先,您必须创建一个自定义的UIView,并且需要为您想要在UIView上进行转换的任何几何对象制作一个类,就像这样(仅适用于直线几何对象:线,折线,矩形)。
class GeoObj {

var path = UIBezierPath()

var points = [PointF]()

func createPath(){
    
    //make your path here
    points.removeAll()
    
    points.append(PointF(0,0))
    points.append(PointF(0,100))
    
    path.move(to: points[0].cgPoint)
    path.addLine(to: points[1].cgPoint)
    
}

}

(您的几何点)

struct PointF {

var x:Float = 0

var y:Float = 0

init(_ x:Float = 0, _ y:Float = 0) {
    self.x = x
    self.y = y
}

//convert to Core Graphic Point
var cgPoint : CGPoint {
    return CGPoint(x: CGFloat(self.x), y: CGFloat(self.y))
}

}

在UIView中,你必须有一些几何束(私有数组)来存储所有的几何模型,如下所示。每当你的自定义视图初始化时,你都会将业务数据注入到它里面,并渲染成Core Graphic对象。(在更高的层次上,当用户触摸某些东西时,你可以更新这些束并回调外部...)
private var geoModels = [GeoObj]() //you can create more

当UIView初始化时,您必须具备在图形上下文中呈现您的捆绑包的功能(将其放置在override func draw()中)

当您从UIViewController接收到移动(如dx,dy)时,运行该函数以移动您的对象

func moveObj(_ id : String,_ dx: Float,_ dy:Float){
    
    for (i,obj) in geoModels.enumerated() {

        if obj.id == id { //identify the obj you want to move
        
         geoModels[i].points = translatePoints(points,dx,dy)
        
         geoModels[i].path = remakePath(geoModels[i].points) //you can do this in your Geometric class (related to your business logic...)

        }
        
    }

    invalidate()

    //invoke function draw() of UIView to force the context clear all its contents 
    
    //and you re-render your geometric bundle here
    
  }

   private func translatePoints(_ points : [PointF],_ dx:Float,_ dy:Float)->[PointF]{
    
    var new_points = points
    
    for (i,_) in new_points.enumerated(){
        
        new_points[i].x += dx
        new_points[i].y += dy
        
    }
    
    return new_points
    
}

private func remakePath(_ points:[PointF])->UIBezierPath{
    
    //do your businees code to re-create Path
    
    let path = UIBezierPath()
    
    path.move(to: points[0])
    path.addLine(to: points[1]) // ....draw as your business logic
    
    return path
    
}


private func invalidate(){
    
    self.setNeedsDisplay()
    
}

请记住,每次从UIView调用self.setNeedsDisplay()时,图形上下文都会清除其内容。
 override func draw(_ rect: CGRect) {
    
    print("draw")
        
    super.draw(rect)
    
    guard let ctx = UIGraphicsGetCurrentContext() else {
        return
    }
    
    //re-render your bundle here

    render(ctx)
       
    
  }

不用担心延迟,我已经在我的真实项目中应用了这种方法,一切都运行得很顺畅。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接