iOS ------ frame和bounds

iOS中用特有的坐标系,以左上角为坐标原点,往右为X正方向,往下是Y正方向。

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

struct CGRect {
    CGPoint origin;
    CGSize size;
};

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

1,frame

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

UIView *viewA = [[UIView alloc] initWithFrame:CGRectMake(50, 50, 300, 300)];
    [viewA setBackgroundColor:[UIColor blueColor]];
    [self.view addSubview:viewA];
    NSLog(@"viewA.frame - %@",NSStringFromCGRect(viewA.frame));
    NSLog(@"viewA.bounds - %@",NSStringFromCGRect(viewA.bounds));
    
    UIView *viewB = [[UIView alloc] initWithFrame:CGRectMake(50, 50, 200, 200)];
    [viewB setBackgroundColor:[UIColor yellowColor]];
    [viewA addSubview:viewB];
    NSLog(@"viewB.frame - %@",NSStringFromCGRect(viewB.frame));
    NSLog(@"viewB.bounds - %@",NSStringFromCGRect(viewB.bounds));
    
    UIView *viewC = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
    [viewC setBackgroundColor:[UIColor redColor]];
    [self.view addSubview:viewC];
    NSLog(@"viewC.frame - %@",NSStringFromCGRect(viewC.frame));
    NSLog(@"viewC.bounds - %@",NSStringFromCGRect(viewC.bounds));

在这里插入图片描述

打印结果:

2024-08-01 21:05:53.857358+0800 frame和bounds[2744:130774] viewA.frame - {{50, 50}, {300, 300}}
2024-08-01 21:05:53.857419+0800 frame和bounds[2744:130774] viewA.bounds - {{0, 0}, {300, 300}}
2024-08-01 21:05:53.857497+0800 frame和bounds[2744:130774] viewB.frame - {{50, 50}, {200, 200}}
2024-08-01 21:05:53.857542+0800 frame和bounds[2744:130774] viewB.bounds - {{0, 0}, {200, 200}}
2024-08-01 21:05:53.857605+0800 frame和bounds[2744:130774] viewC.frame - {{100, 100}, {100, 100}}
2024-08-01 21:05:53.857644+0800 frame和bounds[2744:130774] viewC.bounds - {{0, 0}, {100, 100}}

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

2,bounds

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

2024-08-01 21:05:53.857419+0800 frame和bounds[2744:130774] viewA.bounds - {{0, 0}, {300, 300}}
2024-08-01 21:05:53.857542+0800 frame和bounds[2744:130774] viewB.bounds - {{0, 0}, {200, 200}}
2024-08-01 21:05:53.857644+0800 frame和bounds[2744:130774] viewC.bounds - {{0, 0}, {100, 100}}

因为我们没有设置bounds值,我们打印的每个view的起点都是(0,0)。

bounds的作用是什么?

每个视图都有自己的坐标系,且每个坐标系默认以自身的左上角为坐标原点,所有视图以这个坐标系的原点为基准点。bounds的位置代表的是子视图看待当前视图左上角的位置,bounds的大小代表当前视图的大小。原则如下:

1,更改bounds中的位置对于当前视图没有影响,相当于更改了当前视图的坐标系,对于子视图来说,当前视图的左上角已经不再是(0,0),而是更改后的坐标,坐标系改变后了,那么所有的子视图的位置也会跟着改变。

2,更改bounds的大小,bounds的大小代表当前视图的长和宽,修改长和宽后,中心点继续保持不变,长宽进行改变,通过bounds修改长宽看起来就像是以中心点为基总点对长宽两边同时进行缩放。

3,fram和bounds的区别

3.1origin的区别

我们修改viewA的bounds

[viewA setBounds:CGRectMake(25, 25, 300, 300)];

在这里插入图片描述

我们再只在修改viewB的frame

UIView *viewB = [[UIView alloc] initWithFrame:CGRectMake(25, 25, 200, 200)];

在这里插入图片描述

我们发现出现了同样的效果

通过改变viewA的坐标系原点,会造成子视图viewB的移动,这里的移动是靠viewBFrame的起点相对于父视图的bounds的起点(父视图本身的坐标系)。

同样我们不改变父视图的坐标系,改变子视图的frame的起点也能达到相同的效果。

3.2size的区别

我们改变viewB的bounds

[viewB setBounds:CGRectMake(0, 0, 250, 250)];

在这里插入图片描述

我们发现viewB的左上角也扩大了

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

总结:

  • frame不管对于位置还是大小,改变的都是自己本身。fram的位置是一父视图的坐标系为参照系,从而确定当前视图在父视图中的位置。frame的大小改变时,当前视图的左上角不会改变,只是大小改变。
  • bounds改变位置时,改变的是子视图的位置,自身没有影响,改变了本身的坐标系原点,默认是坐标系的原点是左上角。bounds改变大小时,当前视图的中心点不会改变,当前视图的大小发生改变,以中心点实现缩放的效果。
  • 10
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,这是一个手势解锁的Demo,你可以参考一下: ``` import UIKit class GestureLockViewController: UIViewController { // MARK: - Properties var buttons = [UIButton]() var selectedButtons = [UIButton]() var lines = [CAShapeLayer]() var touchPoint: CGPoint? var isTouching = false // MARK: - Lifecycle override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .white let margin: CGFloat = 40 let distance: CGFloat = 80 let buttonWidth: CGFloat = 60 let buttonHeight: CGFloat = 60 let viewWidth = view.bounds.width let viewHeight = view.bounds.height for i in 0..<9 { let row = CGFloat(i / 3) let col = CGFloat(i % 3) let x = margin + col * (buttonWidth + distance) let y = margin + row * (buttonHeight + distance) let button = UIButton(frame: CGRect(x: x, y: y, width: buttonWidth, height: buttonHeight)) button.layer.cornerRadius = buttonWidth / 2 button.layer.borderWidth = 2 button.layer.borderColor = UIColor.lightGray.cgColor button.tag = i view.addSubview(button) buttons.append(button) } } // MARK: - Gesture Methods override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { guard let touch = touches.first else { return } let point = touch.location(in: view) for button in buttons { if button.frame.contains(point) && !selectedButtons.contains(button) { touchPoint = button.center selectedButtons.append(button) isTouching = true break } } } override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) { guard isTouching, let touch = touches.first else { return } let point = touch.location(in: view) touchPoint = point for button in buttons { if button.frame.contains(point) && !selectedButtons.contains(button) { button.isSelected = true selectedButtons.append(button) let line = CAShapeLayer() line.strokeColor = UIColor.gray.cgColor line.fillColor = UIColor.clear.cgColor line.lineWidth = 3 view.layer.addSublayer(line) lines.append(line) break } } drawLines() } override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { isTouching = false touchPoint = nil for button in buttons { button.isSelected = false } validatePassword() clearSelectedButtons() clearLines() } override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) { isTouching = false touchPoint = nil for button in buttons { button.isSelected = false } clearSelectedButtons() clearLines() } // MARK: - Private Methods private func drawLines() { guard let point = touchPoint else { return } let linePath = UIBezierPath() linePath.move(to: point) for button in selectedButtons { linePath.addLine(to: button.center) } if let lastButton = selectedButtons.last, isTouching { linePath.addLine(to: lastButton.convert(lastButton.center, to: view)) } lines.last?.path = linePath.cgPath } private func clearSelectedButtons() { for button in selectedButtons { button.isSelected = false } selectedButtons.removeAll() } private func clearLines() { for line in lines { line.removeFromSuperlayer() } lines.removeAll() } private func validatePassword() { let password = selectedButtons.map { "\($0.tag)" }.joined() print("Gesture password: \(password)") } } ``` 这个Demo实现了一个3x3的手势解锁界面,使用了`UIButton`和`CAShapeLayer`来实现。当用户滑动手指时,会根据手指位置,自动连接之前选中的按钮,形成一条线。当用户抬起手指时,会根据选中的按钮的顺序,输出一个密码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值