当前位置: 首页 > news >正文

自动布局视图来实现聊天室的界面

自动布局视图来实现聊天室的界面

在share项目中,需要实现私信界面,但由于我比较懒,于是选择了学习自动布局视图来实现聊天室的内容

在实现聊天室之前,我们需要弄清楚聊天室是怎么构成的

请添加图片描述

每条消息实际上是就是一个cell,整个界面就是一个tableView,在这个tableView的底部加上一个textField,就成了基本的聊天室界面

那实现的难点就在于,如何根据你输入文字的多少,来修改整个cell的高度

我这里使用了cell的自动布局

首先我们在tableView里把高度设置设为自动计算,并且给定一个预设高度

self.messageView.tableView.rowHeight = UITableViewAutomaticDimension;
self.messageView.tableView.estimatedRowHeight = 100.0;

这里的预设高度是方便系统计算实际高度的,设置合适的自动高度可以节省内存

由于tableView的计算高度是根据自动布局计算的,所以我们的cel内部需要使用自动布局

第一步,禁用系统默认布局

translatesAutoresizingMaskIntoConstraints

translatesAutoresizingMaskIntoConstraints是系统自动布局转换的一个属性,当你创建一个view的时候,系统会默认

// 系统默认设置
view.translatesAutoresizingMaskIntoConstraints = YES;

这意味系统会自动把你写的frame布局转换为自动布局

默认规则

在默认情况下,转换为

// 假设你设置了 frame
view.frame = CGRectMake(10, 20, 100, 50);// 系统会自动转换为这些约束:
view.leading = superview.leading + 10
view.top = superview.top + 20  
view.width = 100
view.height = 50

如果不设置no,那么我们设置的自动布局就会和系统设置的产生冲突

第二步,设置约束并激活

Apple在iOS9中推出了NSLayoutAnchor,是目前用代码写约束的官方推荐写法

它为一系列控件提供了一系列锚点(Anchor)属性,我们可以通过这些锚点来创建约束关系

在外面写约束的时候,实际上就是用一根绳子(NSLayoutConstraint)来把一个视图的挂钩跟另一个视图的挂钩链接起来

锚点有三种锚点,每个UIView都有一整套的锚点属性,且会自动匹配属性,你不能告诉一个负责垂直的锚点去挂负责水平的锚点

三种锚点分别是:

  • NSLayoutXAxisAnchor:负责水平方向的挂钩
  • NSLayoutYAxisAnchor:负责垂直方向的挂钩
  • NSLayoutDimension:负责**尺寸(宽和高)**的挂钩

NSLayoutXAxisAnchor 水平锚点

这些锚点定义了视图在x轴方向的位置,一共有五种

  • leadingAnchor: 前缘锚点。在“从左到右”的语言环境下(如中文、英文),它就是左边缘。在“从右到左”的语言环境下(如阿拉伯语),它就是右边缘(其实无所谓因为你如果能写出国际化软件你也就不会看我的博客了…)
  • trailingAnchor: 后缘锚点。与leadingAnchor对应,是国际化安全的右边缘。强烈推荐使用
  • leftAnchor: 左边缘锚点。无论语言方向如何,它永远是左边
  • rightAnchor: 右边缘锚dian。无论语言方向如何,它永远是右边
  • centerXAnchor: X轴中心锚点。视图水平方向的中点

NSLayoutYAxisAnchor 垂直锚点

定义了视图在y轴方向的位置

  • topAnchor: 顶部锚点
  • bottomAnchor: 底部锚点
  • centerYAnchor: Y轴中心锚点。视图垂直方向的中点
  • firstBaselineAnchor: 首基线锚点。主要用于对齐包含文字的视图(如UILabel, UITextField),它代表第一行文字的基线
  • lastBaselineAnchor: 末基线锚点。代表最后一行文字的基线。当你希望两个多行标签的文字底端对齐时,这个非常有用

NSLayoutDimension 尺寸锚点

定义了视图自身的大小

  • widthAnchor: 宽度锚点
  • heightAnchor: 高度锚点

常用的约束方法

创建约束的通用发送为

[view1.某个锚点 constraintEqualToAnchor:view2.另一个锚点];

这个方法会返回一个 NSLayoutConstraint 对象,但它默认是 不激活 的。你需要手动激活它,或者用 NSLayoutConstraint 的类方法 activateConstraints: 批量激活

  1. 建立相等/不等关系 (最常用)

这些方法用于将一个锚点与另一个同类型的锚点关联起来。

  • (NSLayoutConstraint *)constraintEqualToAnchor:(NSLayoutAnchor<AnchorType> *)anchor;
    • 作用:本锚点 == 另一个锚点
  • (NSLayoutConstraint *)constraintGreaterThanOrEqualToAnchor:(NSLayoutAnchor<AnchorType> *)anchor;
    • 作用:本锚点 >= 另一个锚点
  • (NSLayoutConstraint *)constraintLessThanOrEqualToAnchor:(NSLayoutAnchor<AnchorType> *)anchor;
    • 作用:本锚点 <= 另一个锚点

带偏移量 (constant):

在上面的基础上,你还可以增加一个固定的偏移量。

  • (NSLayoutConstraint *)constraintEqualToAnchor:(NSLayoutAnchor<AnchorType> *)anchor constant:(CGFloat)c;
    • 作用:本锚点 == 另一个锚点 + c
    • 注意constant的正负值含义取决于锚点。对于top, leading, centerX, centerY,正值表示向下、向右移动。对于bottom, trailing,正值表示向外拉伸,通常需要使用负值来向内收缩
  1. 尺寸锚点的特殊方法

NSLayoutDimension 因为代表的是尺寸,所以它有一些更强大的方法。

与固定值比较:

  • (NSLayoutConstraint *)constraintEqualToConstant:(CGFloat)c;
    • 作用:本尺寸 == 固定值c
  • (NSLayoutConstraint *)constraintGreaterThanOrEqualToConstant:(CGFloat)c;
  • (NSLayoutConstraint *)constraintLessThanOrEqualToConstant:(CGFloat)c;

与另一个尺寸成比例 (multiplier):

这是实现动态比例布局的关键。

  • (NSLayoutConstraint *)constraintEqualToAnchor:(NSLayoutDimension *)anchor multiplier:(CGFloat)m;
    • 作用:本尺寸 == 另一个尺寸 * m
  • (NSLayoutConstraint *)constraintEqualToAnchor:(NSLayoutDimension *)anchor multiplier:(CGFloat)m constant:(CGFloat)c;
    • 作用:本尺寸 == (另一个尺寸 * m) + c

聊天室cell代码

直接给出代码

#import "messageCell.h"// 添加一个类扩展
@interface messageCell()
// 1的布局约束
@property (nonatomic, strong) NSArray<NSLayoutConstraint *> *myLayoutConstraints;
// 0的布局约束
@property (nonatomic, strong) NSArray<NSLayoutConstraint *> *otherLayoutConstraints;
@end@implementation messageCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];if (self) {self.backgroundColor = [UIColor clearColor];self.selectionStyle = UITableViewCellSelectionStyleNone;self.headImage = [[UIImageView alloc] init];self.headImage.layer.cornerRadius = 20;self.headImage.layer.masksToBounds = YES;self.headImage.contentMode = UIViewContentModeScaleAspectFill;self.messageLabel = [[UILabel alloc] init];self.messageLabel.numberOfLines = 0;self.messageLabel.font = [UIFont systemFontOfSize:16];
//        self.messageLabel.layer.cornerRadius = 8;
//        self.messageLabel.layer.masksToBounds = YES;self.backgroundView = [[UIView alloc] init];self.backgroundView.layer.cornerRadius = 8;self.backgroundView.layer.masksToBounds = YES;// 准备工作// 取消掉系统给控件默认的一套布局方法self.headImage.translatesAutoresizingMaskIntoConstraints = NO;self.messageLabel.translatesAutoresizingMaskIntoConstraints = NO;self.backgroundView.translatesAutoresizingMaskIntoConstraints = NO;// 添加到 contentView[self.contentView addSubview:self.headImage];[self.contentView addSubview:self.backgroundView];[self.contentView addSubview:self.messageLabel];// 添加遮罩层来完成背景设置
//        // 公共约束,不管是什么cell都需要遵守,label的顶部与cell的顶部留出10的间距
//        NSLayoutConstraint *labelTop = [self.messageLabel.topAnchor constraintEqualToAnchor:self.contentView.topAnchor constant:10];
//        // label的底部与cell的底部留出10的间距
//        NSLayoutConstraint *labelBottom = [self.messageLabel.bottomAnchor constraintEqualToAnchor:self.contentView.bottomAnchor constant:-10];NSLayoutConstraint* backGroundViewUpPaddingY = [self.backgroundView.topAnchor constraintEqualToAnchor:self.contentView.topAnchor constant:10];NSLayoutConstraint* backGroundViewDownPaddingY = [self.backgroundView.bottomAnchor constraintEqualToAnchor:self.contentView.bottomAnchor constant:-10];NSLayoutConstraint* labelTop = [self.messageLabel.topAnchor constraintEqualToAnchor:self.backgroundView.topAnchor constant:10];NSLayoutConstraint* labelBotton = [self.messageLabel.bottomAnchor constraintEqualToAnchor:self.backgroundView.bottomAnchor constant:-10];NSLayoutConstraint* labelLeft = [self.messageLabel.leadingAnchor constraintEqualToAnchor:self.backgroundView.leadingAnchor constant:10];NSLayoutConstraint* labelRight = [self.messageLabel.trailingAnchor constraintEqualToAnchor:self.backgroundView.trailingAnchor constant:-10];// 头像也是同理NSLayoutConstraint* headTop = [self.headImage.topAnchor constraintEqualToAnchor:self.contentView.topAnchor constant:10];// 固定头像的大小为40 * 40NSLayoutConstraint* headWidth = [self.headImage.widthAnchor constraintEqualToConstant:40];NSLayoutConstraint* headHeight = [self.headImage.heightAnchor constraintEqualToConstant:40];// 设置背景遮罩层的最高高度为多少NSLayoutConstraint* backViewMaxWidth = [self.backgroundView.widthAnchor constraintLessThanOrEqualToConstant:250];// 激活约束[NSLayoutConstraint activateConstraints:@[backGroundViewUpPaddingY, backGroundViewDownPaddingY, labelBotton,labelTop,labelLeft,labelRight, headTop, headWidth, headHeight, backViewMaxWidth]];// 对方self.otherLayoutConstraints = @[// 头像在左边[self.headImage.leadingAnchor constraintEqualToAnchor:self.contentView.leadingAnchor constant:10],// Label在头像右边[self.backgroundView.leadingAnchor constraintEqualToAnchor:self.headImage.trailingAnchor constant:10]];// 我的self.myLayoutConstraints = @[// 头像在右边[self.headImage.trailingAnchor constraintEqualToAnchor:self.contentView.trailingAnchor constant:-10],// Label在头像左边[self.backgroundView.trailingAnchor constraintEqualToAnchor:self.headImage.leadingAnchor constant:-10]];}return self;
}- (void)configureWithMessage:(NSString *)message sender:(NSInteger)sender {self.messageLabel.text = message;// 根据发送者选择布局if (sender == 1) {// 我[NSLayoutConstraint deactivateConstraints:self.otherLayoutConstraints];[NSLayoutConstraint activateConstraints:self.myLayoutConstraints];// 设置样式self.headImage.image = [UIImage imageNamed:@"newfollow0.PNG"];self.backgroundView.backgroundColor = [UIColor colorWithRed:0.2 green:0.6 blue:1.0 alpha:1.0];self.messageLabel.textColor = [UIColor whiteColor];} else {// 对方// 激活“对方”的约束,禁用“我”的约束[NSLayoutConstraint deactivateConstraints:self.myLayoutConstraints];[NSLayoutConstraint activateConstraints:self.otherLayoutConstraints];// 设置样式self.headImage.image = [UIImage imageNamed:@"newfollow1.PNG"];self.backgroundView.backgroundColor = [UIColor colorWithRed:0.9 green:0.9 blue:0.9 alpha:1.0];self.messageLabel.textColor = [UIColor blackColor];}
}

这样就完成了cell的自动布局视图

之后在VC中使用cell的configureWithMessage:(NSString *)message sender:(NSInteger)sender方法即可设置cell,在VC中,也无需使用heightForRowAtIndexPath来设置每个cell的高度

label的高度由label内的文本决定,而其他控件分层向外传递高度,使用cursor解析代码给出高度传递链

文字内容尺寸→messageLabel高度→backgroundView高度→cell高度 文字内容尺寸 → messageLabel 高度 → backgroundView 高度 → cell 高度 文字内容尺寸messageLabel高度backgroundView高度cell高度

http://www.dtcms.com/a/311729.html

相关文章:

  • 【iOS】KVO
  • 20250802让飞凌OK3576-C开发板在飞凌的Android14下【rk3576_u选项】适配NXP的WIFIBT模块88W8987A的蓝牙
  • OTC焊接机器人节能技巧
  • Java内存模型(Java Memory Model,JMM)
  • 关于鸦片战争的历史
  • Dify 上次文件大小突破15MB,解决办法?
  • Store / Slice / Reducer
  • 麦肯锡咨询公司PEI经典面试题目汇总
  • Python编程基础与实践:Python循环结构基础
  • 洛谷 P3870 [TJOI2009] 开关-普及+/提高
  • 音视频学习(四十四):音频处理流程
  • 第三章 用户和权限
  • 线程池的实现
  • SQL Server从入门到项目实践(超值版)读书笔记 22
  • 内网穿透系列十:高性能内网穿透工具 rathole,支持Docker一键部署
  • 什么是DOM和BOM?
  • 机器学习-KNN
  • springboot大学生成绩管理系统设计与实现
  • Git 的基本使用指南(1)
  • 人类学家与建筑师:区分UX研究和项目管理的需求分析
  • TFS-2022《A Novel Data-Driven Approach to Autonomous Fuzzy Clustering》
  • LVGL代码框架简介
  • 从底层架构到多元场景:计算机构成与应用的深度剖析
  • Mac电脑安装HomeBrew
  • 从AI智能体出发,重构数据中台:迈向Agentic时代的数据能力体系
  • 微积分基础 | 核心概念 / 公式推导
  • 【51单片机6位数码管密码锁】2022-10-15
  • 入门MicroPython+ESP32:安装逗脑IDE及驱动
  • 深入 Go 底层原理(十一):Go 的反射(Reflection)机制
  • ESP32 外设控制基础:GPIO 应用详解与输入输出案例