Swift 如何确保CAShapeLayer调整大小以适应UIView
我目前正在将MKPolyline转换为BezierPath,然后转换为CAShapeLayer,然后将该层作为子层添加到UIView。目前正在努力确保路径不会绘制在UIView的边界之外。我不想遮罩并使部分路径不可见,而是要确保每个点都已调整大小并定位在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
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绘制的逻辑仍有待完成。重要的是,当您“复制”多段线时,您将
移动(到:)
正确的点。这就是为什么我认为你没有正确的偏移量AUIBezierPath
可以像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)