还在因为自定义cell的布局而苦恼?——「OC」AutoLayOut的布局学习

「OC」AutoLayOut的布局学习

导入

不知道大家在学习UI的时候,有没有常常因为每次都要不断地通过调整frame的数据,调节视图的位置而头疼?是否会因为当屏幕发生横屏等变化时,我们精心编排的布局又会被打乱而烦躁?如果你也为这些问题而苦恼——那么学习AutoLayOut想必会给你带来很大的帮助。

在笔者学习自定义cell的时候,发现在布局自定义cell的控件时,需要将非常详细的计算控件的相关位置,我们在frame之中确定控件之中的位置,这样做的话不仅十分的麻烦,而且当我们的视图发生外部变化或者内部变化的时候,我们都需要进行布局的重新改变。

常见的外部改变如下:

  • 用户调整窗口大小(OS X)。
  • 用户在iPad(iOS)上进入或离开分屏浏览。
  • 设备旋转(iOS)。
  • 活动通话和音频录制条出现或消失(iOS)。
  • 您希望支持不同大小的类。
  • 您想要支持不同的屏幕尺寸。

内部变化如下:

  • 应用程序显示的内容会发生变化。
  • 该应用程序支持国际化。(在翻译的时候文本需要不同的空间)
  • 该应用程序支持动态类型(iOS)

当我们出现此变化的时候,如果使用AutoLayOut,即我们称之为的自动布局,就方便我们动态地对视图之中的控件进行自动布局,适应不同的屏幕,不同的类等情况,对显示的美观又着十分大的帮助。

创建自定布局的步骤

使用自定义布局,其实本质上就是为布局创建一定的约束(constraint),让系统的自行计算其布局的相对位置。

那么如何创建我们所谓的constraint呢?

  1. 首先创建约束对象 NSLayoutConstraint
  2. 而后将约束对象加入到父视图之中

自动布局属性

在学习设置Constraint之前,我们还需要学习自动布局的相关属性,只有我们全部了解这些属性之后,我们才能够将控件进行正确的布局

attributes_2x-2

以上是对于自动布局的相关属性的具象化表述,我们可以在xcode查看其,NSLayoutAttribute的枚举属性

typedef NS_ENUM(NSInteger, NSLayoutAttribute) {
    NSLayoutAttributeLeft = 1,//视图的左边缘
    NSLayoutAttributeRight,//视图的右边缘。
    NSLayoutAttributeTop,//视图的顶部边缘
    NSLayoutAttributeBottom,//视图的底部边缘
    NSLayoutAttributeLeading,//视图的前导边缘(根据用户界面布局方向的不同,可能是左边或右边)。
    NSLayoutAttributeTrailing,//视图的尾随边缘(根据用户界面布局方向的不同,可能是右边或左边)。
    NSLayoutAttributeWidth,//视图的宽度
    NSLayoutAttributeHeight,//视图的高度。
    NSLayoutAttributeCenterX,//视图的水平中心
    NSLayoutAttributeCenterY,//视图的垂直中心
    NSLayoutAttributeLastBaseline,//视图的最后一行基线。7
//略去中间部分较为少用的部分。。。
#endif
    
    NSLayoutAttributeNotAnAttribute = 0//NSLayoutAttributeNotAnAttribute表示不是有效的布局属性,用于表示缺少约束或无效的约束。
};

实现自动布局约束的方程

视图层次结构的布局被定义为一系列线性方程。每个约束代表一个单一的方程。你的目标是声明一系列方程,其中只有一个可能的解决方案。

一般而言,约束方程的形式如下:

item1.attribute1 = multiplier × item2.attribute2 + constant

我们用一个例子来说明

view_formula_2x

  • item1:方程中的第一个项目——在这种情况下,是红色视图。该项目必须是视图或布局指南。
  • attribute1:第一个项目上要约束的属性——在这种情况下,红色视图的前缘。
  • relationship:左右双方之间的关系。这种关系可以有三个值之一:相等,大于或相等,或小于或相等。在这种情况下,左侧和右侧是相等的。
  • multiplier:属性2的值乘以这个浮点数。在这种情况下,乘数是1.0
  • item2:等式中的第二项——在这种情况下,是蓝色视图。与第一项不同,这可以留空。
  • attribute2:第二个项目上要约束的属性——在这种情况下,是蓝色视图的后缘。如果第二个项目留空,这必须Not an Attribute
  • constant:一个恒定的浮点偏移量——在这种情况下,8.0。该值被添加到属性2的值中。

定位锚的两种方式

在实现自动布局之前,我们要先关闭系统 Autoresizing

代码如下

View1.translatesAutoresizingMaskIntoConstraints = NO;

前面其实我们已经了解了,所谓的约束其实可以等价于一个线性的方程,那么在程序之中我们又该怎么运用所谓的约束去实现自动布局呢?

约束表达式使用Visual Format Language (VFL)或NSLayoutConstraint类来定义。

NSLayoutConstraint类的构造方法

我们来学习如何根据约束表达式来写出对应的NSLayoutConstraint类:

当使用NSLayoutConstraint类来表示约束表达式时,可以将表达式中的每个部分对应到NSLayoutConstraint类的构造方法的参数。

假设我们有一个视图view1,需要将其左侧边缘与父视图的左侧边缘保持20个点的间距,并且宽度为父视图宽度的一半减去10个点。以下是表达式和代码的对应关系:

表达式:view1.left = parentView.left + 20
代码:

NSLayoutConstraint *leftConstraint = [NSLayoutConstraint constraintWithItem:view1
                                                                  attribute:NSLayoutAttributeLeft
                                                                  relatedBy:NSLayoutRelationEqual
                                                                     toItem:parentView
                                                                  attribute:NSLayoutAttributeLeft
                                                                 multiplier:1.0
                                                                   constant:20.0];
[NSLayoutConstraint activateConstraints:@[leftConstraint]];

表达式:view1.width = parentView.width / 2 - 10
代码:

NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:view1
                                                                   attribute:NSLayoutAttributeWidth
                                                                   relatedBy:NSLayoutRelationEqual
                                                                      toItem:parentView
                                                                   attribute:NSLayoutAttributeWidth
                                                                  multiplier:0.5
                                                                    constant:-10.0];
[NSLayoutConstraint activateConstraints:@[widthConstraint]];

在上面的代码中,我们使用constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant: 方法来创建约束对象。view1 是约束的第一个项,parentView 是约束的第二个项。NSLayoutAttributeLeft 表示左侧边缘,NSLayoutAttributeWidth 表示宽度。NSLayoutRelationEqual 表示相等关系。multiplier 参数用于设置宽度的比例关系,constant 参数用于设置约束的常量值。

最后,我们使用[NSLayoutConstraint activateConstraints:] 方法将约束添加到视图中,使其生效。

通过这种方式,您可以将约束表达式转换为使用NSLayoutConstraint类的构造方法来创建相应的约束对象,并将其应用于视图。

以上的约束关系的符号都我们都是使用NSLayoutRelation枚举来表示约束的关系

  1. 大于等于(Greater than or equal to):使用NSLayoutRelationGreaterThanOrEqual
  2. 小于等于(Less than or equal to):使用NSLayoutRelationLessThanOrEqual
  3. 等于(equal to):使用NSLayoutRelationEqual。

下面是使用NSLayoutConstraint类的构造方法来表达大于等于和小于等于关系的示例:

// 创建一个宽度至少为100的约束
NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:100.0];

// 创建一个高度至多为200的约束
NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationLessThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:200.0];

// 将约束添加到视图
[view addConstraint:widthConstraint];
[view addConstraint:heightConstraint];

在上面的示例中,widthConstraint 表示视图的宽度至少为100,使用的关系是NSLayoutRelationGreaterThanOrEqualheightConstraint 表示视图的高度至多为200,使用的关系是NSLayoutRelationLessThanOrEqual

请注意,使用constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant: 方法时,如果第二个参数和第四个参数传入的是nil,那么attribute参数和toAttribute参数应该设置为NSLayoutAttributeNotAnAttribute。这是因为在这种情况下,约束是与一个固定的常量值相关联,而不是与另一个视图的属性相关联。

Visual Format Language (VFL)的用法

VFL提供了一种简洁的方式来定义约束。它使用ASCII字符串来描述视图之间的关系和布局规则。

一个字符串,具有一定的格式,不同的格式代表不同的约束,并且,一个字符串往往能一次性表达出多个约束
如何写VFL字符串?

以下是苹果官网给出的可视化格式约束示例。

  • 标准空间

    [button]-[textField]

    请添加图片描述

  • 宽度约束

    [button(>=50)]

    请添加图片描述

  • 连接到Superview

    |-50-[purpleBox]-50-|

    请添加图片描述

  • 垂直布局

    V:[topField]-10-[bottomField]

    请添加图片描述

  • 连接视图

    [maroonView][blueView]

    请添加图片描述

  • 优先权

    [button(100@20)]

    请添加图片描述

  • 等宽

    [button1(==button2)]

    请添加图片描述

  • 多个谓词

    [flexibleButton(>=70,<=100)]

    请添加图片描述

  • 完整的布局线

    |-[find]-[findNext]-[findField(>=20)]-|

    请添加图片描述

以下是对VFL的语法的部分总结:

  • 文字:button1和button2之间的间距为30

    VFL:[button1]-30-[button]

  • 文字:button1的宽度为50

    VFL:[button1(==50)] 或 [button1(50)]

  • 文字:button1 距离 左边20,button2距离右边20, button1和button2间隔10, button1宽度和button2的宽度相等

    VFL: |-20-[button1(button2)]-10-[button2(button1)]-20-|

  • 文字:button1 距离顶部 为20

    VFL: V:|-20-[button1]

  • 文字:button1 和 button2 垂直方向间隔10

    VFL: V:[button1]-10-[button2]

VFL 特殊符号的含义

| 代表父视图的边缘

H:| 代表父试图的左边缘

V:| 代表父试图的上边缘

[] 代表一个子视图

() 代表一个宽度或高度的条件

-x- 代表间距是 x

-代表标准间距 8

AutoLayOut在cell之中的运用

OK,我们已经学会了AutoLayOut的相关用法,我们可以将自动布局用在cell之中。

那我自己写的自定义cell来举例,我直接展示cell之中的.m文件,.h文件较为简单,此处忽略

#import "UserInfoTableViewCell.h"

@implementation UserInfoTableViewCell

- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        [self setupViews];
    }
    return self;
}

- (void)setupViews {
    self.avatarImageView = [[UIImageView alloc] init];
    self.avatarImageView.translatesAutoresizingMaskIntoConstraints = NO;
    self.avatarImageView.layer.cornerRadius = 5;
    self.avatarImageView.clipsToBounds = YES;
    [self.contentView addSubview:self.avatarImageView];

    self.nameLabel = [[UILabel alloc] init];
    self.nameLabel.translatesAutoresizingMaskIntoConstraints = NO;
    self.nameLabel.font = [UIFont boldSystemFontOfSize:18];
    [self.contentView addSubview:self.nameLabel];
    
    self.wechatIDLabel = [[UILabel alloc] init];
    self.wechatIDLabel.translatesAutoresizingMaskIntoConstraints = NO;
    self.wechatIDLabel.font = [UIFont systemFontOfSize:14];
    self.wechatIDLabel.textColor = [UIColor grayColor];
    [self.contentView addSubview:self.wechatIDLabel];

 //contentView在这里就是cell视图
    [NSLayoutConstraint activateConstraints:@[
        [self.avatarImageView.leadingAnchor constraintEqualToAnchor:self.contentView.leadingAnchor constant:15],
        [self.avatarImageView.centerYAnchor constraintEqualToAnchor:self.contentView.centerYAnchor],
        [self.avatarImageView.widthAnchor constraintEqualToConstant:60],
        [self.avatarImageView.heightAnchor constraintEqualToConstant:60],
        
        [self.nameLabel.leadingAnchor constraintEqualToAnchor:self.avatarImageView.trailingAnchor constant:15],
        [self.nameLabel.topAnchor constraintEqualToAnchor:self.contentView.topAnchor constant:15],
        
        [self.wechatIDLabel.leadingAnchor constraintEqualToAnchor:self.nameLabel.leadingAnchor],
        [self.wechatIDLabel.topAnchor constraintEqualToAnchor:self.nameLabel.bottomAnchor constant:5]
    ]];
}

@end
—————————————————————————————————————————————————————————————————————————————————————————————————————————
#import "MenuItemTableViewCell.h"

@implementation MenuItemTableViewCell

- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        [self setupViews];
    }
    return self;
}

- (void)setupViews {
    self.iconImageView = [[UIImageView alloc] init];
    self.iconImageView.translatesAutoresizingMaskIntoConstraints = NO;
    [self.contentView addSubview:self.iconImageView];

    self.titleLabel = [[UILabel alloc] init];
    self.titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
    [self.contentView addSubview:self.titleLabel];

    [NSLayoutConstraint activateConstraints:@[
        [self.iconImageView.leadingAnchor constraintEqualToAnchor:self.contentView.leadingAnchor constant:15],
        [self.iconImageView.centerYAnchor constraintEqualToAnchor:self.contentView.centerYAnchor],
        [self.iconImageView.widthAnchor constraintEqualToConstant:24],
        [self.iconImageView.heightAnchor constraintEqualToConstant:24],
        
        [self.titleLabel.leadingAnchor constraintEqualToAnchor:self.iconImageView.trailingAnchor constant:15],
        [self.titleLabel.centerYAnchor constraintEqualToAnchor:self.contentView.centerYAnchor],
        [self.titleLabel.trailingAnchor constraintEqualToAnchor:self.contentView.trailingAnchor constant:-15]
    ]];
}

@end

在程序之中我们使用,activateConstraints创建NSLayoutConstraint数组来进行自动布局的创建

UserInfoTableViewCell之中:

  1. self.avatarImageView.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor, constant: 15):这个约束方程表示 self.avatarImageView 的 leading 边距与 self.contentView 的 leading 边距相等,并添加一个常量值 15。
  2. self.avatarImageView.centerYAnchor.constraint(equalTo: self.contentView.centerYAnchor):这个约束方程表示 self.avatarImageView 的 centerY 边距与 self.contentView 的 centerY 边距相等。
  3. self.avatarImageView.widthAnchor.constraint(equalToConstant: 60):这个约束方程表示 self.avatarImageView 的宽度设置为一个常量值 60。
  4. self.avatarImageView.heightAnchor.constraint(equalToConstant: 60):这个约束方程表示 self.avatarImageView 的高度设置为一个常量值 60。
  5. self.nameLabel.leadingAnchor.constraint(equalTo: self.avatarImageView.trailingAnchor, constant: 15):这个约束方程表示 self.nameLabel 的 leading 边距与 self.avatarImageView 的 trailing 边距相等,并添加一个常量值 15。
  6. self.nameLabel.topAnchor.constraint(equalTo: self.contentView.topAnchor, constant: 15):这个约束方程表示 self.nameLabel 的 top 边距与 self.contentView 的 top 边距相等,并添加一个常量值 15。
  7. self.wechatIDLabel.leadingAnchor.constraint(equalTo: self.nameLabel.leadingAnchor):这个约束方程表示 self.wechatIDLabel的 leading 边距与 self.nameLabel 的 leading 边距相等。
  8. self.wechatIDLabel.topAnchor.constraint(equalTo: self.nameLabel.bottomAnchor, constant: 5):这个约束方程表示 self.wechatIDLabel 的 top 边距与 self.nameLabel 的 bottom 边距相等,并添加一个常量值 5。

MenuItemTableViewCell.h也是类似的,读者有兴趣可以观察源代码,理解其相关布局。

接下来我们来看看自定义cell的样子:

image-20240618224644173

总结

自动布局(AutoLayout)是一种用于iOS开发中视图布局的技术,它可以根据设备的屏幕尺寸和方向自动调整视图的位置和大小。下面是关于AutoLayout布局学习的一些总结:

  1. 创建自定义布局的步骤:使用AutoLayout创建自定义布局的基本步骤包括:创建视图,添加视图到父视图,为视图添加约束。

  2. 自动布局属性:AutoLayout使用约束来描述视图之间的关系。常用的约束属性包括:宽度(width)、高度(height)、水平位置(leading、trailing、centerX等)、垂直位置(top、bottom、centerY等)、间距(margin等)等。

  3. 实现自动布局约束的方程:AutoLayout通过线性方程组来解决视图布局的问题。方程的形式为:视图属性1 = 视图属性2 * multiplier + constant。通过设置不同的属性和参数,可以实现各种复杂的布局效果。

  4. 定位锚的两种方式:在AutoLayout中,可以使用视图的边界(edge)或中心点(center)作为定位锚点。边界锚点包括左边界(leading)、右边界(trailing)、顶部边界(top)、底部边界(bottom)等;中心锚点包括水平中心(centerX)和垂直中心(centerY)。

  5. NSLayoutConstraint类的构造方法:NSLayoutConstraint是AutoLayout的核心类之一,用于创建和管理约束。它有多个构造方法,可以根据需要选择适合的方法来创建约束对象。

  6. Visual Format Language (VFL)的用法:VFL是一种简洁的语言,用于描述视图之间的约束关系。通过VFL,可以用一种更紧凑的方式编写约束代码,提高代码的可读性和可维护性。

  7. AutoLayout在cell中的运用:AutoLayout在表格视图(UITableView)或集合视图(UICollectionView)的单元格(cell)中广泛使用。通过使用AutoLayout,可以实现自动调整单元格的大小和位置,以适应不同的设备和内容。

  • 19
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值