UIView

UIView(控件)

  • 功能一:界面显示
    1. 屏幕上显示的所有UI元素都叫做控件,也有人叫做视图、组件;按钮(UIButton)、文本(UILabel)都是控件;
    2. 所有的控件最终都继承自UIView
    3. UI控件能够展示的原因是因为有一个layer层

  • 功能二:事件响应
    1. UIView继承自UIResponder。UIResponder继承自NSObject
    2. 大部分UI开头的类都是继承自UIResponder,UIResponder具有事件响应的能力,这是它们能够处理事件的原因

  • 注意
    1. UI控件没有显示原因总结:
    1. 没有尺寸位置;
    2. 尺寸位置超出视图;
    3. 没有颜色或图片等显示要素;
    4. 没添加到当前窗口或视图中;
    5. 透明(alpha = 0 || opapue = 1);
    6. 其他控件或窗口挡住了;
    7. 按钮、segmentedControl等直接设置属性,而没有分状态设置;
    2. 大部分UI控件通过alloc.init创建都没有尺寸,颜色等;开关,菊花控件、toolBar控件、导航条控件、标签条控件等除外

UIKit中可能用得上的UI控件:

UIView_第1张图片
UI控件.png

UIkit坐标系(UI控件位置、尺寸的基础)

  • UIKit坐标系:
    1. 坐标系的原点(0,0)在屏幕的左上角
    2. x值向右正向延伸
    3. y值向下正向延伸
UIView_第2张图片
UIKit坐标系.png
  • iOS坐标系扩展阅读1
  • iOS坐标系扩展阅读2

UIView位置、尺寸属性

  1. frame:控件矩形框在父控件中的位置和尺寸
    1. 以父控件的左上角为坐标原点

    view.frame = CGRectMake(0, 0, 100, 100);
    
  2. bonuds:控件矩形框的尺寸及在内容层中的位置
    1. 修改bounds的尺寸是以中心点为基准伸缩的
    2. 自己左上角'内容层原点'为坐标原点,内容层是无限大的
    3. bonuds的x,y修改,实际上是影响可视化区域在内容层中的位置。
    4. 可视化区域,参照父控件,位置是不变。所以修改x,y,可以视作是内容层反向移动,有如下规律:
    1. bounds x > 0,表示需要看右边内容,可视区域相对内容层原点往右边走,我们看到的效果,内容层原点往左移动
    2. bounds y > 0,表示需要看下边内容,可视区域相对内容层原点往下边走,我们看到效果,内容层原点往上移动

    view.bounds = CGRectMake(0, 0, 100, 100);
    
  • center:控件中心点的位置
    1. 以父控件的左上角为坐标原点
    2. 注意:在设置center时,先确定尺寸,否则尺寸不确定,center定位不准,布局控件时应:
    1. 先设置frame后设置center.
    2. 或通过设置bonuds,这样则不需要区分先后
    view.center = CGPointMake(100, 100);
    

UIView位置、尺寸属性的修改

以frame的修改为例(位置,尺寸的修改可参照frame的修改)
  1. 不能直接修改->OC对象的结构体属性的成员
    // imageView.frame.size.width = 100; // 不允许
    
  • 方案一:通过CGRectMake函数(常用)

    imageView.frame = CGRectMake(100, 100, 200, 200);
    
  • 方案二:利用临时结构体变量(常用)

    • 使用场景:针对结构体中某个变量的修改
    • C结构体的修改方式
      CGRect tempFrame = imageView.frame;
      tempFrame.origin.x = 100;
      imageView.frame = tempFrame;
      
  • 方案三:使用大括号{}形式(不常用)

    • C结构体的修改方式
      imageView.frame = redView.frame = (CGRect){(100, 100), (100, 100)};;
      
  • 小结:开发中经常需要设置或获取UIView尺寸属性,建议增加UIView(Rect)的分类,提高效率,步骤如下;
    1. 创建Rect分类文件(UIView的分类),在.h文件通过@property声明x,y,width,height,centerX,centerY,top,bottom,leading,trailing属性;
    1. 分类不能定义属性,这里使用@property仅是声明set、get方法,不会生成下划线成员属性并实现set、get方法
    2. 实现set方法;
    3. 实现get方法;
    4. 建议UIView尺寸属性名都带上类前缀,如zq_x、zq_top等,避免以后会与原生或其他框架冲突

    .h文件
    @interface UIView (Rect)
    
    /** 生成set、get方法声明 */
    @property(nonatomic,assign) CGFloat zq_x;
    @property(nonatomic,assign) CGFloat zq_y;
    @property(nonatomic,assign) CGFloat zq_width;
    @property(nonatomic,assign) CGFloat zq_height;
    @property(nonatomic,assign) CGFloat zq_centerX;
    @property(nonatomic,assign) CGFloat zq_centerY;
    @property(nonatomic,assign) CGFloat zq_top;
    @property(nonatomic,assign) CGFloat zq_leading;//左
    @property(nonatomic,assign) CGFloat zq_bottom;
    @property(nonatomic,assign) CGFloat zq_trailing;//右
    
    .m文件(仅以代表举例)
    #pragma mark - x set、get方法实现
    - (void)setZq_x:(CGFloat)zq_x
    {
        CGRect frame = self.frame;
        frame.origin.x = zq_x;
        self.frame = frame;
    }
    - (CGFloat)zq_x
    {
        return self.frame.origin.x;
    }
    
    #pragma mark - leading set、get方法实现
    - (void)setZq_leading:(CGFloat)zq_leading
    {
        self.zq_x = zq_leading;
    }
    
    - (CGFloat)zq_leading
    {
        return self.zq_x;
    }
    
    #pragma mark - bottom set、get方法实现
    - (void)setZq_bottom:(CGFloat)zq_bottom
    {
        self.zq_y = zq_bottom - self.zq_height;
    }
    
    - (CGFloat)zq_bottom
    {
        return self.zq_y + self.zq_height;
    }
    

UIView的transform(形变)属性

  • 控件的形变包括平移、缩放、旋转等属性,类型是CGAffineTransform
  • 常用形变方式举例,效果以Scale(伸缩)为例说明,假设宽高都是100:
    • 方式一:CGAffineTransformMakeXXX:每次从零开始形变,每次都是相对于最开始的状态形变

      • 效果说明:重复执行代码,每次宽高都是从(100,100)->(50,50)
        self.view.transform = CGAffineTransformMakeTranslation(100, 100);
        self.view.transform = CGAffineTransformMakeScale(0.5, 0.5);
        self.view.transform = CGAffineTransformMakeRotation(M_PI);
        
    • 方式二:CGAffineTransformXXX:接着上次的状态形变

      • 效果说明:重复执行代码,宽高是从(100,100)->(50,50)->(25,25)->...
        self.view.transform = CGAffineTransformTranslate(self.view.transform, 100, 100);
        self.view.transform = CGAffineTransformScale(self.view.transform, 0.5, 0.5);
        self.view.transform = CGAffineTransformRotate(self.view.transform, M_PI);
        
    • 方式三:清空形变

      • 效果说明:宽高从(?,?)-> (100, 100)
        self.view.transform = CGAffineTransformIdentity;
        

矩形的坐标系转换

UIView_第3张图片
坐标系转换.png
  1. 坐标系:
    • UIKit坐标系 :以屏幕的左上角为坐标原点
    • view1坐标系 : 以view1的左上角为坐标原点
    • view2坐标系 : 以view2的左上角为坐标原点
  • 转换过程:
    1. 判断旧坐标系,确定旧坐标系原点;
    2. 根据rect判断矩形在坐标系中的位置,可借助UIKit坐标系中转;
    3. 确定新坐标系,确定旧坐标系原点,判断矩形在坐标系中的位置
  • 必须注意:rect不是控件,与控件无关系!只代表一块区域
    convertRect: toView:为例;
    // 0.基础数据:UIKit坐标系下,view1,view2的坐标
    view1.frame = CGRectMake(100, 100, 100, 100);
    view2.frame = CGRectMake(200, 200, 100, 100);
    -------------------------------------------------
    
    // 1.从零点矩形看坐标转换
    // 不代表UIKit坐标系左上角的点!
    CGRect zeroRect = CGRectZero; // {(0,0),(0,0)}
    
    CGRect newRect1 = [view1 convertRect:zeroRect toView:view2];
    // view1坐标系-> view2坐标系
    // UIKit坐标系下,矩形位置为{(100,100),(0,0)},view2坐标系左上角为{(200,200),(0,0)}
    // newRect1{(-100, -100), (0, 0)}
    
    CGRect newRect2 = [view2 convertRect:zeroRect toView:view1];
    // view2坐标系-> view1坐标系
    // UIKit坐标系下,矩形位置为{(200,200),(0,0)},view1坐标系左上角为{(100,100),(0,0)}
    // newRect2:{(100,100),(0,0)}
    
    -------------------------------------------------
    // 2.rect={(100, 100, 100, 100)}看坐标转换
    // 不代表UIKit坐标系中view1控件!
    
    rect1 = redView.frame = CGRectMake(100, 100, 100, 100);
    
    CGRect newRect3 = [view1 convertRect:rect1 toView:view2];
    // view1坐标系-> view2坐标系
    // UIKit坐标系下,矩形位置为{(200,200),(100,100)},view2坐标系左上角为{(200,100),(200,100)}
    // newRect3 = {(0, 0), (100, 100)}
    
    CGRect newRect4 = [view2 convertRect:zeroRect toView:view1];
    // view2坐标系-> view1坐标系
    // UIKit坐标系下,矩形位置为{(300,300),(100,100)},view1坐标系左上角为{(100,100),(100,100)}
    // newRect6 = {(200, 200), (100, 100)}
    
  • 矩形的坐标转换另一个方法的原理与上述一致,只是fromView才是转换前坐标系,不再多论。
    // 结果一致
    CGRect newRect = [view1 convertRect:rect1 fromView:view2];
    CGRect newRect = [view2 convertRect:rect1 toView:view1];
    
  • 点得坐标系转换可参照上述原理,使用方法是:
    CGPoint point = CGPointMake(100, 100);
    // 结果一致
    [view1 convertPoint:point toView:view2];
    [view2 convertRect:point fromView:view1];
    

常用坐标系转换 -> 获得控件在主窗口(屏幕)中的区域(rect)

  1. 坐标系转换:控件的坐标系 -> 主窗口的坐标系
    1. 控件的坐标系:
    1. 父控件坐标系 :以父控件的左上角为坐标原点(常用)
    2. 自身坐标系 :以控件自身内容层的左上角为坐标原点(常用)
    2. 主窗口的坐标系:即UIKit坐标系
  • 注意:在控件的坐标系下,rect描述的必须是控件所在区域;存在规律:
    1. 若以自身左上角为转换前坐标系原点,rect为self.bounds;
    2. 若以父控件左上角为转换前坐标系原点,rect为self.frame
  • 获取写法概况如下:
    // 这里为使显示简洁,定义KEY_WINDOW为主窗口
    #define KEY_WINDOW ([UIApplication sharedApplication].keyWindow)
    
    1. 写法一(推荐):
      // convertRect: toView:
      // 第一个参数(这里是KEY_WINDOW)不能设置为nil
      // redView.bounds的x、y设置不影响转换结果
      
      CGRect newRect = [redView convertRect:redView.bounds toView:KEY_WINDOW];
      CGRect newRect = [redView.superview convertRect:redView.frame toView:KEY_WINDOW];
      
    • 等价于:
      // 规律:交换写法一中第一与第三参数,由toView->fromView
      // redView.bounds的x、y设置不影响转换结果
      
      // convertRect: fromView:
      CGRect newRect = [KEY_WINDOW convertRect:redView.bounds fromView:redView];
      CGRect newRect = [KEY_WINDOW convertRect:redView.frame fromView:redView.superview];
      
    • 等价于(假设redView或其父控件加入的是主窗口):
      // redView.bounds的x、y设置不影响转换结果
      CGRect newRect = [redView convertRect:redView.bounds toView:nil];
      CGRect newRect = [redView.superview convertRect:redView.frame toView:nil];
      // 最后一个参数可以设置(toView:nil)->表示以自身所在的窗口(self.window)的左上角为坐标系原点;
      // 尽管可以省略,开发中建议标明转换的坐标系,而不是设置为nil,这是因为:
      // self.window大部分情况下是keyWindow;但如果控件是加入到其他窗口中(如状态栏等),则不是keyWindow
      // 若是比较两矩形间、点与矩形的关系,此时需要保证转换后的坐标系必须是相同的坐标系;
      
    • 等价于:
      // 通过 @protocol UICoordinateSpace  中的方法
      // CoordinateSpace:坐标空间(系)
      
      CGRect newRect = [redView convertRect:redView.bounds toCoordinateSpace:KEY_WINDOW];
      CGRect newRect = [KEY_WINDOW convertRect:redView.bounds fromCoordinateSpace:redView];
      
      CGRect newRect = [redView.superview convertRect:redView.frame toCoordinateSpace:KEY_WINDOW];
      CGRect newRect = [KEY_WINDOW convertRect:redView.frame fromCoordinateSpace:redView.superview];
      
    • 不等价于
      // 正确转换(写法一):
      CGRect newRect = [redView convertRect:redView.bounds toView:KEY_WINDOW];
      CGRect newRect = [redView.superview convertRect:redView.frame toView:KEY_WINDOW];
      
      // 伪转换 -> 描述的区域不是redView
      CGRect newRect = [redView convertRect:redView.frame toView:KEY_WINDOW];
      CGRect newRect = [redView.superview convertRect:redView.bounds toView:KEY_WINDOW];
      
      // 正确转换(写法二):
      CGRect newRect = [KEY_WINDOW convertRect:redView.bounds fromView:redView];
      CGRect newRect = [KEY_WINDOW convertRect:redView.frame fromView:redView.superview];
      
      // 伪转换 -> 描述的区域不是redView
      CGRect newRect = [KEY_WINDOW convertRect:redView.frame fromView:redView];
      CGRect newRect = [KEY_WINDOW convertRect:redView.bounds fromView:redView.superview];
      
      // 伪转换 -> 逻辑错误
      // 是获取redView坐标系下区域为rect的矩形在KEY_WINDOW坐标系下的区域newRect,而不是相反
      CGRect newRect = [KEY_WINDOW convertRect:redView.bounds toView:redView];
      CGRect newRect = [KEY_WINDOW convertRect:redView.frame toView:redView.superview];
      CGRect newRect = [KEY_WINDOW convertRect:redView.frame toView:redView];
      CGRect newRect = [KEY_WINDOW convertRect:redView.bounds toView:redView.superview];
      

坐标系转换应用 -> 矩形区域(Rect)、点(Point)比较

  • 不同坐标系下的区域(点)转换成同一个坐标系下的区域(点)时,可以进行区域间关系判断及区域与点的关系判断
  • 判断rect1是否包含了rect2
    bool CGRectContainsRect(CGRect rect1, CGRect rect2)
    
  • 判断rect1和rect2是否有重叠
    bool CGRectIntersectsRect(CGRect rect1, CGRect rect2)
    
  • 判断point是不是在rect上
    bool CGRectContainsPoint(CGRect rect1, CGRect rect2)
    

你可能感兴趣的:(UIView)