爬爬爬之路:UI(二十) iOS动画 通知

iOS基本动画

在iOS开发中, 基本动画主要有UIView动画和layer动画(层动画)两种


UIView动画

UIView动画的特点: 全是类方法, 直接用类(UIView)调用
主要分为两大类方法:

  • UIView 直接调用的类方法
  • Block方法

具体的稍后慢慢介绍

UIView直接调用的方法

UIView直接调用的方法实现动画效果 需要遵循一个固定步骤

  1. 步骤一
    开始动画

    + (void)beginAnimations:(NSString *)animationID context:(void *)context
    // 使用方法如下:
    // 第一个参数是标记当前动画的标识符, 第二个参数是可携带的参数
    [UIView beginAnimations:nil context:nil]; //标记动画块开始
  2. 步骤二
    实现动画

    // 这里是动画实现的主体代码
  3. 步骤三
    提交动画

    + (void)commitAnimations;
    [UIView commitAnimations]; //标记动画块结束

这里步骤一三没啥可说的. 主要介绍的是步骤二, 也就是具体实现动画的方法.

UIView具体用到的常用方法如下
  1. 设置代理

     + (void)setAnimationDelegate:(id)delegate;
    /*
     解释:
     这里的代理相有点类似于button类控件的代理, 没有需要遵循的协议 需要自己设置代理方法
     若是不设置代理, 系统就会找不到动画开始前和结束后触发的方法
    */
    
  2. 设置动画开始前触发方法

    + (void)setAnimationWillStartSelector:(SEL)selector;
    /*
     本方法并不常用, 若是有需求要用到本方法, 必须结合上一个设置代理方法, 
     在代理对象所在的类中实现本方法. 若不设置, 则本方法不会被触发
    */
    // 用法如下. 可以获取标记步骤一中标记动画开始设置的两个参数. 
    
    [UIView setAnimationWillStartSelector:@selector(animationWillStart:context:)];
    /*
     可以只获取其中的一个参数, 具体获取哪个参数可以根据参数的类型
     比如只获取动画标记的携带参数 可以把方法写成
     - (void)animationWillStart:(void *)something;
     接不接受参数或者接受哪个参数, 以什么顺序接受参数均可有自己定义. 根据参数类型即可
    */
  3. 设置动画结束后触发的方法

    + (void)setAnimationDidStopSelector:(SEL)selector;
    /*
     本方法较为常用, 可以用于触发另一个动画, 或者作为一个动画接受后的收尾操作. 
     比如可以做复原操作.
    */
    // 用法如下
    [UIView setAnimationDidStopSelector:@selector(didStop)];
    // 也可以获得动画标记的参数, 获取方法同方法2的介绍. 这里不再赘述
    
  4. 设置动画一次运行花费的时间

    + (void)setAnimationDuration:(NSTimeInterval)duration;
    // 若是不设置, 默认是0.2秒
    // 用法如下:
    [UIView setAnimationDuration:2];
    /* 
      注意, 这里设置的是动画一遍执行的时间. 
      但是动画反转的效果虽然也是属于一次完整的动画的部分, 但是不包括一次执行的时间内.
    */
    
  5. 设置动画的节奏

    + (void)setAnimationCurve:(UIViewAnimationCurve)curve;
    /*
    
        UIViewAnimationCurveEaseInOut,         开始和结束时候慢中间过程快 (默认)
        UIViewAnimationCurveEaseIn,            开始时慢
        UIViewAnimationCurveEaseOut,           结束时慢
        UIViewAnimationCurveLinear             开始和结束快, 中间过程慢
    */
    
  6. 设置动画的重复次数

    + (void)setAnimationRepeatCount:(float)repeatCount;
    // 可以控制动画执行的次数, 0也是一次, CGFLOAT_MAX为无限次
    
  7. 设置动画是否反转

    + (void)setAnimationRepeatAutoreverses:(BOOL)repeatAutoreverses;
    /* 
    设置动画是否反向执行,  设置反转可以实现从目标位置回到起始位置 
    属于完整动画的一部分, 但是不包括在动画执行的时间内. 也就是说动画设置的是2秒
    可能一个完整的动画就变成了4秒了.
    
    但要注意的是反转效果只是视觉上的反转, 控件实际上还是停留在目标位置没有回来.
    /*
    
  8. 设置动画延迟的时间

    + (void)setAnimationDelay:(NSTimeInterval)delay;
    // 设置动画在delay秒后执行
    

以上是设置动画的属性, 实际上动画的实现是把一个控件上的外观变化放慢了而已.
在步骤二内修改一个控件的可视属性. 比如说改变alpha(透明度), frame(包括坐标, 大小两者任意一个), 颜色, 旋转等可视的属性值.
可以修改一个控件的外观值, 也可以修改多个控件的外观值. 若是修改多个, 则在同一个动画不周内, 多个控件会共享同一套动画的属性.

代码如下:

@property (nonatomic, retain) UIImageView *imageView;
@property (nonatomic, retain) UIButton *button;

- (void)viewDidLoad {
    [super viewDidLoad];
    [self addSubviews];
}

- (void)addSubviews {
    self.imageView = [[UIImageView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
    _imageView.backgroundColor = [UIColor redColor];
    [self.view addSubview:_imageView];
    [_imageView release];

    self.button = [UIButton buttonWithType:(UIButtonTypeSystem)];
    self.button.frame = CGRectMake(100, 400, 100, 50);
    [self.button addTarget:self action:@selector(clickButton:) forControlEvents:(UIControlEventTouchUpInside)];
    [self.button setTitle:@"点我" forState:(UIControlStateNormal)];
    self.button.backgroundColor = [UIColor lightGrayColor];
    [self.view addSubview:self.button];
}

- (void)clickButton:(UIButton *)button {
    // 开始动画
    // 参数1: 标识符
    // 参数2: 携带的参数
    [UIView beginAnimations:@"donghua" context:@"hahaha"];

    // 设置动画(setAnimation)
    // 设置动画执行的时间
    [UIView setAnimationDuration:1];
    // 设置是否反转
    [UIView setAnimationRepeatAutoreverses:YES];
    // 设置执行的次数
    [UIView setAnimationRepeatCount:2];

    // 改变动画控件的位置
    self.imageView.frame = CGRectMake(220, 250, 150, 150);
    self.imageView.backgroundColor = [UIColor greenColor];
    self.imageView.alpha = 0.5;
    self.button.frame = CGRectMake(250, 400, 100, 50);
    self.button.backgroundColor = [UIColor blueColor];
    self.button.alpha = 0.2;

    // 提交动画
    [UIView commitAnimations];
}
Block实现的方法

Block方法常用的有以下几种

// 参数1:持续时间   参数2:执行的动画
+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations
// 参数3 动画结束后调用的block, 可以将结束后的操作写在里面
+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion

// 参数2:动画延迟执行的时间    
// 参数3: 动画属性控制 比如可以设置控件在动画过程中依然可交互
// 操作为: UIViewAnimationOptionAllowUserInteraction
+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion;

Block方法需要配合控件的transform属性使用
transform可以实现平移, 缩放, 旋转等操作.

代码如下:

// 平移
[UIView animateWithDuration:1 animations:^{

    // 填写需要执行的变化
    // 改变的视图 形变属性
    // 第一个参数是需要改变的视图的属性, 第二个参数相对于当前横坐标的差值, 第三个是纵坐标的差值
    self.imageView.transform = CGAffineTransformTranslate(self.imageView.transform, 0, 200);
    // 可以在此处再嵌套一个动画
    [UIView animateWithDuration:2 animations:^{

        self.imageView.transform = CGAffineTransformTranslate(self.imageView.transform, 100, 0);
    }];
    // 注意我这里设置时间. 此时运行结果是横坐标变化的比纵坐标慢, 且纵坐标变化结束后横坐标还没结束
} completion:^(BOOL finished) {
    // 注意结束后的操作是不自带动画的, 可以手动创建动画
    [UIView animateWithDuration:2 animations:^{
        // 动画结束后执行的Block
        self.imageView.transform = CGAffineTransformTranslate(self.imageView.transform, 0, -200);
    }];

    [UIView animateWithDuration:2 animations:^{
        self.imageView.transform = CGAffineTransformTranslate(self.imageView.transform, -100, 0);
    }];

}];

// 缩放
[UIView animateWithDuration:1 animations:^{
    // 第一个参数是横坐标的缩放比例, 第二个参数是纵坐标的缩放比例
    self.imageView.transform = CGAffineTransformScale(self.imageView.transform, 2, 2);
} completion:^(BOOL finished) {
    [UIView animateWithDuration:2 animations:^{
        self.imageView.transform = CGAffineTransformScale(self.imageView.transform, 0.5, 0.5);
    }];

}];



// 旋转
[UIView animateWithDuration:0.01 animations:^{
    // 第一个参数是旋转控件的transform属性, 第二个参数是旋转的角度
    self.imageView.transform = CGAffineTransformRotate(self.imageView.transform, M_PI);

    } completion:^(BOOL finished) {
        // 转完之后再转一次
        [UIView animateWithDuration:1 animations:^{
            self.imageView.transform = CGAffineTransformRotate(self.imageView.transform, M_PI);
        }];

    }];
}

CGAffineTransfrom的API

// 通过设置矩阵值进⾏变换
CGAffineTransformMake(CGFloat a, CGFloat b, CGFloat c,
CGFloat d,CGFloat tx, CGFloat ty);

// 放⼤缩⼩(基于初始)
CGAffineTransformMakeScale(CGFloat sx,
CGFloat sy);

// 旋转(基于初始)
CGAffineTransformMakeRotation(CGFloat
angle);

// 放⼤缩⼩(基于前⼀次变化)
CGAffineTransformScale(CGAffineTransform
t,CGFloat sx, CGFloat sy);

// 旋转(基于前⼀次变化)
CGAffineTransformRotate(CGAffineTransform t,CGFloat angle)

切换视图的动画

// 第一个参数是变化前, 第二个参数是变化后的视图, 第三个参数是变化的样式 最后一个是变化结束的操作
+ (void)transitionFromView:(UIView *)fromView toView:(UIView *)toView duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options completion:(void (^)(BOOL finished))completion

(layer动画)层动画

UIView和CALayer的区别和联系
CALayer负责绘制, 提供UIView需要展示的内容, CALayer是不能交互的, 不像UIView一样可以响应触摸等事件. CALayer本身是UIView的一个readonly属性

CALayer的常用属性

  1. 圆角
    CornerRadius
  2. 阴影颜色
    ShadowColor
  3. 阴影偏移距离
    ShadowOffset
  4. 阴影模糊程度
    ShadowRadius
  5. 阴影的透明度
    ShadowOpacity
  6. 描边的粗细
    BorderWidth
  7. 描边的颜色
    BorderColor
  8. 锚点 (也就是旋转依据的旋转轴点, 默认是控件的原点)
    anchorPoint
  9. 位置信息 一般记录的是控件的中心点
    position
  10. 控制CALayer产生3D空间内的平移, 缩放, 旋转等变化
    注意与控件本身的transform属性不同
    transfrom

CALayer层动画的相关类

CALayer层动画的动画类

CAAnimation是层动画动画类的基类 本身是一个抽象类, 没有实例对象的能力.

CAAnimationGroup组动画类, 本身没有特殊的动画效果, 是作为添加动画类的一个容器. 可以添加一组动画类的对象. 并且自动并发进行.

CATransition类是过渡动画相关的类. 比如页面控制器切换的动画.

这里主要介绍的是CAPropertyAnimation属性类

CAPropertyAnimation

CAPropertyAnimation本身也是一个抽象类, 没有实例动画对象的能力, 它可以响应CALayer的属性的变化而产生动画.

具体的响应动画由它的两个子类CABasicAnimation和CAKeyFrameAnimation实现

CABasicAnimation类

CABasicAnimation基础动画类
它可以响应单个属性变化 而产生动画效果. 只能从基础过渡到最终的结果. 若是有中间值会被忽略.

CABasicAnimation本身只有三个属性

@property(strong) id fromValue;
@property(strong) id toValue;
@property(strong) id byValue;
// 注意属性都是id类型的, 若是变化数值是基本数据类型或者结构体需要转化成NSNumber或者NSValue

当fromValue和toValue不为空byValue为空时, 作用效果是从fromValue过渡到toValue
当fromValue和byValue不为空toValue为空时, 作用效果是从fromValue过渡fromValue到fromValue+byValue
当byValue和toValue不为空fromValue为空时, 作用效果是从toValue - byValue过渡到toValue
当fromValue不为空, 其余两个值为空时, 作用效果是从fromValue过渡到当前值
当toValue不为空, 其余两个值为空时, 作用效果是从当前值过渡到toValue值
当byValue不为空, 其余两个值为空时, 作用效果是从当前值过渡到当前值+byValue值
当三个属性均不会空时, 第三个值不会被响应到. 只响应前两个值.

利用CABasicAnimation类实现控件旋转的代码如下

// 旋转 围绕X轴
- (void)animationRotationX {
    // 创建基本动画
    // 修改的是 形变属性中弧度的x轴的值
    // 此处用到的是属性transform的属性rotation, rotation分为3个坐标轴, x,y,z其中z轴默认是垂直于屏幕的
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.x"];
    // 修改值
    // 修改时 需要把基本数据类型 或者结构体类型转化成对象类型
    animation.toValue = [NSNumber numberWithFloat:M_PI];
    // 设置动画的时间
    animation.duration = 5;

    // 重复次数
    animation.repeatCount = 5;

    // 把动画添加到layer层上 第二个属性是动画的唯一标识
    [self.imageView.layer addAnimation:animation forKey:@"transform.rotation.x"];
}

// 3D旋转
- (void)animationTransform {
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform"];
    animation.repeatCount = 5;
    animation.duration = 1;

    animation.toValue = [NSValue valueWithCATransform3D:CATransform3DRotate(self.imageView.layer.transform, (M_PI), 100, -200, 300)];

    [self.imageView.layer addAnimation:animation forKey:@"transform"];
}

缩放

// 改变视图大小
- (void)animationBoundsSize {
    // 创建基本动画
    // keyPath 一个字母都不能差
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"bounds.size"];

    animation.fromValue = [NSValue valueWithCGSize:CGSizeMake(0, 0)];
    animation.toValue = [NSValue valueWithCGSize:CGSizeMake(200, 200)];
    _imageView.layer.cornerRadius = self.imageView.bounds.size.width / 2;
    animation.duration = 5;
    animation.repeatCount = 5;

    [self.imageView.layer addAnimation:animation forKey:@"bounds.size"];

}

CAKeyframeAnimation类

CAKeyframeAnimation关键帧动画
可以实现让动画变化一组不同的值

// 改变一组背景颜色
- (void)animationBackgroundColor {

    // 由于是改一组值的变化 不是单个值的变化
    CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"backgroundColor"];

    CGColorRef red = [UIColor redColor].CGColor;
    CGColorRef yellow = [UIColor yellowColor].CGColor;
    CGColorRef blue = [UIColor blueColor].CGColor;
    // 需要把颜色强制转化成对象类型
    animation.values = @[(id)red, (id)yellow, (id)blue];
    animation.duration = 5;
    animation.repeatCount = 5;

    [self.imageView.layer addAnimation:animation forKey:@"bgc"];

}
// 给视图设置一组运动轨迹
- (void)animationPath {
    CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];

    animation.values = @[[NSValue valueWithCGPoint:CGPointMake(200, 200)], [NSValue valueWithCGPoint:CGPointMake(300, 400)], [NSValue valueWithCGPoint:CGPointMake(100, 500)], [NSValue valueWithCGPoint:CGPointMake(150, 150)]];

    animation.duration = 5;
    animation.repeatCount = 5;

    [self.imageView.layer addAnimation:animation forKey:@"position"];
}

这里只是简单的介绍了一下动画 对于复杂动画的实现有兴趣研究的读者可以深入专研.
提供一个链接, 点这里


通知

通知模式:

⼀个对象能够给其他任意数量的对象⼲播信息。对象之间可以没有耦合关系。
NSNotification(通知),封装了要⼲播的信息。
NSNotificationCenter(通知中⼼),管理注册接收消息对象,⼲播消息。
observer(观察者),需要监测⼲播信息的对象,即接收信息的对象。

通知的使用方法:

接收信息对象在通知中⼼进⾏注册,包括:信息名称、接收信息时的处理⽅法。
对象通过通知中⼼⼲播信息,包括:信息名称、信息内容。
已经注册过的对象如果不需要接收信息时,在通知中⼼注销。

注册:

[[NSNotificationCenter defaultCenter] addObserver:注册对象
selector:@selector(⽅法名) name:信息名称 object:nil];

注销:

[[NSNotificationCenter defaultCenter] removeObserver:注销
对象 name:信息名称 object:nil];
// 一般写在dealloc方法中

发送信息:

[[NSNotificationCenter defaultCenter]
postNotificationName:信息名称 object:发信息对象 userInfo:发送消息时
传递的信息];

通知的使用比较简单. 通知的作用主要是在于同时给多个注册了通知对象发送消息.

只是让对象在消息发送之前已经注册了通知算是一个不大不小的坑点. 比如其中可能涉及到了懒加载的概念, 即某个视图控制器注册方法是写在其viewDidLoad方法中, 就有可能发现该视图控制器并没有接受到消息. 这是由于该界面的viewDidLoad方法是在该视图控制器出现或者该视图控制器的view未被使用时是不执行的. 一个处理方法是在该视图控制器alloc, init的时候给其view设置背景色. 另一个处理方法是在将接受信息写在AppDelegate类内, 写法如下:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
    // Override point for customization after application launch.
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];

    /* 
      RootViewController是UITabBarController的子类, 
      UITabBarController上添加了4个UINavigationController, 
      每个UINavigationController均有一个UIViewController作为根视图控制器
    */
    RootViewController *rootVC = [[RootViewController alloc] init];
    self.window.rootViewController = rootVC;
    [rootVC release];


    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notification:) name:@"EARTHQUAKE" object:nil];

    return YES;
}

- (void)notification:(NSNotification *)notification {

    NSLog(@"AppDelegate接收到了通知");

    UIViewController *rootVC = self.window.rootViewController; 
    // 注意这里必须要把window的根视图控制器置为空
    self.window.rootViewController = nil;
    // appearance可以获得程序内所有显示的实例
    UINavigationBar *navBar = [UINavigationBar appearance];
    [navBar setBarTintColor:[UIColor magentaColor]];
    // 设置TabBar上按键的文字颜色
    UITabBarItem *barItem = [UITabBarItem appearance];
    [barItem setTitleTextAttributes:@{NSForegroundColorAttributeName:[UIColor blackColor]} forState:(UIControlStateNormal)];
    [barItem setTitleTextAttributes:@{NSForegroundColorAttributeName:[UIColor whiteColor]} forState:(UIControlStateSelected)];

    UITabBar *tabBar = [UITabBar appearance];
    [tabBar setBarTintColor:[UIColor magentaColor]];
    // 给window的根视图控制器重新赋值
    self.window.rootViewController = rootVC;

}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值