2016-07-29 34 views
1

Ich entwarf eine benutzerdefinierte Header-Ansicht, die ein Bild maskiert und einen Rand auf die untere Kante zeichnet, die ein Bogen ist. Es sieht wie folgt aus:Warum beeinflusst UIView.animateWithDuration diese benutzerdefinierte Ansicht nicht?

enter image description here

Hier ist der Code für die Klasse ist:

class HeaderView: UIView 
{ 
    private let imageView = UIImageView() 
    private let dimmerView = UIView() 

    private let arcShape = CAShapeLayer() 
    private let maskShape = CAShapeLayer() // Masks the image and the dimmer 

    private let titleLabel = UILabel() 

    @IBInspectable var image: UIImage? { didSet { self.imageView.image = self.image } } 
    @IBInspectable var title: String? { didSet {self.titleLabel.text = self.title} } 

    @IBInspectable var arcHeight: CGFloat? { didSet {self.setupLayers()} } 

    // MARK: Initialization 

    override init(frame: CGRect) 
    { 
     super.init(frame:frame) 
     initMyStuff() 
    } 

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

    override func prepareForInterfaceBuilder() 
    { 
     backgroundColor = UIColor.clear() 
    } 

    internal func initMyStuff() 
    { 
     backgroundColor = UIColor.clear() 

     titleLabel.font = Font.AvenirNext_Bold(24) 
     titleLabel.text = "TITLE" 
     titleLabel.textColor = UIColor.white() 
     titleLabel.layer.shadowColor = UIColor.black().cgColor 
     titleLabel.layer.shadowOffset = CGSize(width: 0.0, height: 2.0) 

     titleLabel.layer.shadowRadius = 0.0; 
     titleLabel.layer.shadowOpacity = 1.0; 

     titleLabel.layer.masksToBounds = false 
     titleLabel.layer.shouldRasterize = true 

     imageView.contentMode = UIViewContentMode.scaleAspectFill 
     addSubview(imageView) 

     dimmerView.frame = self.bounds 
     dimmerView.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.6) 
     addSubview(dimmerView) 
     addSubview(titleLabel) 

     // Add the shapes 
     self.layer.addSublayer(arcShape) 
     self.layer.addSublayer(maskShape) 
     self.layer.masksToBounds = true // This seems to be unneeded...test more 

     // Set constraints 
     imageView.translatesAutoresizingMaskIntoConstraints = false 
     imageView .autoPinEdgesToSuperviewEdges() 
     titleLabel.autoCenterInSuperview() 
    } 

    func setupLayers() 
    { 
     let aHeight = arcHeight ?? 10 

     // Create the arc shape 
     arcShape.path = AppocalypseUI.createHorizontalArcPath(CGPoint(x: 0, y: bounds.size.height), width: bounds.size.width, arcHeight: aHeight) 
     arcShape.strokeColor = UIColor.white().cgColor 
     arcShape.lineWidth = 1.0 
     arcShape.fillColor = UIColor.clear().cgColor 

     // Create the mask shape 
     let maskPath = AppocalypseUI.createHorizontalArcPath(CGPoint(x: 0, y: bounds.size.height), width: bounds.size.width, arcHeight: aHeight, closed: true) 
     maskPath.moveTo(nil, x: bounds.size.width, y: bounds.size.height) 
     maskPath.addLineTo(nil, x: bounds.size.width, y: 0) 
     maskPath.addLineTo(nil, x: 0, y: 0) 
     maskPath.addLineTo(nil, x: 0, y: bounds.size.height) 

     //let current = CGPathGetCurrentPoint(maskPath); 
     //print(current) 

     let mask_Dimmer = CAShapeLayer() 
     mask_Dimmer.path = maskPath.copy() 

     maskShape.fillColor = UIColor(red: 0, green: 0, blue: 0, alpha: 1.0).cgColor 
     maskShape.path = maskPath 

     // Apply the masks 
     imageView.layer.mask = maskShape 
     dimmerView.layer.mask = mask_Dimmer 
    } 

    override func layoutSubviews() 
    { 
     super.layoutSubviews() 

     // Let's go old school here... 
     imageView.frame = self.bounds 
     dimmerView.frame = self.bounds 
     setupLayers() 
    } 
} 

So etwas wie dies dazu führen, wird es nur auf die neue Größe schnappen, ohne ihren Rahmen allmählich zu ändernden:

UIView.animate(withDuration: 1.0) 
{ 
    self.headerView.arcHeight  = self.new_headerView_arcHeight 
    self.headerView.frame   = self.new_headerView_frame 
} 

Ich denke, es muss etwas mit der Tatsache zu tun haben, dass ich CALayers verwende, aber ich weiß nicht wirklich genug darüber, was hinter der SCE passiert nein.

EDIT: Hier ist die Funktion, die ich verwenden, um die Bogenbahn zu erstellen:

class func createHorizontalArcPath(_ startPoint:CGPoint, width:CGFloat, arcHeight:CGFloat, closed:Bool = false) -> CGMutablePath 
    { 
     // http://www.raywenderlich.com/33193/core-graphics-tutorial-arcs-and-paths 

     let arcRect = CGRect(x: startPoint.x, y: startPoint.y-arcHeight, width: width, height: arcHeight) 

     let arcRadius = (arcRect.size.height/2) + (pow(arcRect.size.width, 2)/(8*arcRect.size.height)); 
     let arcCenter = CGPoint(x: arcRect.origin.x + arcRect.size.width/2, y: arcRect.origin.y + arcRadius); 

     let angle = acos(arcRect.size.width/(2*arcRadius)); 
     let startAngle = CGFloat(M_PI)+angle // (180 degrees + angle) 
     let endAngle = CGFloat(M_PI*2)-angle // (360 degrees - angle) 
     //  let startAngle = radians(180) + angle; 
     //  let endAngle = radians(360) - angle; 

     let path = CGMutablePath(); 
     path.addArc(nil, x: arcCenter.x, y: arcCenter.y, radius: arcRadius, startAngle: startAngle, endAngle: endAngle, clockwise: false); 
     if(closed == true) 
     {path.addLineTo(nil, x: startPoint.x, y: startPoint.y);} 
     return path; 
    } 

BONUS: Einstellen der arcHeight Eigenschaft auf 0 ergibt keine weiße Linie gezogen werden. Warum?

Antwort

0

Die Path-Eigenschaft kann nicht animiert werden. Sie müssen das Problem anders angehen. Sie können einen Bogen "sofort" zeichnen, jeden Bogen, so dass wir die Animation manuell behandeln müssen. Wenn Sie erwarten, dass der gesamte Zeichenprozess etwa 3 Sekunden dauert, sollten Sie den Prozess auf 1000 Teile aufteilen und die Bogenzeichnungsfunktion alle 0,3 Millisekunden 1000 Mal aufrufen, um den Bogen erneut vom Anfang bis zum aktuellen Punkt zu zeichnen.

0

self.headerView.arcHeight ist keine animierbare Eigenschaft. Es ist UIView nur eigene Eigenschaften animierbaren sind

Sie so etwas wie dieses

let displayLink = CADisplayLink(target: self, selector: #selector(update)) 
displayLink.addToRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode 

let expectedFramesPerSecond = 60 


var diff : CGFloat = 0 
func update() { 
    let diffUpdated = self.headerView.arcHeight - self.new_headerView_arcHeight 
    let done = (fabs(diffUpdated) < 0.1) 
    if(!done){ 
     self.headerView.arcHeight -= diffUpdated/(expectedFramesPerSecond*0.5) 
     self.setNeedsDisplay() 
    } 
} 
tun können