This is one of my notes on learning iOS Animations by Tutorials. The code in detail on andyRon/LearniOSAnimations my lot.
Auto Layout, first introduced in iOS 6, has been around for a while, with a series of successful iterations each time a new version of iOS and Xcode is released.
The core idea behind automatic layout is simple: it allows you to define the layout of your application’s UI elements based on the relationships created between each element in the layout.
We’ve used automatic layouts for static layouts in normal development, and in this article we’ll learn how to use constraints to set up animations.
6- Introduction to automatic layout
This chapter uses automatic layout to complete the Packing List of items needed in the next chapter. For automatic Layout, please refer to my previous article to start iOS 10-3 with Swift to introduce Auto Layout, which will not be repeated here.
7- Constraint animation
Animating Constraints is no more difficult than Animating properties; It’s just a little different. Typically, you just replace the existing constraint with the new one, and let Auto Layout animate the UI between the two states.
Set constraint animation
The Packing List for the starting project is roughly as follows:
Navigation bar height changes
Add constraint interface to ViewController:
@IBOutlet weak var menuHeightConstraint: NSLayoutConstraint!
Copy the code
And associate it with the height constraint of the navigation bar view:
Add the following to the Action method actionToggleMenu() in the upper right corner of the plus button:
isMenuOpen = ! isMenuOpen menuHeightConstraint.constant = isMenuOpen ? 200.0:60.0 titlelabel. text = isMenuOpen? "Select Item" : "Packing List"Copy the code
After clicking the plus button, the height of the navigation bar increases and the title changes.
Animation of layout changes
Continue adding the spring animation for layout changes in actionToggleMenu() :
UIView.animate(withDuration: 1.0, delay: 0.0, usingSpringWithDamping: 0.4, initialSpringVelocity: 10.0, options: .curveEaseIn, animations: {
// Force a layout update
self.view.layoutIfNeeded()
}, completion: nil)
Copy the code
In menuHeightConstraint. Constant = isMenuOpen? 200.0:60.0 has updated the constraint values, but iOS hasn’t had a chance to update the layout yet. By calling layoutIfNeeded() from an animation closure to force an update of the layout, you can set the center and boundaries of each view involved in the layout. For example, the table view also shrinks or increases as the Menu shrinks or increases. This is the effect of the constraint, which is now equivalent to setting two animations at once 😊.
Effect:
rotating
Rotate + 45° to x in the animation closure above:
let angle: CGFloat = self.isMenuOpen ? .pi/4 : 0.0
self.buttonMenu.transform = CGAffineTransform(rotationAngle: angle)
Copy the code
Check constraints
It is relatively easy to add code outlets to view constraints directly and visually. When it is not convenient to add interfaces in The Interfa Builder control-drag mode or to add too many outlets, you can use the constraints property provided by UIView, which is an array of all the constraints of the current view.
For example:
titleLabel.superview? .constraints.forEach { constraintin
print("- >\(constraint.description)\n")}Copy the code
Print result:
-> <NSLayoutConstraint:0x600002d04320 UIView:0x7ff7df530c00.height == 200 (active)>
-> <NSLayoutConstraint:0x600002d02210 UILabel:0x7ff7df525350'Select Item'.centerX == UIView:0x7ff7df530c00.centerX (active)>
-> <NSLayoutConstraint:0x600002d02a30 UILabel:0x7ff7df525350'Select Item'.centerY == UIView:0x7ff7df530c00.centerY + 5 (active)>
-> <NSLayoutConstraint:0x600002d02d00 H: [UIButton:0x7ff7df715d20'+'] - (8)-| (active, names: '|':UIView:0x7ff7df530c00) > - > <NSLayoutConstraint:0x600002d030c0 UIButton:0x7ff7df715d20'+'.centerY == UILabel:0x7ff7df525350'Select Item'.centerY (active)>
Copy the code
It looks a bit messy, but if you look closely you can see that there are five constraints that correspond to:
Set the constraint animation for UILabel
IsMenuOpen =! In actionToggleMenu() Add under isMenuOpen:
titleLabel.superview? .constraints.forEach { constraintin
if constraint.firstItem === titleLabel && constraint.firstAttribute == .centerX {
constraint.constant = isMenuOpen ? -100.0 : 0.0
return}}Copy the code
The general form of a constraint expression is as follows:
firstItem.firstItemAttribute == secondItem.secondItemAttribute * multiplier + constant
Copy the code
The various attributes that correspond to NSLayoutConstraint have obvious names, where == corresponds to relation, and can also be <=, >=, etc.
Practical examples:
Superview.CenterX = 1.0 * UILabel.CenterX + 0.0
Copy the code
Here’s the effect:
Alternative constraint
Each constraint can have an Identifier attribute added to it, and the constraint can be retrieved from the Identifier in the code.
Continue with the above constraint and add:
if constraint.identifier == "TitleCenterY" {
constraint.isActive = false
let newConstraint = NSLayoutConstraint(item: titleLabel, attribute: .centerY, relatedBy: .equal, toItem: titleLabel.superview! , attribute: .centerY, multiplier: isMenuOpen ?0.67 : 1.0, constant: 5.0)
newConstraint.identifier = "TitleCenterY"
newConstraint.isActive = true
return
}
Copy the code
CenterY = menu. CenterY * 0.67 + 0.0.
Effect after operation:
Add navigation bar content
In actionToggleMenu() add:
if isMenuOpen {
slider = HorizontalItemList(inView: view)
slider.didSelectItem = { index in
print("add \(index)")
self.items.append(index)
self.tableView.reloadData()
self.actionToggleMenu(self)}self.titleLabel.superview! .addSubview(slider) }else {
slider.removeFromSuperview()
}
Copy the code
HorizontalItemList is a custom UIScrollView subclass for left and right scrolling views in menu,
Dynamically creating views
When the TableView cell is clicked, showItem(_:) is called and added to this method:
// Click to create an image
let imageView = UIImageView(image: UIImage(named: "summericons_100px_0\(index).png"))
imageView.backgroundColor = UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.5)
imageView.layer.cornerRadius = 5.0
imageView.layer.masksToBounds = true
imageView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(imageView)
Copy the code
Add constraint code:
let conx = imageView.centerXAnchor.constraint(equalTo: view.centerXAnchor)
Copy the code
This method uses the new NSLayoutAnchor class, which makes it easy to create common constraints. Here, you will create constraints between the center X anchor of the image view and the view controller’s view.
Add a constraint at the bottom of the image:
let conBottom = imageView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: imageView.frame.height)
Copy the code
This constraint sets the bottom of the image view to match the bottom of the view controller view, plus the image height; This positions the image beyond the bottom edge of the screen, which will be the starting point for the animation.
Add image width constraint:
let conWidth = imageView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.33, constant: -50.0)
Copy the code
This sets the image width to 1/3 of the screen width minus 50 LBS. The target size is 1/3 of the screen; You will animate 50 pounds of difference so that the image “grows” into place.
Finally, add the height and width equality constraint and activate all of the above constraints:
let conHeight = imageView.heightAnchor.constraint(equalTo: imageView.widthAnchor)
NSLayoutConstraint.activate([conx, conBottom, conWidth, conHeight])
Copy the code
Click on the TableView Cell and see the following:
Create animations for dynamically created views
In showItem(_:) add:
UIView.animate(withDuration: 0.8, delay: 0.0, usingSpringWithDamping: 0.4, initialSpringVelocity: 0.0, animations: {
conBottom.constant = -imageView.frame.size.height/2
conWidth.constant = 0.0
self.view.layoutIfNeeded()
}, completion: nil)
Copy the code
But here’s the effect:
** Think about it: ** adds a view, sets some constraints, then changes those constraints and animates layout changes. However, the view never has a chance to perform its initial layout, so the image starts at its default position (0, 0) in the upper left corner 🙄.
To fix this, just do the initial layout before the animation starts, and add before the animation:
view.layoutIfNeeded()
Copy the code
The effect is to come up from below:
Remove the image that already appears
The images that pop up above will overlap and need to be removed before the next image comes out.
Add below the previous code:
UIView.animate(withDuration: 0.8, delay: 1.0, usingSpringWithDamping: 0.4, initialSpringVelocity: 0.0, animations: {
conBottom.constant = imageView.frame.size.height
conWidth.constant = -50.0
self.view.layoutIfNeeded()
}) { (_) in
imageView.removeFromSuperview()
}
Copy the code
The result is 😝
This article is in my personal blog address: System learning iOS animation ii: Automatic layout animation