Auto Layout and UIViewAnimation - The gap between two connected views

I created a “tooltip” that has a UILabel and a UIView with a CAShapeLayer for the shape of a triangle. I set the “tip” so that the label is on top and the triangle is attached to the bottom and centered on UILabel.

When I show the “hint”, I use UIViewAnimation with Spring Damping and Spring Velocity to give it a “pop animation”. This works great, with one exception, you can notice a small gap between the triangle and the UILabel at the beginning of the animation (which is then fixed when the animation ends.

Any suggestions on how to fix this?

Here is the view / restriction setting:

    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

Here is the animation script:

    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)

Video: enter image description here

0
1

- UILabel

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)
    }
}

:

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
            })        }



    }

}

: Popup tooltip

0

All Articles