Swift 如何确保CAShapeLayer调整大小以适应UIView

Swift 如何确保CAShapeLayer调整大小以适应UIView,swift,scale,uibezierpath,polyline,cashapelayer,Swift,Scale,Uibezierpath,Polyline,Cashapelayer,我目前正在将MKPolyline转换为BezierPath,然后转换为CAShapeLayer,然后将该层作为子层添加到UIView。目前正在努力确保路径不会绘制在UIView的边界之外。我不想遮罩并使部分路径不可见,而是要确保每个点都已调整大小并定位在UIView的中心 func addPathToView() { guard let path = createPath(onView: polylineView) else { return } path.fit(into: p

我目前正在将MKPolyline转换为BezierPath,然后转换为CAShapeLayer,然后将该层作为子层添加到UIView。目前正在努力确保路径不会绘制在UIView的边界之外。我不想遮罩并使部分路径不可见,而是要确保每个点都已调整大小并定位在UIView的中心

func addPathToView() {
    guard let path = createPath(onView: polylineView) else { return }
    path.fit(into: polylineView.bounds).moveCenter(to: polylineView.center).fill()
    path.lineWidth     = 3.0
    path.lineJoinStyle = .round

    guard let layer  = createCAShapeLayer(fromBezierPath: path) else { return }
    layer.path       = getScaledPath(fromPath: path, layer: layer)
    layer.frame      = polylineView.bounds
    layer.position.x = polylineView.bounds.minX
    layer.position.y = polylineView.bounds.minY

    polylineView.layer.addSublayer(layer)
}

func createCAShapeLayer( fromBezierPath path: UIBezierPath? ) -> CAShapeLayer? {
    guard let path = path else { print("No Path"); return nil }
    let pathLayer = CAShapeLayer(path: path, lineColor: UIColor.red, fillColor: UIColor.clear)
    return pathLayer
}

func createPath( onView view: UIView? ) -> UIBezierPath? {
    guard let polyline = Polyline().createPolyline(forLocations: locations) else { print("No Polyline"); return nil }
    guard let points   = convertMapPointsToCGPoints(fromPolyline: polyline) else { print("No CGPoints"); return nil }

    let path = UIBezierPath(points: points)

    return path
}

func convertMapPointsToCGPoints( fromPolyline polyline: MKPolyline? ) -> [CGPoint]? {
    guard let polyline = polyline else { print( "No Polyline"); return nil }

    let mapPoints = polyline.points()

    var points = [CGPoint]()

    for point in 0..<polyline.pointCount {
        let coordinate = MKCoordinateForMapPoint(mapPoints[point])
        points.append(mapView.convert(coordinate, toPointTo: view))
    }

    return points
}

func getScaledPath( fromPath path: UIBezierPath, layer: CAShapeLayer ) -> CGPath? {
    let boundingBox = path.cgPath.boundingBoxOfPath

    let boundingBoxAspectRatio = boundingBox.width / boundingBox.height
    let viewAspectRatio = polylineView.bounds.size.width / polylineView.bounds.size.height

    let scaleFactor: CGFloat
    if (boundingBoxAspectRatio > viewAspectRatio) {
        // Width is limiting factor
        scaleFactor = polylineView.bounds.size.width / boundingBox.width
    } else {
        // Height is limiting factor
        scaleFactor = polylineView.bounds.size.height/boundingBox.height
    }

    var affineTransorm = CGAffineTransform(scaleX: scaleFactor, y: scaleFactor)
    let transformedPath = path.cgPath.copy(using: &affineTransorm)

    guard let tPath = transformedPath else { print ("nope"); return nil }

    return tPath
}

extension UIBezierPath
{
    func moveCenter(to:CGPoint) -> Self{
        let bound  = self.cgPath.boundingBox
        let center = bounds.center

        let zeroedTo = CGPoint(x: to.x-bound.origin.x, y: to.y-bound.origin.y)
        let vector = center.vector(to: zeroedTo)

        offset(to: CGSize(width: vector.dx, height: vector.dy))
        return self
    }

    func offset(to offset:CGSize) -> Self{
        let t = CGAffineTransform(translationX: offset.width, y: offset.height)
        applyCentered(transform: t)
        return self
    }

    func fit(into:CGRect) -> Self{
        let bounds = self.cgPath.boundingBox

        let sw     = into.size.width/bounds.width
        let sh     = into.size.height/bounds.height
        let factor = min(sw, max(sh, 0.0))

        return scale(x: factor, y: factor)
    }

    func scale(x:CGFloat, y:CGFloat) -> Self{
        let scale = CGAffineTransform(scaleX: x, y: y)
        applyCentered(transform: scale)
        return self
    }

    func applyCentered(transform: @autoclosure () -> CGAffineTransform ) -> Self{
        let bound  = self.cgPath.boundingBox
        let center = CGPoint(x: bound.midX, y: bound.midY)
        var xform  = CGAffineTransform.identity

        xform = xform.concatenating(CGAffineTransform(translationX: -center.x, y: -center.y))
        xform = xform.concatenating(transform())
        xform = xform.concatenating( CGAffineTransform(translationX: center.x, y: center.y))
        apply(xform)

        return self
    }
}

extension UIBezierPath
{
    convenience init(points:[CGPoint])
    {
        self.init()

        //connect every points by line.
        //the first point is start point
        for (index,aPoint) in points.enumerated()
        {
            if index == 0 {
                self.move(to: aPoint)
            }
            else {
                self.addLine(to: aPoint)
            }
        }
    }
}

//2. To create layer use this extension

extension CAShapeLayer
{
    convenience init(path:UIBezierPath, lineColor:UIColor, fillColor:UIColor)
    {
        self.init()
        self.path = path.cgPath
        self.strokeColor = lineColor.cgColor
        self.fillColor = fillColor.cgColor
        self.lineWidth = path.lineWidth

        self.opacity = 1
        self.frame = path.bounds
    }
}
func addPathToView(){
guard let path=createPath(onView:polylineView)else{return}
fit(到:polylineView.bounds中)。moveCenter(到:polylineView.center)。fill()
path.lineWidth=3.0
path.lineJoinStyle=.round
guard let layer=createCAShapeLayer(fromBezierPath:path)else{return}
layer.path=getScaledPath(fromPath:path,layer:layer)
layer.frame=polylineView.bounds
layer.position.x=polylineView.bounds.minX
layer.position.y=polylineView.bounds.minY
polylineView.layer.addSublayer(图层)
}
func createCAShapeLayer(从BezierPath路径:UIBezierPath?->CAShapeLayer?{
guard let path=path else{print(“No path”);返回nil}
让pathLayer=CAShapeLayer(路径:path,lineColor:UIColor.red,fillColor:UIColor.clear)
返回路径层
}
func createPath(onView视图:UIView?->UIBezierPath?{
guard let polyline=polyline().createPolyline(forLocations:locations)else{打印(“无多段线”);返回nil}
guard let points=convertMapPointsToCGPoints(fromPolyline:polyline)else{print(“无CGPoints”);返回nil}
让路径=UIBezierPath(点:点)
返回路径
}
func CONVERTMAPPOINTS到CGPOINTS(从多段线到多段线:MKPolyline?)->[CGPoint]?{
guard let polyline=polyline else{print(“No polyline”);返回nil}
设mapPoints=polyline.points()
变量点=[CGPoint]()
对于0..CGPath中的点{
设boundingBox=path.cgPath.boundingBoxOfPath
让boundingBox Aspectratio=boundingBox.width/boundingBox.height
让viewAspectRatio=polylineView.bounds.size.width/polylineView.bounds.size.height
让scaleFactor:CGFloat
如果(boundingBoxAspectRatio>viewAspectRatio){
//宽度是限制因素
scaleFactor=polylineView.bounds.size.width/boundingBox.width
}否则{
//高度是限制因素
scaleFactor=polylineView.bounds.size.height/boundingBox.height
}
var affinetransform=cgafinetransform(scaleX:scaleFactor,y:scaleFactor)
让transformedPath=path.cgPath.copy(使用:&仿射变换)
guard let tPath=transformedPath else{print(“nope”);返回nil}
返回tPath
}
扩展UIBezierPath
{
func移动中心(至:CGPoint)->自{
let bound=self.cgPath.boundingBox
设center=bounds.center
设zeroedTo=CGPoint(x:to.x-bound.origin.x,y:to.y-bound.origin.y)
设vector=center.vector(to:zeroedTo)
偏移量(到:CGSize(宽度:vector.dx,高度:vector.dy))
回归自我
}
func offset(到offset:CGSize)->Self{
设t=CGAffineTransform(translationX:offset.width,y:offset.height)
应用程序居中(转换:t)
回归自我
}
func-fit(进入:CGRect)->Self{
设边界=self.cgPath.boundingBox
设sw=into.size.width/bounds.width
设sh=into.size.height/bounds.height
let系数=最小值(sw,最大值(sh,0.0))
回报比例(x:因子,y:因子)
}
func比例(x:CGFloat,y:CGFloat)->自{
设scale=CGAffineTransform(scaleX:x,y:y)
应用程序居中(变换:比例)
回归自我
}
func applyCentered(转换:@autoclosure()->cgfaffinetransform)->Self{
let bound=self.cgPath.boundingBox
设中心=CGPoint(x:bound.midX,y:bound.midY)
var xform=CGAffineTransform.identity
变换=变换连接(CGAffineTransform(translationX:-center.x,y:-center.y))
xform=xform.concatenating(transform())
变换=变换.连接(CGAffineTransform(translationX:center.x,y:center.y))
应用(变换)
回归自我
}
}
扩展UIBezierPath
{
便利初始化(点数:[CGPoint])
{
self.init()
//用直线连接每个点。
//第一点是起点
以点为单位的(索引,aPoint)。枚举()
{
如果索引==0{
self.move(到:aPoint)
}
否则{
self.addLine(to:aPoint)
}
}
}
}
//2.要创建图层,请使用此扩展
延伸层
{
便利初始化(路径:UIBezierPath,lineColor:UIColor,fillColor:UIColor)
{
self.init()
self.path=path.cgPath
self.strokeColor=lineColor.cgColor
self.fillColor=fillColor.cgColor
self.lineWidth=path.lineWidth
self.opacity=1
self.frame=path.bounds
}
}

以下是我用来缩放UIBezierPath的方法: 我将使用原始(MKPolyline大小,我的原始数据)和最终(接收视图大小,显示方式)

1.计算原始振幅(对我来说,它只是高度,但对你来说,它也将是宽度)

2.编写一个函数,将原始数据缩放到新的X轴和Y轴比例(对于点位置,如下所示):

3.开始绘图

let path = UIBezierPath()
let path.move(to: CGPoint(x: yourOriginForDrawing, y: yourOriginForDrawing)) // final scale position

path.addLine(to: CGPoint(x: nextXPoint, y: nextYPoint)) // this is not relevant for you as you don't draw point by point
// what is important here is the fact that you take your original
//data X and Y and make them go though your scale functions 

let layer = CAShapeLayer()
let layer.path = path.cgPath
let layer.lineWidth = 1.0
let layer.strokeColor = UIColor.black

yourView.layer.addSublayer(layer)

正如您所见,从MKPolyline绘制的逻辑仍有待完成。重要的是,当您“复制”多段线时,您将
移动(到:)
正确的点。这就是为什么我认为你没有正确的偏移量A
UIBezierPath
可以像
CGRect
CGPoint
或使用
cgraffeTransform
的“CGSize”一样缩放。
为什么要在移动中心函数中进行偏移?@Ocunidee UIBezierPathExtension是在网上找到的一个解决我的问题的尝试,目前我觉得我只是在组装
let path = UIBezierPath()
let path.move(to: CGPoint(x: yourOriginForDrawing, y: yourOriginForDrawing)) // final scale position

path.addLine(to: CGPoint(x: nextXPoint, y: nextYPoint)) // this is not relevant for you as you don't draw point by point
// what is important here is the fact that you take your original
//data X and Y and make them go though your scale functions 

let layer = CAShapeLayer()
let layer.path = path.cgPath
let layer.lineWidth = 1.0
let layer.strokeColor = UIColor.black

yourView.layer.addSublayer(layer)