2016-06-03 35 views
5

Ho un'animazione di disegnare un rettangolo a una percentuale specifica di esso.CABasicIl disegno di un tracciato non viene ridimensionato quando la scala di SuperView cambia

Per questo sto disegnando un CAShapeLayer:

func drawTheLayer() -> CAShapeLayer { 

    let lineWidth: CGFloat = borderWidth * bounds.size.width/standardSizeWidth 
    let cornerRadiusResized: CGFloat = cornerRadiusRanged * bounds.size.width/standardSizeWidth 
    let insetRect = CGRectInset(bounds, lineWidth/2.0, lineWidth/2.0) 
    let apath = ShapeDraw.createRoundedCornerPath(insetRect, cornerRadius: cornerRadiusResized, percent: percentageRanged) 
    let apathLayer = CAShapeLayer() 

    apathLayer.frame = bounds 
    apathLayer.bounds = insetRect 
    apathLayer.path = apath 
    apathLayer.strokeColor = AppColor.OnColor.CGColor 
    apathLayer.fillColor = nil 
    apathLayer.lineWidth = lineWidth 
    apathLayer.lineJoin = kCALineJoinRound 
    apathLayer.lineCap = kCALineCapRound 
    apathLayer.geometryFlipped = true 

    let flipTransform = CGAffineTransformMakeScale(1, -1) 

    apathLayer.setAffineTransform(flipTransform) 
    return apathLayer 

} 

per animare il disegno:

func animateToLevel() { 
    if percentage <= 0 { 
     return 
    } 

    pathLayer?.removeFromSuperlayer() 
    pathLayer?.removeAllAnimations() 

    animating = true 
    let apathLayer = drawTheLayer() 
    layer.addSublayer(apathLayer) 
    let pathAnimation = CABasicAnimation(keyPath: "strokeEnd") 
    pathAnimation.delegate = self 
    pathAnimation.duration = 0.5 
    pathAnimation.fromValue = 0.0 
    pathAnimation.setValue("levelAnimation", forKey: "animationId") 
    apathLayer.addAnimation(pathAnimation, forKey: nil) 

    pathLayer = apathLayer 
} 

Irrilevante per questa animazione, c'è un'altra animazione che può accadere. Ridimensionamento del rettangolo superView. Il problema si verifica quando comincio a disegnare il tracciato viene disegnato in base al superView di piccole dimensioni e quindi quando il frame diventa più grande il percorso dell'animazione del disegno rimane lo stesso che è previsto ma esiste una soluzione logica e non hacky per questo? O devo farlo in drawRect

Per superview l'animazione sta cambiando l'altezza costante UILayout in un'animazione UIView:

heightOfContainerConstraint.constant = 100 // or 400 when it is expanded 
UIView.animateWithDuration(animated ? animationDuration : 0) { 
    self.view.layoutIfNeeded() 
} 

Codice di creare un angolo arrotondato:

import UIKit 

class ShapeDraw { 

static func createRoundedCornerPath(rect: CGRect, cornerRadius: CGFloat, percent: CGFloat) -> CGMutablePathRef { 

    let piNumber: CGFloat = CGFloat(M_PI) 

    // get the 4 corners of the rect 
    let topLeft = CGPointMake(rect.origin.x, rect.origin.y) 
    let topRight = CGPointMake(rect.origin.x + rect.size.width, rect.origin.y) 
    let bottomRight = CGPointMake(rect.origin.x + rect.size.width, rect.origin.y + rect.size.height) 
    let bottomLeft = CGPointMake(rect.origin.x, rect.origin.y + rect.size.height) 

    // Set 4 corner arc starting angles 
    let startAngleTopRight: CGFloat = 3 * piNumber/2 
    let startAngleBottomRight: CGFloat = 0 
    let startAngleBottomLeft: CGFloat = piNumber/2 
    let startAngleTopLeft: CGFloat = piNumber 

    let slices = (CGRectGetWidth(rect)/cornerRadius) * 4 
    let partValue: CGFloat = 100/slices // %100 is total -> 1 piece is 100/16 percent 

    let wayToGoLine = CGRectGetWidth(rect) - 2 * cornerRadius 
    let linePartValue = partValue * (slices/4 - 2) 

    var remainingPercent: CGFloat = percent 
    let path = CGPathCreateMutable() 
    // move to top left 
    CGPathMoveToPoint(path, nil, topRight.x/2, topRight.y) 

    // add top right half line 
    remainingPercent = addLine(path, x: topRight.x/2 + (wayToGoLine/2 * getConstantForThis(remainingPercent, partValue: linePartValue/2)), y: topRight.y, remainingPercent: remainingPercent, currentPartPercent: linePartValue/2) 

    // add top right curve 
    let endingAngleTopRight = endingAngleForThis(startAngleTopRight, remainingPercent: remainingPercent, partValue: partValue) 
    remainingPercent = addArc(path, x: topRight.x - cornerRadius, y: topRight.y + cornerRadius, radius: cornerRadius, startAngle: startAngleTopRight, endingAngle: endingAngleTopRight, remainingPercent: remainingPercent, currentPartPercent: partValue * 2) 

    // add right line 
    remainingPercent = addLine(path, x: bottomRight.x, y: topRight.y + cornerRadius + (wayToGoLine * getConstantForThis(remainingPercent, partValue: linePartValue)), remainingPercent: remainingPercent, currentPartPercent: linePartValue) 

    // add bottom right curve 
    let endingAngleBottomRight = endingAngleForThis(startAngleBottomRight, remainingPercent: remainingPercent, partValue: partValue) 
    remainingPercent = addArc(path, x: bottomRight.x - cornerRadius, y: bottomRight.y - cornerRadius, radius: cornerRadius, startAngle: startAngleBottomRight, endingAngle: endingAngleBottomRight, remainingPercent: remainingPercent, currentPartPercent: partValue * 2) 

    // add bottom line 
    remainingPercent = addLine(path, x: bottomRight.x - cornerRadius - (wayToGoLine * getConstantForThis(remainingPercent, partValue: linePartValue)), y: bottomLeft.y, remainingPercent: remainingPercent, currentPartPercent: linePartValue) 

    // add bottom left curve 
    let endingAngleBottomLeft = endingAngleForThis(startAngleBottomLeft, remainingPercent: remainingPercent, partValue: partValue) 
    remainingPercent = addArc(path, x: bottomLeft.x + cornerRadius, y: bottomLeft.y - cornerRadius, radius: cornerRadius, startAngle: startAngleBottomLeft, endingAngle: endingAngleBottomLeft, remainingPercent: remainingPercent, currentPartPercent: partValue * 2) 

    // add left line 
    remainingPercent = addLine(path, x: topLeft.x, y: bottomLeft.y - cornerRadius - (wayToGoLine * getConstantForThis(remainingPercent, partValue: linePartValue)), remainingPercent: remainingPercent, currentPartPercent: linePartValue) 

    // add top left curve 
    let endingAngleTopLeft = endingAngleForThis(startAngleTopLeft, remainingPercent: remainingPercent, partValue: partValue) 
    remainingPercent = addArc(path, x: topLeft.x + cornerRadius, y: topLeft.y + cornerRadius, radius: cornerRadius, startAngle: startAngleTopLeft, endingAngle: endingAngleTopLeft, remainingPercent: remainingPercent, currentPartPercent: partValue * 2) 

    // add top left half line 
    remainingPercent = addLine(path, x: topLeft.x + cornerRadius + (wayToGoLine/2 * getConstantForThis(remainingPercent, partValue: linePartValue/2)), y: topRight.y, remainingPercent: remainingPercent, currentPartPercent: linePartValue/2) 

    return path 
} 

static func endingAngleForThis(startAngle: CGFloat, remainingPercent: CGFloat, partValue: CGFloat) -> CGFloat { 
    return startAngle + (CGFloat(M_PI) * getConstantForThis(remainingPercent, partValue: partValue * 2)/2) 
} 

static func getConstantForThis(percent: CGFloat, partValue: CGFloat) -> CGFloat { 
    let percentConstant = percent - partValue > 0 ? 1 : percent/partValue 
    return percentConstant 
} 

static func addLine(path: CGMutablePath?, x: CGFloat, y: CGFloat, remainingPercent: CGFloat, currentPartPercent: CGFloat) -> CGFloat { 
    if remainingPercent > 0 { 
     CGPathAddLineToPoint(path, nil, x, y) 
     return remainingPercent - currentPartPercent 
    } 
    return 0 
} 

static func addArc(path: CGMutablePath?, x: CGFloat, y: CGFloat, radius: CGFloat, startAngle: CGFloat, endingAngle: CGFloat, remainingPercent: CGFloat, currentPartPercent: CGFloat) -> CGFloat { 
    if remainingPercent > 0 { 

     CGPathAddArc(path, nil, x, y, radius, startAngle, endingAngle, false) 
     return remainingPercent - currentPartPercent 
    } 
    return 0 
} 

} 
+0

@AlessandroOrnano ho aggiunto il codice, ma io non credo che sia necessario che uno restituisce un CGMutablePathRef e la funzione in sé non ha molta importanza. –

+0

Ho aggiunto anche la schermata gif, spero che qualcuno risponda ora: D @AlessandroOrnano –

+0

Bene, questa è una domanda chiara. Non so se ho capito bene ma ho cercato di dare una risposta. –

risposta

1

Essenzialmente , abbiamo due animazioni:

  • seguire il rect percorso Angolo ad una percentuale specifica
  • ridimensionamento del rettangolo superview o l'animazione di limiti che cambiano

L'obiettivo è: queste animazioni devono lavorare insieme nello stesso tempo (un gruppo), non sequenzialmente .

Il codice che segue è solo un esempio e non seguono le proprietà esatte o obiettivi personalizzati, voglio spiegare quello che vorrei fare in questo caso:

// follow the rectangle path 
    let pathAnimation = CABasicAnimation(keyPath: "strokeEnd") 
    let cornerRadiusResized: CGFloat = cornerRadiusRanged * bounds.size.width/standardSizeWidth 
    let apath = ShapeDraw.createRoundedCornerPath(insetRect, cornerRadius: cornerRadiusResized, percent: percentageRanged) 
    pathAnimation.toValue = apath 

    // scaling of the rectangle superview 
    let newBounds = CGRectMake(self.view.bounds.origin.x, self.view.bounds.origin.y, self.view.bounds.width, self.view.bounds.height) 
    let boundsAnimation = CABasicAnimation(keyPath: "bounds") 
    boundsAnimation.toValue = NSValue(CGRect:newBounds) 

    // The group 
    var theGroup: CAAnimationGroup = CAAnimationGroup() 
    theGroup.animations = [pathAnimation,boundsAnimation] 
    theGroup.duration = 2.0 
    theGroup.repeatCount = 1 
    theGroup.fillMode = kCAFillModeForwards 
    apathLayer.addAnimation(theGroup, forKey: "theGroup") 

EDIT:

Se avete bisogno di un terzo di animazione, come si parla nei vostri commenti, per cambiare UIButton dimensione si può anche aggiungere:

var buttonAnimation:CABasicAnimation = CABasicAnimation(keyPath: "transform.scale") 
pulseAnimation.duration = 2.0 
pulseAnimation.toValue = NSNumber(float: 0.5) 
pulseAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut) 

e aggiungerlo nella matrice:

theGroup.animations = [pathAnimation,buttonAnimation, boundsAnimation] 
+0

L'ho provato prima, ma il percorso che ho creato con createRoundedCornerPath non viene ridimensionato mentre la visualizzazione è in scala. Quando uso dispatch_after (delayTime, dispatch_get_main_queue()) {l'animazione del disegno inizia dopo il ridimensionamento. –

+0

Entrambe le animazioni devono essere eseguite nella coda principale. Eseguono in sequenza o è solo un problema di durata (diverso NSTimeInterval)?Hai provato a modificare entrambi i parametri di durata, ad esempio pathAnimation più lungo di boundAnimation? –

+0

effettivamente ci sono 3 animazioni 1. disegno di un percorso all'interno del pulsante, 2. ridimensionamento del pulsante 3. ridimensionamento del contenitore Vista. e se imposto la coda principale di animazione prima 3 sta funzionando rispetto a 1 e 2 ovviamente funziona allo stesso tempo poiché si tratta di un'animazione di gruppo. funzionano tutti della stessa durata, ho già controllato che la durata di 10.0 secondi per tutte le animazioni per vedere meglio. –