ios swift autolayout uiviewanimation

ios - Diseño automático y UIViewAnimation: brecha entre dos vistas conectadas



swift autolayout (1)

Creé una vista de "información sobre herramientas" que tiene un UILabel y un UIView con un CAShapeLayer para una forma de triángulo. He configurado la "información sobre herramientas" para que la etiqueta esté en la parte superior, con el triángulo unido a la parte inferior y centrado en UILabel.

Cuando muestro la información sobre herramientas, utilizo UIViewAnimation con Spring Damping y Spring Velocity para darle una animación "pop". Esto funciona muy bien con una excepción, se puede notar un pequeño espacio entre el triángulo y el UILabel durante el comienzo de la animación (que luego se arregla cuando termina la animación).

Alguna sugerencia en como arreglar esto?

Aquí está la configuración view / constraint:

let containerView = UIView() containerView.alpha = 1.0 containerView.layer.cornerRadius = 5.0 containerView.clipsToBounds = true containerView.backgroundColor = UIColor.orangeColor() self.addSubview(containerView) self.containerView = containerView let titleLabel = UILabel() titleLabel.font = UIFont.systemFontOfSize(14.0) titleLabel.textColor = UIColor.whiteColor() titleLabel.numberOfLines = 0 titleLabel.adjustsFontSizeToFitWidth = true containerView.addSubview(titleLabel) self.titleLabel = titleLabel let triangleView = UIView() self.addSubview(triangleView) self.triangleView = triangleView let views: [String: UIView] = [ "containerView" : containerView, "titleLabel" : titleLabel, "triangleView" : triangleView, ] let metrics = [String:AnyObject]() for (_, view) in views { view.translatesAutoresizingMaskIntoConstraints = false } NSLayoutConstraint.activateConstraints(NSLayoutConstraint.constraintsWithVisualFormat("|-8-[titleLabel]-8-|", options: [], metrics: metrics, views: views)) NSLayoutConstraint.activateConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-8-[titleLabel]-8-|", options: [], metrics: metrics, views: views)) let widthConstraint = NSLayoutConstraint(item: self, attribute: .Width, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, constant: 0) widthConstraint.active = true self.widthConstraint = widthConstraint let heightConstraint = NSLayoutConstraint(item: self, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, constant: 0) heightConstraint.active = true self.heightConstraint = heightConstraint let trianglePath = UIBezierPath() trianglePath.moveToPoint(CGPoint(x: 0, y: 0)) trianglePath.addLineToPoint(CGPoint(x: 8.0, y: 10.0)) trianglePath.addLineToPoint(CGPoint(x: 16.0, y: 0)) trianglePath.closePath() let mask = CAShapeLayer() mask.frame = triangleView.bounds mask.path = trianglePath.CGPath triangleView.layer.mask = mask NSLayoutConstraint.activateConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[containerView][triangleView]|", options: [], metrics: metrics, views: views)) NSLayoutConstraint.activateConstraints(NSLayoutConstraint.constraintsWithVisualFormat("|[containerView]|", options: [], metrics: metrics, views: views)) NSLayoutConstraint.activateConstraints(NSLayoutConstraint.constraintsWithVisualFormat("|-(>=8)-[self]-(>=8)-|", options: [], metrics: metrics, views: views)) NSLayoutConstraint.activateConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-(>=8)-[self][anchorView]", options: [], metrics: metrics, views: views)) NSLayoutConstraint.activateConstraints(NSLayoutConstraint.constraintsWithVisualFormat("[triangleView(>=16)]", options: [], metrics: metrics, views: views)) NSLayoutConstraint.activateConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:[triangleView(>=10)]", options: [], metrics: metrics, views: views)) NSLayoutConstraint(item: self.triangleView, attribute: .CenterX, relatedBy: .Equal, toItem: self.anchorView, attribute: .CenterX, multiplier: 1.0, constant: 1.0).active = true let centerXConstraint = NSLayoutConstraint(item: triangleView, attribute: .CenterX, relatedBy: .Equal, toItem: containerView, attribute: .CenterX, multiplier: 1.0, constant: 0.0) centerXConstraint.priority = UILayoutPriorityDefaultLow // Required to allow tooltip to grow beyond anchorView bounds without changing the anchorView constraints. centerXConstraint.active = true

Aquí está el guión de animación:

self.layoutIfNeeded() // Set starting position for tooltip before animation self.widthConstraint.active = false self.heightConstraint.active = false UIView.animateWithDuration(0.5, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.5, options: .CurveEaseInOut, animations: { () -> Void in self.alpha = 1.0 self.layoutIfNeeded() }, completion: nil)

Vídeo:


Una forma de abordarlo sería tener una UILabel personalizada

class ToolTip: UILabel { var roundRect:CGRect! override func drawTextInRect(rect: CGRect) { super.drawTextInRect(roundRect) } override func drawRect(rect: CGRect) { roundRect = CGRect(x: rect.minX, y: rect.minY, width: rect.width, height: rect.height * 4 / 5) let roundRectBez = UIBezierPath(roundedRect: roundRect, cornerRadius: 10.0) let triangleBez = UIBezierPath() triangleBez.moveToPoint(CGPoint(x: roundRect.minX + roundRect.width / 2.5, y:roundRect.maxY)) triangleBez.addLineToPoint(CGPoint(x:rect.midX,y:rect.maxY)) triangleBez.addLineToPoint(CGPoint(x: roundRect.maxX - roundRect.width / 2.5, y:roundRect.maxY)) triangleBez.closePath() roundRectBez.appendPath(triangleBez) let bez = roundRectBez UIColor.lightGrayColor().setFill() bez.fill() super.drawRect(rect) } }

y luego realice el diseño y la animación de la siguiente manera:

import UIKit class ViewController: UIViewController { var label:ToolTip! var labelTransform:CGAffineTransform! let buttonHeight:CGFloat = 100 let buttonWidth:CGFloat = 200 override func viewDidLoad() { super.viewDidLoad() let button = UIButton() button.setTitle("Push Me", forState: .Normal) button.addTarget(self, action: Selector("buttonPushed"), forControlEvents: .TouchUpInside) button.backgroundColor = UIColor.orangeColor() view.addSubview(button) button.translatesAutoresizingMaskIntoConstraints = false button.centerXAnchor.constraintEqualToAnchor(view.centerXAnchor).active = true button.centerYAnchor.constraintEqualToAnchor(view.centerYAnchor).active = true button.heightAnchor.constraintEqualToConstant(buttonHeight).active = true button.widthAnchor.constraintEqualToConstant(buttonWidth).active = true label = ToolTip() view.insertSubview(label, belowSubview: button) label.translatesAutoresizingMaskIntoConstraints = false label.heightAnchor.constraintEqualToConstant(buttonHeight).active = true label.widthAnchor.constraintEqualToConstant(buttonWidth).active = true label.bottomAnchor.constraintEqualToAnchor(button.topAnchor).active = true label.centerXAnchor.constraintEqualToAnchor(view.centerXAnchor).active = true label.text = "This button is orange!" label.textColor = UIColor.whiteColor() label.textAlignment = .Center let trans1 = CGAffineTransformMakeScale(0, 0) let trans2 = CGAffineTransformMakeTranslation(0, buttonHeight) labelTransform = CGAffineTransformConcat(trans1, trans2) label.transform = labelTransform } func buttonPushed() { if label.transform.ty > 0 { UIView.animateWithDuration(0.75, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.5, options: .CurveEaseInOut, animations: { () -> Void in self.label.transform = CGAffineTransformIdentity }, completion: nil) } else { UIView.animateWithDuration(0.5, delay: 0, options: .CurveEaseInOut, animations: { () -> Void in self.label.alpha = 0 }, completion: {_ in self.label.transform = self.labelTransform self.label.alpha = 1 }) } } }

que crea el siguiente efecto: