iOS Development-Animation chapter layout animation in Depth

Source: Internet
Author: User

"It must be said that the title of the article alone may not be sufficient to explain the contents of this article." So before I go on with the constraint animations, I'll put on the animation effects I'm going to implement in this article. "

Editor: Bison
Submission: Sindri's small nest

Constrained animations are not very complex tricks, and you can always create ingenious animations after you have mastered the constraints. In the first experience of AutoLayout animation, we changed the constraint values according to the scrolling offset of the listening list view, so as to make the effect of the moving picture. But the implementation of the last animation is more like the creation of a continuous frame of the interface to achieve the effect of animation-this is too complicated. In this article we will abandon this cumbersome way to UIView implement animations by invoking the redraw view method.

There are several main animations in this article:

    • Popup and recall of Account record list
    • Click deformation of the login button
    • The sign-in button is clicked after the Circle animation (does not do the detailed narration)

In fact, the above-mentioned spinning animation I CoreAnimation realized through the framework of the animation + timer, of course, this also means that in this article is the end of constrained animation

Get ready

First we need to figure out the hierarchy of all the controls and then build the entire interface. In the demo, there are five controls that are visible before the animation starts--the user picture, the Account entry box, the dropdown button, the Password entry box, and the login button, along with a list of saved account information under the Account entry box. We make it move down by modifying this list with the top constraint value of the account:

In the constraints of these controls we need to change the constraints used in the code to include:

    • The top constraint of the record list listTopConstraint , which can be changed to move the list down
    • The height constraint of the record list listHeightConstraint , which is used to set the resulting unfolding animation
    • The height constraint of the account input box accountHeightConstraint to set the drop-down amount of the list
    • The spacing constraint of the left and right side of the login button relative to the parent view loginLeftConstraint , and the loginRightConstraint button is reduced by changing the two constraints
    • The height constraint of the login button to loginHeightConstraint calculate the narrowed button width

In addition to the constraint properties, we need some data to support our animations:

@property(assign, nonatomic) BOOL isAnimating; // 用来判断登录按钮的动画状态@property(strong, nonatomic) NSArray * records; // 列表的数据源,demo中存储五个字符串

To ensure that the list is not obscured by other views, set the view hierarchy as follows:

下拉按钮 > 账户输入框 > 纪录列表 > 头像 = 登录按钮 = 密码输入框
Pull the picture down

Here I have the demo animation divided into two sections to explain, because the two animations of the implementation of a very big difference. Of course, both can be achieved by directly modifying the value of the constraint constant , but this is not the purpose of this article.

When we click the drop-down button, there will be two animations, including the 180° rotation of the drop-down list and the drop-down or hidden disappears. Normally, we need to use a BOOL type of variable to identify whether the list is in the expanded state to determine the way the animation is, but when the associated event method as the sender's drop-down button has been provided to us this variable isSelected , by modifying this value to complete the tag list expansion state. So the code for the Rotate drop-down button is as follows:

/// 点击打开或者隐藏列表- (IBAction)actionToOpenOrCloseList:(UIButton *)sender {    [self.view endEditing: YES];    [self animateToRotateArrow: sender.selected];    sender.isSelected ? [self showRecordList] : [self hideRecordList];}/// 按钮转向动画- (void)animateToRotateArrow: (BOOL)selected{    CATransform3D transform = selected ? CATransform3DIdentity : CATransform3DMakeRotation(M_PI, 0, 0, 1);    [_dropdownButton setSelected: !selected];    [UIView animateWithDuration: 0.25 animations: ^{        _dropdownButton.layer.transform = transform;    }];}

You can see that our code is based on the properties of the button to isSelected determine the expansion or retraction of the list, we need to modify the list of Listheightconstraint and Listtopconstraint to set the size and position of the list, And we need to add a pop-up animation to the expanded list:

/// 显示纪录列表- (void)showRecordList{    [UIView animateWithDuration: 0.25 delay: 0 usingSpringWithDamping: 0.4 initialSpringVelocity: 5 options: UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionAllowUserInteraction animations: ^{        _listTopConstraint.constant = _accountHeightConstraint.constant;        _listHeightConstraint.constant = _accountHeightConstraint.constant * 5;    } completion: nil];}/// 隐藏纪录列表- (void)hideRecordList{    [UIView animateWithDuration: 0.25 animations: ^{        _listTopConstraint.constant = 0;        _listHeightConstraint.constant = 0;    } completion: nil];}

Well, run your code and see the effect, which certainly won't be the effect you want.

One UIView of the interesting things about animations is that if you submit changes to the block view's related properties directly in the animation, they will animate as you expect. But when you modify the constraint values, the layout results of the constraint are calculated directly and displayed directly-even if you put the code that modifies the constraint into the animation block .

For this issue, iOS provides a way for all views to - (void)layoutIfNeeded refresh the interface immediately, which invokes the sub-view to re-layout all the child views above the current view - (void)layoutSubviews . If we set the constraint values first, then the animation's execution code call layoutIfNeeded will allow the interface to continue to redraw the animation effect. Therefore, the above expansion retract code is changed to the following:

/// 显示纪录列表- (void)showRecordList{    _listTopConstraint.constant = _accountHeightConstraint.constant;    _listHeightConstraint.constant = _accountHeightConstraint.constant * 5;    [UIView animateWithDuration: 0.25 delay: 0 usingSpringWithDamping: 0.4 initialSpringVelocity: 5 options: UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionAllowUserInteraction animations: ^{        [self.view layoutIfNeeded];    } completion: nil];}/// 隐藏纪录列表- (void)hideRecordList{    _listTopConstraint.constant = 0;    _listHeightConstraint.constant = 0;    [UIView animateWithDuration: 0.25 animations: ^{        [self.view layoutIfNeeded];    } completion: nil];}

Now run your code again, the record list has been able to implement the pop-up and resume the animation.

Login button Animations

It is not polite to say, in this demo, my favorite is the login button click after the animation effect. Of course, this also means that the animation effect is the longest time.

As shown in the demo animation, there is a spinning animation of the progress of the button in the middle of clicking the login animation. If this effect is implemented in a controller, it involves the operation of the layer layer, and this should not be the function of the controller, so I enclose the login button separately for processing, and provides two interfaces: -(void) Start and -(void) stop The facilitates controller tuning to start and stop animations. These two methods are implemented internally as follows:

const NSTimeInterval duration = 1.2;///动画开始隐藏文字- (void)start{    [self addAnimate];    if (_timer) {        [_timer invalidate];    }    NSTimer * timer = [NSTimer scheduledTimerWithTimeInterval: duration target: self selector: @selector(addAnimate) userInfo: nil repeats: YES];    _timer = timer;    [UIView animateWithDuration: 0.5 animations: ^{        _circle.opacity = 1;        [self setTitleColor: [UIColor colorWithWhite: 1 alpha: 0] forState: UIControlStateNormal];    }];}///动画结束时显示文字- (void)stop{    if (_timer) {        [_timer invalidate];    }    [self.circle removeAllAnimations];    [UIView animateWithDuration: 0.5 animations: ^{        _circle.opacity = 0;        [self setTitleColor: [UIColor colorWithWhite: 1 alpha: 1] forState: UIControlStateNormal];    }];}

I already said the button in the Circle animation based on the timer and CoreAnimation frame animation implementation, because this does not belong to the constraint animation category, I do not elaborate on the implementation of the specific, interested in the demo in this article to see the implementation.

The

includes its own dimension change code, in addition to the animation that the button itself circles and the text-behind display. The button remains in the center of the x-axis of the view after shrinking, so if we modify the left and right constraints, then make sure the two values are equal. The height of the buttons before and after the animation has not changed, in the process of narrowing the width to the same size as the height, we now have the height of the button height , the code calculates the left and right new constraints:

Click Login Animation-(ibaction) Actiontosignin: (UIButton *) Sender {_isanimating =!_isanimating;        if (_isanimating) {[_signinbutton start];    [Self animatetomakebuttonsmall];        } else {[_signinbutton stop];    [Self animatetomakebuttonbig];    }}///reduced Animation-(void) Animatetomakebuttonsmall {cgfloat height = _loginheightconstraint.constant;    CGFloat screenwidth = Cgrectgetwidth (self.view.frame);    CGFloat spacingconstant = (screenwidth-height)/2;    _loginleftconstraint.constant = _loginrightconstraint.constant = Spacingconstant; [UIView animatewithduration:0.15 delay:0 options:uiviewanimationoptioncurveeaseout animations: ^{[Self.view Lay    Outifneeded]; } Completion:nil];}    Magnification animation-(void) Animatetomakebuttonbig {_loginleftconstraint.constant = _loginrightconstraint.constant = 0; [UIView animatewithduration:0.15 delay:0 options:uiviewanimationoptioncurveeaseout animations: ^{[Self.view Lay    Outifneeded]; } Completion:nil];}

We passed the calculation of the left and right interval to set the login button animation, which is very good, but we think, the above animation implementation idea is:

获取按钮高度和父视图宽度 -> 计算按钮左右间隔 -> 实现动画


But what are our first ideas of realization?

按钮变小,保持宽高一致 -> 按钮居中 -> 实现动画


This is absurd, although the animation is implemented, but this should not be our way of implementation. Therefore, in order to ensure that our thinking can be carried out correctly, our operation steps should be as follows:

1, remove the login button left and right constraints

2, add the width of higher than the constraint

3. Add the center constraint of the button relative to the parent view

Before performing these steps, let's take a look at the calculation formulas for constraints and the properties that these computed variables represent in the NSLayoutConstraint object:

Based on this formula, I'm now going to add a horizontal center constraint to a button on the current view, so the constraint's creation code is as follows:

NSLayoutConstraint * centerXConstraint = [NSLayoutConstraint         constraintWithItem: _button                   //firstItem                 attribute: NSLayoutAttributeCenterX  //firstAttribute                 relatedBy: NSLayoutRelationEqual     //relation                       toItem: _button.superview         //secondItem                 attribute: NSLayoutAttributeCenterX  //secondAttribute                multiplier: 1.0                       //multiplier                  constant: 0];                       //constant

We can clearly see the layout of the relevant properties and code corresponding to the above code, if you in Xcode by looking into NSLayoutConstraint the class file, you will also find that these properties constant are only writable, which means that you can not set multipier the normal way Such a value to change the width of a control in the parent view. Although KVC can do this, this should not be a solution.

Therefore, we need to animate the login button by creating a new constraint and removing the old constraint. Before iOS8 this work is undoubtedly complicated, we need to pass

- (void)addConstraint:(NSLayoutConstraint *)constraint;- (void)addConstraints:(NSArray<__kindof NSLayoutConstraint *> *)constraints;- (void)removeConstraint:(NSLayoutConstraint *)constraint;- (void)removeConstraints:(NSArray<__kindof NSLayoutConstraint *> *)constraints;

This series of methods to delete and subtract constraints, but after IOS8, NSLayoutConstraint provides a active variable of type bool for us to provide the setting constraint is valid, this value is set NO when the constraint is invalidated. Also, once we have created a constraint object, we only need to set active YES it to take effect automatically. Therefore, according to the above formula, we modify the code as follows:

Zoom Out button-(void) Animatetomakebuttonsmall {_loginleftconstraint.active = NO;    _loginrightconstraint.active = NO; Create a aspect ratio constraint nslayoutconstraint * Ratioconstraint = [nslayoutconstraint constraintwithitem: _signinbutton attribute:nslay Outattributewidth relatedby:nslayoutrelationequal Toitem: _signinbutton attribute:nslayoutattributeheight    Multiplier:1. constant:0];    Ratioconstraint.active = YES;    _loginratioconstraint = Ratioconstraint; Create a center constraint nslayoutconstraint * centerxconstraint = [nslayoutconstraint constraintwithitem: _signinbutton Attribute:nsla Youtattributecenterx relatedby:nslayoutrelationequal toitem: _signinbutton.superview attribute:    Nslayoutattributecenterx multiplier:1. constant:0.];    Centerxconstraint.active = YES;    _logincenterxconstraint = Centerxconstraint; [UIView animatewithduration:0.15 delay:0 Options:uiviewanimationoptioncurveeasein animations: ^{[Self.view Layo    Utifneeded]; } Completion:nil];} Restore button-(void) AnimatEtomakebuttonbig {_logincenterxconstraint.active = NO;    _loginratioconstraint.active = NO; Nslayoutconstraint * Leftconstraint = [nslayoutconstraint constraintwithitem: _signinbutton attribute: Nslayoutattributeleading relatedby:nslayoutrelationequal toitem: _signinbutton.superview attribute:    Nslayoutattributeleading multiplier:1. constant:25];    _loginleftconstraint = Leftconstraint;    Leftconstraint.active = YES; Nslayoutconstraint * Rightconstraint = [nslayoutconstraint constraintwithitem: _signinbutton attribute: Nslayoutattributetrailing relatedby:nslayoutrelationequal toitem: _signinbutton.superview attribute:    Nslayoutattributetrailing multiplier:1. Constant:-25];    _loginrightconstraint = Rightconstraint;    Rightconstraint.active = YES; [UIView animatewithduration:0.15 delay:0 options:uiviewanimationoptioncurveeaseout animations: ^{[Self.view Lay    Outifneeded]; } Completion:nil];}

Add and subtract constraints when animating, you also have to remember that when a constraint active property is set NO , the constraint remains invalid even if we reactivate it, and must be recreated.

In the code above, I also added two properties and a loginRatioConstraint loginCenterXConstraint new constraint to each animation creation, which makes it easy to invalidate the constraint when you stop the animation. Of course, in addition to this way of referencing, we can also get the corresponding constraint and invalidate it directly by judging the property types that constrain both objects and constraints:

[_signInButton.constraints enumerateObjectsWithOptions: NSEnumerationReverse usingBlock: ^(__kindof NSLayoutConstraint * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {    if (obj.firstItem == _signInButton && obj.firstAttribute == NSLayoutAttributeCenterX) {        obj.active = NO;    } else if (obj.firstAttribute == NSLayoutAttributeWidth && obj.secondAttribute == NSLayoutAttributeHeight) {        obj.active = NO;    }}];

This code is equivalent to the above

_loginCenterXConstraint.active = NO;_loginRatioConstraint.active = NO;

Although it is more complex to remove constraints using code, it is always possible to use them when we encapsulate the controls, so this is the trick we need to master. Of course, this way of judging is also really too cumbersome, and NSLayoutConstraint also provides a type of string identifier attribute to help us identify constraints. In the storyboard we can see this property directly from the property bar on the right and set it up:

Thus the above judgment code can be simplified into a simple ID:

static NSString * centerXIdentifier = @"centerXConstraint";static NSString * ratioIdentifier = @"ratioIdentifier";/// 缩小按钮- (void)animateToMakeButtonSmall {    ......    //创建宽高比约束    NSLayoutConstraint * ratioConstraint  = ...//create ratioConstraint    ratioConstraint.identifier = ratioIdentifier;    //创建居中约束    NSLayoutConstraint * centerXConstraint = ...//create centerXConstraint    centerXConstraint.identifier = centerXIdentifier;    ......}/// 还原按钮- (void)animateToMakeButtonBig {    ......    [_signInButton.constraints enumerateObjectsWithOptions: NSEnumerationReverse usingBlock: ^(__kindof NSLayoutConstraint * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {        if ([obj.identifier isEqualToString: centerXIdentifier]) {            obj.active = NO;        } else if ([obj.identifier isEqualToString: ratioIdentifier]) {            obj.active = NO;        }    }];    ......}
Tail words

Distance constraint animation has a period of time, the production of constrained animation for me for a long time has been blank, until this period of time writing the animation of the article before contact with the use, feeling quite deep. As in the previous article, this article means that I am the end of the article that constrains animation, and in this demo I also doped the content of the core animation, perhaps this also illustrates my use of constrained animation is a shallow place. I know and have too little to learn about animation, or think that it's not enough to support my ambition for animation. Of course, I will continue to pursue the more cool and easy-to-use animations.

Mastering constraints should be an essential skill for our iOS developers. On the one hand, the IB visual programming in my daily work is inseparable, it greatly improved my development efficiency (I used to animate the place of the pure code to create the control), on the other hand, the size of the iphone will not continue to increase, class size and are autolayout our best help to adapt to different screens. Therefore, I hope this article will bring you more restrictions on the use of the content.

iOS Development-Animation chapter layout animation in Depth

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.