「iOS」————frame和bounds

发布于:2025-08-17 ⋅ 阅读:(17) ⋅ 点赞:(0)

iOS学习


我们经常听到,frame的修改是相对的,bounds是绝对的。
具体来说是什么意思呢?我们来用一个小demo演示一下:

#import "ViewController.h"

@interface ViewController ()

// Views for the demo
@property (nonatomic, strong) UIView *containerView;
@property (nonatomic, strong) UIView *childView;
@property (nonatomic, strong) UILabel *infoLabel;
@property (nonatomic, assign) CGAffineTransform initialTransform;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    self.view.backgroundColor = [UIColor whiteColor];
    
    self.containerView = [[UIView alloc] initWithFrame:CGRectMake(50, 120, 280, 280)];
    self.containerView.backgroundColor = [UIColor lightGrayColor];
    self.containerView.layer.borderWidth = 2.0;
    self.containerView.layer.borderColor = [UIColor blackColor].CGColor;
    
    self.containerView.clipsToBounds = YES;
    [self.view addSubview:self.containerView];
    self.initialTransform = self.containerView.transform; // Save initial transform state

    self.childView = [[UIView alloc] initWithFrame:CGRectMake(10, 10, 80, 80)];
    self.childView.backgroundColor = [UIColor colorWithRed:1.0 green:0.2 blue:0.2 alpha:0.9];
    [self.containerView addSubview:self.childView];

    self.infoLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, self.view.bounds.size.height - 320, self.view.bounds.size.width - 40, 150)];
    self.infoLabel.numberOfLines = 0;
    self.infoLabel.font = [UIFont monospacedSystemFontOfSize:12 weight:UIFontWeightRegular];
    self.infoLabel.text = @"点击下方按钮查看效果。";
    [self.view addSubview:self.infoLabel];
    
    [self setupButtons];
    
    [self updateInfoLabel:@"初始状态"];
        

- (void)setupButtons {
    CGFloat buttonWidth = 160;
    CGFloat buttonHeight = 40;
    CGFloat startY = self.view.bounds.size.height - 160;
    
    UIButton *frameButton = [self createButtonWithTitle:@"改变子视图Frame" frame:CGRectMake(30, startY, buttonWidth, buttonHeight) action:@selector(changeChildFrame)];
    [self.view addSubview:frameButton];
    
    UIButton *boundsButton = [self createButtonWithTitle:@"改变容器Bounds" frame:CGRectMake(self.view.bounds.size.width - buttonWidth - 30, startY, buttonWidth, buttonHeight) action:@selector(changeContainerBounds)];
    [self.view addSubview:boundsButton];
    
    UIButton *rotateButton = [self createButtonWithTitle:@"旋转容器" frame:CGRectMake(30, startY + 50, buttonWidth, buttonHeight) action:@selector(rotateContainer)];
    [self.view addSubview:rotateButton];

    UIButton *resetButton = [self createButtonWithTitle:@"全部重置" frame:CGRectMake(self.view.bounds.size.width - buttonWidth - 30, startY + 50, buttonWidth, buttonHeight) action:@selector(resetAll)];
    [self.view addSubview:resetButton];
}

- (UIButton *)createButtonWithTitle:(NSString *)title frame:(CGRect)frame action:(SEL)action {
    UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
    button.frame = frame;
    [button setTitle:title forState:UIControlStateNormal];
    [button addTarget:self action:action forControlEvents:UIControlEventTouchUpInside];
    button.titleLabel.font = [UIFont systemFontOfSize:14];
    button.backgroundColor = [UIColor colorWithWhite:0.9 alpha:1.0];
    button.layer.cornerRadius = 8;
    return button;
}

#pragma mark - Demo Actions

- (void)changeChildFrame {
    [UIView animateWithDuration:0.5 animations:^{
        CGRect currentFrame = self.childView.frame;
        if (currentFrame.origin.x == 10) {
            self.childView.frame = CGRectMake(100, 100, currentFrame.size.width, currentFrame.size.height);
        } else {
            self.childView.frame = CGRectMake(10, 10, currentFrame.size.width, currentFrame.size.height);
        }
    } completion:^(BOOL finished) {
        [self updateInfoLabel:@"已改变子视图Frame"];
    }];
}

- (void)changeContainerBounds {
    [UIView animateWithDuration:0.5 animations:^{
        CGRect currentBounds = self.containerView.bounds;
        if (currentBounds.origin.x == 0) {
            self.containerView.bounds = CGRectMake(50, 50, currentBounds.size.width, currentBounds.size.height);
        } else {
            self.containerView.bounds = CGRectMake(0, 0, currentBounds.size.width, currentBounds.size.height);
        }
    } completion:^(BOOL finished) {
        [self updateInfoLabel:@"已改变容器Bounds"];
    }];
}

- (void)rotateContainer {
    [UIView animateWithDuration:0.5 animations:^{
        if (CGAffineTransformIsIdentity(self.containerView.transform)) {
            self.containerView.transform = CGAffineTransformMakeRotation(M_PI_4); // 45 degrees
        } else {
            self.containerView.transform = CGAffineTransformIdentity;
        }
    } completion:^(BOOL finished) {
        [self updateInfoLabel:@"已旋转容器"];
    }];
}

- (void)resetAll {
    [UIView animateWithDuration:0.5 animations:^{
        self.containerView.transform = self.initialTransform;
        self.containerView.bounds = CGRectMake(0, 0, 280, 280);
        self.childView.frame = CGRectMake(10, 10, 80, 80);
    } completion:^(BOOL finished) {
        [self updateInfoLabel:@"已重置"];
    }];
}

- (void)updateInfoLabel:(NSString *)action {
    NSString *info = [NSString stringWithFormat:
                      @"操作: %@\n\n"
                      @"容器 Frame:  %@\n"
                      @"容器 Bounds: %@\n\n"
                      @"子视图 Frame:      %@\n"
                      @"子视图 Bounds:     %@",
                      action,
                      NSStringFromCGRect(self.containerView.frame),
                      NSStringFromCGRect(self.containerView.bounds),
                      NSStringFromCGRect(self.childView.frame),
                      NSStringFromCGRect(self.childView.bounds)];
    self.infoLabel.text = info;
    NSLog(@"--- %@ ---", action);
    NSLog(@"Container Frame:  %@", NSStringFromCGRect(self.containerView.frame));
    NSLog(@"Container Bounds: %@", NSStringFromCGRect(self.containerView.bounds));
    NSLog(@"Child Frame:      %@", NSStringFromCGRect(self.childView.frame));
    NSLog(@"Child Bounds:     %@", NSStringFromCGRect(self.childView.bounds));
}
@end

运行后我们可以得到下面的效果:
请添加图片描述
再解释效果之前,我们先来看一下iOS的坐标系:
在这里插入图片描述
iOS中的坐标系是以左上角为原点,向右为x轴正方向,向下为y轴正方向。

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

bounds也是每个view都有的属性,这个属性我们一般不进行设置,表示view在本地坐标系统中的位置和大小。参照点是本地坐标系统。

对他们进行修改的效果如下:

  • frame
  • frame的位置是以父视图的坐标系作为参考,从而确定当前视图在父视图中的位置。
  • frame的大小改变时,当前视图的左上角位置不会改变,改变的只是大小。
  • frame的位置改变时,是根据父视图的原点进行改变的。

bounds

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

在知道以上内容后,我们可以解释效果图了:
frame的修改较好理解,对size修改就是放大缩小,对原点修改就是相对父视图原点移动。
但是当bounds的原点修改时,子视图会跟着移动。此时可以将bounds看作一个取景器,移动原点位置就是移动这个取景器,当对原点进行50 50的修改时,相当于取景器向右下移动,那么视图此时就会向左上移动。从而就是demo中的效果。


网站公告

今日签到

点亮在社区的每一天
去签到