iOS--frame和bounds

坐标系

首先,我们来看一下iOS特有的坐标系,在iOS坐标系中以左上角为坐标原点,往右为X正方向,往下是Y正方向如下图:

bounds和frame都是属于CGRect类型的结构体,系统的定义如下,包含一个CGPoint(起点)和一个CGSize(尺寸)子结构体。

objectivec 复制代码
struct CGRect {
    CGPoint origin;
    CGSize size;
};

origin决定了view的起点,size决定View的尺寸。

frame

frame是每个view必备的属性,表示view在父view坐标系统中的位置和大小,参照点是父视图的坐标系统。

示例代码:

objectivec 复制代码
UIView *viewA = [[UIView alloc] initWithFrame:CGRectMake(50, 50, 300, 300)];
[viewA setBackgroundColor:[UIColor blueColor]];
[self.view addSubview:viewA];
NSLog(@"viewA - %@",NSStringFromCGRect(viewA.frame));

UIView *viewB = [[UIView alloc] initWithFrame:CGRectMake(50, 50, 200, 200)];
[viewB setBackgroundColor:[UIColor yellowColor]];
[viewA addSubview:viewB];
NSLog(@"viewB - %@",NSStringFromCGRect(viewB.frame));

UIView *viewC = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
[viewC setBackgroundColor:[UIColor redColor]];
[self.view addSubview:viewC];
NSLog(@"viewC - %@",NSStringFromCGRect(viewC.frame));

以上可以看出,viewB和viewC的起点重合,但是从打印结果来看,viewB的起点为(50,50),而viewC的起点为(100,100)。原因就是frame中的位置是以父视图的坐标系为标准来确定当前视图的位置,viewB的父视图为viewA,viewC的父视图为self.view,而由于viewA的起点为(50,50),所以viewB与viewC起点才会重合。

bounds

objectivec 复制代码
    UIView *viewA = [[UIView alloc] initWithFrame:CGRectMake(50, 50, 300, 300)];
    [viewA setBackgroundColor:[UIColor blueColor]];
    [self.view addSubview:viewA];
    NSLog(@"viewA - %@",NSStringFromCGRect(viewA.bounds));

    UIView *viewB = [[UIView alloc] initWithFrame:CGRectMake(50, 50, 200, 200)];
    [viewB setBackgroundColor:[UIColor yellowColor]];
    [viewA addSubview:viewB];
    NSLog(@"viewB - %@",NSStringFromCGRect(viewB.bounds));

    UIView *viewC = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
    [viewC setBackgroundColor:[UIColor redColor]];
    [self.view addSubview:viewC];
    NSLog(@"viewC - %@",NSStringFromCGRect(viewC.bounds));

bounds也是每个view都有的属性,这个属性我们一般不进行设置,表示view在本地坐标系统中的位置和大小。参照点是本地坐标系统。如果我们对上例打印bounds,将会得到以下结果:

因为我们并没有设置bounds值,那么,bounds到底有什么作用呢。这里强调,每个视图都有自己的坐标系,且这个坐标系默认以自身的左上角为坐标原点,所有子视图以这个坐标系的原点为基准点。bounds的位置代表的是子视图看待当前视图左上角的位置,bounds的大小代表当前视图的大小。原则如下:

  • 更改bounds中的位置对于当前视图没有影响,相当于更改了当前视图的坐标系,对于子视图来说当前视图的左上角已经不再是(0,0), 而是改变后的坐标,坐标系改了,那么所有子视图的位置也会跟着改变。
  • 更改bounds的大小,bounds的大小代表当前视图的长和宽,修改长宽后,中心点继续保持不变, 长宽进行改变,通过bounds修改长宽看起来就像是以中心点为基准点对长宽两边同时进行缩放

两者的区别

origin的区别

此时,如果我们把ViewA的bounds改为(0,100),结果如下:

我们始终要清楚,bounds的位置代表的是子视图看待当前视图左上角的位置。 bounds遵守的原则一中,更改bounds中的位置对于当前视图(ViewA)没有影响,相当于更改了ViewA的坐标系,但是子视图(ViewB)不同,对于ViewB来说ViewA的左上角已经不再是(0,0), 而是(0,100),所以对于ViewB来说,ViewA坐标系的原点其实是在红色箭头所指处的上方100处,而此时ViewB的frame.origin为(200,100),所以ViewB的上边与ViewA上边重合。

如果我们更改ViewA的bounds为(200,0),同理(可以自己思考试试),结果如下

size的区别

frame的size直接决定了view的大小,而bounds的size修改后,view的中心点不变,长宽以中心点进行缩放

objectivec 复制代码
    UIView *viewA = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 240)];
    [viewA setBackgroundColor:[UIColor grayColor]];
    [self.view addSubview:viewA];

    UIView *viewB = [[UIView alloc] initWithFrame:CGRectMake(100, 50, 160, 120)];
    [viewB setBackgroundColor:[UIColor blueColor]];
    [viewA addSubview:viewB];

    //viewB设置size(320,160)
    [viewB setBounds:CGRectMake(0, 0, 320, 240)];

设置了size之后的结果,viewB左上点距离viewA显然不为(100,50),而是进行了基于viewB视图中心点的缩放操作

总结

  • frame不管对于位置还是大小,改变的都是自己本身。
  • frame的位置是以父视图的坐标系为参照,从而确定当前视图在父视图中的位置。
  • frame的大小改变时,当前视图的左上角位置不会发生改变,只是大小发生改变。
  • bounds改变位置时,改变的是子视图的位置,自身没有影响;其实就是改变了本身的坐标系原点,默认本身坐标系的原点是左上角。
  • bounds的大小改变时,当前视图的中心点不会发生改变,当前视图的大小发生改变,看起来效果就像缩放一样。
相关推荐
用户0917 小时前
Flutter构建速度深度优化指南
android·flutter·ios
namehu21 小时前
搞定 iOS App 测试包分发,也就这么简单!😎
前端·ios·app
用户091 天前
如何避免写垃圾代码:iOS开发篇
ios·swiftui·swift
HarderCoder2 天前
iOS 知识积累第一弹:从 struct 到 APP 生命周期的全景复盘
ios
goodSleep2 天前
更新Mac OS Tahoe26用命令恢复 Mac 启动台时不小心禁用了聚焦搜索
macos
叽哥2 天前
Flutter Riverpod上手指南
android·flutter·ios
用户093 天前
SwiftUI Charts 函数绘图完全指南
ios·swiftui·swift
YungFan3 天前
iOS26适配指南之UIColor
ios·swift
权咚4 天前
阿权的开发经验小集
git·ios·xcode
用户094 天前
TipKit与CloudKit同步完全指南
ios·swift