UI-2

作者: AlanGe | 来源:发表于2017-08-26 01:12 被阅读32次

    172.简述视图控制器的生命周期。

    1)init函数(init;initWithFrame;initWithCoder;等)--初始化
    2)awakeFromNib--在loadView之前的工作放在这里
    3)viewDidLoad--注意,一个ViewController一个生命周期内这个函数只会调用一次
    4)viewWillAppear--view将要出现,每次View消失再出现都会调用
    5)viewWillLayoutSubviews--简要对子试图进行布局
    6)viewDidLayoutSubivews--完成对子试图布局
    7)viewDidAppear--视图将要出现在屏幕上
    ---上述代码不含部分
    8)、viewWillDisappear--View将要消失
    9)viewDidDisappear--View已经消失

    7.描述下viewWillAppear、layoutSubviews、viewDidAppear、viewDidLayoutSubviews、 viewWillLayoutSubviews、 viewDidLoad 的执行顺序。

    答:UIViewController中loadView, viewDidLoad, viewWillUnload, viewDidUnload, viewWillAppear, viewDidAppear, viewWillLayoutSubviews,viewDidLayoutSubviews,viewWillDisappear, viewDidDisappear方法,按照调用顺序说明如下:
    当一个视图显示时:
    1.initWithNibName:bundle:
    2.loadView
    3.viewDidLoad
    4.viewWillAppear
    5.viewDidAppear
    6.viewWillLayoutSubviews
    7.viewDidLayoutSubviews
    8.viewWillDisappear
    9.viewDidDisappear
    10.viewWillUnload
    11.viewDidUnload

    当一个视图被移除屏幕并且销毁的时候的执行顺序,这个顺序差不多和上面的相反
    1、viewWillDisappear 视图将被从屏幕上移除之前执行
    2、viewDidDisappear 视图已经被从屏幕上移除,用户看不到这个视图了
    3、dealloc 视图被销毁,此处需要对你在init和viewDidLoad中创建的对象进行释放

    调试日志:
    2013-07-14
    12:15:49.048
    VCTest[13412:907]
    initWithNibName:bundle  /  initWithCoder
    #如果使用的StoryBoard
    
    2013-07-14
    12:15:49.056
    VCTest[13412:907]
    loadView
    
    2013-07-14
    12:15:49.059
    VCTest[13412:907]
    viewDidLoad
    
    2013-07-14
    12:15:49.061
    VCTest[13412:907]
    viewWillAppear
    
    2013-07-14
    12:15:49.078
    VCTest[13412:907]
    viewWillLayoutSubviews
    
    2013-07-14
    12:15:49.083
    VCTest[13412:907]
    viewDidLayoutSubviews
    
    2013-07-14
    12:15:49.445
    VCTest[13412:907]
    viewDidAppear
    
    2013-07-14
    12:16:00.624
    VCTest[13412:907]
    viewWillDisappear
    
    2013-07-14
    12:16:00.997
    VCTest[13412:907]
    viewDidDisappear
    
    1. initWithNibName:bundle:
      初始化UIViewController,执行关键数据初始化操作,注意这里不要做view相关操作,view在loadView方法中才初始化,这时loadView还未调用。如果使用StoryBoard进行视图管理,程序不会直接初始化一个UIViewController,StoryBoard会自动初始化或在segue被触发时自动初始化,因此方法initWithNibName:bundle:不会被调用。如果在代码里面使用instantiateViewControllerWithIdentifier:方法显示初始化一个UIViewController,则initWithCoder方法会被调用。

    如果是通过调用initWithNibName:bundle指定nib文件名初始化的话,ViewController会根据此nib来创建View。如果name参数为nil,则ViewController会通过以下两个步骤找到与其关联的nib:
    1)如果ViewController的类名以“Controller”结尾,例如ViewController的类名是MyViewController,则查找是否存在MyView.nib;
    2)找跟ViewController类名一样的文件,例如MyViewController,则查找是否存在MyViewController.nib

    1. loadView
      当访问UIViewController的view属性时,view如果此时是nil,那么VC会自动调用loadView方法来初始化一个UIView并赋值给view属性。此方法用在初始化关键view,需要注意的是,在view初始化之前,不能先调用view的getter方法,否则将导致死循环(除非先调用了[supper loadView];)。
    -(void)loadView{
        NSLog(@"loadView");
        //错误,将导致死循环,因此此时view=nil,VC会再次调用loadView来初始化view
        self.view.backgroundColor = [UIColor greenColor];
    }
    
    -(void)loadView{
        NSLog(@"loadView");
        //正确,先初始化view
        self.view=[[MyView alloc] init];
        self.view.backgroundColor=[UIColor greenColor];
    }
    
    如果没有重载loadView方法,则UIViewController会从nib或StoryBoard中查找默认的loadView,默认的loadView会返回一个空白的UIView对象。
    
    1. viewDidLoad
      当VC的view对象载入内存后调用,用于对view进行额外的初始化操作
    1. viewWillAppear
      在view即将添加到视图层级中(显示给用户)且任意显示动画切换之前调用(这个时候supperView还是nil)。这个方法中完成任何与视图显示相关的任务,例如改变视图方向、状态栏方向、视图显示样式等
    1. viewDidAppear
      在view被添加到视图层级中,显示动画切换之后调用(这时view已经添加到supperView中)。在这个方法中执行视图显示相关附件任务,如果重载了这个方法,必须在方法中调用[supper viewDidAppear];
    1. viewWillLayoutSubviews
      view即将布局其Subviews。比如view的bounds改变了(例如状态栏从不显示到显示,视图方向变化),要调整Subviews的位置,在调整之前要做的一些工作就可以在该方法中实现。
    1. viewDidLayoutSubviews
      view已经布局其Subviews。比如view的bounds改变了(例如状态栏从不显示到显示,视图方向变化),已经调整Subviews的位置,在调整完成之后要做的一些工作就可以在该方法中实现。
    1. viewWillDisappear
      view即将从superView中移除且移除动画切换之前,此时还没有调用removeFromSuperview。
    1. viewDidDisappear
      view从superView中移除,移除动画切换之后调用,此时已调用removeFromSuperview。
    1. viewWillUnload
      在VC的view对象从内存中释放之前调用,可以在view被释放前做一些资源清理操作。在iOS6.0开始就废弃了,该方法不再会调用
    1. viewDidUnload
      在VC的view对象从内存中释放之后调用,可以在view被释放后做一些view相关的引用清理操作,此时view为nil。在iOS6.0开始就废弃了,该方法不再会调用

    当一个视图被移除屏幕并且销毁的时候的执行顺序,这个顺序差不多和上面的相反
    1、viewWillDisappear 视图将被从屏幕上移除之前执行
    2、viewDidDisappear 视图已经被从屏幕上移除,用户看不到这个视图了
    3、dealloc 视图被销毁,此处需要对你在init和viewDidLoad中创建的对象进行释放

    7、+(void)load; +(void)initialize;有什么用处?

    答:
    +(void)load;
    当类对象被引入项目时, runtime 会向每一个类对象发送 load 消息,load 方法会在每一个类甚至分类被引入时仅调用一次,调用的顺序:父类优先于子类, 子类优先于分类
    load 方法不会被类自动继承

    +(void)initialize;
    也是在第一次使用这个类的时候会调用这个方法。

    8、实现一个第三方控件,可以在任何时候出现在APP界面最上层

    1.将这个视图添加在Window上
    2.将这个控件做成一个新的window,显示在最上层

    3 int retVal = UIApplicationMain(argc, argv, nil, nil); 是什么意思?

    答:对UIApplication对象进行了初始化,这个方法除了argc 和 argv 参数外,另外这个函数还有2个两个字符串参数来识别UIApplication类和UIApplication代理类,在这里默认是2个nil,第一个参数为nil就默认把UIApplication类作为缺省值进行初始化,可以在这里不填nil而是使用自己定义的UIApplication子类。至于第二个参数nil就设置为nil就把模板生成的HelloWorldAppdelegate类作为默认值。

    23 实例化一个UITableView对象 要求写出关键语句

    答:
    UITableView *my = [[UITableView alloc] initWithFrame:<#(CGRect)frame#> style:<#(UITableViewStyle)style#>];
    my.delegate = self;
    my.dataSource = self;
    首先需要分配空间 设置表格类型 然后需要设置两个必须的委托对象。

    9、下面代码中obj2是否需要dealloc?

    ClassA *obj1 = [[ClassA alloc] init];
    ClassA *obj2= obj1
    [obj1 hello]; //输出 hello
    [obj1 dealloc];
    [obj1 hello]; //程序输出能否执行到这一行?
    [obj2 dealloc];

    不能,因为obj1和obj2只是指针,它们指向同一个对象,[obj1 dealloc]已经销毁这个对象了,不能再调用[obj2 hello]和[obj2 dealloc]。obj2实际上是个无效指针。

    19、UIView的动画效果有哪些?

    UIViewAnimationOptionCurveEaseInOut
    UIViewAnimationOptionCurveEaseIn
    UIViewAnimationOptionCurveEaseOut
    UIViewAnimationOptionTransitionFlipFromLeft
    UIViewAnimationOptionTransitionFlipFromRight
    UIViewAnimationOptionTransitionCurlUp
    UIViewAnimationOptionTransitionCurlDown

    4、简述在一个App中间有一个button,在你手触摸屏幕点击后,到这个button收到点击事件,中间发生了什么?

    响应链大概有以下几个步骤
    1、设备将touch到的UITouch和UIEvent对象打包, 放到当前活动的Application的事件队列中
    2、单例的UIApplication会从事件队列中取出触摸事件并传递给单例UIWindow
    3、UIWindow使用hitTest:withEvent:方法查找touch操作的所在的视图view

    RunLoop这边我大概讲一下
    1、主线程的RunLoop被唤醒
    2、通知Observer,处理Timer和Source 0
    3、Springboard接受touch event之后转给App进程
    4、RunLoop处理Source 1,Source1 就会触发回调,并调用_UIApplicationHandleEventQueue() 进行应用内部的分发。
    5、RunLoop处理完毕进入睡眠,此前会释放旧的autorelease pool并新建一个autorelease pool
    深挖请去看此文深入理解RunLoop

    5、如果UIView *view已经实例化,在view仅添加了N个UIButton类的实例,这些button不是全局的,并且button已经用tag区分开,如何快速找出其中指定的一个button改变它的某个属性?

    答:view中有一个方法可以根据tag值把他上面的子视图找出来 [view subviewWithTag: ];

    8、一个UITableView的实例,重新加载数据的方法是什么?

    答:[tableView reloadData]方法

    6、请问可以通过哪些方法在UIView里创建元素?

    答:拖控件,xib,纯代码

    1.简述下UIViewController的生命周期?

    当一个视图控制器被创建,并在屏幕上显示的时候。 代码的执行顺序
    1、alloc 创建对象,分配空间
    2、init (initWithNibName) 初始化对象,初始化数据
    3、loadView 加载视图。
    4、viewDidLoad 载入完成,可以进行自定义数据以及动态创建其他控件
    5、viewWillAppear 视图将出现在屏幕之前,马上这个视图就会被展现在屏幕上了
    6、viewDidAppear 视图已在屏幕上渲染完成

    当一个视图被移除屏幕并且销毁的时候的执行顺序,这个顺序差不多和上面的相反
    1、viewWillDisappear 视图将被从屏幕上移除之前执行
    2、viewDidDisappear 视图已经被从屏幕上移除,用户看不到这个视图了
    3、dealloc 视图被销毁,此处需要对你在init和viewDidLoad中创建的对象进行释放

    当我们创建一个UIViewController类的对象时,通常系统会生成几个默认的方法,这些方法大多与视图的调用有关,但是在视图调用时,这些方法的调用顺序如何,需要整理下。
    通常上述方法包括如下几种,这些方法都是UIViewController类的方法:
    -(void)loadView;
    -(void)viewDidLoad;
    -(void)viewWillAppear:(BOOL)animated;
    -(void)viewDidAppear:(BOOL)animated;
    -(void)viewWillDisappear:(BOOL)animated;
    -(void)viewDidDisappear:(BOOL)animated;

    下面我将讲解一下APP在实际调用的过程中所运行的顺序:
    1.在第一次打开应用后,会立即加载四个方法他们分别是:
    -(void)loadView;
    -(void)viewDidLoad;
    -(void)viewWillAppear:(BOOL)animated;
    -(void)viewDidAppear:(BOOL)animated;
    其中要注意的是:在视图将要出现之前我们是不是应该将所有的控件铺好呢,答案是肯定的,在用户体验的角度考虑当用户在进入APP后应该立即就能看见所有的信息展示(在网络较好的前提下),所以对于开发者来说应该在第二个方法中进行所有的空间及信息的载入等工作,这样用户在打开APP后立即就能看见所有的信息了.

    当进入第二个页面的时候其加载(push到新的界面)顺序是:
    (1)当前界面会走的方法
    -(void)viewWillDisappear:(BOOL)animated;
    (2)新加载的视图走的方法
    -(void)loadView;
    -(void)viewDidLoad;
    -(void)viewWillAppear:(BOOL)animated;
    (3)同时走的方法:
    当前界面
    -(void)viewDidDisappear:(BOOL)animated;
    新加载的方法
    -(void)viewDidAppear:(BOOL)animated;

    当返回第一个界面的时候(pop返回原界面)顺序是:
    (1)当前的界面会走的方法
    -(void)viewWillDisappear:(BOOL)animated;
    (2)要返回的视图走的方法
    -(void)loadView;(该方法可能走也可能不走,因为视图已经在之前加载完毕,但有时,程序员会在进入一个界面后清除数据,因此根据具体情况而定)
    -(void)viewDidLoad;(该方法可能走也肯能不走,因为视图已经在之前加载完毕,但有时,程序员会在进入一个界面后清除数据,因此根据具体情况而定)
    -(void)viewWillAppear:(BOOL)animated;
    (3)同时走的方法:
    当前界面
    -(void)viewDidDisappear:(BOOL)animated;
    新加载的方法
    -(void)viewDidAppear:(BOOL)animated;

    2.简述ViewController的生命周期。

    -(void)viewDidLoad;
    -(void)viewDidUnload;
    -(void)viewWillAppear:(BOOL)animated;
    -(void)viewDidAppear:(BOOL)animated;
    -(void)viewWillDisappear:(BOOL)animated;
    -(void)viewDidDisappear:(BOOL)animated;

    1.loadView 如果重写了这个方法,则控制器的view按照loadView方法的描述去创建

    2.viewDidLoad 只调用一次

    3.viewWillAppear 在视图显示之前该函数可以被多次调用

    4.viewDidAppear 视图显示完毕后调用

    5.viewWillDisAppear 在视图消失之前,该函数可以被多次调用

    6.viewWillLayoutSubviews 布局变化前

    7.viewDidLayoutSubviews 布局变化后

    8.控制器还有三个需要注意的方法
    viewWillUnload\viewDidUnload\didReceiveMemoryWarning
    当程序内存不够用时,最先拿到内存警告的是
    UIApplication-Window-window.rootViewController一层层往下传
    当控制器收到内存警告时,如果要确定要销毁view,则会调用viewWillUnload\viewDidUnload

    9.当控制器销毁后,又需要显示,则控制器会调用loadView,又一步一步开始走一遍

    12.谈一下UITableViewCell的重用机制

    TableView的重用机制,为了做到显示和数据分离,UITableView的实现并且不是为每个数据项创建一个Cell。而是只创建屏幕可显示最大个数的cell,然后重复使用这些cell,对cell做单独的显示配置,来达到既不影响显示效果,又能充分节约内容的目的。下面简要分析一下它的实现原理。

    重用实现分析:

    UITableView头文件中,会找到NSMutableArray* visiableCells,和NSMutableDictnary* reusableTableCells两个结构。visiableCells内保存当前显示的cells,reusableTableCells保存可重用的cells。

    TableView显示之初,reusableTableCells为空,那么tableView dequeueReusableCellWithIdentifier:CellIdentifier返回nil。开始的cell都是通过[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]来创建,而且cellForRowAtIndexPath只是调用最大显示cell数的次数。

    比如:有100条数据,iPhone一屏最多显示10个cell。程序最开始显示TableView的情况是:
    1、用[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]创建10次cell,并给cell指定同样的重用标识(当然,可以为不同显示类型的cell指定不同的标识)。并且10个cell全部都加入到visiableCells数组,reusableTableCells为空。
    2、向下拖动tableView,当cell1完全移出屏幕,并且cell11(它也是alloc出来的,原因同上)完全显示出来的时候。cell11加入到visiableCells,cell1移出visiableCells,cell1加入到reusableTableCells。
    3、接着向下拖动tableView,因为reusableTableCells中已经有值,所以,当需要显示新的cell,cellForRowAtIndexPath再次被调用的时候,tableView dequeueReusableCellWithIdentifier:CellIdentifier,返回cell1。cell1加入到visiableCells,cell1移出reusableTableCells;cell2移出visiableCells,cell2加入到reusableTableCells。之后再需要显示的Cell就可以正常重用了。
    所以整个过程并不难理解,但需要注意正是因为这样的原因:配置Cell的时候一定要注意,对取出的重用的cell做重新赋值,不要遗留老数据。

    一些情况:
      使用过程中,并不是只有拖动超出屏幕的时候才会更新reusableTableCells表,还有:
    1、reloadData,这种情况比较特殊。一般是部分数据发生变化,需要重新刷新cell显示的内容时调用。在cellForRowAtIndexPath调用中,所有cell都是重用的。我估计reloadData调用后,把visiableCells中所有cell移入reusableTableCells,visiableCells清空。cellForRowAtIndexPath调用后,再把reuse的cell从reusableTableCells取出来,放入到visiableCells。
    2、reloadRowsAtIndex,刷新指定的IndexPath。如果调用时reusableTableCells为空,那么cellForRowAtIndexPath调用后,是新创建cell,新的cell加入到visiableCells。老的cell移出visiableCells,加入到reusableTableCells。于是,之后的刷新就有cell做reuse了。

    注意:
    1、重取出来的cell是有可能已经捆绑过数据或者加过子视图的,所以,如果有必要,要清除数据(比如textlabel的text)和remove掉add过的子视图(使用tag)。
    2、这样设计的目的是为了避免频繁的 alloc和delloc cell对象而已,没有多复杂。
    3、设计的关键是实现cell和数据的完全分离

    重点:避免重用机制出错
    1、重用机制调用的就是dequeueReusableCellWithIdentifier这个方法,方法的意思就是“出列可重用的cell”,因而只要将它换为cellForRowAtIndexPath(只从要更新的cell的那一行取出cell),就可以不使用重用机制,因而问题就可以得到解决,但会浪费一些空间
    2、为每个cell指定不同的重用标识符(reuseIdentifier)来解决。重用机制是根据相同的标识符来重用cell的,标识符不同的cell不能彼此重用。
    NSString *identifier = [NSString stringWithFormat:@"TimeLineCell%d%d",indexPath.section,indexPath.row];
    3、删除重用的cell的所有子视图,从而得到一个没有特殊格式的cell,供其他cell重用。

    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
    } else {
        //删除cell的所有子视图
        while ([cell.contentView.subviews lastObject] != nil)
        {
            [(UIView*)[cell.contentView.subviews lastObject] removeFromSuperview];
        }
    }
    

    11.frame和bounds的区别

    frame
    该view在父view坐标系统中的位置和大小,它的参考坐标系是父view的坐标系
    bounds
    该view在本地坐标系统中的位置和大小,它的参考坐标系是自身的坐标系,原点始终为(0,0)

    概括
    frame.size == bounds.size
    scrollView.bounds.origin == scrollView.contentOffset

    11.为什么很多内置类如:UITableViewController的delegate属性都是assign而不是retain的?

    会引起循环引用
    所有的引用计数系统,都存在循环应用的问题。
    例如下面的引用关系:
    • 对象a创建并引用到了对象b.
    • 对象b创建并引用到了对象c.
    • 对象c创建并引用到了对象b.这时候b和c的引用计数分别是2和1。
    当a不再使用b,调用release释放对b的所有权,因为c还引用了b,所以b的引用计数为1,b不会被释放。
    b不释放,c的引用计数就是1,c也不会被释放。
    从此,b和c永远留在内存中。
    这种情况,必须打断循环引用,通过其他规则来维护引用关系。

    我们常见的delegate往往是assign方式的属性而不是retain方式 的属性,赋值不会增加引用计数,就是为了防止delegation两端产生不必要的循环引用。如果一个UITableViewController 对象a通过retain获取了UITableView对象b的所有权,这个UITableView对象b的delegate又是a, 如果这个delegate是retain方式的,那基本上就没有机会释放这两个对象了。自己在设计使用delegate模式时,也要注意这点。

    1.什么时候会报unrecognized selector的异常?

    当使用某对象上的某个方法,而该对象上没有实现这个方法的时候。
    拉线,删了方法,忘记删线

    简单来说:
    当调用该对象上某个方法,而该对象上没有实现这个方法的时候,
    可以通过“消息转发”进行解决。

    简单的流程如下,在上一题中也提到过:
    objc是动态语言,每个方法在运行时会被动态转为消息发送,即:objc_msgSend(receiver, selector)。
    objc在向一个对象发送消息时,runtime库会根据对象的isa指针找到该对象实际所属的类,然后在该类中的方法列表以及其父类方法列表中寻找方法运行,如果,在最顶层的父类中依然找不到相应的方法时,程序在运行时会挂掉并抛出异常unrecognized selector sent to XXX 。但是在这之前,objc的运行时会给出三次拯救程序崩溃的机会:
    1、Method resolution
    objc运行时会调用+resolveInstanceMethod:或者 +resolveClassMethod:,让你有机会提供一个函数实现。如果你添加了函数,那运行时系统就会重新启动一次消息发送的过程,否则 ,运行时就会移到下一步,消息转发(Message Forwarding)。
    2、Fast forwarding
    如果目标对象实现了-forwardingTargetForSelector:,Runtime 这时就会调用这个方法,给你把这个消息转发给其他对象的机会。
    只要这个方法返回的不是nil和self,整个消息发送的过程就会被重启,当然发送的对象会变成你返回的那个对象。否则,就会继续Normal Fowarding。
    这里叫Fast,只是为了区别下一步的转发机制。因为这一步不会创建任何新的对象,但下一步转发会创建一个NSInvocation对象,所以相对更快点。
    3、Normal forwarding
    这一步是Runtime最后一次给你挽救的机会。首先它会发送-methodSignatureForSelector:消息获得函数的参数和返回值类型。如果-methodSignatureForSelector:返回nil,Runtime则会发出-doesNotRecognizeSelector:消息,程序这时也就挂掉了。如果返回了一个函数签名,Runtime就会创建一个NSInvocation对象并发送-forwardInvocation:消息给目标对象。

    13.在一个UIV结尾中嵌入一个UIWebView(大小自适应完整显示),UIView中的其他子控件和UIWebView的布局位置是相对的,请问怎样才能保证在UIWebView的页面动态变化是,其他控件能根据它的尺寸变化自适应?

    有的网页中会使用"<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">"这个标签来设置网页的宽度,不过带来的问题是,如果展示这个webview的宽度不等于设备的宽度的时候,就会出现网页内容过宽左右可以滑动或者网页左右内容没有占满。找了一下,有两个解决方法:
    
    1. 利用webview中的scrollview的zoom特性,这个方法会让网页内容变小
    -(void)webViewDidFinishLoad:(UIWebView *)theWebView {
            CGSize contentSize = theWebView.scrollView.contentSize;
            CGSize viewSize = self.view.bounds.size;
            float rw = viewSize.width / contentSize.width;
            theWebView.scrollView.minimumZoomScale = rw;
            theWebView.scrollView.maximumZoomScale = rw;
            theWebView.scrollView.zoomScale = rw;
    }
    
    2. 第二个方法,在客户端使用js重写meta标签,这个也是在webview的delegate的webViewDidFinished回调中调用;我们使用的这种方法来操作,内容不会变小
    javascript = [NSString stringWithFormat:@"var viewPortTag=document.createElement('meta');  \
                                viewPortTag.id='viewport';  \
                                [viewPortTag.name](http://viewPortTag.name) = 'viewport';  \
                                viewPortTag.content = 'width=%d; initial-scale=1.0; maximum-scale=1.0; user-scalable=0;';  \
                                document.getElementsByTagName('head')[0].appendChild(viewPortTag);" , (int)authWebView.bounds.size.width];
    [authWebView stringByEvaluatingJavaScriptFromString:javascript];
    

    14.让界面呈现动画效果除了使用[UIView animateWithDuration:animations:]做简单动画之外,还有其他方式实现动画吗?

    帧动画,隐式动画,核心动画,动图GIF



    iOS的动画效果一直都很棒很,给人的感觉就是很炫酷很流畅,起到增强用户体验的作用。在APP开发中实现动画效果有很多种方式,对于简单的应用场景,我们可以使用UIKit提供的动画来实现。

    UIView动画简介
    UIView动画实质上是对Core Animation的封装,提供简洁的动画接口。
    
    UIView动画可以设置的动画属性有:
    1、大小变化(frame)
    2、拉伸变化(bounds)
    3、中心位置(center)
    4、旋转(transform)
    5、透明度(alpha)
    6、背景颜色(backgroundColor)
    7、拉伸内容(contentStretch)
    
    一、UIview 类方法动画
    1、动画的开始和结束方法
    1.1 动画开始标记
    [UIView beginAnimations:(nullable NSString *) context:(nullable void *)];
    第一个参数:动画标识
    第二个参数:附加参数,在设置了代理的情况下,此参数将发送到setAnimationWillStartSelector和setAnimationDidStopSelector所指定的方法。大部分情况下,我们设置为nil即可。
    
    1.2 结束动画标记
    [UIView commitAnimations];
    
    2、动画参数的设置方法
    //动画持续时间
    [UIView setAnimationDuration:(NSTimeInterval)];
    //动画的代理对象
    [UIView setAnimationDelegate:(nullable id)];
    //设置动画将开始时代理对象执行的SEL
    [UIView setAnimationWillStartSelector:(nullable SEL)];
    //设置动画结束时代理对象执行的SEL
    [UIView setAnimationDidStopSelector:(nullable SEL)];
    //设置动画延迟执行的时间
    [UIView setAnimationDelay:(NSTimeInterval)];
    //设置动画的重复次数
    [UIView setAnimationRepeatCount:(float)];
    //设置动画的曲线
    [UIView setAnimationCurve:(UIViewAnimationCurve)];
    UIViewAnimationCurve的枚举值如下:
    UIViewAnimationCurveEaseInOut,         // 慢进慢出(默认值)
    UIViewAnimationCurveEaseIn,            // 慢进
    UIViewAnimationCurveEaseOut,           // 慢出
    UIViewAnimationCurveLinear             // 匀速
    //设置是否从当前状态开始播放动画
    [UIView setAnimationBeginsFromCurrentState:YES];
    假设上一个动画正在播放,且尚未播放完毕,我们将要进行一个新的动画:
    当为YES时:动画将从上一个动画所在的状态开始播放
    当为NO时:动画将从上一个动画所指定的最终状态开始播放(此时上一个动画马上结束)
    //设置动画是否继续执行相反的动画
    [UIView setAnimationRepeatAutoreverses:(BOOL)];
    //是否禁用动画效果(对象属性依然会被改变,只是没有动画效果)
    [UIView setAnimationsEnabled:(BOOL)];
    //设置视图的过渡效果
    [UIView setAnimationTransition:(UIViewAnimationTransition) forView:(nonnull UIView *) cache:(BOOL)];
    第一个参数:UIViewAnimationTransition的枚举值如下
    UIViewAnimationTransitionNone,              //不使用动画
    UIViewAnimationTransitionFlipFromLeft,      //从左向右旋转翻页
    UIViewAnimationTransitionFlipFromRight,     //从右向左旋转翻页
    UIViewAnimationTransitionCurlUp,            //从下往上卷曲翻页
    UIViewAnimationTransitionCurlDown,          //从上往下卷曲翻页
    第二个参数:需要过渡效果的View
    第三个参数:是否使用视图缓存,YES:视图在开始和结束时渲染一次;NO:视图在每一帧都渲染
    
    3、实例代码:
    1、属性变化动画(以frame变化为例)
    - (void)changeFrame {
        [UIView beginAnimations:@"FrameAni" context:nil];
        [UIView setAnimationDuration:1.0];
        [UIView setAnimationDelegate:self];
        [UIView setAnimationWillStartSelector:@selector(startAni:)];
        [UIView setAnimationDidStopSelector:@selector(stopAni:)];
        [UIView setAnimationRepeatCount:1];
        [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
        self.cartCenter.frame = self.centerShow.frame;
        [UIView commitAnimations];
    }
    
    - (void)startAni:(NSString *)aniID {
        NSLog(@"%@ start",aniID);
    }
    
    - (void)stopAni:(NSString *)aniID {
        NSLog(@"%@ stop",aniID);
    }
    动画效果:
    
    NormalAni.gif
    
    2、转场效果动画(以Flip效果为例)
    - (void)flipAni {
        [UIView beginAnimations:@"FlipAni" context:nil];
        [UIView setAnimationDuration:1.0];
        [UIView setAnimationDelegate:self];
        [UIView setAnimationWillStartSelector:@selector(startAni:)];
        [UIView setAnimationDidStopSelector:@selector(stopAni:)];
        [UIView setAnimationRepeatCount:1];
        [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
        [UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:self.centerShow cache:YES];
        self.centerShow.image = [UIImage imageNamed:@"service"];
        [UIView commitAnimations];
    }
    动画效果:
    
    ScreenTransitionAni.gif
    
    二、UIview Block动画
    iOS4.0以后,增加了Block动画块,提供更简洁的方式来实现动画。
    1)Block动画方法
    1、最简洁的Block动画:包含时间和动画
    [UIView animateWithDuration:(NSTimeInterval)  //动画持续时间
    
                     animations:^{
    
                         //执行的动画
    
                     }];
    
    2、带有动画完成回调的Block动画
    [UIView animateWithDuration:(NSTimeInterval)  //动画持续时间
    
                     animations:^{
    
                         //执行的动画
    
                     }                completion:^(BOOL finished) {
    
                         //动画执行完毕后的操作
    
                     }];
    
    3、可设置延迟时间和过渡效果的Block动画
    [UIView animateWithDuration:(NSTimeInterval) //动画持续时间
                          delay:(NSTimeInterval) //动画延迟执行的时间
                        options:(UIViewAnimationOptions) //动画的过渡效果
                     animations:^{
                         //执行的动画
                     }                completion:^(BOOL finished) {
                         //动画执行完毕后的操作
                     }];
    
    UIViewAnimationOptions的枚举值如下,可组合使用:
    UIViewAnimationOptionLayoutSubviews            //进行动画时布局子控件
    UIViewAnimationOptionAllowUserInteraction      //进行动画时允许用户交互
    UIViewAnimationOptionBeginFromCurrentState     //从当前状态开始动画
    UIViewAnimationOptionRepeat                    //无限重复执行动画
    UIViewAnimationOptionAutoreverse               //执行动画回路
    UIViewAnimationOptionOverrideInheritedDuration //忽略嵌套动画的执行时间设置
    UIViewAnimationOptionOverrideInheritedCurve    //忽略嵌套动画的曲线设置
    UIViewAnimationOptionAllowAnimatedContent      //转场:进行动画时重绘视图
    UIViewAnimationOptionShowHideTransitionViews   //转场:移除(添加和移除图层的)动画效果
    UIViewAnimationOptionOverrideInheritedOptions  //不继承父动画设置
    
    UIViewAnimationOptionCurveEaseInOut            //时间曲线,慢进慢出(默认值)
    UIViewAnimationOptionCurveEaseIn               //时间曲线,慢进
    UIViewAnimationOptionCurveEaseOut              //时间曲线,慢出
    UIViewAnimationOptionCurveLinear               //时间曲线,匀速
    
    UIViewAnimationOptionTransitionNone            //转场,不使用动画
    UIViewAnimationOptionTransitionFlipFromLeft    //转场,从左向右旋转翻页
    UIViewAnimationOptionTransitionFlipFromRight   //转场,从右向左旋转翻页
    UIViewAnimationOptionTransitionCurlUp          //转场,下往上卷曲翻页
    UIViewAnimationOptionTransitionCurlDown        //转场,从上往下卷曲翻页
    UIViewAnimationOptionTransitionCrossDissolve   //转场,交叉消失和出现
    UIViewAnimationOptionTransitionFlipFromTop     //转场,从上向下旋转翻页
    UIViewAnimationOptionTransitionFlipFromBottom  //转场,从下向上旋转翻页
    
    4、spring动画
    iOS7.0后新增Spring动画(ios系统动画大部分采用Spring Animation,适用于所有可被添加动画效果的属性)
    [UIView animateWithDuration:(NSTimeInterval)//动画持续时间
                          delay:(NSTimeInterval)//动画延迟执行的时间
         usingSpringWithDamping:(CGFloat)//震动效果,范围0~1,数值越小震动效果越明显
          initialSpringVelocity:(CGFloat)//初始速度,数值越大初始速度越快
                        options:(UIViewAnimationOptions)//动画的过渡效果
                     animations:^{
                         //执行的动画
                     }
                     completion:^(BOOL finished) {
                         //动画执行完毕后的操作
                     }];
    
    5、Keyframes动画
    iOS7.0后新增关键帧动画,支持属性关键帧,不支持路径关键帧
    [UIView animateKeyframesWithDuration:(NSTimeInterval)//动画持续时间
                                   delay:(NSTimeInterval)//动画延迟执行的时间
                                 options:(UIViewKeyframeAnimationOptions)//动画的过渡效果
                              animations:^{
                                  //执行的关键帧动画
                              }
                              completion:^(BOOL finished) {
                                  //动画执行完毕后的操作
                              }];
    UIViewKeyframeAnimationOptions的枚举值如下,可组合使用:
    
    UIViewAnimationOptionLayoutSubviews           //进行动画时布局子控件
    UIViewAnimationOptionAllowUserInteraction     //进行动画时允许用户交互
    UIViewAnimationOptionBeginFromCurrentState    //从当前状态开始动画
    UIViewAnimationOptionRepeat                   //无限重复执行动画
    UIViewAnimationOptionAutoreverse              //执行动画回路
    UIViewAnimationOptionOverrideInheritedDuration //忽略嵌套动画的执行时间设置
    UIViewAnimationOptionOverrideInheritedOptions //不继承父动画设置
    
    UIViewKeyframeAnimationOptionCalculationModeLinear     //运算模式 :连续
    UIViewKeyframeAnimationOptionCalculationModeDiscrete   //运算模式 :离散
    UIViewKeyframeAnimationOptionCalculationModePaced      //运算模式 :均匀执行
    UIViewKeyframeAnimationOptionCalculationModeCubic      //运算模式 :平滑
    UIViewKeyframeAnimationOptionCalculationModeCubicPaced //运算模式 :平滑均匀
    各种运算模式的直观比较如下图:
    
    图片来源网络.png
    增加关键帧的方法:
    
    [UIView addKeyframeWithRelativeStartTime:(double)//动画开始的时间(占总时间的比例)
                            relativeDuration:(double) //动画持续时间(占总时间的比例)
                                  animations:^{
                                      //执行的动画
                                  }];
    
    6、转场动画
    6.1 从旧视图转到新视图的动画效果
    [UIView transitionFromView:(nonnull UIView *)
                        toView:(nonnull UIView *)
                      duration:(NSTimeInterval)
                       options:(UIViewAnimationOptions)
                    completion:^(BOOL finished) {
                        //动画执行完毕后的操作
                    }];
    在该动画过程中,fromView 会从父视图中移除,并讲 toView 添加到父视图中,注意转场动画的作用对象是父视图(过渡效果体现在父视图上)。
    调用该方法相当于执行下面两句代码:
    
    [fromView.superview addSubview:toView];
    [fromView removeFromSuperview];
    
    6.2 单个视图的过渡效果
    [UIView transitionWithView:(nonnull UIView *)
                      duration:(NSTimeInterval)
                       options:(UIViewAnimationOptions)
                    animations:^{
                        //执行的动画
                    }
                    completion:^(BOOL finished) {
                        //动画执行完毕后的操作
                    }];
    
    2)实例代码:
    1、普通动画
    下面三段代码都实现了相同的视图frame的变化,不同之处只在于其延迟时间、过渡效果和结束回调。
    - (void)blockAni1 {
        [UIView animateWithDuration:1.0 animations:^{
            self.cartCenter.frame = self.centerShow.frame;
        }];
    }
    - (void)blockAni2 {
        [UIView animateWithDuration:1.0 animations:^{
            self.cartCenter.frame = self.centerShow.frame;
        } completion:^(BOOL finished) {
            NSLog(@"动画结束");
        }];
    }
    - (void)blockAni3 {
        [UIView animateWithDuration:1.0 delay:1.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
            self.cartCenter.frame = self.centerShow.frame;
        } completion:^(BOOL finished) {
            NSLog(@"动画结束");
        }];
    }
    动画效果:
    
    NormalAni.gif
    
    2、Spring动画
    - (void)blockAni4 {
        [UIView animateWithDuration:1.0 delay:0.f usingSpringWithDamping:0.5 initialSpringVelocity:5.0 options:UIViewAnimationOptionCurveLinear animations:^{
            self.cartCenter.frame = self.centerShow.frame;
        } completion:^(BOOL finished) {
            NSLog(@"动画结束");
        }];
    }
    动画效果:
    
    SpringAni.gif
    
    3、Keyframes动画
    这里以实现视图背景颜色变化(红-绿-蓝-紫)的过程来演示关键帧动画。
    - (void)blockAni5 {
        self.centerShow.image = nil;
        [UIView animateKeyframesWithDuration:9.0 delay:0.f options:UIViewKeyframeAnimationOptionCalculationModeLinear animations:^{
            [UIView addKeyframeWithRelativeStartTime:0.f relativeDuration:1.0 / 4 animations:^{
                self.centerShow.backgroundColor = [UIColor colorWithRed:0.9475 green:0.1921 blue:0.1746 alpha:1.0];
            }];
            [UIView addKeyframeWithRelativeStartTime:1.0 / 4 relativeDuration:1.0 / 4 animations:^{
                self.centerShow.backgroundColor = [UIColor colorWithRed:0.1064 green:0.6052 blue:0.0334 alpha:1.0];
            }];
            [UIView addKeyframeWithRelativeStartTime:2.0 / 4 relativeDuration:1.0 / 4 animations:^{
                self.centerShow.backgroundColor = [UIColor colorWithRed:0.1366 green:0.3017 blue:0.8411 alpha:1.0];
            }];
            [UIView addKeyframeWithRelativeStartTime:3.0 / 4 relativeDuration:1.0 / 4 animations:^{
                self.centerShow.backgroundColor = [UIColor colorWithRed:0.619 green:0.037 blue:0.6719 alpha:1.0];
            }];
        } completion:^(BOOL finished) {
            NSLog(@"动画结束");
        }];
    }
    动画效果:
    
    KeyFramesAni.gif
    
    4、转场动画
    4.1 单个视图的过渡效果
    - (void)blockAni6 {
        [UIView transitionWithView:self.centerShow duration:1.0 options:UIViewAnimationOptionTransitionCrossDissolve animations:^{
            self.centerShow.image = [UIImage imageNamed:@"Service"];
        } completion:^(BOOL finished) {
            NSLog(@"动画结束");
        }];
    }
    动画效果:
    
    TransitionAni.gif
    
    4.2 从旧视图转到新视图的动画效果
    - (void)blockAni7 {
        UIImageView * newCenterShow = [[UIImageView alloc]initWithFrame:self.centerShow.frame];
        newCenterShow.image = [UIImage imageNamed:@"Service"];
        [UIView transitionFromView:self.centerShow toView:newCenterShow duration:1.0 options:UIViewAnimationOptionTransitionFlipFromLeft completion:^(BOOL finished) {
            NSLog(@"动画结束");
        }];
    }
    动画效果:
    
    ScreenTransitionAni.gif
    

    相关文章

      网友评论

          本文标题:UI-2

          本文链接:https://www.haomeiwen.com/subject/jrxmdxtx.html