iOS学习笔记(1)— UIView 渲染和内容管理

 iOS学习笔记(1)— UIView 渲染和内容管理 

  iOS中应用程序基本上都是基于MVC模式开发的。UIView就是模型-视图-控制器中的视图,在iOS终端上看到的、摸到的都是UIView。

  UIView在屏幕上定义了一个矩形区域和管理区域内容的接口。在运行时,一个视图对象控制该区域的渲染;UIView继承自UIResponder,UIResponder是用来响应事件的类,UIView也具有响应事件的能力。所以说UIView具有三个基本的功能,绘制内容并管理内容的布局,响应用户交互,动画。正是因为UIView具有这些功能,它才能担当起MVC中视图层的作用。

  在开发中可以使用UIKit框架中已经提供的视图组件他们大多继承自UIView,当然也可以通过继承UIView定义自己的视图。只有主线程才能更新UI。UIKit框架内的组件包括UIView及其子类的所有操作都必须在主线程中进行,否则会出现不可预知的问题

  本章主要介绍渲染和内容管理。

  1、创建

  - (id)initWithFrame:(CGRect)frame; 通过frame创建一个view。

  2、几何属性

  视图对象使用frame, bounds和center属性来跟踪它的尺寸和位置:

  frame属性指定了在视图的尺寸和在父视图中的位置。

  center属性指定了视图的中点在父视图的位置。

  bounds属性指定了在视图本地坐标系统中视图的尺寸。默认原点是(0,0)。

  可以通过center和bounds计算得到frame,反之同理。frame.origin.x == center.x - bounds.size.width / 2; frame.size == bounds.size;

  transform属性用于视图的动画,通过操作transform实现视图的旋转、缩放。  

      - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event

      - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event

这两个方法主要用于响应者链, 用于确定视图是否为第一响应者,当用户碰触屏幕的时候系统会生成一个碰触点。为了确认哪个控件是这个碰触的第一响应者系统会调用window的hitTest方法,hitTest会调用pointInside方法判断触碰点是否在当前视图的区域内。如果在返回YES,则调用该试图所有子试图的hitTest方法;如果不在则返回NO,hitTest函数直接返回nil;如果pointInside方法返回YES,且没有子视图或者子视图的hiteTest方法返回都为nil则此视图为第一响应者,第一响应者是响应者链的开端

点转换方法。比如一个view上有一个button,在button上有一个点p,这个点相对button的坐标知道了,想知道这个点在这个view上的坐标,用这两个api去转换。

 - (CGPoint)convertPoint:(CGPoint)point toView:(UIView *)view

 [button convertPoint:p toView:view] 这样得到view上的点。

- (CGPoint)convertPoint:(CGPoint)point fromView:(UIView *)view

 [view convertPoint:p fromView:button];

同点转换方法,转换一个矩形

      - (CGRect)convertRect:(CGRect)rect toView:(UIView *)view

      - (CGRect)convertRect:(CGRect)rect fromView:(UIView *)view

  - (CGSize)sizeThatFits:(CGSize)size

  返回最合适的尺寸,size是首选尺寸。只返回尺寸不会改变尺寸,如一个UILabel的text发生改变,需要UILabel的bounds调整,调用这个方法会返回一个最合适的值。

  - (void)sizeToFit

  按照sizeThatFits的返回值重新设置视图的bounds。

autoresizingMask 当父视图的bounds发生改变时通过这个属性决定如何调整自己的frame。缺省的值为UIViewAutoresizingNone,表示当父视图bound变化时,自己相对于父视图的frame不变。可以通过|设置多个规则,如:UIViewAutoresizingFlexibleWidth  |   UIViewAutoresizingFlexibleHeight

  UIViewAutoresizingNone

  UIViewAutoresizingFlexibleLeftMargin   到屏幕左边的距离随着父视图的宽度按比例改变      

  UIViewAutoresizingFlexibleWidth  视图的宽度随着父视图的宽度按比例改变      

  UIViewAutoresizingFlexibleRightMargin   到屏幕右边的距离随着父视图的宽度按比例改变      

  UIViewAutoresizingFlexibleTopMargin  到屏幕顶部的距离随着父视图的高度按比例改变      

  UIViewAutoresizingFlexibleHeight     视图的高度随着父视图的高度等比例改变      

  UIViewAutoresizingFlexibleBottomMargin

  autoresizesSubviews 默认值YES,如果设置为NO那么该视图的所有直接子视图的frame自动调整行为将被忽略,也就是说无论子视图的autoresizingMask属性设置成什么都相当于置为UIViewAutoresizingNone。

  3、视图层次

  UIView除了提供自己的内容外,还可以作为一个视图容器。当一个视图包含其他视图时,就在两者之间建立了一个父子关系。在视觉上子视图隐藏了父视图的内容,如果一个子视图是完全不透明的,那么子视图所在区域就完全遮挡了父视图的相应区域。如果子视图是部分透明的那么两个视图在显示上就混合在了一起。父子视图关系也会影响一些视图行为,改变父视图的尺寸也会相应的改变子视图的尺寸。隐藏父视图,改变父视图的alpha值,转换父视图都会影响到子视图。

  UIView通过NSArray管理子视图的。通过属性subviews可以访问视图的所有子视图。通过NSArray的特点可以得出两点:

  (1)子视图的引用计数会+1,当父视图释放的时候子视图的引用计数-1。

  (2)子视图是有序的,后加入的子视图会叠在上一个子视图之上。

  - (void)addSubview:(UIView *)view 方法增加一个子视图。

  操作子视图的方法:

  - (void)insertSubview:(UIView *)view atIndex:(NSInteger)index                   在指定的层级插入一个子视图。最底层是0;最顶层就是subviews count,相当于addSubview

  - (void)insertSubview:(UIView *)b belowSubview:(UIView *)c                    b成为子视图并且在已有的子视图c的下面

  - (void)insertSubview:(UIView *)b aboveSubview:(UIView *)c                                   b成为子视图并且在已有的子视图c的上面

  - (void)exchangeSubviewAtIndex:(NSInteger)index1 withSubviewAtIndex:(NSInteger)index2  交换两个子视图的层级。

  - (void)bringSubviewToFront:(UIView *)view                                                                           视图上升到最顶层。

  - (void)sendSubviewToBack:(UIView *)view                                                                             视图下降到最底层

  - (void)removeFromSuperview                                                                                                 删除所有子视图。

  superview 属性用于获取当前视图的父视图。

  

  - (void)setNeedsLayout

  调用此方法通知系统view的内容需要重新绘制,会异步调用layoutSubviews方法,view会在下一个drawing周期绘制。如果只是简单的改变view的几何形状或者当前视图并没有在窗口中显示,系统可能不会调用layoutSubviews方法。

  - (void)layoutIfNeeded

  调用此方法通知系统view的内容需要重新绘制,调用layoutSubviews方法。与setNeedLayout不同的时不会等到下一个drawing周期,会立即调用layoutSubviews方法。

  - (void)layoutSubviews

  此方法的缺省实现是空。子类可以去重写此方法当需要更精确的subviews布局。当subviews的autoresizes行为不能满足要求时才去重写此方法。可以在实现中直接设置subviews的frame。此方法不能被直接调用。如果想要在下一个drawing周期去更新view布局,应该调用setNeedsLayout方法;如果想立即更新view的布局,应该调用layoutIfNeeded方法。

  layoutSubviews之所以不能被直接调用的原因可能是系统在绘制时需要进行一些操作并判断需不需要调用layoutSubviews的方法,并且在一次运行循环(run loop)中无论调用多少次setNeedsLayout都只调用一次layoutSubviews。这样就避免了资源的重复调用。

  

  4、渲染属性

  clipsToBounds 当值为YES时,子视图如果超过了当前视图的区域,超出的部分就会被裁掉。默认值是NO,也就是说当子视图显示区域大于主视图的时候还是正常显示不会裁剪。

  backgroundColor 设置背景色

  alpha 设置视图的透明度,当为1时不透明,为0时完全透明,既隐藏。默认值是1。alpha值会影响到子视图。如果想让父视图半透明而子视图不受影响可以设置父视图的backgroundColor = [UIColor colorWithWhite:0 alpha:0.8],这样就ok了。

  opaque 给绘图系统提供一个性能优化开关。如果该值为YES,那么绘图在绘制该视图的时候把整个视图当作不透明对待。这样,绘图系统在执行绘图过程中会优化一些操作并提升系统性能;如果是设置为NO, 绘图系统将其和其他内容平等对待,不去做优化操作。为了性能方面的考量,默认被置为YES(意味着‘优化’)。

  一个不透明视图需要整个边界里面的内容都是不透明的。基于这个原因,opaque设置为YES,要求对应的alpha必须为1.0。如果一个UIView实例opaque被设置为YES, 而同时它又没有完全填充它的边界(bounds),或者它包含了整个或部分的透明的内容视图,那么将会导致未知的结果。

  clearsContextBeforeDrawing  决定绘制前是否清屏,默认为YES。用于提高描画性能,特别是在可滚动的视图中。当这个属性被设置为YES时,UIKIt会在调用drawRect:方法之前,把即将被该方法更新的区域填充为透明的黑色。将这个属性设置为NO可以取消相应的填充操作,view中原有内容会保留。  

  hidden 视图是否隐藏,默认为NO(显示),YES为隐藏。

  contentMode 视图内容的填充方式。类型是UIViewContentMode。默认值是UIViewContentModeScaleToFill,填充到整个视图区域,不等比例拉伸。

  typedef NS_ENUM(NSInteger, UIViewContentMode) {
      UIViewContentModeScaleToFill,      填充到整个视图区域,不等比例拉伸
      UIViewContentModeScaleAspectFit,      长宽等比填充视图区域,当某一个边到达视图边界的时候就不再拉伸,保证内容的长宽比是不变的同时尽可能的填充视图区域。
      UIViewContentModeScaleAspectFill,      长宽等比填充视图区域,当某一个边到达视图边界的时候还继续拉伸,直到另一个方向达到视图边界。内容的长宽比不变的同时填满整个视图区域,不显示超过的部分。
      UIViewContentModeRedraw,                重绘视图边界
      UIViewContentModeCenter,                 视图居中
      UIViewContentModeTop,         视图顶部对齐
      UIViewContentModeBottom,                视图底部对齐
      UIViewContentModeLeft,                     视图左侧对齐
      UIViewContentModeRight,                   视图右侧对齐
      UIViewContentModeTopLeft,                视图左上角对齐
      UIViewContentModeTopRight,              视图右上角对齐
      UIViewContentModeBottomLeft,           视图左下角对齐
      UIViewContentModeBottomRight,         视图右下角对齐
  };

  以上模式中凡是没有带scale的,内容bounds超出视图bounds时只有未超出部分才在视图bounds中显示。

  下图很好的解释了contentMode效果

  

 

  autoresizingMask 当父视图的bounds发生改变时通过这个属性决定如何调整自己的frame。缺省的值为UIViewAutoresizingNone,表示当父视图bound变化时,自己相对于父视图的frame不变。可以通过|设置多个规则,如:UIViewAutoresizingFlexibleWidth  |   UIViewAutoresizingFlexibleHeight

 enum {
     UIViewAutoresizingNone                 = 0,
     UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,  到屏幕左边的距离随着父视图的宽度按比例改变
     UIViewAutoresizingFlexibleWidth        = 1 << 1,  视图的宽度随着父视图的宽度按比例改变
     UIViewAutoresizingFlexibleRightMargin  = 1 << 2,  到屏幕右边的距离随着父视图的宽度按比例改变
     UIViewAutoresizingFlexibleTopMargin    = 1 << 3,  到屏幕顶部的距离随着父视图的高度按比例改变
     UIViewAutoresizingFlexibleHeight       = 1 << 4, 视图的高度随着父视图的高度等比例改变
     UIViewAutoresizingFlexibleBottomMargin = 1 << 5
  };
  typedef NSUInteger UIViewAutoresizing;

  autoresizesSubviews 默认值YES,如果设置为NO那么该视图的所有直接子视图的frame自动调整行为将被忽略,也就是说无论子视图的autoresizingMask属性设置成什么都相当于置为UIViewAutoresizingNone。

  contentStretch 用于制定哪部分是可拉伸的,取值在 0.0到1.0之间。下面用一个例子解释contentStretch是如何工作的。

  [imageView setContentStretch:CGRectMake(150.0/300.0, 100.0/200.0, 10.0/300.0, 10.0/200.0)];

   image.png的大小是 200  x  150 ;

   mageView的frame是(0,0,300,200);

   150.0/300.0表示x轴上,前150个像素不进行拉伸。

   100.0/200.0表示y轴上,前100个像素不进行拉伸。

   10.0/300.0表示x轴上150后的10个像素(151-160)进行拉伸,直到image.png铺满imageView。

   10.0/200.0表示y轴上100后的10个像素(101-110)进行拉伸,直到image.png铺满imageView。

  

  - (void)setNeedsDisPlay

  调用此方法通知系统view需要重新绘制,会异步自动调用drawRect方法。

  - (void)setNeedsDisplayInRect:(CGRect)rect

  同样异步调用drawRect方法。在一次运行循环(run loop)中无论调用setNeedsDisplay或setNeedsDisplayInRect多少次,只调用drawRect一次。也是从减少资源开销的角度考虑的。

  - (void)drawRect:(CGRect)rect

  此方法的缺省实现是空。子类使用原生的绘制技术(Core Graphics and UIKit)绘制内容时应该重写此方法,在方法里面写出自己的drawing code。如果view设置自己的内容用其他的方法,则不需要去重写此方法。如果直接从UIView对象继承,实现此方法不需要call super。然而如果从其他的UIView对象继承,则应该调用super。当view 第一次显示或者当view的可视的一部分无效时,此方法会调用。此方法不能直接被调用。调用setNeedsDisplay 或者 setNeedsDisplayInRect: 方法会触发重新绘制

  

  5、视图绘制周期

  UIView类使用一个点播绘制模型来展示内容,当一个视图第一次出现在屏幕前,系统会要求它绘制自己的内容,在该流程中系统会创建一个快照,这个快照是出现在屏幕中得视图内容的可见部分。如果从来没有改变视图的内容,则这个视图绘制的方法drawRect可能永远都不会再被调用。这个快照图像在大部分涉及到视图的操作中被重用。

  如果改变了视图的内容,系统也不会马上重绘视图。使用setNeedsDisPlay或setNeedsDisplayInRect方法废除该视图同时让系统在稍后重绘视图。系统等待当前运行循环(run loop)结束,然后开始重绘。这个延迟可以用来废除多个视图、增加或删除视图、隐藏、重设大小。所有的改变会在稍后一起生效。

  什么是run loop?个人认为可以简单的理解为一个事件的处理过程。例如:用户点击屏幕会产生两个run loop。当用户按下的时候会产生一个run loop;当用户抬起的时候会产生另一个run loop。

  如果在一个run loop中调用setNeedsDisplayInRect方法,系统会保证在这个run loop结束前调用一次drawRect方法;无论在当前run loop调用setNeedsDisplayInRect方法多少次都只调用一次drawRect。setNeedsDisplayInRect方法如果在一个run loop中刷新不同的区域,最后drawRect方法会将这些区域组合起来一起刷新,组合原则用最小的矩形圈起来所有区域,并刷新这个区域。如果刷新区域是屏幕左下角和右上角两个点,有可能刷新整个屏幕。

  6、相关控件

  UIWindow

  UIWindow对象是所有UIView的父视图,UIWindow类是UIView的子类,可以看作是特殊的UIView。一般应用程序只有一个UIWindow对象,即使有多个UIWindow对象,也只有一个UIWindow可以接受到用户的触屏事件。UIWindow初始化在appDelegate里。 [self.window makeKeyAndVisible]方法,使window显示。

  UIScreen

  UIScreen用于获取设备屏幕的尺寸。

  [UIScreen mainScreen].bounds 获取整个屏幕的大小;当应用程序有状态栏时,返回值:{{0, 0}, {320, 480}}

  [UIScreen mainScreen].applicationFrame获取应用程序窗口的大小;当应用程序有状态栏时,返回值:{{0, 20}, {320, 460}}

  

  UIViewController

  UIViewController是MVC中的C控制器,控制管理UIView。UIViewController同UIView的关系相当于相框和相片的关系,相框可以操作相片、替换相片、决定相片的显隐藏,反之则不行。UIView工作在第一线,向用户展示表现的内容,并接受用户的交互;UIViewController负责两个方面,数据和行为,通过数据更新view,通过行为处理用户交互操作,注意这里是“处理”而不是“响应”。

  UIViewController同UIView一样继承与UIResponder,所以同样处于响应者链上,同样可以接收触碰等用户事件。

  可以通过下面的方法获得UIView所属的UIViewController

- (UIViewController*)viewController

{

        for (UIView* next = [self superview]; next; next = next.superview)

        {

                UIResponder* nextResponder = [next nextResponder];

         if ([nextResponder isKindOfClass:[UIViewController class]])

       {

             return (UIViewController*)nextResponder;

         }

    }

        return nil;

}               

 

时间: 2024-08-03 15:22:47

iOS学习笔记(1)— UIView 渲染和内容管理的相关文章

iOS学习笔记(2)— UIView用户事件响应

iOS学习笔记(2)— UIView用户事件响应 UIView除了负责展示内容给用户外还负责响应用户事件.本章主要介绍UIView用户交互相关的属性和方法. 1.交互相关的属性 userInteractionEnabled 默认是YES ,如果设置为NO则不响应用户事件,并且把当前控件从事件队列中删除.也就是说设置了userInterfaceEnabled属性的视图会打断响应者链导致该view的subview都无法响应事件. multipleTouchEnabled  默认是NO,如果设置为YE

IOS学习笔记 -- Modal和Quartz2D

一. Modal1.Modal的默认效果:新控制器从屏幕的最底部往上钻,直到盖住之前的控制器为止;Modal只是改变了View的现实,没有改变rootViewController 2.常用方法1>.以Modal的形式展示控制器- (void)presentViewController:(UIViewController *)viewControllerToPresent animated: (BOOL)flag completion:(void (^)(void))completion2>.关

iOS学习笔记-精华整理

iOS学习笔记总结整理 一.内存管理情况 1- autorelease,当用户的代码在持续运行时,自动释放池是不会被销毁的,这段时间内用户可以安全地使用自动释放的对象.当用户的代码运行告一段 落,开始等待用户的操作,自动释放池就会被释放掉(调用dealloc),池中的对象都会收到一个release,有可能会因此被销毁. 2-成员属性:     readonly:不指定readonly,默认合成getter和setter方法.外界毫不关心的成员,则不要设置任何属性,这样封装能增加代码的独立性和安全

Vue学习笔记入门篇——组件的内容分发(slot)

本文为转载,原文:Vue学习笔记入门篇--组件的内容分发(slot) 介绍 为了让组件可以组合,我们需要一种方式来混合父组件的内容与子组件自己的模板.这个过程被称为 内容分发 (或 "transclusion" 如果你熟悉 Angular).Vue.js 实现了一个内容分发 API,使用特殊的 'slot' 元素作为原始内容的插槽. 编译作用域 在深入内容分发 API 之前,我们先明确内容在哪个作用域里编译.假定模板为: <child-component> {{ messa

iOS学习笔记之UITableViewController&amp;UITableView

iOS学习笔记之UITableViewController&UITableView 写在前面 上个月末到现在一直都在忙实验室的事情,与导师讨论之后,发现目前在实验室完成的工作还不足以写成毕业论文,因此需要继续思考新的算法.这是一件挺痛苦的事情,特别是在很难找到与自己研究方向相关的文献的时候.也许网格序列水印这个课题本身的研究意义就是有待考证的.尽管如此,还是要努力的思考下去.由于实验室的原因,iOS的学习进度明显受到影响,加之整理文档本身是一件耗费时间和精力的事情,因此才这么久没有写笔记了. M

ios学习笔记图片+图片解释(c语言 oc语言 ios控件 ios小项目 ios小功能 swift都有而且笔记完整喔)

下面是目录其中ios文件夹包括了大部分ios控件的介绍和演示,swift的时完整版,可以学习完swift(这个看的是swift刚出来一周的视频截图,可能有点赶,但是完整),c语言和oc语言的也可以完整的学习完所需知识,,其他文件夹的内容如其名说描述一样 没张图片都有文字说明,可以需要该功能的时候搜索一下然后打开图片就可以学习到 网盘下载地址:需要的话给留言我再传上去 http://www.cnblogs.com/langtianya原创 ios学习笔记图片+图片解释(c语言 oc语言 ios控件

iOS学习笔记---c语言第九天

高级指针 指向结构体变量的指针,称为结构体指针 可以使用->指向内容. %p打印地址 void pLenth(cPoint *p1,cPoint *p2) //求两点间的距离  用的开方函数sqrt()和平方函数pow(,) { float a = sqrt(pow((p1->x-p2->x), 2)+pow((p1->y-p2->y), 2)); printf("两点距离为%.2f\n",a); } //main.m中代码 #import <Fou

iOS: 学习笔记, 用代码驱动自动布局实例

iOS自动布局是设置iOS界面的利器. 本实例展示了如何使用自动布局语言设置水平布局, 垂直布局 1. 创建空白iOS项目 2. 添加一个控制器类, 修改YYAppDelegate.m文件 #import "YYAppDelegate.h" #import "YYViewController.h" @implementation YYAppDelegate - (BOOL)application:(UIApplication *)application didFin

iOS学习笔记(4) — UITableView的 重用机制

iOS学习笔记(4) — UITableView的 重用机制 UITableView中的cell是动态的,在使用过程中,系统会根据屏幕的高度(480)和每个cell的高度计算屏幕中需要显示的cell的个数.比如,cell高度为90.那么480 / 90 = 5 + 1,也就是说最多有6个cell能显示在屏幕中. 系统会创建1个cel池,无论tableview有多少行都只创建6个cell放在池中.当某行移出屏幕的时候,将这个cell放回在池中:当某行需要显示在屏幕中时,从池中取出一个cell. 重