UIViewPropertyAnimator的使用

在iOS 10之前校翔,執(zhí)行動(dòng)畫(huà)可以使用UIView以下三個(gè)類(lèi)方法:

  • animateWithDuration: animations:
  • animateWithDuration: animations: completion:
  • animateWithDuration: delay: options: animations: completion:

上面創(chuàng)建動(dòng)畫(huà)塊(animations: )的均是類(lèi)方法宦棺,所以這些動(dòng)畫(huà)塊沒(méi)有綁定任何視圖。因此,可以使用這些方法創(chuàng)建一個(gè)單一的動(dòng)畫(huà)硼补,應(yīng)用于多個(gè)視圖利花。

下面代碼演示了在1秒鐘內(nèi)淡出firstView微酬、淡入secondView的操作。

    [UIView animateWithDuration:1.0 animations:^{
        self.firstView.alpha = 0.0;
        self.secondView.alpha = 1.0;
    }];

使用上述類(lèi)方法操縱動(dòng)畫(huà)過(guò)程中橄务,firstViewsecondView兩個(gè)視圖將暫時(shí)禁用用戶交互幔托,即用戶不能與正在運(yùn)行動(dòng)畫(huà)的視圖交互。如果想要實(shí)現(xiàn)更復(fù)雜的功能蜂挪,需要使用CoreAnimation框架柑司。雖然使用CoreAnimation框架可以對(duì)動(dòng)畫(huà)進(jìn)行暫停、恢復(fù)锅劝、停止等操作攒驰,但會(huì)產(chǎn)生大量代碼,需要大量工作故爵,可以使用pop一類(lèi)的第三方庫(kù)玻粪。

1. UIViewPropertyAnimator

在iOS 10,Apple在UIKit中增加了UIViewPropertyAnimator類(lèi)诬垂,使用該類(lèi)實(shí)現(xiàn)的動(dòng)畫(huà)可以在結(jié)束前進(jìn)行暫停劲室、恢復(fù)、停止等操作结窘。UIViewPropertyAnimator類(lèi)對(duì)象通過(guò)操作視圖的屬性來(lái)產(chǎn)生所需動(dòng)畫(huà)很洋,但不是所有屬性的改變都會(huì)產(chǎn)生動(dòng)畫(huà),可產(chǎn)生動(dòng)畫(huà)的屬性包括frame隧枫、center喉磁、alphatransform

UIViewPropertyAnimator類(lèi)遵守了UIViewAnimatingUIViewImplicitlyAnimating協(xié)議官脓。UIViewAnimating協(xié)議中方法用于控制動(dòng)畫(huà)的狀態(tài)协怒,包括開(kāi)始startAnimation、暫停pauseAnimation卑笨、結(jié)束stopAnimation:動(dòng)畫(huà)孕暇,也有一些屬性用于反映當(dāng)前動(dòng)畫(huà)狀態(tài)。動(dòng)畫(huà)運(yùn)行時(shí)赤兴,這些狀態(tài)隨之更新妖滔。UIViewPropertyAnimator對(duì)象(后面稱(chēng)為animator)在處理動(dòng)畫(huà)期間會(huì)經(jīng)歷UIViewAnimatingStateInactiveUIViewAnimatingStateActive桶良、UIViewAnimatingStateStopped不同狀態(tài)座舍。下圖顯示了animator狀態(tài)變化。

PropertyAnimatorState.png

Inactive狀態(tài)是animator的初始狀態(tài)艺普。每一個(gè)新創(chuàng)建的animator均處于inactive狀態(tài)簸州,animator在動(dòng)畫(huà)結(jié)束后返回到inactive狀態(tài)鉴竭。在非活躍狀態(tài)配置的動(dòng)畫(huà),其運(yùn)行持續(xù)時(shí)間為指定的完整持續(xù)時(shí)間岸浑;使用addAnimations:方法添加的動(dòng)畫(huà)搏存,添加后立即開(kāi)始執(zhí)行,與其它動(dòng)畫(huà)同時(shí)結(jié)束矢洲,即運(yùn)行時(shí)間為剩余持續(xù)時(shí)間璧眠,非完整持續(xù)時(shí)間。

當(dāng)調(diào)用startAnimationpauseAnimation方法后读虏,animator變?yōu)閍ctive狀態(tài)责静。處于active狀態(tài)的animator可能正在運(yùn)行動(dòng)畫(huà),也可能將動(dòng)畫(huà)暫停盖桥,以便修改動(dòng)畫(huà)灾螃。當(dāng)動(dòng)畫(huà)運(yùn)行到指定位置結(jié)束后,animator將返回到inactive狀態(tài)揩徊,以便重新配置動(dòng)畫(huà)腰鬼。

調(diào)用stopAnimation:方法后,將停止所有正在運(yùn)行的動(dòng)畫(huà)塑荒,并將相應(yīng)視圖屬性的值更新為調(diào)用stopAnimation:方法時(shí)的值熄赡。當(dāng)stopAnimation:參數(shù)為YES時(shí),animator狀態(tài)直接改變?yōu)?code>UIViewAnimatingStateInactive齿税,且不再執(zhí)行任何動(dòng)作彼硫;當(dāng)stopAnimation:參數(shù)為NO時(shí),animator狀態(tài)改變?yōu)?code>UIViewAnimatingStateStopped凌箕,隨后拧篮,可以調(diào)用finishAnimationAtPosition:方法執(zhí)行animator的最終動(dòng)作。例如陌知,執(zhí)行completion塊他托。對(duì)finishAnimationAtPosition:方法的調(diào)用不是必須的,也可以在調(diào)用finishAnimationAtPosition:方法前先執(zhí)行其它動(dòng)畫(huà)仆葡。

UIViewImplicitlyAnimating協(xié)議用于在動(dòng)畫(huà)進(jìn)行過(guò)程中修改動(dòng)畫(huà),如添加動(dòng)畫(huà)addAnimations:志笼、添加completion塊addCompletion:等沿盅。

2. 示例1

下面通過(guò)一個(gè)demo來(lái)學(xué)習(xí)UIViewPropertyAnimator

創(chuàng)建Single View Application模板的應(yīng)用纫溃,demo名稱(chēng)為PropertyAnimator腰涧。在storyboard中添加一個(gè)UIView、三個(gè)UIButton紊浩、兩個(gè)UILabel窖铡、一個(gè)UISwitch疗锐、一個(gè)UIStepper和一個(gè)UISlider。布局如下:

PropertyAnimatorStoryboard.png

使用Auto Layout费彼、Stack View對(duì)視圖進(jìn)行自動(dòng)布局滑臊,通過(guò)上圖可以看到所添加的約束,其中Stack View的spacing均為20箍铲。如果你對(duì)自動(dòng)布局不熟悉雇卷,可以查看Auto Layout的使用Auto Layout中Stack View的使用兩篇文章。

storyboardViewController.m添加IBOutlet屬性颠猴。如下所示:

@interface ViewController ()

@property (weak, nonatomic) IBOutlet UIView *redView;
@property (weak, nonatomic) IBOutlet UIButton *restartButton;
@property (weak, nonatomic) IBOutlet UIButton *startStopButton;
@property (weak, nonatomic) IBOutlet UIButton *pauseButton;
@property (weak, nonatomic) IBOutlet UISwitch *reverseAnimationSwitch;
@property (weak, nonatomic) IBOutlet UIStepper *durationStepper;
@property (weak, nonatomic) IBOutlet UILabel *durationLabel;

@end

最后再添加以下三個(gè)屬性关划。

@interface ViewController ()

...
@property (strong, nonatomic) UIViewPropertyAnimator *propertyAnimator;
@property (assign, nonatomic) NSTimeInterval duration;
@property (assign, nonatomic) CGRect startFrame;

@end

現(xiàn)在添加一個(gè)setupAnimator的方法,用于配制動(dòng)畫(huà)翘瓮。

- (void)setupAnimator {
    // 1.動(dòng)畫(huà)初始位置贮折。
    self.redView.frame = self.startFrame;
    
    // 2.動(dòng)畫(huà)終點(diǎn)位置。
    CGFloat margin = 16.0;
    CGFloat screenWidth = CGRectGetWidth(self.view.frame);
    CGFloat finalX = screenWidth - CGRectGetWidth(self.redView.frame) - margin;
    CGRect finalRect = CGRectMake(finalX, self.redView.frame.origin.y, self.redView.frame.size.width, self.redView.frame.size.height);
    
    // 3.初始化動(dòng)畫(huà)资盅。
    self.propertyAnimator = [[UIViewPropertyAnimator alloc] initWithDuration:self.duration
                                                                       curve:UIViewAnimationCurveEaseIn
                                                                  animations:^{
                                                                      self.redView.frame = finalRect;
                                                                  }];
    
    // 4.為動(dòng)畫(huà)添加完成塊调榄。
    ViewController * __weak weakSelf = self;
    ViewController *vc = weakSelf;
    if (vc) {
        [self.propertyAnimator addCompletion:^(UIViewAnimatingPosition finalPosition) {
            [vc.startStopButton setTitle:@"Start" forState:UIControlStateNormal];
            vc.startStopButton.enabled = NO;
            vc.pauseButton.enabled = NO;
            vc.restartButton.enabled = YES;
        }];
    }
    vc = nil;
}

在上面的代碼中,注釋3部分用于初始化動(dòng)畫(huà)律姨,其中initWithDuration:的參數(shù)為動(dòng)畫(huà)持續(xù)時(shí)間振峻;curve:參數(shù)用來(lái)設(shè)定動(dòng)畫(huà)曲線。這里有以下四種可選曲線:

  • UIViewAnimationCurveEaseInOut:這種曲線的動(dòng)畫(huà)開(kāi)始緩慢择份,在其持續(xù)時(shí)間的中間加速扣孟,然后在完成之前再次減慢。這是大多數(shù)動(dòng)畫(huà)的默認(rèn)曲線荣赶。
  • UIViewAnimationCurveEaseIn:動(dòng)畫(huà)開(kāi)始時(shí)緩慢凤价,然后加速,直到動(dòng)畫(huà)結(jié)束拔创。這里選用這種類(lèi)型動(dòng)畫(huà)曲線利诺。
  • UIViewAnimationCurveEaseOut:動(dòng)畫(huà)開(kāi)始時(shí)速度很快,在結(jié)束前開(kāi)始減速剩燥。
  • UIViewAnimationCurveLinear:在動(dòng)畫(huà)持續(xù)時(shí)間內(nèi)慢逾,動(dòng)畫(huà)勻速運(yùn)行。

最后在動(dòng)畫(huà)塊內(nèi)配置動(dòng)畫(huà)灭红。注釋4部分侣滩,為動(dòng)畫(huà)塊添加完成回調(diào)函數(shù)。這里需要注意避免形成循環(huán)引用变擒,使用__weak聲明的weakSelf替代self君珠,因?yàn)閴K內(nèi)多次用到weakSelf,可能在第一次調(diào)用weakSelf時(shí)weakSelf存在娇斑,但后面再次調(diào)用weakSelf時(shí)策添,weakSelf已被釋放材部,所以這里再次將其轉(zhuǎn)換為強(qiáng)引用類(lèi)型vc,不再需要vc時(shí)唯竹,需要手動(dòng)釋放乐导。

想要了解更多關(guān)于塊的用法,查看Block的用法一文摩窃。

storyboard中選中UIStepper控件兽叮,拖拽到代碼中,以添加響應(yīng)事件猾愿。

- (IBAction)stepperValueChanged:(UIStepper *)sender {
    // 1.設(shè)置動(dòng)畫(huà)持續(xù)時(shí)間為UIStpper的值鹦聪。
    self.duration = sender.value;
    
    // 2.同步更新到label。
    self.durationLabel.text = [NSString stringWithFormat:@"%.1f",sender.value];
}

上述代碼用于設(shè)定動(dòng)畫(huà)持續(xù)時(shí)間蒂秘。

storyboard中選中Start按鈕泽本,拖拽到ViewController.m實(shí)現(xiàn)部分,以添加響應(yīng)事件姻僧,響應(yīng)事件方法名稱(chēng)為startStopPause规丽。在storyboard中選中Pause按鈕,也拖拽到startStopPause:響應(yīng)事件撇贺,以便在點(diǎn)擊StartPause任一按鈕時(shí)赌莺,均調(diào)用startStopPause:方法。

- (IBAction)startStopPause:(UIButton *)sender {
    // 1.禁用restartButton松嘶,propertyAnimator不存在時(shí)艘狭,配置propertyAnimator。
    self.restartButton.enabled = NO;
    if (!self.propertyAnimator) {
        [self setupAnimator];
    }
    
    if (sender == self.pauseButton) {
        // 2.點(diǎn)擊Pause按鈕時(shí)翠订,根據(jù)當(dāng)前動(dòng)畫(huà)狀態(tài)更新視圖巢音。
        self.propertyAnimator.isRunning ? [self.propertyAnimator pauseAnimation] : [self.propertyAnimator startAnimation];
        NSString *title = self.propertyAnimator.isRunning ? @"Pause" : @"Unpause";
        [self.pauseButton setTitle:title forState:UIControlStateNormal];
        self.startStopButton.enabled = self.propertyAnimator.isRunning;
    }
    else
    {
        // 3.點(diǎn)擊Start按鈕時(shí),根據(jù)當(dāng)前動(dòng)畫(huà)狀態(tài)更新視圖尽超。
        switch (self.propertyAnimator.state) {
            case UIViewAnimatingStateInactive:
                [self.propertyAnimator startAnimation];
                [self.startStopButton setTitle:@"Stop" forState:UIControlStateNormal];
                self.pauseButton.enabled = YES;
                NSLog(@"UIViewAnimatingStateInactive");
                break;
                
            case UIViewAnimatingStateActive:
                if (self.propertyAnimator.isRunning) {
                    [self.propertyAnimator stopAnimation:NO];
                    [self.propertyAnimator finishAnimationAtPosition:UIViewAnimatingPositionCurrent];
                    [self.startStopButton setTitle:@"Start" forState:UIControlStateNormal];
                    self.pauseButton.enabled = NO;
                }
                else
                {
                    [self.propertyAnimator startAnimation];
                    [self.startStopButton setTitle:@"Stop" forState:UIControlStateNormal];
                    self.pauseButton.enabled = YES;
                }
                NSLog(@"UIViewAnimatingStateActive");
                break;
                
            case UIViewAnimatingStateStopped:
                NSLog(@"UIViewAnimatingStateStopped");
                break;
                
            default:
                break;
        }
    }
}

上面代碼根據(jù)所點(diǎn)擊的按鈕官撼,進(jìn)行不同的響應(yīng)操作。當(dāng)點(diǎn)擊的是Start按鈕時(shí)似谁,根據(jù)propertyAnimator所處的狀態(tài)執(zhí)行不同操作傲绣。

storyboard中拖拽UISwitch控件到代碼中,以創(chuàng)建響應(yīng)事件巩踏。

- (IBAction)reverseAnimationSwitchValueChanged:(UISwitch *)sender {
    if (self.propertyAnimator && self.propertyAnimator.isRunning) {
        // 在propertyAnimator存在斜筐,且正在運(yùn)行時(shí),根據(jù)UISwitch的值調(diào)整propertyAnimator的方向蛀缝。
        self.propertyAnimator.reversed = sender.isOn;
    }
}

storyboard中拖拽Restart Animation按鈕到代碼中,以創(chuàng)建響應(yīng)事件目代。

- (IBAction)restartAnimation:(UIButton *)sender {
    // 點(diǎn)擊RestartAnimation按鈕時(shí)屈梁,重新配置動(dòng)畫(huà)嗤练,啟用Start按鈕,將reverseAnimationSwitch設(shè)置為關(guān)閉狀態(tài)在讶。
    [self setupAnimator];
    self.startStopButton.enabled = YES;
    self.reverseAnimationSwitch.on = NO;
}

可以通過(guò)為fractionComplete屬性賦值煞抬,設(shè)置動(dòng)畫(huà)進(jìn)度百分比。從storyboard中拖拽UISlider控件到代碼中构哺,創(chuàng)建響應(yīng)事件革答。

- (IBAction)scrubAnimation:(UISlider *)sender {
    if (!self.propertyAnimator) {
        [self setupAnimator];
    }
    
    // 1.當(dāng)propertyAnimator正在運(yùn)行時(shí),暫停該動(dòng)畫(huà)曙强,否則在停止拖動(dòng)UISlider后残拐,動(dòng)畫(huà)會(huì)繼續(xù)運(yùn)行。
    if (self.propertyAnimator.isRunning) {
        [self.propertyAnimator pauseAnimation];
    }
    
    // 2.設(shè)置動(dòng)畫(huà)百分比碟嘴。
    self.propertyAnimator.fractionComplete = sender.value;
}

最后溪食,記得設(shè)置startFrame位置、pauseButton初始狀態(tài)娜扇。

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 設(shè)置startFrame位置错沃,pauseButton狀態(tài)。
    self.startFrame = CGRectMake(16, 45, 100, 100);
    self.pauseButton.enabled = NO;
}

現(xiàn)在運(yùn)行demo雀瓢,效果如下枢析。

PropertyAnimatorAnimator.gif

3. 示例2

UIViewPropertyAnimator除了可以使用原有的UIViewAnimationCurve時(shí)間曲線函數(shù),還新增了UISpringTimingParameters刃麸、UICubicTimingParameters兩個(gè)時(shí)間曲線函數(shù)醒叁。

添加Cocoa Touch Class模版的文件,其父類(lèi)為UIViewController嫌蚤,名稱(chēng)為TimingCurvesViewController辐益。在storyboard中添加UIViewController,并設(shè)置其父類(lèi)為TimingCurvesViewController脱吱。

在剛添加的視圖控制器上添加四個(gè)UIView智政、四個(gè)UILabel。同時(shí)選中兩個(gè)視圖控制器箱蝠,添加UITabBarController续捂,如下所示:

PropertyAnimatorCurve.png

TimingCurvesViewController上的UIView創(chuàng)建IBOutlet連接,并添加以下幾個(gè)屬性宦搬。

#import "TimingCurvesViewController.h"

@interface TimingCurvesViewController ()

@property (weak, nonatomic) IBOutlet UIView *redView;
@property (weak, nonatomic) IBOutlet UIView *blueView;
@property (weak, nonatomic) IBOutlet UIView *greenView;
@property (weak, nonatomic) IBOutlet UIView *yellowView;
@property (assign, nonatomic) CGRect redStartFrame;
@property (assign, nonatomic) CGRect blueStartFrame;
@property (assign, nonatomic) CGRect greenStartFrame;
@property (assign, nonatomic) CGRect yellowStartFrame;
@property (strong, nonatomic) UIViewPropertyAnimator *redAnimator;
@property (strong, nonatomic) UIViewPropertyAnimator *blueAnimator;
@property (strong, nonatomic) UIViewPropertyAnimator *greenAnimator;
@property (strong, nonatomic) UIViewPropertyAnimator *yellowStartAnimator;
@property (strong, nonatomic) NSTimer *timer;
@property (assign, nonatomic) NSTimeInterval durationOfAnimation;

@end

TimingCurvesViewController.mviewDidLoad方法中牙瓢,設(shè)置四個(gè)UIView的初始位置和durationOfAnimation值。

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 1.設(shè)置UIView初始位置间校。
    self.redStartFrame = CGRectMake(16, 50, squareSize, squareSize);
    self.blueStartFrame = CGRectMake(16, 150, squareSize, squareSize);
    self.greenStartFrame = CGRectMake(16, 250, squareSize, squareSize);
    self.yellowStartFrame = CGRectMake(16, 350, squareSize, squareSize);
    
    // 2.為durationOfAnimation賦初始值2.0矾克。
    self.durationOfAnimation = 2.0;
}

3.1 UIViewAnimationCurve

UIViewAnimationCurve時(shí)間曲線函數(shù)分為UIViewAnimationCurveEaseInOutUIViewAnimationCurveEaseIn憔足、UIViewAnimationCurveEaseOutUIViewAnimationCurveLinear四種胁附。

TimingCurvesViewController.m實(shí)現(xiàn)部分添加以下代碼來(lái)使用上述四種時(shí)間曲線酒繁。

// Default Curves
- (void)startAnimationsWithDefaultCurves {
    // 1.四個(gè)視圖使用不同類(lèi)型時(shí)間曲線函數(shù)。
    self.redAnimator = [self animatorForView:self.redView startFrame:self.redStartFrame curve:UIViewAnimationCurveLinear];
    self.blueAnimator = [self animatorForView:self.blueView startFrame:self.blueStartFrame curve:UIViewAnimationCurveEaseIn];
    self.greenAnimator = [self animatorForView:self.greenView startFrame:self.greenStartFrame curve:UIViewAnimationCurveEaseOut];
    self.yellowAnimator = [self animatorForView:self.yellowView startFrame:self.yellowStartFrame curve:UIViewAnimationCurveEaseInOut];
}

- (UIViewPropertyAnimator *)animatorForView:(UIView *)view startFrame:(CGRect)startFrame curve:(UIViewAnimationCurve)curve {
    // 2.視圖初始位置控妻,動(dòng)畫(huà)結(jié)束時(shí)視圖位置州袒。
    view.frame = startFrame;
    CGRect finalRect = [self finalRectWithStartFrame:startFrame];
    
    // 3.配置動(dòng)畫(huà)。
    UIViewPropertyAnimator *animator = [[UIViewPropertyAnimator alloc] initWithDuration:self.durationOfAnimation curve:curve animations:^{
        view.frame = finalRect;
    }];
    return animator;
}

#pragma mark Help Methods

- (CGRect)finalRectWithStartFrame:(CGRect)startFrame {
    // 4.計(jì)算出動(dòng)畫(huà)結(jié)束時(shí)位置弓候。
    CGFloat margin = 16;
    CGFloat screenWidth = CGRectGetWidth(self.view.frame);
    CGFloat finalX = screenWidth - margin - squareSize;
    CGRect finalRect = CGRectMake(finalX, startFrame.origin.y, squareSize, squareSize);
    return finalRect;
}

上述代碼非常簡(jiǎn)單郎哭,在注釋1部分使用UIViewAnimationCurveLinearUIViewAnimationCurveEaseIn菇存、UIViewAnimationCurveEaseOutUIViewAnimationCurveEaseInOut四種時(shí)間曲線夸研。注釋4的方法后面會(huì)多次用到。

TimingCurvesViewController.m實(shí)現(xiàn)部分添加viewDidAppear:方法撰筷,用定時(shí)器觸發(fā)動(dòng)畫(huà)陈惰。

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    
    // 1.在1.5倍durationOfAnimation時(shí)間后,觸發(fā)動(dòng)畫(huà)毕籽。
    self.timer = [NSTimer scheduledTimerWithTimeInterval:self.durationOfAnimation*1.5 repeats:YES block:^(NSTimer * _Nonnull timer) {
        // 2.動(dòng)畫(huà)使用默認(rèn)時(shí)間曲線函數(shù)抬闯。
        [self startAnimationsWithDefaultCurves];
        
        // 啟動(dòng)動(dòng)畫(huà)。
        [self.redAnimator startAnimation];
        [self.blueAnimator startAnimation];
        [self.greenAnimator startAnimation];
        [self.yellowAnimator startAnimation];
    }];
}

如果使用標(biāo)準(zhǔn)方法(initWith方法)創(chuàng)建動(dòng)畫(huà)关筒,必須通過(guò)調(diào)用startAnimation方法開(kāi)始動(dòng)畫(huà)溶握;如果想要?jiǎng)?chuàng)建動(dòng)畫(huà)后立即開(kāi)始執(zhí)行動(dòng)畫(huà),請(qǐng)使用runningPropertyAnimatorWithDuration: delay: options: animations: completion:方法蒸播。

運(yùn)行demo睡榆,如下所示:

PropertyAnimatorDefaultCurves.gif

通過(guò)上面代碼可以看到:

  • UIViewAnimationCurveEaseInOut:這種曲線的動(dòng)畫(huà)開(kāi)始緩慢,在其持續(xù)時(shí)間的中間加速袍榆,然后在完成之前再次減慢胀屿。這是大多數(shù)動(dòng)畫(huà)的默認(rèn)曲線。
  • UIViewAnimationCurveEaseIn:動(dòng)畫(huà)開(kāi)始時(shí)緩慢包雀,然后加速宿崭,直到動(dòng)畫(huà)結(jié)束。
  • UIViewAnimationCurveEaseOut:動(dòng)畫(huà)開(kāi)始時(shí)速度很快才写,在結(jié)束前開(kāi)始減速葡兑。
  • UIViewAnimationCurveLinear:在動(dòng)畫(huà)持續(xù)時(shí)間內(nèi),動(dòng)畫(huà)勻速運(yùn)行赞草。

3.2 UICubicTimingParameters

UICubicTimingParameters允許通過(guò)多個(gè)控制點(diǎn)來(lái)定義三階貝塞爾曲線讹堤,該貝塞爾時(shí)間曲線起點(diǎn)為(0,0),終點(diǎn)為(1,1)厨疙,曲線形狀由兩個(gè)控制點(diǎn)決定洲守。每個(gè)時(shí)間點(diǎn)線的斜率定義了此時(shí)動(dòng)畫(huà)速度。曲線越陡峭,動(dòng)畫(huà)運(yùn)行速度越快岖沛;曲線越平緩暑始,動(dòng)畫(huà)運(yùn)行速度越慢。

下圖的時(shí)間曲線表示動(dòng)畫(huà)在開(kāi)始和結(jié)束時(shí)速度很快婴削,中間運(yùn)行較慢。

PropertyAnimatorCubicTiming.png

控制點(diǎn)范圍為0.0至1.0牙肝。

UICubicTimingParameters遵守UITimingCurveProvider協(xié)議唉俗,為遵守UIViewAnimating協(xié)議的對(duì)象提供時(shí)間曲線(timing curves),如UIViewPropertyAnimator配椭。

繼續(xù)更新TimingCurvesViewController.m內(nèi)代碼虫溜,使用UICubicTimingParameters作為動(dòng)畫(huà)時(shí)間參數(shù)。

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    
    // 1.在1.5倍durationOfAnimation時(shí)間后股缸,觸發(fā)動(dòng)畫(huà)衡楞。
    self.timer = [NSTimer scheduledTimerWithTimeInterval:self.durationOfAnimation*1.5 repeats:YES block:^(NSTimer * _Nonnull timer) {
        // 2.動(dòng)畫(huà)使用默認(rèn)時(shí)間曲線函數(shù)。
//        [self startAnimationsWithDefaultCurves];
        
        // 3.使用UICubicTimingParameters
        [self startAnimationWithCubicCurves];
        
        // 啟動(dòng)動(dòng)畫(huà)敦姻。
        [self.redAnimator startAnimation];
        [self.blueAnimator startAnimation];
        [self.greenAnimator startAnimation];
        [self.yellowAnimator startAnimation];
    }];
}

// UICubicTimingParameters
- (void)startAnimationWithCubicCurves {
    // 1.中間慢瘾境,開(kāi)始、結(jié)尾快镰惦。
    UICubicTimingParameters *redCubicTimingParameters = [[UICubicTimingParameters alloc] initWithControlPoint1:CGPointMake(0.45, 1.0) controlPoint2:CGPointMake(0.55, 0)];
    // 2.中間快迷守,開(kāi)始、結(jié)尾慢旺入。
    UICubicTimingParameters *blueCubicTimingParameters = [[UICubicTimingParameters alloc] initWithControlPoint1:CGPointMake(1.0, 0.45) controlPoint2:CGPointMake(0, 0.55)];
    self.redAnimator = [self animatorForView:self.redView startFrame:self.redStartFrame timingParameters:redCubicTimingParameters];
    self.blueAnimator = [self animatorForView:self.blueView startFrame:self.blueStartFrame timingParameters:blueCubicTimingParameters];
}

- (UIViewPropertyAnimator *)animatorForView:(UIView *)view startFrame:(CGRect)startFrame timingParameters:(UICubicTimingParameters *)cubicTimingParameters {
    view.frame = startFrame;
    CGRect finalRect = [self finalRectWithStartFrame:startFrame];
    UIViewPropertyAnimator *animator = [[UIViewPropertyAnimator alloc] initWithDuration:self.durationOfAnimation timingParameters:cubicTimingParameters];
    [animator addAnimations:^{
        view.frame = finalRect;
    }];
    return animator;
}

其中兑凿,startAnimationWithCubicCurves方法內(nèi),注釋1中的控制點(diǎn)決定動(dòng)畫(huà)曲線茵瘾,動(dòng)畫(huà)在中間慢礼华,開(kāi)始和結(jié)尾處快。注釋2相反拗秘。這里只使用了redViewblueView兩個(gè)視圖圣絮。

運(yùn)行如下:

PropertyAnimatorCubicParameters.gif

3.3 UISpringTimingParameters

UISpringTimingParameters提供的時(shí)間曲線會(huì)讓動(dòng)畫(huà)行為與彈簧相似。視圖會(huì)加速向目標(biāo)點(diǎn)運(yùn)動(dòng)聘殖,然后圍繞該目標(biāo)點(diǎn)震蕩晨雳,直到停止。

UISpringTimingParameters遵守UITimingCurveProvider協(xié)議奸腺,為遵守UIViewAnimating協(xié)議的對(duì)象提供時(shí)間曲線餐禁,如UIViewPropertyAnimatorUISpringTimingParameters一般用于移動(dòng)屏幕上的視圖突照,也可用于視圖的其它屬性帮非,以獲取類(lèi)似的動(dòng)畫(huà)效果。

繼續(xù)更新TimingCurvesViewController.m內(nèi)代碼,使用UISpringTimingParameters作為動(dòng)畫(huà)運(yùn)行時(shí)間參數(shù)末盔。

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    
    // 1.在1.5倍durationOfAnimation時(shí)間后筑舅,觸發(fā)動(dòng)畫(huà)。
    self.timer = [NSTimer scheduledTimerWithTimeInterval:self.durationOfAnimation*1.5 repeats:YES block:^(NSTimer * _Nonnull timer) {
        // 2.動(dòng)畫(huà)使用默認(rèn)時(shí)間曲線函數(shù)陨舱。
//        [self startAnimationsWithDefaultCurves];
        
        // 3.使用UICubicTimingParameters翠拣。
//        [self startAnimationWithCubicCurves];
        
        // 4.使用UISpringTimingParameters。
        [self startAnimationWithSpringCurves];
        
        // 啟動(dòng)動(dòng)畫(huà)游盲。
        [self.redAnimator startAnimation];
        [self.blueAnimator startAnimation];
        [self.greenAnimator startAnimation];
        [self.yellowAnimator startAnimation];
    }];
}

// UISpringTimingParameters
- (void)startAnimationWithSpringCurves {
    // dampingRatio值分別為0.2 1.0 2.0误墓。
    CGFloat underDamped = 0.2;
    CGFloat criticalDamped = 1.0;
    CGFloat overDamped = 2.0;
    self.redAnimator = [self animatorForView:self.redView startFrame:self.redStartFrame dampingRatio:underDamped];
    self.blueAnimator = [self animatorForView:self.blueView startFrame:self.blueStartFrame dampingRatio:criticalDamped];
    self.greenAnimator = [self animatorForView:self.greenView startFrame: self.greenStartFrame dampingRatio:overDamped];
}

- (UIViewPropertyAnimator *)animatorForView:(UIView *)view startFrame:(CGRect)startFrame dampingRatio:(CGFloat)dampingRatio {
    view.frame = startFrame;
    CGRect finalRect = [self finalRectWithStartFrame:startFrame];
    UIViewPropertyAnimator *animator = [[UIViewPropertyAnimator alloc] initWithDuration:self.durationOfAnimation dampingRatio:dampingRatio animations:^{
        view.frame = finalRect;
    }];
    return animator;
}

另外,也可以使用initWithMass: stiffness: damping: initialVelocity:方法益缎,該方法會(huì)把阻尼系數(shù)(damping)谜慌、質(zhì)量參數(shù)(mass)、剛性系數(shù)(stiffness)和初始速度(initial velocity)帶入給定公式莺奔,以獲取更為真實(shí)的效果欣范。

運(yùn)行demo,如下所示:

PropertyAnimatordampingRatio.gif

UIDynamicAnimator中的UISnapBehavior也可以產(chǎn)生彈簧(spring)效果令哟,UISnapBehavior是移動(dòng)到指定點(diǎn)point恼琼,如下圖所示。想要全面了解UIKitDynamicAnimator励饵,可以查看一篇文章學(xué)會(huì)使用UIKit Dynamics驳癌。

Dynamics4UISnapBehavior.gif

Demo名稱(chēng):PropertyAnimator
源碼地址:https://github.com/pro648/BasicDemos-iOS

參考資料:

  1. iOS10 – UIViewPropertyAnimator
  2. iOS 10 Day by Day :: Day 4 :: UIViewPropertyAnimator
  3. QUICK GUIDE: ANIMATIONS WITH UIVIEWPROPERTYANIMATOR

歡迎更多指正:https://github.com/pro648/tips/wiki

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市役听,隨后出現(xiàn)的幾起案子颓鲜,更是在濱河造成了極大的恐慌,老刑警劉巖典予,帶你破解...
    沈念sama閱讀 217,509評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件甜滨,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡瘤袖,警方通過(guò)查閱死者的電腦和手機(jī)衣摩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)捂敌,“玉大人艾扮,你說(shuō)我怎么就攤上這事≌纪瘢” “怎么了泡嘴?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,875評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)逆济。 經(jīng)常有香客問(wèn)我酌予,道長(zhǎng)磺箕,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,441評(píng)論 1 293
  • 正文 為了忘掉前任抛虫,我火速辦了婚禮松靡,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘建椰。我一直安慰自己雕欺,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布广凸。 她就那樣靜靜地躺著阅茶,像睡著了一般。 火紅的嫁衣襯著肌膚如雪谅海。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,365評(píng)論 1 302
  • 那天蹦浦,我揣著相機(jī)與錄音扭吁,去河邊找鬼。 笑死盲镶,一個(gè)胖子當(dāng)著我的面吹牛侥袜,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播溉贿,決...
    沈念sama閱讀 40,190評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼枫吧,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了宇色?” 一聲冷哼從身側(cè)響起九杂,我...
    開(kāi)封第一講書(shū)人閱讀 39,062評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎宣蠕,沒(méi)想到半個(gè)月后例隆,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,500評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡抢蚀,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評(píng)論 3 335
  • 正文 我和宋清朗相戀三年镀层,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片皿曲。...
    茶點(diǎn)故事閱讀 39,834評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡唱逢,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出屋休,到底是詐尸還是另有隱情坞古,我是刑警寧澤,帶...
    沈念sama閱讀 35,559評(píng)論 5 345
  • 正文 年R本政府宣布博投,位于F島的核電站绸贡,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜听怕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評(píng)論 3 328
  • 文/蒙蒙 一捧挺、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧尿瞭,春花似錦闽烙、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,779評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至疏旨,卻和暖如春很魂,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背檐涝。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,912評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工遏匆, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人谁榜。 一個(gè)月前我還...
    沈念sama閱讀 47,958評(píng)論 2 370
  • 正文 我出身青樓幅聘,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親窃植。 傳聞我的和親對(duì)象是個(gè)殘疾皇子帝蒿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)、插件巷怜、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,102評(píng)論 4 62
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,111評(píng)論 25 707
  • 看題目就知道我偷懶了好久葛超,上次日日更的時(shí)候已經(jīng)是兩個(gè)月前了。這三個(gè)月我做了很多事情丛版,也忘記了很多事情巩掺,于是現(xiàn)在就讓...
    killerking閱讀 225評(píng)論 0 0
  • 有句話說(shuō),找男(女)友千萬(wàn)不要找能減肥成功的人页畦,他們非撑痔妫可怕,他們無(wú)情豫缨,他們有鋼鐵般的意志独令。 但我認(rèn)為,能減肥好芭,證...
    方老司閱讀 361評(píng)論 0 1
  • 幸運(yùn)的時(shí)候一定要努力燃箭,因?yàn)轱L(fēng)水輪流轉(zhuǎn),不幸的時(shí)候一定要堅(jiān)持舍败,因?yàn)榱痘饔忠淮逭欣辍N业娜堪踩卸紒?lái)自于想做的事敬拓,我...
    蒙味集APP閱讀 220評(píng)論 0 0