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控件:

UI控件.png

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

  • UIKit坐标系:
    1. 坐标系的原点(0,0)在屏幕的左上角
    2. x值向右正向延伸
    3. y值向下正向延伸
UIKit坐标系.png

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;
        

矩形的坐标系转换

坐标系转换.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 <NSObject> 中的方法
      // 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)
    
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容