问题
I created a "tooltip" view that has a UILabel and an UIView with a CAShapeLayer for a triangle shape. I have setup the "tooltip" so that the label is on top, with the triangle attached to the bottom and centered on the UILabel.
When I show the "tooltip", I use the UIViewAnimation with Spring Damping and Spring Velocity to give it a "pop" animation. This works great with one exception, a small gap can be noticed between the triangle and the UILabel during the beginning of the animation (which is then fixed when the animation ends.
Any suggestions on how to fix this?
Here is the view/constraint setup:
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:
回答1:
One way to approach it would be to have a custom 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)
}
}
and then perform layout and animation like so:
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
}) }
}
}
which creates the follow effect:

来源:https://stackoverflow.com/questions/36027410/auto-layout-and-uiviewanimation-gap-between-two-connected-views