2017-02-07 2 views
1

Je suis en train de convertir un MKPolyline en un BezierPath puis un CAShapeLayer puis en ajoutant la couche en tant que sous-couche à un UIView. En train de lutter pour s'assurer que le chemin n'est pas tracé en dehors des limites de l'UIView. Je ne veux pas masquer et faire disparaître une partie du chemin, mais plutôt m'assurer que chaque point est redimensionné et positionné au centre de l'UIView.Comment faire pour que CAShapeLayer se redimensionne pour s'adapter à 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 
    } 
} 

enter image description here

+0

pourquoi vous décalez dans votre fonction moveCenter? – Ocunidee

+0

@Ocunidee le UIBezierPathExtension était une tentative trouvée en ligne pour résoudre mon problème [link] (https://github.com/xhamr/paintcode-path-scale/blob/master/UIBezierPath%2B.swift) Actuellement, je me sens comme je suis Il suffit de mettre en place des parties de différents casse-tête et de faire à peine quelque chose de travail, c'est pourquoi je suis venu à empiler débordement pour de l'aide! Je ne peux pas déterminer pourquoi je ne peux pas simplement redimensionner la couche, définir son cadre aux limites de l'uiview et être fait. – lifewithelliott

+0

avez-vous essayé de commenter la partie sur le décalage? – Ocunidee

Répondre

1

Voici une approche que je l'utilise à l'échelle d'un UIBezierPath: Je vais utiliser d'origine (taille de votre MKPolyline, mes données d'origine) et finale (la taille d'affichage de réception, comment il sera affiché).

1.Calculate l'amplitude d'origine (pour moi il était juste la hauteur, mais pour vous ce sera la largeur ainsi)

2.Ecrire une fonction à l'échelle vos données d'origine au nouvel axe X et Y échelles (pour une position de point il serait ressembler à ceci):

func scaleValueToYAxis(_ value: Double) -> CGFloat { 
    return finalHeight - CGFloat(value)/originalYAmplitude) * finalHeight 
} 

func scaleValueToXAxis(_ value: Double) -> CGFloat { 
    return finalWidth - CGFloat(value)/originalXAmplitude) * finalWidth 

} 

3.Start dessin

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) 

comme vous pouvez le voir la logique de dessin depuis MKPolyline reste à faire. Ce qui importe est que lorsque vous "copiez" la polyligne vous avez le bon point de le faire. C'est pourquoi je pense que vous n'avez pas le bon décalage

+0

Encore du mal à obtenir une sortie de travail. Légèrement confus sur "les données originales X et Y et les faire passer à travers vos fonctions d'échelle" J'ai ajouté deux extensions au bas du code pour montrer comment je crée le BezierPath et CAShapeLayer. Merci pour l'aide jusqu'à présent et j'espère que mes détails supplémentaires ont pu se concentrer sur mon problème @Ocunidee – lifewithelliott

+0

votre première méthode semble bonne (vous avez un tableau de points alors?) Votre ne doit pas définir un cadre à la CAShapeLayer. Le fait de l'ajouter à une vue spécifique lui donnera déjà les limites de la vue. Il vous manque une dernière chose, c'est la mise à l'échelle. Avez-vous un tableau pour le CGPoint de votre MKPolyline ou non? – Ocunidee

+0

J'ai un tableau CGPoint qui est rempli avec les MapPoints. convertMapPointsToCGPoints (_ :) Je pense que tout est correct. Donc, il se peut que ce soit juste la mise à l'échelle? – lifewithelliott