当前位置: 首页 > 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.lryc.cn/news/608241.html

相关文章:

  • AI Agent开发学习系列 - LangGraph(3): 有多个输入的Graph
  • 小智服务器Java安装编译(xinnan-tech)版
  • 算法: 字符串part02: 151.翻转字符串里的单词 + 右旋字符串 + KMP算法28. 实现 strStr()
  • LLM Prompt与开源模型资源(3)如何写一个好的 Prompt
  • 什么叫湖仓一体
  • 质数时间(二分查找)
  • GraphRag安装过程中的报错:系统找不到指定的文件(Could not install packages due to an OSError)
  • Day25-对称二叉树-
  • PyTorch 张量核心操作——比较、排序与数据校验
  • 边缘智能网关在水务行业中的应用—龙兴物联
  • 模拟激光相机工作站版本6.0 5.2.32 6.0.44 6.031 5.2.20
  • 双机并联无功环流抑制虚拟阻抗VSG控制【simulink仿真模型实现】
  • 详解Python标准库之并发执行
  • OneCode 3.0表达式从语法到执行的全链路设计
  • 文件同步神器-rsync命令讲解
  • MySQL学习从零开始--第八部分
  • Python中元组,字典,集合的易错题(含解析)
  • 译|Netflix 数据平台运营中基于机器学习自动修复系统
  • Docker--将非root用户添加docker用户组,解决频繁sudo执行输入密码的问题
  • Docker 部署与配置 MySQL 5.7
  • CMake 命令行参数完全指南 (1)
  • Ubuntu18网络连接不上也ping不通网络配置问题排查与解决方法
  • 2 安装 Docker 和 Jenkins:持续构建环境起步
  • 音视频学习(四十七):模数转换
  • 题单【模拟与高精度】
  • lumerical——布拉格光栅(2)
  • VS2019安装HoloLens 没有设备选项
  • 类似 Pixso 但更侧重「网页 / 软件界面设计」「前后端可视化开发」的工具
  • 【AI】AIService(基本使用与指令定制)
  • 【MODIS数据】MYD021KM