UI-1

96
AlanGe
2017.08.26 01:12* 字数 10070

7、不使用IB是,下面这样做有什么问题?

- (void)viewDidLoad {
    
    [super viewDidLoad];
    
    CGRect frame = CGRectMake(0, 0, self.view.bounds.size.width * 0.5, self.view.bounds.size.height * 0.5);
    
    UIView *view = [[UIView alloc] initWithFrame:frame];
    
    view.backgroundColor = [UIColor redColor];
    
    [self.view addSubview:view];
    
}

解释:不使用IB手动创建ViewController时,在viewDidLoad中并未进行位置的初始化,原来遇到过不少次这个小坑,当外部创建这个vc时:
TestViewController *vc = [[TestViewController alloc] init];
vc.view.frame = CGRectMake(0,0,100,100);
//...
我们知道,ViewController的view初始化大概流程是:
- (UIView *)view {
    if (!_view) {

        [self loadView];
        
        [self viewDidLoad];
        
    }
    
}
所以在外部执行到vc.view.frame = CGRectMake(0, 0, 100, 100);这句话时,在赋值操作执行前,viewDidLoad就已经被调用,因而在viewDidLoad中对view frame的取值都是默认值(window的大小),而非设定值。
注: 使用IB加载时如上情况也会发生,只是一般在IB就已经有一个预设值了。

6、请说说Layer和View的关系,以及你是如何使用它们的。

1.首先UIView可以响应事件,Layer不可以.
UIKit使用UIResponder作为响应对象,来响应系统传递过来的事件并进行处理。UIApplication、UIViewController、UIView、和所有从UIView派生出来的UIKit类(包括UIWindow)都直接或间接地继承自UIResponder类。
在 UIResponder中定义了处理各种事件和事件传递的接口, 而 CALayer直接继承 NSObject,并没有相应的处理事件的接口。

2.View和CALayer的Frame映射及View如何创建CALayer.
一个 Layer 的 frame 是由它的 anchorPoint,position,bounds,和 transform 共同决定的,而一个 View 的 frame 只是简单的返回 Layer的 frame,同样 View 的 center和 bounds 也是返回 Layer 的一些属性。

3.UIView主要是对显示内容的管理,而 CALayer 主要侧重显示内容的绘制。

4.在做 iOS 动画的时候,修改非 RootLayer的属性(譬如位置、背景色等)会默认产生隐式动画,而修改UIView则不会。

总结来说就是如下几点:

  1. 每个 UIView 内部都有一个 CALayer 在背后提供内容的绘制和显示,并且 UIView 的尺寸样式都由内部的 Layer 所提供。两者都有树状层级结构,layer 内部有 SubLayers,View 内部有 SubViews.但是 Layer 比 View 多了个AnchorPoint
  2. 在 View显示的时候,UIView 做为 Layer 的 CALayerDelegate,View 的显示内容由内部的 CALayer 的 display
  3. CALayer 是默认修改属性支持隐式动画的,在给 UIView 的 Layer 做动画的时候,View 作为 Layer 的代理,Layer 通过 actionForLayer:forKey:向 View请求相应的 action(动画行为)
  4. layer 内部维护着三分 layer tree,分别是 presentLayer Tree(动画树),modeLayer Tree(模型树), Render Tree (渲染树),在做 iOS动画的时候,我们修改动画的属性,在动画的其实是 Layer 的 presentLayer的属性值,而最终展示在界面上的其实是提供 View的modelLayer
  5. 两者最明显的区别是 View可以接受并处理事件,而 Layer 不可以

==================================================================================
CALayer属于Core Animation部分的内容,比较重要而不太好理解。以下是园子中看到的一篇文章的摘录:

  1. UIView是iOS系统中界面元素的基础,所有的界面元素都是继承自它。
    它本身完全是由CoreAnimation来实现的。它真正的绘图部分,是由一个CALayer类来管理。UIView本身更像是一个CALayer的管理器,访问它的跟绘图和跟坐标有关的属性,例如frame,bounds等,实际上内部都是在访问它所包含的CALayer的相关属性。

  2. UIView有个重要属性layer,可以返回它的主CALayer实例。
    // 要访问层,读取UIView实例的layer属性
    CALayer *layer = myView.layer
    所有从UIView继承来的对象都继承了这个属性。这意味着你可以转换、缩放、旋转,甚至可以在Navigation bars,Tables,Text boxes等其它的View类上增加动画。每个UIView都有一个层,控制着各自的内容最终被显示在屏幕上的方式。
    UIView的layerClass方法,可以返回主layer所使用的类,UIView的子类可以通过重载这个方法,来让UIView使用不同的CALayer来显示。代码示例:
    -(class)layerClass {
    return ([CAEAGLLayer class]);
    }
    上述代码使得某个UIView的子类使用GL来进行绘制。

  3. UIView的CALayer类似UIView的子View树形结构,也可以向它的layer上添加子layer,来完成某些特殊的表示。即CALayer层是可以嵌套的。
    示例代码:
    grayCover = [[CALayer alloc] init];
    grayCover.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.2] CGColor];
    [self.layer addSubLayer:grayCover];
    上述代码会在目标View上敷上一层黑色透明薄膜的效果。

  4. UIView的layer树形在系统内部,被维护着三份copy。
    分别是:
    逻辑树,这里是代码可以操纵的;
    动画树,是一个中间层,系统就在这一层上更改属性,进行各种渲染操作;
    显示树,其内容就是当前正被显示在屏幕上得内容。

  5. 动画的运作:对UIView的subLayer(非主Layer)属性进行更改,系统将自动进行动画生成,动画持续时间的缺省值似乎是0.5秒。

  6. 坐标系统:CALayer的坐标系统比UIView多了一个anchorPoint属性,使用CGPoint结构表示,值域是0~1,是个比例值。
    这个点是各种图形变换的坐标原点,同时会更改layer的position的位置,它的缺省值是{0.5,0.5},即在layer的中央。
    某layer.anchorPoint = CGPointMake(0.f,0.f);
    如果这么设置,只会将layer的左上角被挪到原来的中间位置,必须加上这一句:
    某layer.position = CGPointMake(0.f,0.f);
    最后:layer可以设置圆角显示(cornerRadius),也可以设置阴影 (shadowColor)。但是如果layer树中某个layer设置了圆角,树种所有layer的阴影效果都将不显示了。因此若是要有圆角又要阴影,变通方法只能做两个重叠的UIView,一个的layer显示圆角,一个layer显示阴影......

7.渲染:当更新层,改变不能立即显示在屏幕上。当所有的层都准备好时,可以调用setNeedsDisplay方法来重绘显示。
[gameLayer setNeedsDisplay];
若要重绘部分屏幕区域,请使用setNeedsDisplayInRect:方法,通过在CGRect结构的区域更新:
[gameLayer setNeedsDisplayInRect:CGRectMake(150.0,100.0,50.0,75.0)];
如果是用的Core Graphics框架来执行渲染的话,可以直接渲染Core Graphics的内容。用renderInContext:来做这个事。
[gameLayer renderInContext:UIGraphicsGetCurrentContext()];

8.变换:要在一个层中添加一个3D或仿射变换,可以分别设置层的transform或affineTransform属性。
characterView.layer.transform = CATransform3DMakeScale(-1.0,-1.0,1.0);
CGAffineTransform transform = CGAffineTransformMakeRotation(45.0);
backgroundView.layer.affineTransform = transform;

9.变形:Quartz Core的渲染能力,使二维图像可以被自由操纵,就好像是三维的。图像可以在一个三维坐标系中以任意角度被旋转,缩放和倾斜。CATransform3D的一套方法提供了一些魔术般的变换效果。

13、你是怎么实现导航栏透明效果的,会引起其他问题吗?
第一种方法:

- (void)viewWillAppear:(BOOL)animated{
    
    [super viewWillAppear:animated];
    
    for (UIView *aView in self.navigationController.navigationBar.subviews) {
        
        if ([aView isKindOfClass:NSClassFromString(@"_UINavigationBarBackground")]) {
            
            aView.hidden = YES;
            
        }
        
    }
    
}

- (void)viewWillDisappear:(BOOL)animated

{
    
    [super viewWillDisappear:animated];
    
    for (UIView *aView in self.navigationController.navigationBar.subviews) {
        
        if ([aView isKindOfClass:NSClassFromString(@"_UINavigationBarBackground")]) {
            
            aView.hidden = NO;
        }    
    }
}

第二种方法:

[self.navigationController.navigationBar setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];

//去除 navigationBar 底部的细线

self.navigationController.navigationBar.shadowImage = [UIImage new];

第三种方法

self.navigationController.navigationBar.shadowImage = [[UIImage alloc] init];

UIImage *image = [self createAImageWithColor:[UIColor clearColor] alpha:0.0];

[self.navigationController.navigationBar setBackgroundImage:image forBarMetrics:UIBarMetricsDefault];

- (UIImage *)createAImageWithColor:(UIColor *)color alpha:(CGFloat)alpha{
    
    CGRect rect = CGRectMake(0.0f, 0.0f, 1.0f, 1.0f);
    
    UIGraphicsBeginImageContext(rect.size);
    
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    CGContextSetFillColorWithColor(context, [color CGColor]);
    
    CGContextSetAlpha(context, alpha);
    
    CGContextFillRect(context, rect);
    
    UIImage *theImage = UIGraphicsGetImageFromCurrentImageContext();
    
    UIGraphicsEndImageContext();
    
    return theImage;
    
}

注意点:

设置导航栏透明的时候,如果在Push到其他的控制器,其他的控制器导航栏也是会变得透明,所以为了防止这类情况的发生,最好在

- (void)viewWillDisappear:(BOOL)animated

方法中,把导航栏的颜色还原过来。

2、简述UIEvent和UITouch对象,并说明两者之间的关系

UITouch
当用户用一根手指触摸屏幕时,会创建一个与手指相关联的UITouch对象,一根手指对应一个UITouch对象
UITouch的作用:保存着跟手指相关的信息,比如触摸的位置、时间、阶段
当手指移动时,系统会更新同一个UITouch对象,使之能够一直保存该手指在的触摸位置,当手指离开屏幕时,系统会销毁相应的UITouch对象
提示:iPhone开发中,要避免使用双击事件!

UITouch的属性
触摸产生时所处的窗口
@property(nonatomic,readonly,retain)UIWindow *window;
触摸产生时所处的视图
@property(nonatomic,readonly,retain)UIView *view;
短时间内点按屏幕的次数,可以根据tapCount判断单击、双击或更多的点击
@property(nonatomic,readonly)NSUInteger tapCount;
记录了触摸事件产生或变化时的时间,单位是秒
@property(nonatomic,readonly)NSTimeInterval timestamp;
当前触摸事件所处的状态
@property(nonatomic,readonly)UITouchPhasep hase;

UItouch方法
-(CGPoint)locationInView:(UIView)view;
返回值表示触摸在view上的位置这里返回的位置是针对view的坐标系的(以view的左上角为原点(0,0))调用时传入的view参数为nil的话,返回的是触摸点在UIWindow的位置
-(CGPoint)previousLocationInView:(UIView
)view;
该方法记录了前一个触摸点的位置

UIevent
每产生一个事件,就会产生一个UIEvent对象
UIevent:称为事件对象,记录事件产生的时刻和类型

常见属性
1.事件类型
@property(nonatomic,readonly)UIEventTypetype;
@property(nonatomic,readonly)UIEventSubtypesubtype;
2.事件产生的时间
@property(nonatomic,readonly)NSTimeIntervaltimestamp;
UIEvent还提供了相应的方法可以获得在某个view上面的触摸对象(UITouch)

touch和event
一次完整的触摸过程,会经历3个状态:
触摸开始:- (void)touchesBegan:(NSSet)touches withEvent:(UIEvent)event
触摸移动:- (void)touchesMoved:(NSSet)touches withEvent:(UIEvent)event
触摸结束:- (void)touchesEnded:(NSSet)touches withEvent:(UIEvent)event
触摸取消(可能会经历):- (void)touchesCancelled:(NSSet)touches withEvent:(UIEvent)event

4个触摸事件处理方法中,都有NSSettouches和UIEventevent两个参数,一次完整的触摸过程中,只会产生一个事件对象,4个触摸方法都是同一个event参数。
如果两根手指同时触摸一个view,那么view只会调用一次touchesBegan:withEvent:方法,touches参数中装着2个UITouch对象。
如果这两根手指一前一后分开触摸同一个view,那么view会分别调用2次touchesBegan:withEvent:方法,并且每次调用时的touches参数中只包含一个UITouch对象。
根据touches中UITouch的个数可以判断出是单点触摸还是多点触摸。

UIView不接收触摸事件的三种情况
1.不接收用户交互 userInteractionEnabled = NO
2.隐藏 hidden = YES
3.透明 alpha = 0.0 ~ 0.01

提示:UIImageView的userInteractionEnabled默认就是NO,因此UIImageView以及它的子控件默认是不能接收触摸事件的。

3、简述iOS屏幕触摸事件的传递触发链。

iOS中的事件
用户与app交互的时候会产生各种各样的的事件,iOS中事件分为三大类型


//触摸事件
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
//加速计事件
-(void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event;
-(void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event;
-(void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event;
//远程控制事件
-(void)remoteControlReceivedWithEvent:(UIEvent *)event;

事件的产生和传递
1.视图发生触摸事件后,系统会将该事件加入到一个由UIApplication管理的事件队列中
2.UIApplication会从事件队列中取出最前面的事件,并将事件分发下去以便处理,通常,先发送事件给应用程序的主窗口(keyWindow)
3.主窗口会在视图层次结构中找到一个最合适的视图来处理触摸事件,这也是整个事件处理过程的第一步
4.找到合适的视图控件后,就会调用视图控件的touches方法来作具体的事件处理
touchesBegan…
touchesMoved…
touchedEnded…

如何找到最合适的控件来处理事件?
两个重要的条件:
1.自己是否能接收触摸事件?
2.触摸点是否在自己身上?
二者都满足的情况下,去遍历自己的子控件是否满足上述两个条件,如果符合两个条件则继续遍历子控件,如果没有符合条件的子控件,那么就自己最适合处理。
值得注意的是:如果父控件不能接收触摸事件,那么子控件就不可能接收到触摸事件

UIView不接收触摸事件的3种情况
1.交互关闭不接收触摸事件
userInteractionEnabled = NO
2.隐藏不接收触摸事件
hidden = YES
3.透明不接收触摸事件
alpha = 0.0 ~ 0.01

注意:UIImageView的userInteractionEnabled默认就是NO,因此UIImageView以及它的子控件默认是不能接收触摸事件的,即使是imageView里面添加了UIButton,也无法捕捉按钮的点击

思考:当一个控件不能被点击的时候,那么就应该考虑到是不是触发了上面三种情况或者父控件触发了上面三种情况

响应者链条

在iOS中,由响应者链来对事件进行响应,所有事件响应的类都是UIResponder的子类,响应者链是一个由不同对象组成的层次结构,其中的每个对象将依次获得响应事件消息的机会。当发生事件时,事件首先被发送给第一响应者,第一响应者往往是事件发生的视图,也就是用户触摸屏幕的地方。事件将沿着响应者链一直向下传递,直到被接受并做出处理。一般来说,第一响应者是个视图对象或者其子类对象,当其被触摸后事件被交由它处理,如果它不处理,事件就会被传递给它的视图控制器对象viewcontroller(如果存在),然后是它的父视图(superview)对象(如果存在),以此类推,直到顶层视图。接下来会沿着顶层视图(top view)到窗口(UIWindow对象)再到程序(UIApplication对象)。如果整个过程都没有响应这个事件,该事件就被丢弃。一般情况下,在响应者链中只要由对象处理事件,事件就停止传递。

不是任何对象都能处理事件,只有继承了UIResponder的对象才能接收并处理事件。也就是有响应和处理事件能力的对象,我们称之为“响应者对象”,响应者链就是由一系列的响应者对象构成的一个层次结构。

UIApplication、UIViewController、UIView都继承自UIResponder,因此它们都是响应者对象,都能够接收并处理事件

响应者链条示意图



1.如果view的控制器存在,就传递给控制器;如果控制器不存在,则将其传递给它的父视图
2.在视图层次结构的最顶级视图,如果也不能处理收到的事件或消息,则其将事件或消息传递给window对象进行处理
3.如果window对象也不处理,则其将事件或消息传递给UIApplication对象
4.如果UIApplication也不能处理该事件或消息,则将其丢弃

响应者链条特点
1、响应者链通常是由视图(UIView)构成的;
2、一个视图的下一个响应者是它视图控制器(UIViewController)(如果有的话),然后再转给它的父视图(Super View);
3、视图控制器(如果有的话)的下一个响应者为其管理的视图的父视图;
4、单例的窗口(UIWindow)的内容视图将指向窗口本身作为它的下一个响应者需要指出的是,Cocoa Touch应用不像Cocoa应用,它只有一个UIWindow对象,整个响应者链会简单一些;
5、单例的应用(UIApplication)是一个响应者链的终点,它的下一个响应者指向nil,以结束整个循环。

事件分发
1、第一响应者(First responder)指的是当前接受触摸的响应者对象(通常是一个UIView对象),即表示当前该对象正在与用户交互,它是响应者链的开端。整个响应者链和事件分发的使命都是找出第一响应者。
2、UIWindow对象以消息的形式将事件发送给第一响应者,使其有机会首先处理事件。如果第一响应者没有进行处理,系统就将事件(通过消息)传递给响应者链中的下一个响应者,看看它是否可以进行处理。
3、iOS系统检测到手指触摸(Touch)操作时会将其打包成一个UIEvent对象,并放入当前活动Application的事件队列,单例的UIApplication会从事件队列中取出触摸事件并传递给单例的UIWindow来处理,UIWindow对象首先会使用hitTest:withEvent:方法寻找此次Touch操作初始点所在的视图(View),即需要将触摸事件传递给其处理的视图,这个过程称之为hit-test view。
4、UIWindow实例对象会首先在它的内容视图上调用hitTest:withEvent:,此方法会在其视图层级结构中的每个视图上调用pointInside:withEvent:(该方法用来判断点击事件发生的位置是否处于当前视图范围内,以确定用户是不是点击了当前视图),如果pointInside:withEvent:返回YES,则继续逐级调用,直到找到touch操作发生的位置,这个视图也就是要找的hit-test view。

hitTest:withEvent:的处理流程
1.首先调用当前视图的pointInside:withEvent:方法判断触摸点是否在当前视图内;
2.若返回NO,则hitTest:withEvent:返回nil;
3.若返回YES,则向当前视图的所有子视图(subviews)发送hitTest:withEvent:消息,所有子视图的遍历顺序是从最顶层视图一直到到最底层视图,即从subviews数组的末尾向前遍历,直到有子视图返回非空对象或者全部子视图遍历完毕;
4.若第一次有子视图返回非空对象,则hitTest:withEvent:方法返回此对象,处理结束;
5.如所有子视图都返回非,则hitTest:withEvent:方法返回自身(self)。


事件响应链

12、ViewController的loadView,viewDidLoad,viewDidUnload分别是在什么时候调用的?在自定义ViewController的时候,这几个函数里面该做什么工作?

loadView

1.什么时候被调用?
每次访问UIViewController的view(比如controller.view、self.view)而且view为nil,loadView方法就会被调用。

2.有什么作用?
loadView方法是用来负责创建UIViewController的view

3.默认实现是怎样的?
默认实现即[super loadView]里面做了什么事情。
1> 它会先去查找与UIViewController相关联的xib文件,通过加载xib文件来创建UIViewController的view

  • 如果在初始化UIViewController指定了xib文件名,就会根据传入的xib文件名加载对应的xib文件
    [[MJViewController alloc] initWithNibName:@"MJViewController" bundle:nil];
  • 如果没有明显地传xib文件名,就会加载跟UIViewController同名的xib文件
    [[MJViewController alloc] init]; // 加载MJViewController.xib

2> 如果没有找到相关联的xib文件,就会创建一个空白的UIView,然后赋值给UIViewController的view属性,大致如下
self.view = [[[UIView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame] autorelease];
// applicationFrame的值是:{{x = 0, y = 20}, {width = 320, height = 460}}
[super loadView]里面就大致完成1>和2>中叙述的内容

4.怎样正确使用这个方法?

大家都知道UIViewController的view可以通过xib文件来创建,但是在某些情况下,xib不是那么地灵活,所以有时候我们想通过代码来创建UIView,比如:
self.view = [[[UIWebView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame] autorelease];
如果想通过代码来创建UIViewController的view,就要重写loadView方法,并且不需要调用[super loadView],因为在第3点里面已经提到:
若没有xib文件,[super loadView]默认会创建一个空白的UIView。
我们既然要通过代码来自定义UIView,那么就没必要事先创建一个空白的UIView,以节省不必要的开销。

正确的做法应该是这样:
-(void)loadView {
self.view = [[[UIWebView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame] autorelease];
}
不需要调用[super loadView],你调用了也不会出错,只是造成了一些不必要的开销。
总结一句话,苹果设计这个方法就是给我们自定义UIViewController的view用的。

viewDidLoad

1.什么时候被调用?
不论你是通过xib文件还是重写loadView创建UIViewController的view,在view创建完毕后,最终都会调用viewDidLoad方法。

2.有什么作用?
一般我们会在这里做界面上的初始化操作,比如往view中添加一些子视图、从数据库或者网络加载模型数据装配到子视图中。例如:

-(void)viewDidLoad
    [super viewDidLoad];
    // 添加一个按钮
    UIButton *button = [UIButton buttonWithType:UIButtonTypeContactAdd];
    [button addTarget:self action:@selector(click) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:button];
}

viewDidUnload

1.什么时候被调用?
iOS设备的内存是极其有限的,如果应用程序占用的内存过多的话,系统就会对应用程序发出内存警告。UIViewController就会收到didReceiveMemoryWarning消息。didReceiveMemoryWarning方法的默认实现是:如果当前UIViewController的view不在应用程序的视图层次结构(View Hierarchy)中,即view的superview为nil的时候,就会将view释放,并且调用viewDidUnload方法

2.有什么作用?
上面说到,发出内存警告且view被释放的时候就会调用viewDidUnload方法,所以一般在释放资源,主要是释放界面元素相关的资源,将相关的实例都赋值为nil

- (void)viewDidUnload {

    [super viewDidUnload];

    self.name = nil;

    self.pwd = nil;

}

3.dealloc也是用来释放资源的,那跟viewDidUnload有什么关系?
当发出内存警告调用viewDidUnload方法时,只是释放了view,并没有释放UIViewController,所以并不会调用dealloc方法。即viewDidUnload和dealloc方法并没有任何关系,dealloc方法只会在UIViewController被释放的时候调用

三个方法的关系
1.第一次访问UIViewController的view时,view为nil,然后就会调用loadView方法创建view。
2.view创建完毕后会调用viewDidLoad方法进行界面元素的初始化。
3.当内存警告时,系统可能会释放UIViewController的view,将view赋值为nil,并且调用viewDidUnload方法
4.当再次访问UIViewController的view时,view已经在3中被赋值为nil,所以又会调用loadView方法重新创建view。
5.view被重新创建完毕后,还是会调用viewDidLoad方法进行界面元素的初始化。

================================================================

init方法
在init方法中实例化必要的对象(遵从LazyLoad思想)。
init方法中初始化ViewController本身。

loadView方法
当view需要被展示而它却是nil时,viewController会调用该方法。不要直接调用该方法。
如果手工维护views,必须重载重写该方法。
如果使用IB维护views,必须不能重载重写该方法。

loadView和IB构建view,你在控制器中实现了loadView方法,那么你可能会在应用运行的某个时候被内存管理控制调用。 如果设备内存不足的时候, view 控制器会收到didReceiveMemoryWarning的消息。 默认的实现是检查当前控制器的view是否在使用。 如果它的view不在当前正在使用的view hierarchy里面,且你的控制器实现了loadView方法,那么这个view将被release,loadView方法将被再次调用来创建一个新的view。

viewDidLoad方法
viewDidLoad 此方法只有当view从nib文件初始化的时候才被调用。
重载重写该方法以进一步定制view
在iPhone OS 3.0及之后的版本中,还应该重载重写viewDidUnload来释放对view的任何索引
viewDidLoad后调用数据Model

viewDidUnload方法
当系统内存吃紧的时候会调用该方法(注:viewController没有被dealloc)
内存吃紧时,在iPhone OS 3.0之前didReceiveMemoryWarning是释放无用内存的唯一方式,但是OS 3.0及以后viewDidUnload方法是更好的方式。
1、在该方法中将所有IBOutlet(无论是property还是实例变量)置为nil(系统release view时已经将其release掉了)。
2、在该方法中释放其他与view有关的对象、其他在运行时创建(但非系统必须)的对象、在viewDidLoad中被创建的对象、缓存数据等 release对象后,将对象置为nil(IBOutlet只需要将其置为nil,系统release view时已经将其release掉了)

一般认为viewDidUnload是viewDidLoad的镜像,因为当view被重新请求时,viewDidLoad还会重新被执行

viewDidUnload中被release的对象必须是很容易被重新创建的对象(比如在viewDidLoad或其他方法中创建的对象),不要release用户数据或其他很难被重新创建的对象。

dealloc方法

viewDidUnload和dealloc方法没有关联,dealloc还是继续做它该做的事情

viewDidLoad在view 从nib文件初始化时调用,
loadView在controller的view为nil时调用。此方法在编程实现view时调用,view 控制器默认会注册memory warning notification。
当view controller的任何view 没有用的时候,viewDidUnload会被调用,在这里实现将retain 的view release,如果是retain的IBOutlet view 属性则不要在这里release,IBOutlet会负责release 。

6、简要说说UITableView和UICollectionView的主要区别。

两者在使用上大致相同,在初始化之后,都会设置相应的数据源和代理,通过重写相应的数据源和代理方法来进行具体操作,现在着重说一下它们的一些小不同:
1.初始化
UITableViewController在初始化后,只要实现它的数据源和代理方法,就可以直接展现出来。而UICollectionViewController推荐使用的是initWithCollectionViewLayout:方法,该中会传入一个layout,只有有了这个layout才能显示出来。或者重写init方法,在init方法中设置它的布局方式,也就是layout。这个layout可以使用继承自UICollectionViewLayout的两个子类 :UICollectionViewFlowLayout 和UICollectionViewTransitionLayout。

这里贴一下常用的流水布局的代码:
[objc] view plain copy
//1.创建layout
// 1.1创建流水布局
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
// 1.2设置每个格子的尺寸
layout.itemSize = CGSizeMake(thumbnailWidth, thumbnailWidth);
// 1.3设置整个collectionView的内边距
CGFloat paddingY = 20;
layout.minimumInteritemSpacing = minimumItemspace;
layout.sectionInset = UIEdgeInsetsMake(0, minimumLinespace, paddingY, minimumLinespace);
// 1.4设置每一行之间的间距
layout.minimumLineSpacing = minimumLinespace;

2.重用
两个里面的cell都可以进行重用,但是UICollectionViewCell在初始化时不会传入identifier ,这会导致cell没有identifier可以用于在缓存池中寻找,所以我们需要在ViewDidload方法中注册一个cell,这时它会传入一个identifier,当缓存池中找不到cell时,系统会用注册的cell自动创建一个供使用。如果要注册自己写得类,而该类的视图是从xib文件中生成的,必须注册nib文件。

在UITableViewController中,index取出相应组合组中行的两个属性是section和row,而在UICollectionViewController中,index对应的是section和item.

  1. UIView如果需要重新绘制整个界面,需要调用什么方法?

UIView setNeedsDisplay和setNeedsLayout方法。
首先两个方法都是异步执行的。而setNeedsDisplay会调用自动调用drawRect方法,这样可以拿到UIGraphicsGetCurrentContext,就可以画画了。而setNeedsLayout会默认调用layoutSubViews,就可以处理子视图中的一些数据。

综上所述:setNeedsDisplay方便绘图,而layoutSubViews方便出来数据。

setNeedDisplay告知视图它发生了改变,需要重新绘制自身,就相当于刷新界面。

95.在没有navigationController的情况下,要从一个ViewController切换到另一个ViewController应该(C)

A.{self.navigationController pushViewController:nextViewController animated:YES};

B.{self .view addSubview:nextViewController}

C. {self presentModalViewController:nextViewController animated:YES};

D. {self pushViewController:nextViewController animated:YES};

分析:A、C都需要有navigationController,B一个控制器的view是无法加载另一个控制器的view的,所以选C!

104.简述通过Storyboard实现一个tableView(自定义cell的关键步骤).

答:首先,创建自己的自定义cell的类,我们叫做CustomCell,要继承于UITableViewCell。在这个类中定义自己所需要的控件。

然后,打开storyboard,选择自己要添加自定义cell的UIViewController,我们叫它为ViewController。在UITableView里面添加一个cell(或者修改原有的cell)。将cell的style改为custom,将cell的类改为CustomCell,将identifier改为CustomCellIdentifier。然后,可以在cell中添加控件,将控件和刚才在CustomCell中定义的控件连起来。

最后,在ViewController的UITableView的tableView:cellForRowAtIndexPath:代理方法中添加以下代码:
[plain]
CustomCell *cell = [tableViewdequeueReusableCellWithIdentifier:@"CustomCellIdentifier"];

这样,就创建了一个cell,可以在这句代码之后对自己添加的控件进行设置。

  1. UINavigationController如何要使用push/pop功能的话,需要怎么实现?
1.用UINavigationController的时候用pushViewController:animated

//返回之前的视图
[selfnavigationController popViewControllerAnimated:YES];

//ps:push以后会在navigation的left bar自动添加back按钮,它的响应方法就是返回。所以一般不需要写返回方法,点back按钮即可。

2.其他时候用presentModalViewController:animated
    [self presentModalViewController:controller animated:YES];//YES有动画效果
    //返回之前的视图
    [selfdismissModalViewControllerAnimated:YES];

3.切换视图一般用不到addSubview
    UINavigationController是导航控制器,如果pushViewController的话,会跳转到下一个ViewController,点返回会回到现在这个ViewController;

    如果是addSubview的话,其实还是对当前的ViewController操作,只是在当前视图上面又“盖”住了一层视图,其实原来的画面在下面呢,看不到而已。

68.简单说一下APP的启动过程,从main文件开始说起。

进入main函数,在main.m的main函数中执行了UIApplicationMain这个方法,这是ios程序的入口点!

int UIApplicationMain(int argc, char argv[], NSString principalClassName, NSString *delegateClassName)

argc、argv:ISO C标准main函数的参数,直接传递给UIApplicationMain进行相关处理即可。

principalClassName:指定应用程序类,该类必须是UIApplication(或子类)。如果为nil,则用UIApplication类作为默认值

delegateClassName:指定应用程序类的代理类,该类必须遵守UIApplicationDelegate协议

此函数会根据principalClassName创建UIApplication对象,根据delegateClassName创建一个delegate对象,并将该delegate对象赋值给UIApplication对象中的delegate属性

lUIApplication对象会依次给delegate对象发送不同的消息,接着会建立应用程序的main runloop(事件循环),进行事件的处理(首先会调用delegate对象的application:didFinishLaunchingWithOptions:)

程序正常退出时这个函数才返回。如果进程要被系统强制杀死,一般这个函数还没来得及返回进程就终止了。

91.UITableView需要实现哪些代理?列出UITableView代理中必须实现的与其他一些常用的函数.

答:
//一组有多少行
-( NSInteger )tableView:( UITableView *)tableViewnumberOfRowsInSection:( NSInteger)section;

-( UITableViewCell *)tableView:( UITableView *)tableViewcellForRowAtIndexPath:(NSIndexPath *)indexPath;
//每行中的cell的实现以上两个方法为必须要实现的

//常用的有
- ( void )tableView:( UITableView *)tableViewdidSelectRowAtIndexPath:( NSIndexPath*)indexPath

//选中以后事件设置

-( CGFloat )tableView:( UITableView *)tableViewheightForRowAtIndexPath:( NSIndexPath*)indexPath

//设置cell的高度

//等等。。。。。
  1. iOS里面的手势是如何实现的?
#import "ViewController.h"

@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *iconView;
@property (nonatomic,assign) NSInteger index;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
   
    //0.开启用户交互
    [self.iconView setUserInteractionEnabled:YES];
   
    //创建手势
   
    /*********** *********** 1.创建轻敲手势--单击 *********** ***********/
    //1.1创建手势对象
    UITapGestureRecognizer *tap=[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tap)];
    //1.2添加手势到指定的控件
    [self.iconView addGestureRecognizer:tap]; //多态
    //1.3设置手势的常用属性
    //tap.numberOfTapsRequired=2; //轻敲的次数,说明轻敲2次才会触发
    //tap.numberOfTouchesRequired=2; //轻敲时的手指数量
   
   
    /*********** *********** 2.创建捏合手势 *********** ***********/
    //(pinch:)会将当前手势对象做为参数传递(捕获缩放比例)
    //2.1创建手势对象
    UIPinchGestureRecognizer *pinch=[[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(pinch:)];
    //2.2添加手势到指定的控件
    [self.iconView addGestureRecognizer:pinch];
   
   
    /*********** *********** 3.创建旋转手势 *********** ***********/
    //3.1创建手势对象
    UIRotationGestureRecognizer *rotate=[[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(rotation:)];
    //3.2添加手势到指定的控件
    [self.iconView addGestureRecognizer:rotate];
   
    /*********** *********** 4.创建轻扫 滑动手势 *********** ***********/
    //4.1创建手势对象
    UISwipeGestureRecognizer *swipeRight=[[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipe:)];
    //4.2添加手势到指定的控件
    [self.iconView addGestureRecognizer:swipeRight];
    //如果需要不同的方向轻扫,那么就需要创建不同的手势对象:意味着,一个轻扫手势对象只能设置一个方向
    UISwipeGestureRecognizer *swipeLeft=[[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipe:)];
    //设置轻扫方向
    swipeLeft.direction=UISwipeGestureRecognizerDirectionLeft;
    //4.2添加手势到指定的控件
    [self.iconView addGestureRecognizer:swipeLeft];
   
   
    /*********** *********** 5.创建拖拽手势 *********** ***********/
    //5.1创建手势对象
    UIPanGestureRecognizer *pan=[[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
    //5.2添加手势到指定的控件
    [self.iconView addGestureRecognizer:pan];
   
    //设置手势的优先级别:我们需要系统先识别轻扫,如果轻扫手势不能识别,再来识别拖拽
    [pan requireGestureRecognizerToFail:swipeLeft]; //这句代码说明如果swipeLeft识别失败才会识别pan
    [pan requireGestureRecognizerToFail:swipeRight];//这句代码说明如果swipeRight识别失败才会识别pan
   
   
    /*********** *********** 6.设置长按手势 *********** ***********/
    //6.1创建手势对象
    UILongPressGestureRecognizer *longPress=[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPress:)];
    //设置长按时间
    longPress.minimumPressDuration=0.5;
    //6.2添加手势到指定的控件
    [self.view addGestureRecognizer:longPress];
}

//6.3长按手势处理方法
- (void) longPress:(UILongPressGestureRecognizer *)gesture
{
    //默认会打印两个状态下的数据
    if (gesture.state==UIGestureRecognizerStateBegan) {
        //弹出消息框
        UIAlertView *alert=[[UIAlertView alloc] initWithTitle:@"操作" message:@"请选择" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"确定", nil];
        [alert show];
    }
}

//5.3拖拽手势处理方法
- (void)pan:(UIPanGestureRecognizer *)gesture
{
    //NSLog(@"UIPanGestureRecognizer");
    //获取当前的偏移值
    CGPoint offset=[gesture translationInView:self.iconView];
    //设置transform
    self.iconView.transform=CGAffineTransformMakeTranslation(offset.x, offset.y);
    //当手势响应结束的时候,让图片回到原始位置
    if (gesture.state==UIGestureRecognizerStateEnded) {
        [UIView animateWithDuration:0.5 animations:^{
            //CGAffineTransformIdentity就是控件的原始的transform值
            self.iconView.transform=CGAffineTransformIdentity;
           
        }];
    }
}

//4.3轻扫手势处理方法
//默认方向是向右扫
- (void)swipe:(UISwipeGestureRecognizer *)gesture
{
//    //转场动画
//    CATransition *tran=[CATransition animation];
//    // type:设置转动方向
//    tran.type=@"fade";
//    tran.type=@"cube";
//    //设置动画时间
//    tran.duration=2;
   
   
    if (gesture.direction==UISwipeGestureRecognizerDirectionLeft) {
        self.index++;
        //转场动画向左
//        tran.subtype=kCATransitionFromRight;
    }
    else if(gesture.direction==UISwipeGestureRecognizerDirectionRight){
        self.index--;
        //转场动画向右
//        tran.subtype=kCATransitionFromLeft;
    }
    self.index= ABS(self.index+9)%9;
    NSString *fileName=[NSString stringWithFormat:@"%ld",self.index];
    //重新设置图片
    self.iconView.image=[UIImage imageNamed:fileName];
    //添加转场动画,要在设置完属性后才能添加
//    [self.iconView.layer addAnimation:tran forKey:nil];
}

//3.3旋转手势处理方法
-(void)rotation:(UIRotationGestureRecognizer *)guesture
{
    //获取当前手势所产生的角度值---弧度值
    CGFloat angle=guesture.rotation;
    self.iconView.transform=CGAffineTransformMakeRotation(angle);
}

//2.3捏合手势处理方法
- (void) pinch:(UIPinchGestureRecognizer *)gesture
{
    //缩放比例
    CGFloat scale=gesture.scale;
    NSLog(@"%f",scale);
    //每次都是从原来尺寸捏合,一般情况下用这种方法
    self.iconView.transform=CGAffineTransformMakeScale(scale, scale);
    //参照上一次捏合的结果来捏合,同时scale也会变化
    //self.iconView.transform=CGAffineTransformScale(self.iconView.transform, scale, scale);
}

//1.3轻敲手势处理方法
- (void) tap
{
    [self.navigationController setNavigationBarHidden:!self.navigationController.navigationBarHidden animated:YES];
}
@end
手势的种类
所有手势的父类:UIGestureRecognizer
6种手势:UI XXX GestureRecognizer
UITapGestureRecognizer  点击一下屏幕
UISwipeGestureRecognizer  轻扫屏幕,如解锁
UILongPressGestureRecognizer 长按手势
UIPinchGestureRecognizer  捏合手势
UIPanGestureRecognizer 移动手势
UIRotationGestureRecognizer 旋转手势

如何使用手势
step1:创建手势对象
step2:设置与该手势相关的属性
step3:将手势对象与需要检测的视图关联在一起

1.具体的手势的使用

1.1 UITapGestureRecognizer  (一次性手势)


1.2 UISwipeGestureRecognizer (一次性手势)

- (void)viewDidLoad
{
    [superviewDidLoad];
    UISwipeGestureRecognizer *swipeGR = [[UISwipeGestureRecognizeralloc]initWithTarget:selfaction:@selector(swipe:)];
    swipeGR.numberOfTouchesRequired =1;
    //设置轻扫动作的方向
    swipeGR.direction =UISwipeGestureRecognizerDirectionRight|UISwipeGestureRecognizerDirectionLeft;
    [self.viewaddGestureRecognizer:swipeGR];
}

-(void)swipe:(UISwipeGestureRecognizer *)gr{
    NSLog(@"...");
}

注意:direction属性为枚举,并且是可以进行组合的枚举值,多个枚举值之间可以使用“|”按位“或”进行运算,每一个二进制位代表一个状态,组合后,系统通过读取哪一个位置为1,就代表支持这个状态

1.3 UILongPressGestureRecognizer(连续性手势)
【Demo3_LongPressGestureRecognizer】
@interfaceViewController ()

@end

@implementation ViewController

- (void)viewDidLoad
{
    [superviewDidLoad];
    UILongPressGestureRecognizer *longGR = [[UILongPressGestureRecognizeralloc]initWithTarget:selfaction:@selector(longPress:)];
    longGR.minimumPressDuration =2;
    [self.viewaddGestureRecognizer:longGR];
}

-(void)longPress:(UILongPressGestureRecognizer *)gr{
    NSLog(@".....");
}


1.4 UIPinchGestureRecognizer(连续性手势,捏合)
【Demo4_PinchGestureRecognizer】

-(void)pinch:(UIPinchGestureRecognizer *)gr{
    //比例 大小的比率
    CGFloat scale = gr.scale;
    //速率
    CGFloat velocity = gr.velocity;
    //NSLog(@"scale=%.2f,velocity=%.2f",scale,velocity);
    if (velocity >6) {
        self.textView.hidden =NO;
    }elseif (velocity < -6){
        self.textView.hidden =YES;
    }else{
        self.textView.font = [UIFont systemFontOfSize:17*scale];
    }
   
}

练习:界面上有一个UITextView,增加手势识别,如果快速扩,文本出现,快速捏,文本隐藏,慢速扩,字体放大,慢速捏,字体缩小
self.textView.editable = NO;
self.textView.hidden
self.textView.font

1.5 UIPanGestureRecognizer(连续性手势,移动)
【Demo5_PanGestureRecognizer】
-(void)pan:(UIPanGestureRecognizer *)gr{
    //触点移动的绝对距离
    //CGPoint location = [gr locationInView:self.view];
    //移动两点之间的相对距离
    CGPoint translation = [gr translationInView:self.view];
    CGPoint center = self.imageView.center;
    center.x+=translation.x;
    center.y+=translation.y;
    self.imageView.center= center;
    //每次移动后,将本次移动的距离置零
    //下一次再移动时,记录的距离就是最后两点间的距离
    //而不是距离第一个点的距离
    [gr setTranslation:CGPointZero inView:self.view];
}

练习:图片随手势的移动而移动

1.6 UIRotationGestureRecognizer(连续性手势,旋转)
【Demo6_RotationGestureRecognizer】
- (void)viewDidLoad
{
    [superviewDidLoad];
    UIRotationGestureRecognizer *rotationGR = [[UIRotationGestureRecognizeralloc]initWithTarget:selfaction:@selector(rotation:)];
    [self.viewaddGestureRecognizer:rotationGR];
}

-(void)rotation:(UIRotationGestureRecognizer *)gr{
    //获取当前位置相对于手势开始时经过的弧度
    CGFloat rotation = gr.rotation;
    NSLog(@"%.2f",rotation);
}


2.变形(Transform)
2.1什么是变形?
视图发生了位移、缩放、旋转这样的变化叫做变形

2.2 如何实现变形?
通过修改视图对象的 .transform 属性 就可以完成变化的效果
位移:translation
缩放:scale
旋转:rotation
2.3 transform属性
类型:CGAffineTransform (仿射变换)类型的结构体
结构体中包含了3X3矩阵的9个值,其中有3个值不变,所以只要修改剩余的6个值,就可以实现视图的变换
这6个数值很难计算,借助于一些系统的API可以方便的实现数值的改变

2.4 修改transform属性的API
位移变换:
CGAffineTransformMakeTransition()
CGAffineTransformTranslate()

缩放变换:
CGAffineTransformMakeScale()
CGAffineTransformScale()

旋转变换:
CGAffineTransformMakeRotation()
CGAffineTransformRotate()

重点注意:变形与自动布局是冲突的,所以在使用变形时,一定要关闭Auto Layout,不关闭的话,产生的效果无法预料
【Demo7_Transform】

@interfaceViewController ()
@property (weak,nonatomic) IBOutletUIImageView *imageView;

@end

@implementation ViewController

- (void)viewDidLoad
{
    [superviewDidLoad];
}

//位移
- (IBAction)translation:(id)sender {
   
    CGPoint center =self.imageView.center;
    center.x+=30;
    center.y+=30;
    self.imageView.center =center;
   
}
//缩放
- (IBAction)scale:(id)sender {
    //self.imageView.transform = CGAffineTransformMakeScale(1.5, 1.5);
    self.imageView.transform =CGAffineTransformScale(self.imageView.transform,1.5, 1.5);
}
//旋转
- (IBAction)rotation:(id)sender {
    //self.imageView.transform = CGAffineTransformMakeRotation(M_PI_2);
    self.imageView.transform =CGAffineTransformRotate(self.imageView.transform,M_PI_2);
   
}

- (IBAction)identity:(id)sender {
    self.imageView.transform =CGAffineTransformIdentity;
}

界面创建后,没有做任何变形之前,系统会将这个状态记录到一个常量中 CGAffineTransformIdentity,当使用Makexxxx()函数进行计算新的矩阵时,都是基于这个常量进行变形计算的,当使用没有make的那组方法事,每次计算新的矩阵都会依据传入的transform做为变换的基础


3. 手势+变形
【Demo8_GestureRecognizer_Transform】
@property (weak,nonatomic) IBOutletUIImageView *imageView;

@end

@implementation ViewController

- (void)viewDidLoad
{
    [superviewDidLoad];
    UIPanGestureRecognizer *panGR = [[UIPanGestureRecognizeralloc]initWithTarget:selfaction:@selector(pan:)];
    UIPinchGestureRecognizer *pinchGR =[[UIPinchGestureRecognizeralloc]initWithTarget:selfaction:@selector(pinch:)];
    UIRotationGestureRecognizer *rotationGR = [[UIRotationGestureRecognizeralloc]initWithTarget:selfaction:@selector(rotation:)];
   
    pinchGR.delegate =self;
    rotationGR.delegate =self;
   
   
    [self.viewaddGestureRecognizer:panGR];
    [self.viewaddGestureRecognizer:pinchGR];
    [self.viewaddGestureRecognizer:rotationGR];
}
//移动
-(void)pan:(UIPanGestureRecognizer *)gr{
    CGPoint translation = [grtranslationInView:self.view];
    CGPoint center =self.imageView.center;
    center.x+=translation.x;
    center.y+=translation.y;
    self.imageView.center = center;
    //self.imageView.transform = CGAffineTransformTranslate(self.imageView.transform, translation.x, translation.y);
    [gr setTranslation:CGPointZeroinView:self.view];
}
// 缩放
-(void)pinch:(UIPinchGestureRecognizer *)gr{
    self.imageView.transform =CGAffineTransformScale(self.imageView.transform, gr.scale, gr.scale);
    //将本次变化的比率归1  下一次的新比率就是相对本次的增长,而不是相对于最初的比率
    gr.scale =1;
}

//旋转
-(void)rotation:(UIRotationGestureRecognizer *)gr{
    self.imageView.transform =CGAffineTransformRotate(self.imageView.transform, gr.rotation);
    gr.rotation =0;
   
}

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
    returnYES;
}


//注意:第一件事  先关 auto layout

3.1 使用pan手势实现图片位移

3.2 使用pinch手势实现图片的缩放

3.3 使用rotation手势实现图片的旋转

3.4 多手势并存实现旋转缩放
step1:分别设置两个手势的代理为当前控制器
step2:控制器这个代理对象需要遵守协议
step3:控制器实现一个方法

4.在storyboard中实现手势
step1:从右侧资源库中选择合适的手势图标,拖拽到场景中
step2:在拖拽到场景的过程中,要有意识的选择将这个手势与哪个视图绑定,拖拽到视图上方,视图变蓝即可松手
step3:在场景的条目上,会发现拖拽的那个手势,选中按住control,连线到代码区,编写针对这个手势的响应方法

36.简述CALayer和UIView的关系

答:UIView和CALayer是相互依赖的关系。UIView依赖与calayer提供的内容,CALayer依赖uivew提供的容器来显示绘制的内容。归根到底CALayer是这一切的基础,如果没有CALayer,UIView自身也不会存在,UIView是一个特殊的CALayer实现,添加了响应事件的能力。
结论:
UIView来自CALayer,高于CALayer,是CALayer高层实现与封装。UIView的所有特性来源于CALayer支持。

20、简述你对UIView、UIWindow和CALayer的理解

UIView: 属于UIkit.framework框架,负责渲染矩形区域的内容,为矩形区域添加动画,响应区域的触摸事件,布局和管理一个或多个子视图

UIWindow:属于UIKit.framework框架,是一种特殊的UIView,通常在一个程序中只会有一个UIWindow,但可以手动创建多个UIWindow,同时加到程序里面。UIWindow在程序中主要起到三个作用:
作为容器,包含app所要显示的所有视图;
传递触摸消息到程序中view和其他对象;
与UIViewController协同工作,方便完成设备方向旋转的支持。

CAlayer:属于QuartzCore.framework,是用来绘制内容的,对内容进行动画处理依赖与UIView来进行显示,不能处理用户事件。

UIView和CALayer是相互依赖的,UIView依赖CALayer提供内容,CALayer依赖UIView的容器显示绘制内容。

(补充)UIViewController:每个视图控制器都有一个自带的视图,并且负责这个视图相关的一切事务。方便管理视图中的子视图,负责model与view的通信;检测设备旋转以及内存警告;是所有视图控制类的积累,定义了控制器的基本功能。

16、一个tableView是否可以关联两个不同的数据源?

参考答案:
当然是可以关联多个不同的数据源,但是不可同时使用多个数据源而已。比如,一个列表有两个筛选功能,一个是筛选城市,一个是筛选时间,那么这两个就是两个数据源了。当筛选城市时,就会使用城市数据源;当筛选时间时,就会使用时间数据源。

216.ViewController的didReceiveMemoryWarning怎么被调用:

1、当程序收到内存警告时候ViewController会调用didReceiveMemoryWarning这个方法。
2、调用了这个方法之后,对view进行释放并且调用viewDidUnload方法
3、从iOS3.0开始,不需要重载这个函数,把释放内存的代码放到viewDidUnload中去。

  1. Write something about what you achievedby and what you learned from the UITableView class

首先,Controller需要实现两个delegate,分别是UITableViewDelegate和UITableViewDataSource,然后UITableView对象的delegate要设置为self,注册cell,之后给定分区数和行数,注意cell的重用机制,可以设置系统cell或者自定义cell。

9、实现一个最简单的点击拖拽功能。上面那个拖拽之外,如果在手放开时,需要根据速度往前滑动呢?

给要拖拽的视图添加上UIPanGestureRecognizer手势,设置视图的center
停止之后的减速利用velocityInView来获取停止时候的速度。

代码:
CGPoint velocity = [panGestureRecognizer velocityInView:self.view];

CGFloat vMax = 3000;
CGFloat distanceMax = 60;

CGFloat finalDisX = (velocity.x * distanceMax)/vMax;
CGFloat finalDisY = (velocity.y * distanceMax)/vMax;

CGFloat finalDisXY = sqrtf((finalDisX * finalDisX) + (finalDisY* finalDisY));

CGPoint finalPoint = CGPointMake(panGestureRecognizer.view.center.x + finalDisX, panGestureRecognizer.view.center.y + finalDisY);

finalPoint.x = MIN(MAX(finalPoint.x, 0), self.view.bounds.size.width);

finalPoint.y = MIN(MAX(finalPoint.y, 0), self.view.bounds.size.height);

[UIView animateWithDuration:finalDisXY/distanceMax delay:0 options:UIViewAnimationOptionCurveEaseOut | UIViewAnimationOptionAllowUserInteraction animations:^{
   
    panGestureRecognizer.view.center = finalPoint;
   
} completion:nil];

10、系统如何寻找到需要响应用户操作的那个Responder?

响应对象(Responder):是具有响应和处理事件能力的对象,UIResponder是所有响应对象的基类。
用户点击屏幕 ->
检测到触摸的事件将其打包成UIEvent对象,并放入当前活动的UIApplication事件队列中 ->
取出该事件交给UIWindow来处理 ->
window首先使用hitTest:withEvent寻找此次touch操作的初始点所在的视图view ->
次方法会调用pointInside:withEvent判断点击点是否在视图内以确定是不是点击了当前视图 ->
如果点击的是当前的视图以此类推遍历这个视图的子视图 ->
直到找到这个touch操作所在的位置

11、多屏幕尺寸的适配

自动布局,使用Masonry

12、UIButton的父类是什么?UILabel呢?

UIButton:父类 UIControl,UIControl的父类是UIView,多了一层,在UIControl中多了一些交互事件的处理。

UILabel:父类 UIView

19.UIButton从子类到父类一次继承自:( D )

A. UIView-> UIViewController->UIController
B. UIResponder-> UIControl-> UIView
C. UIControl-> UIResponder->UIViewController
D. UIControl-> UIView-> UIResponder

13、push view controller 和 present view controller的区别?

使用Push,必须要求当前的控制器在UINavigationController里面,present只要求当前是一个UIViewControler就行。
push是基于当前逻辑操作上叠加一个新的控制器
present一般是临时性的弹出一个界面

14、描述下tableview cell的重用机制

重用通过每个cell指定一个重用标识reuseIdentity,即指定单元格的种类,当cell滚出屏幕的时候,会将滚出屏幕的单元格放入重用的queue中,当某个未在屏幕上显示的单元格要显示的时候,就会从这个queue中取出进行重新,通过重用cell可以达到节省内存给的目的。

15.在UIKit中,frame与bounds的关系是( C )

A. frame是bounds的别名
B. frame是bounds的继承类
C. frame的参考系是父规图坐标, bounds的参考系是自身的坐标
D.frame的参考系是自身坐标,bounds的参考系是父规图的坐标

15、UIView的frame和bounds的区别是什么

frame是参照父视图的坐标系统
bounds是本身的坐标系统。原点是(0,0)

140.Storyboard or Xib, whichdoyouprefer?And why
Storyboard和Xib你喜欢哪个为什么?

答:
(附网址:http://blog.mobilejazz.com/storyboards-xibs-best-practices/)

喜欢哪个大家可以选择:以下是Storyboard和Xib的优缺点可以参考以下

以下解释是英文翻译过来的(建议大家可以看网址原文章)

xibs是XML文件定义和配置的一组对象,并专门操纵主要观点(UIView子类)。Xcode具有友好的编辑器,可以显示这些意见,它是一个运行的应用程序,使得它的配置和设计布局非常容易(节省很多行代码)。

即使大多数开发商关联一个xib文件“屏幕”或“视图控制器”,是通用的容器xibs对象和可以一起使用,与其它类型的对象作为nsobjects UIViews,或者只是一个人。

Storyboard

这是一个XML文件代表统一一套xibs为了定义导航之间的一组视图控制器。有一个故事一个开发者可以定义多个“屏幕”(或和导航之间的UIViewController类)他们。

作为一个差的XIB,故事是由定义的视图和导航处理他们之间。

xibs VS代码的好处
视图的可视化配置。
自动布局的视觉形态。
大小班的视觉形态。
节省时间和代码,而“创建实例”。
节省时间和代码,而“配置对象”。
快速UITableViewCell原型。
快速配置控制动作(ibactions)。
Storyboard与xibs效益
可以直接在脚本本身原型的行。
可以定义静态表格部分和行。
可以使用自动布局约束添加到toplayoutguide和bottomlayoutguide。
可以指定导航和过渡(这是一个主要的目的!)。
可以定义多个“屏幕”(处理的)在一个地方(不需要多xibs)。
对xibs VS代码不便
容易破裂合并时(GIT)。
不复杂的自动布局定义。
不能引用(或包括)其他xibs。
Storyboard与代码xibs不便
故事情节是:大文件的加载速度慢,有时。
需要一个大屏幕显示所有(或部分)的内容。
容易破裂合并时(GIT)。
高概率打破它时,合并(GIT)作为故事包含很多信息。
许多警告当支持iOS 7和iOS 8(利润,大小班)。
不能引用(或包括)其他xibs。

157.Storyboard or Xib, which do you prefer ? And why ?
你喜欢使用Storyboard还是Xib?为什么?

详细解释:“http://stackoverflow.com/questions/13834999/storyboards-vs-the-old-xib-way”

答案: The reasons for liking Storyboard:There are things you can do with

a storyboard that you can't do with a nib. A storyboard lets you create segues

between view controllers, and it lets you design table view cells in-place. One

big advantage of Storyboard is the ability to view your entire application's

GUI(图形用户界面)flow. Zoom out and you can see how everything interconnects andflows.

The reasons for liking Xib : There are

things you can do with a nib that you can't do with a storyboard. In a nib, you

can create references to the File's Owner placeholder(占位符). You

can create multiple top-level views, edit them, and create connections between

them. See this answer for an example of why you'd want to do that. You can add

external(外部的)object placeholders (a rarely-used feature很少使用的功能).

1.Apartfrom the historical value in the xib approach, xib's also provide modularity.Perhaps you have a library of code or wish to share a useful widget you made.Taking the xib approach would facilitate that sharing and reuse.

2.Thexib approach also allows you some greater flexibility in terms of your owncode.

3.Withxibs, while the modularity is nice, it's tougher to envision how everythingconnects and flows together. This can be a useful feature for yourself, or ifyou have a larger team to share with, to allow others to see how the app flows.

8.遇到过 Unable to simultaneously satisfy constraints 错误吗?描述一下。

约束问题,约束冲突了

66.控制器View的生命周期及相关函数是什么?你在开发中是如何用的?

1.在视图显示之前调用viewWillAppear;该函数可以调用多次;
2.视图显示完毕,调用viewDidAppear;
3.在视图消失之前调用viewWillDisAppear;该函数可以调用多次(如需要);
4.在布局变化前后,调用viewWill/DidLayoutSubviews处理相关信息;

06-面试题
Web note ad 1