Implementation of WeChat chat page for iOS development
聊天界面的效果图如下:在下面的聊天界面中中用到了3类cell,一类是显示文字和表情的,一类是显示录音的,一类是显示图片的。当点击图片时会跳转到另一个Controller中来进行图片显示,在图片显示页面中添加了一个捏合的手势。点击播放按钮,会播放录制的音频,cell的大学会根据内容的多少来调整,而cell中textView的高度是通过约束来设置的。
一,定义我们要用的cell,代码如下:
1,显示表情和text的cell,代码如下,需要根据NSMutableAttributedString求出bound,然后改变cell上的ImageView和TextView的宽度的约束值,动态的调整气泡的大小,具体代码如下:
#import "TextCell.h" @interface TextCell() @property (strong, nonatomic) IBOutlet UIImageView *headImageView; @property (strong, nonatomic) IBOutlet UIImageView *chatBgImageView; @property (strong, nonatomic) IBOutlet UITextView *chatTextView; @property (strong, nonatomic) IBOutlet NSLayoutConstraint *chatBgImageWidthConstraint; @property (strong, nonatomic) IBOutlet NSLayoutConstraint *chatTextWidthConstaint; @property (strong, nonatomic) NSMutableAttributedString *attrString; @end @implementation TextCell -(void)setCellValue:(NSMutableAttributedString *)str { //移除约束 [self removeConstraint:_chatBgImageWidthConstraint]; [self removeConstraint:_chatTextWidthConstaint]; self.attrString = str; NSLog(@"%@",self.attrString); //由text计算出text的宽高 CGRect bound = [self.attrString boundingRectWithSize:CGSizeMake(150, 1000) options:NSStringDrawingUsesLineFragmentOrigin context:nil]; //根据text的宽高来重新设置新的约束 //背景的宽 NSString *widthImageString; NSArray *tempArray; widthImageString = [NSString stringWithFormat:@"H:[_chatBgImageView(%f)]", bound.size.width+45]; tempArray = [NSLayoutConstraint constraintsWithVisualFormat:widthImageString options:0 metrics:0 views:NSDictionaryOfVariableBindings(_chatBgImageView)]; _chatBgImageWidthConstraint = tempArray[0]; [self addConstraint:self.chatBgImageWidthConstraint]; widthImageString = [NSString stringWithFormat:@"H:[_chatTextView(%f)]", bound.size.width+20]; tempArray = [NSLayoutConstraint constraintsWithVisualFormat:widthImageString options:0 metrics:0 views:NSDictionaryOfVariableBindings(_chatTextView)]; _chatBgImageWidthConstraint = tempArray[0]; [self addConstraint:self.chatBgImageWidthConstraint]; //设置图片 UIImage *image = [UIImage imageNamed:@"chatfrom_bg_normal.png"]; image = [image resizableImageWithCapInsets:(UIEdgeInsetsMake(image.size.height * 0.6, image.size.width * 0.4, image.size.height * 0.3, image.size.width * 0.4))]; //image = [image stretchableImageWithLeftCapWidth:image.size.width * 0.5 topCapHeight:image.size.height * 0.5]; [self.chatBgImageView setImage:image]; self.chatTextView.attributedText = str; } @end
2.显示图片的cell,通过block回调把图片传到Controller中,用于放大图片使用。
#import "MyImageCell.h" @interface MyImageCell() @property (strong, nonatomic) IBOutlet UIImageView *bgImageView; @property (strong, nonatomic) IBOutlet UIButton *imageButton; @property (strong, nonatomic) ButtonImageBlock imageBlock; @property (strong, nonatomic) UIImage *buttonImage; @end @implementation MyImageCell -(void)setCellValue:(UIImage *)sendImage { self.buttonImage = sendImage; UIImage *image = [UIImage imageNamed:@"chatto_bg_normal.png"]; image = [image resizableImageWithCapInsets:(UIEdgeInsetsMake(image.size.height * 0.6, image.size.width * 0.4, image.size.height * 0.3, image.size.width * 0.4))]; [self.bgImageView setImage:image]; [self.imageButton setImage:sendImage forState:UIControlStateNormal]; } -(void)setButtonImageBlock:(ButtonImageBlock)block { self.imageBlock = block; } - (IBAction)tapImageButton:(id)sender { self.imageBlock(self.buttonImage); } @end
3.显示录音的cell,点击cell上的button,播放对应的录音,代码如下:
#import "VoiceCellTableViewCell.h" @interface VoiceCellTableViewCell() @property (strong, nonatomic) NSURL *playURL; @property (strong, nonatomic) AVAudioPlayer *audioPlayer; @end @implementation VoiceCellTableViewCell -(void)setCellValue:(NSDictionary *)dic { _playURL = dic[@"body"][@"content"]; } - (IBAction)tapVoiceButton:(id)sender { NSError *error = nil; AVAudioPlayer *player = [[AVAudioPlayer alloc]initWithContentsOfURL:_playURL error:&error]; if (error) { NSLog(@"播放错误:%@",[error description]); } self.audioPlayer = player; [self.audioPlayer play]; } @end
二,cell搞定后要实现我们的ChatController部分
ChatController.m中的延展和枚举代码如下:
//枚举Cell类型 typedef enum : NSUInteger { SendText, SendVoice, SendImage } MySendContentType; //枚举用户类型 typedef enum : NSUInteger { MySelf, MyFriend } UserType; @interface ChatViewController () //工具栏 @property (nonatomic,strong) ToolView *toolView; //音量图片 @property (strong, nonatomic) UIImageView *volumeImageView; //工具栏的高约束,用于当输入文字过多时改变工具栏的约束 @property (strong, nonatomic) NSLayoutConstraint *tooViewConstraintHeight; //存放所有的cell中的内容 @property (strong, nonatomic) NSMutableArray *dataSource; //storyBoard上的控件 @property (strong, nonatomic) IBOutlet UITableView *myTableView; //用户类型 @property (assign, nonatomic) UserType userType; //从相册获取图片 @property (strong, nonatomic) UIImagePickerController *imagePiceker; @end
实现工具栏中的回调的代码如下,通过Block,工具栏和ViewController交互
//实现工具栏的回调 -(void)setToolViewBlock { __weak __block ChatViewController *copy_self = self; //通过block回调接收到toolView中的text [self.toolView setMyTextBlock:^(NSString *myText) { NSLog(@"%@",myText); [copy_self sendMessage:SendText Content:myText]; }]; //回调输入框的contentSize,改变工具栏的高度 [self.toolView setContentSizeBlock:^(CGSize contentSize) { [copy_self updateHeight:contentSize]; }]; //获取录音声量,用于声音音量的提示 [self.toolView setAudioVolumeBlock:^(CGFloat volume) { copy_self.volumeImageView.hidden = NO; int index = (int)(volume*100)%6+1; [copy_self.volumeImageView setImage:[UIImage imageNamed:[NSString stringWithFormat:@"record_animate_%02d.png",index]]]; }]; //获取录音地址(用于录音播放方法) [self.toolView setAudioURLBlock:^(NSURL *audioURL) { copy_self.volumeImageView.hidden = YES; [copy_self sendMessage:SendVoice Content:audioURL]; }]; //录音取消(录音取消后,把音量图片进行隐藏) [self.toolView setCancelRecordBlock:^(int flag) { if (flag == 1) { copy_self.volumeImageView.hidden = YES; } }]; //扩展功能回调 [self.toolView setExtendFunctionBlock:^(int buttonTag) { switch (buttonTag) { case 1: //从相册获取 [copy_self presentViewController:copy_self.imagePiceker animated:YES completion:^{ }]; break; case 2: //拍照 break; default: break; } }]; }
把聊天工具栏中返回的内容显示在tableView中,代码如下:
//发送消息 -(void)sendMessage:(MySendContentType) sendType Content:(id)content { //把收到的url封装成字典 UserType userType = self.userType; NSMutableDictionary *tempDic = [[NSMutableDictionary alloc] initWithCapacity:2]; [tempDic setValue:@(userType) forKey:@"userType"]; NSDictionary *bodyDic = @{@"type":@(sendType), @"content":content}; [tempDic setValue:bodyDic forKey:@"body"]; [self.dataSource addObject:tempDic]; //重载tableView [self.myTableView reloadData]; NSIndexPath *indexPath = [NSIndexPath indexPathForRow:self.dataSource.count-1 inSection:0]; [self.myTableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES]; }
根据ToolView中回调接口,获取工具栏中textView的ContentSize,通过ContentSize来调整ToolView的高度约束,代码如下:
//更新toolView的高度约束 -(void)updateHeight:(CGSize)contentSize { float height = contentSize.height + 18; if (height <= 80) { [self.view removeConstraint:self.tooViewConstraintHeight]; NSString *string = [NSString stringWithFormat:@"V:[_toolView(%f)]", height]; NSArray * tooViewConstraintV = [NSLayoutConstraint constraintsWithVisualFormat:string options:0 metrics:0 views:NSDictionaryOfVariableBindings(_toolView)]; self.tooViewConstraintHeight = tooViewConstraintV[0]; [self.view addConstraint:self.tooViewConstraintHeight]; } }
从本地获取图片,并显示在相应的Cell上,代码如下:
//获取图片后要做的方法 -(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { UIImage *pickerImage = info[UIImagePickerControllerEditedImage]; //发送图片 [self sendMessage:SendImage Content:pickerImage]; [self dismissViewControllerAnimated:YES completion:^{}]; } -(void)imagePickerControllerDidCancel:(UIImagePickerController *)picker { //在ImagePickerView中点击取消时回到原来的界面 [self dismissViewControllerAnimated:YES completion:^{}]; }
把NSString 转换成NSMutableAttributeString,用于显示表情,代码如下:
//显示表情,用属性字符串显示表情 -(NSMutableAttributedString *)showFace:(NSString *)str { //加载plist文件中的数据 NSBundle *bundle = [NSBundle mainBundle]; //寻找资源的路径 NSString *path = [bundle pathForResource:@"emoticons" ofType:@"plist"]; //获取plist中的数据 NSArray *face = [[NSArray alloc] initWithContentsOfFile:path]; //创建一个可变的属性字符串 NSMutableAttributedString *attributeString = [[NSMutableAttributedString alloc] initWithString:str]; UIFont *baseFont = [UIFont systemFontOfSize:17]; [attributeString addAttribute:NSFontAttributeName value:baseFont range:NSMakeRange(0, str.length)]; //正则匹配要替换的文字的范围 //正则表达式 NSString * pattern = @"\\[[a-zA-Z0-9\\u4e00-\\u9fa5]+\\]"; NSError *error = nil; NSRegularExpression * re = [NSRegularExpression regularExpressionWithPattern:pattern options:NSRegularExpressionCaseInsensitive error:&error]; if (!re) { NSLog(@"%@", [error localizedDescription]); } //通过正则表达式来匹配字符串 NSArray *resultArray = [re matchesInString:str options:0 range:NSMakeRange(0, str.length)]; //用来存放字典,字典中存储的是图片和图片对应的位置 NSMutableArray *imageArray = [NSMutableArray arrayWithCapacity:resultArray.count]; //根据匹配范围来用图片进行相应的替换 for(NSTextCheckingResult *match in resultArray) { //获取数组元素中得到range NSRange range = [match range]; //获取原字符串中对应的值 NSString *subStr = [str substringWithRange:range]; for (int i = 0; i < face.count; i ++) { if ([face[i][@"chs"] isEqualToString:subStr]) { //face[i][@"gif"]就是我们要加载的图片 //新建文字附件来存放我们的图片 NSTextAttachment *textAttachment = [[NSTextAttachment alloc] init]; //给附件添加图片 textAttachment.image = [UIImage imageNamed:face[i][@"png"]]; //把附件转换成可变字符串,用于替换掉源字符串中的表情文字 NSAttributedString *imageStr = [NSAttributedString attributedStringWithAttachment:textAttachment]; //把图片和图片对应的位置存入字典中 NSMutableDictionary *imageDic = [NSMutableDictionary dictionaryWithCapacity:2]; [imageDic setObject:imageStr forKey:@"image"]; [imageDic setObject:[NSValue valueWithRange:range] forKey:@"range"]; //把字典存入数组中 [imageArray addObject:imageDic]; } } } //从后往前替换 for (int i = imageArray.count -1; i >= 0; i--) { NSRange range; [imageArray[i][@"range"] getValue:&range]; //进行替换 [attributeString replaceCharactersInRange:range withAttributedString:imageArray[i][@"image"]]; } return attributeString; }
根据Cell显示内容来调整Cell的高度,代码如下:
//调整cell的高度 -(float)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { //根据文字计算cell的高度 if ([self.dataSource[indexPath.row][@"body"][@"type"] isEqualToNumber:@(SendText)]) { NSMutableAttributedString *contentText = [self showFace:self.dataSource[indexPath.row][@"body"][@"content"]]; CGRect textBound = [contentText boundingRectWithSize:CGSizeMake(150, 1000) options:NSStringDrawingUsesLineFragmentOrigin context:nil]; float height = textBound.size.height + 40; return height; } if ([self.dataSource[indexPath.row][@"body"][@"type"] isEqualToNumber:@(SendVoice)]) { return 73; } if ([self.dataSource[indexPath.row][@"body"][@"type"] isEqualToNumber:@(SendImage)]) { return 125; } return 100; }
根据cell内容和用户类型,来选择Cell,代码如下:
//设置cell - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { //根据类型选cell MySendContentType contentType = [self.dataSource[indexPath.row][@"body"][@"type"] integerValue]; if ([self.dataSource[indexPath.row][@"userType"] isEqual: @(MyFriend)]) { switch (contentType) { case SendText: { TextCell *cell = [tableView dequeueReusableCellWithIdentifier:@"textCell" forIndexPath:indexPath]; NSMutableAttributedString *contentText = [self showFace:self.dataSource[indexPath.row][@"body"][@"content"]]; [cell setCellValue:contentText]; return cell; } break; case SendImage: { heImageCell *cell = [tableView dequeueReusableCellWithIdentifier:@"heImageCell" forIndexPath:indexPath]; [cell setCellValue:self.dataSource[indexPath.row][@"body"][@"content"]]; __weak __block ChatViewController *copy_self = self; //传出cell中的图片 [cell setButtonImageBlock:^(UIImage *image) { [copy_self displaySendImage:image]; }]; return cell; } break; case SendVoice: { VoiceCellTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"heVoiceCell" forIndexPath:indexPath]; [cell setCellValue:self.dataSource[indexPath.row]]; return cell; } break; default: break; } } if ([self.dataSource[indexPath.row][@"userType"] isEqual: @(MySelf)]) { switch (contentType) { case SendText: { TextCell *cell = [tableView dequeueReusableCellWithIdentifier:@"myselfTextCell" forIndexPath:indexPath]; NSMutableAttributedString *contentText = [self showFace:self.dataSource[indexPath.row][@"body"][@"content"]]; [cell setCellValue:contentText]; return cell; } break; case SendImage: { MyImageCell *cell = [tableView dequeueReusableCellWithIdentifier:@"myImageCell" forIndexPath:indexPath]; [cell setCellValue:self.dataSource[indexPath.row][@"body"][@"content"]]; __weak __block ChatViewController *copy_self = self; //传出cell中的图片 [cell setButtonImageBlock:^(UIImage *image) { [copy_self displaySendImage:image]; }]; return cell; } break; case SendVoice: { VoiceCellTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"myVoiceCell" forIndexPath:indexPath]; [cell setCellValue:self.dataSource[indexPath.row]]; return cell; } break; default: break; } } UITableViewCell *cell; return cell; }
点击发送的图片来放大图片代码如下:
//发送图片的放大 -(void) displaySendImage : (UIImage *)image { //把照片传到放大的controller中 UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]]; ImageViewController *imageController = [storyboard instantiateViewControllerWithIdentifier:@"imageController"]; [imageController setValue:image forKeyPath:@"image"]; [self.navigationController pushViewController:imageController animated:YES]; }
根据键盘的高度来调整ToolView的位置,代码如下:
//键盘出来的时候调整tooView的位置 -(void) keyChange:(NSNotification *) notify { NSDictionary *dic = notify.userInfo; CGRect endKey = [dic[@"UIKeyboardFrameEndUserInfoKey"] CGRectValue]; //坐标系的转换 CGRect endKeySwap = [self.view convertRect:endKey fromView:self.view.window]; //运动时间 [UIView animateWithDuration:[dic[UIKeyboardAnimationDurationUserInfoKey] floatValue] animations:^{ [UIView setAnimationCurve:[dic[UIKeyboardAnimationCurveUserInfoKey] doubleValue]]; CGRect frame = self.view.frame; frame.size.height = endKeySwap.origin.y; self.view.frame = frame; [self.view layoutIfNeeded]; }]; }
代码有点多,不过在关键的部分都加有注释,在图片显示View中通过捏合手势来调整图片的大小,代码如下:
- (IBAction)tapPichGesture:(id)sender { UIPinchGestureRecognizer *gesture = sender; //手势改变时 if (gesture.state == UIGestureRecognizerStateChanged) { //捏合手势中scale属性记录的缩放比例 self.myImageView.transform = CGAffineTransformMakeScale(gesture.scale, gesture.scale); } }
更多iOS开发之微信聊天页面实现 相关文章请关注PHP中文网!

Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics

In Elden's Ring, the UI page of this game will be automatically hidden after a period of time. Many players do not know how the UI is always displayed. Players can select the gauge display configuration in the display and sound configuration. Click to turn it on. Why does the Elden Ring UI keep displaying? 1. First, after we enter the main menu, click [System Configuration]. 2. In the [Display and Sound Configuration] interface, select the meter display configuration. 3. Click Enable to complete.

Vue is a popular JavaScript framework that uses a component-based approach to build web applications. In the Vue ecosystem, there are many UI component libraries that can help you quickly build beautiful interfaces and provide rich functions and interactive effects. In this article, we will introduce some common VueUI component libraries. ElementUIElementUI is a Vue component library developed by the Ele.me team. It provides developers with a set of elegant,

For AI, "playing with mobile phones" is not an easy task. Just identifying various user interfaces (UI) is a big problem: not only must the type of each component be identified, but also the symbols used , position to determine the function of the component. Understanding the UI of mobile devices can help realize various human-computer interaction tasks, such as UI automation. Previous work on mobile UI modeling usually relies on the view hierarchy information of the screen, directly utilizing the structural data of the UI, and thereby bypassing the problem of identifying components starting from the screen pixels. However, view hierarchy is not available in all scenarios. This method usually outputs erroneous results due to missing object descriptions or misplaced structural information, so even if you use

UI is the abbreviation of "User Interface", which is mainly used to describe the human-computer interaction, operation logic and beautiful interface of the software. The purpose of UI design is to make software operation easier and more comfortable, and fully reflect its positioning and characteristics. Common UI designs are divided into physical UI and virtual UI, among which virtual UI is widely used in the Internet field.

The jQuery mobile UI framework is a tool for developing mobile applications. It provides rich interface components and interactive effects, allowing developers to quickly build excellent mobile user interfaces. In this article, we will explore some of the most popular jQuery mobile UI frameworks and provide specific code examples to help readers better understand and use these frameworks. 1.jQueryMobiljQueryMobile is an open source mobile UI framework based on HTML5 and CSS3.

A few days ago, Google officially pushed the Android 15 Beta 4 update to eligible Pixel smartphone and tablet users. This marks that the Android 15 operating system has entered the platform stable stage, indicating that its stable version will be officially released with global users in the next few days. Meet. At the same time, this development also injects new vitality into Samsung Electronics' Galaxy device series to accelerate the development process of its OneUI7.0 version. 1.[Android15Beta4 promotes Samsung OneUI7.0 stable build](https://www.cnbeta.com/articles/tech/1427022.htm) With Android15Bet

The difference between ux and ui design: 1. UX makes the interface easier to use, and UI makes the interface more beautiful; 2. UX allows users to achieve their goals, and UI makes the interface enhance the brand sense; 3. UX core goals guide users to complete tasks, UI does not; 4. The deliverables of UI and UX are different. The output of UX includes UX experience report, function definition, function planning, project progress, etc., while the output of UI includes visual and interaction, visual design, brand design, motion design, and component design. and design language, etc.

UI, the full name of user interface, refers to the design of human-computer interaction, operation logic and beautiful interface in software. It is divided into physical UI and virtual UI, among which virtual UI is widely used in mobile Internet. Good UI design not only makes the software look tasteful, but more importantly, makes software operation comfortable and easy, fully reflecting the software's positioning and characteristics.
