一直觉得自己写的不是技术而是情怀一个个的教程是自己这一路走来的痕迹。靠专业技能的成功是最具可复制性的希望我的这条路能让你们少走弯路希望我能帮你们抹去知识的蒙尘希望我能帮你们理清知识的脉络希望未来技术之巅上有你们也有我。OC-私聊页面代码下载地址文章目录前言技术要点模型思路布局思路文本设置计算文本行高和行间距使用文本设置内容行间距设置使用图片是如何进行平铺(拉伸)的如何设置背景聊天框的宽高间距与文字的宽高间距如何写信息显示的时间戳关系监听键盘弹起收回添加监听移除监听监听的实现前言该聊天框2025.03.22的时候做公司的AI需要用到聊天框做过顺便记录一下里面的一些要点技术要点模型思路json数据回来之后赋值好内容到对应的属性里面需要自定义尺寸相关的属性计算好tableview刷新的时候可以直接拿计算好的值进行使用。布局思路1.首先先局部日期 timeLabelself.timeLabel.makeCons(^{ make.left.top.right.equal.view(self.contentView); make.height.equal.constants(28); });2.再布局右边头像按钮 leftTitleLabelself.rightHeaderImageView.makeCons(^{ make.width.height.equal.constants(36); make.right.equal.view(self.contentView).constants(-15); make.top.equal.view(self.timeLabel).bottom.constants(1); });3.再布局右边气泡背景图 rightChatImageViewself.rightChatImageView.makeCons(^{ make.right.equal.view(self.rightHeaderImageView).left.constants(-8); make.top.equal.view(self.leftHeaderImageView).constants(18); make.width.equal.constants(0.0); make.height.equal.constants(0.0); });4.最后布局文本内容 rightTitleLabelself.rightTitleLabel.makeCons(^{ make.top.equal.view(self.rightChatImageView).constants(20); make.left.equal.view(self.rightChatImageView).constants(25); make.width.equal.constants(0.0); make.height.equal.constants(0.0); });文本设置计算文本行高和行间距显示文字内容一定要有行间距计算也包含行间距这个文本显示内容的灵魂/* 返回一个包含宽度和高度的CGSize,计算高度的时候也会考虑宽度如果实际宽度小于最大宽度则使用实际宽度高度是单行高度 1.计算行高高度 2.如果实际宽度小于最大宽度则使用实际宽度高度是单行高度(就是一行都不够) 3.考虑每一行的间距 */ - (CGSize)calculateTextSizeWithText:(NSString *)text font:(UIFont *)font maxWidth:(CGFloat)maxWidth lineSpacing:(CGFloat)lineSpacing { // 创建段落样式并设置行间距 NSMutableParagraphStyle *paragraphStyle [[NSMutableParagraphStyle alloc] init]; paragraphStyle.lineSpacing lineSpacing; // 创建包含字体和段落样式的属性字典 NSDictionary *attributes { NSFontAttributeName: font, NSParagraphStyleAttributeName: paragraphStyle }; // 先计算实际需要的尺寸不限制高度 CGRect rect [text boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading attributes:attributes context:nil]; CGFloat actualWidth ceil(rect.size.width); CGFloat actualHeight ceil(rect.size.height); // 如果实际宽度小于最大宽度则使用实际宽度 if (actualWidth maxWidth) { return CGSizeMake(actualWidth, actualHeight); } // 否则使用最大宽度计算多行高度 else { CGRect constrainedRect [text boundingRectWithSize:CGSizeMake(maxWidth, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading attributes:attributes context:nil]; return CGSizeMake(maxWidth, ceil(constrainedRect.size.height)); } }使用文本设置内容行间距设置#import UILabelFHXLabel.h implementation UILabel (FHXLabel) // 设置label行间距 - (void)setText:(NSString*)text lineSpacing:(CGFloat)lineSpacing { if (lineSpacing 0.01 || !text) { self.text text; return; } NSMutableAttributedString *attributedString [[NSMutableAttributedString alloc] initWithString:text]; [attributedString addAttribute:NSFontAttributeName value:self.font range:NSMakeRange(0, [text length])]; NSMutableParagraphStyle *paragraphStyle [[NSMutableParagraphStyle alloc] init]; [paragraphStyle setLineSpacing:lineSpacing]; [paragraphStyle setLineBreakMode:self.lineBreakMode]; [paragraphStyle setAlignment:self.textAlignment]; [attributedString addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, [text length])]; self.attributedText attributedString; } end使用图片是如何进行平铺(拉伸)的图片是如何进行平铺(拉伸)视频讲解拉伸原理图原理分析使用下面的方法解决问题:裁切图片- (UIImage *) getMyImg:(NSString *)imgName { UIImage *img [UIImage imageNamed:imgName]; //1.CapInset:设置受保护区域在inset范围之内的内容可以自由的进行拉伸或者平铺以外的区域(四个角点)就是受保护 的区域不能进行拉伸或者变形 //2.设置变形模式拉伸 还是平铺如果是单一色则没有区别 return [img resizableImageWithCapInsets:UIEdgeInsetsMake(30, 25, 22, 28) resizingMode:UIImageResizingModeTile]; }如何设置InsetsUIEdgeInsetsMake(CGFloat top, CGFloat left, CGFloat bottom, CGFloat right)看下面的图片对应上面的代码就知道了resizingMode 设置UIImageResizingModeTile平铺 UIImageResizingModeStretch拉伸如何设置背景聊天框的宽高间距与文字的宽高间距看上面图片右边的文字和方框没有问题但是左边的文字和方框就出现问题了测试发现刚好显示刚刚结束的中国两会上中国官官算法上就出现了问题经过查找发现设置背景的大小间距和文字间距时有技巧的。问题出现在设置背景的大小间距和文字间距上面。首先设置背景的大小间距和文字大小间距我们先设置一个文字开始单个文字设置无问题了我从设置一个我字开始从上面的图片可以看出第一个我字紫色背景是没有问题, 第四个我字粉红色背景是有问题, 第四个背景歪了。看下面的图片正面的角度斜看的角度如果我们设置背景的宽高直接用文字算出来的宽高设置的话刚好背景的大小就是文字的大小。但是这样肯定不行文字刚好挡住背景。那么我们需要把背景放大把文字装在里面才能看到背景的效果。那么如何正确设置它避免上面的错误呢设置的技巧很简单的我们要把一个文字完全的装载的背景里面就可以了那怎么才算完全装载呢看下面的图片设置图片的大小足够大保证能把label放进去然后在设置文字label左边顶部的边距举例放到中间就好了。例如我的label字体是16的rightChatImageView图片的背景大少是文字算出来高度40宽度50self.rightChatImageView.updateCons(^{ make.height.equal.constants(self.model.textSize.height40); make.width.equal.constants(self.model.textSize.width50); });rightTitleLabel文字的大少左右边距self.rightTitleLabel.addTo(self.rightChatImageView).fnt(16).lines(0).bgColor([Color randomColor]).leftAlignment.makeCons(^{ make.top.equal.view(self.rightChatImageView).constants(20); make.left.equal.view(self.rightChatImageView).constants(25); });按照这种思想根据公司给我们的图片进行调节那么后面无论是多少个文字就没有问题了如何写信息显示的时间戳关系.h#import Foundation/Foundation.h NS_ASSUME_NONNULL_BEGIN interface TimeDisplayHelper : NSObject (NSString *)wechatStyleTimeStringFromDate:(NSDate *)date; end NS_ASSUME_NONNULL_END.m#import TimeDisplayHelper.h implementation TimeDisplayHelper (NSString *)wechatStyleTimeStringFromDate:(NSDate *)date { if (!date) return ; NSDate *now [NSDate date]; NSCalendar *calendar [NSCalendar currentCalendar]; // 计算时间差秒 NSTimeInterval interval [now timeIntervalSinceDate:date]; // 今天的时间显示 if ([calendar isDateInToday:date]) { if (interval 60) { return 刚刚; } else if (interval 60 * 60) { return [NSString stringWithFormat:%d分钟前, (int)(interval/60)]; } else { NSDateFormatter *formatter [[NSDateFormatter alloc] init]; formatter.dateFormat HH:mm; return [formatter stringFromDate:date]; } } // 昨天的时间显示 if ([calendar isDateInYesterday:date]) { NSDateFormatter *formatter [[NSDateFormatter alloc] init]; formatter.dateFormat 昨天 HH:mm; return [formatter stringFromDate:date]; } // 本周内的时间显示周一、周二... NSDateComponents *components [calendar components:NSCalendarUnitWeekOfYear fromDate:date toDate:now options:0]; if (components.weekOfYear 0) { NSDateFormatter *formatter [[NSDateFormatter alloc] init]; // 设置本地化为中文 formatter.locale [[NSLocale alloc] initWithLocaleIdentifier:zh_CN]; // 使用自定义的星期几格式 NSCalendar *cal [NSCalendar currentCalendar]; NSInteger weekday [cal component:NSCalendarUnitWeekday fromDate:date]; // 星期几的中文简称周日到周六 NSArray *weekdaySymbols [周日, 周一, 周二, 周三, 周四, 周五, 周六]; NSString *weekdayString weekdaySymbols[weekday-1]; // weekday从1开始 // 获取时间部分 formatter.dateFormat HH:mm; NSString *timeString [formatter stringFromDate:date]; return [NSString stringWithFormat:% %, weekdayString, timeString]; } // 今年的显示月-日 时:分 components [calendar components:NSCalendarUnitYear fromDate:date toDate:now options:0]; if (components.year 0) { NSDateFormatter *formatter [[NSDateFormatter alloc] init]; formatter.dateFormat MM-dd HH:mm; return [formatter stringFromDate:date]; } // 更早的时间年-月-日 时:分 NSDateFormatter *formatter [[NSDateFormatter alloc] init]; formatter.dateFormat yyyy-MM-dd HH:mm; return [formatter stringFromDate:date]; } end监听键盘弹起收回添加监听- (void)registerForKeyboardNotifications { [[NSNotificationCenter defaultCenter] addObserver:self selector:selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil]; }移除监听- (void)dealloc { // 移除键盘通知 [[NSNotificationCenter defaultCenter] removeObserver:self]; }监听的实现- (void)keyboardWillShow:(NSNotification *)notification { // 获取键盘高度 NSDictionary *userInfo [notification userInfo]; CGRect keyboardFrame [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]; self.keyboardHeight keyboardFrame.size.height; // 计算动画时间 NSTimeInterval duration [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]; // 调整 inputView 的位置 [UIView animateWithDuration:duration animations:^{ self.inputView.frame CGRectMake(0, Phone_Height - 100 - self.keyboardHeight, Phone_Width, 100); self.tableview.frame CGRectMake(0, 0, Phone_Width, Phone_Height - 100 - self.keyboardHeight); }]; // 滚动到最底部 [self scrollToBottomAnimated:YES]; } - (void)keyboardWillHide:(NSNotification *)notification { // 获取动画时间 NSDictionary *userInfo [notification userInfo]; NSTimeInterval duration [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]; // 恢复 inputView 的位置 [UIView animateWithDuration:duration animations:^{ self.inputView.frame CGRectMake(0, Phone_Height - 100, Phone_Width, 100); self.tableview.frame CGRectMake(0, 0, Phone_Width, Phone_Height - 100); }]; }