聊天界面的效果圖如下:在下面的聊天界面中中用到了3類cell,一類是顯示文字和表情的,一類是顯示錄音的,一類是顯示圖片的。當(dāng)點擊圖片時會跳轉(zhuǎn)到另一個Controller中來進行圖片顯示,在圖片顯示頁面中添加了一個捏合的手勢。點擊播放按鈕,會播放錄制的音頻,cell的大學(xué)會根據(jù)內(nèi)容的多少來調(diào)整,而cell中textView的高度是通過約束來設(shè)置的。
一,定義我們要用的cell,代碼如下:
1,顯示表情和text的cell,代碼如下,需要根據(jù)NSMutableAttributedString求出bound,然后改變cell上的ImageView和TextView的寬度的約束值,動態(tài)的調(diào)整氣泡的大小,具體代碼如下:
#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]; //根據(jù)text的寬高來重新設(shè)置新的約束 //背景的寬 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]; //設(shè)置圖片 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回調(diào)把圖片傳到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,播放對應(yīng)的錄音,代碼如下:
#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搞定后要實現(xiàn)我們的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; //工具欄的高約束,用于當(dāng)輸入文字過多時改變工具欄的約束 @property (strong, nonatomic) NSLayoutConstraint *tooViewConstraintHeight; //存放所有的cell中的內(nèi)容 @property (strong, nonatomic) NSMutableArray *dataSource; //storyBoard上的控件 @property (strong, nonatomic) IBOutlet UITableView *myTableView; //用戶類型 @property (assign, nonatomic) UserType userType; //從相冊獲取圖片 @property (strong, nonatomic) UIImagePickerController *imagePiceker; @end
實現(xiàn)工具欄中的回調(diào)的代碼如下,通過Block,工具欄和ViewController交互
//實現(xiàn)工具欄的回調(diào) -(void)setToolViewBlock { __weak __block ChatViewController *copy_self = self; //通過block回調(diào)接收到toolView中的text [self.toolView setMyTextBlock:^(NSString *myText) { NSLog(@"%@",myText); [copy_self sendMessage:SendText Content:myText]; }]; //回調(diào)輸入框的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; } }]; //擴展功能回調(diào) [self.toolView setExtendFunctionBlock:^(int buttonTag) { switch (buttonTag) { case 1: //從相冊獲取 [copy_self presentViewController:copy_self.imagePiceker animated:YES completion:^{ }]; break; case 2: //拍照 break; default: break; } }]; }
把聊天工具欄中返回的內(nèi)容顯示在tableView中,代碼如下:
//發(fā)送消息 -(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]; }
根據(jù)ToolView中回調(diào)接口,獲取工具欄中textView的ContentSize,通過ContentSize來調(diào)整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]; } }
從本地獲取圖片,并顯示在相應(yīng)的Cell上,代碼如下:
//獲取圖片后要做的方法 -(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { UIImage *pickerImage = info[UIImagePickerControllerEditedImage]; //發(fā)送圖片 [self sendMessage:SendImage Content:pickerImage]; [self dismissViewControllerAnimated:YES completion:^{}]; } -(void)imagePickerControllerDidCancel:(UIImagePickerController *)picker { //在ImagePickerView中點擊取消時回到原來的界面 [self dismissViewControllerAnimated:YES completion:^{}]; }
把NSString 轉(zhuǎn)換成NSMutableAttributeString,用于顯示表情,代碼如下:
//顯示表情,用屬性字符串顯示表情 -(NSMutableAttributedString *)showFace:(NSString *)str { //加載plist文件中的數(shù)據(jù) NSBundle *bundle = [NSBundle mainBundle]; //尋找資源的路徑 NSString *path = [bundle pathForResource:@"emoticons" ofType:@"plist"]; //獲取plist中的數(shù)據(jù) NSArray *face = [[NSArray alloc] initWithContentsOfFile:path]; //創(chuàng)建一個可變的屬性字符串 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)]; //用來存放字典,字典中存儲的是圖片和圖片對應(yīng)的位置 NSMutableArray *imageArray = [NSMutableArray arrayWithCapacity:resultArray.count]; //根據(jù)匹配范圍來用圖片進行相應(yīng)的替換 for(NSTextCheckingResult *match in resultArray) { //獲取數(shù)組元素中得到range NSRange range = [match range]; //獲取原字符串中對應(yīng)的值 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"]]; //把附件轉(zhuǎn)換成可變字符串,用于替換掉源字符串中的表情文字 NSAttributedString *imageStr = [NSAttributedString attributedStringWithAttachment:textAttachment]; //把圖片和圖片對應(yīng)的位置存入字典中 NSMutableDictionary *imageDic = [NSMutableDictionary dictionaryWithCapacity:2]; [imageDic setObject:imageStr forKey:@"image"]; [imageDic setObject:[NSValue valueWithRange:range] forKey:@"range"]; //把字典存入數(shù)組中 [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; }
根據(jù)Cell顯示內(nèi)容來調(diào)整Cell的高度,代碼如下:
//調(diào)整cell的高度 -(float)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { //根據(jù)文字計算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; }
根據(jù)cell內(nèi)容和用戶類型,來選擇Cell,代碼如下:
//設(shè)置cell - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { //根據(jù)類型選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; }
點擊發(fā)送的圖片來放大圖片代碼如下:
//發(fā)送圖片的放大 -(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]; }
根據(jù)鍵盤的高度來調(diào)整ToolView的位置,代碼如下:
//鍵盤出來的時候調(diào)整tooView的位置 -(void) keyChange:(NSNotification *) notify { NSDictionary *dic = notify.userInfo; CGRect endKey = [dic[@"UIKeyboardFrameEndUserInfoKey"] CGRectValue]; //坐標(biāo)系的轉(zhuǎn)換 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]; }]; }
代碼有點多,不過在關(guān)鍵的部分都加有注釋,在圖片顯示View中通過捏合手勢來調(diào)整圖片的大小,代碼如下:
- (IBAction)tapPichGesture:(id)sender { UIPinchGestureRecognizer *gesture = sender; //手勢改變時 if (gesture.state == UIGestureRecognizerStateChanged) { //捏合手勢中scale屬性記錄的縮放比例 self.myImageView.transform = CGAffineTransformMakeScale(gesture.scale, gesture.scale); } }
更多iOS開發(fā)之微信聊天頁面實現(xiàn)?相關(guān)文章請關(guān)注PHP中文網(wǎng)!

? AI ??

Undress AI Tool
??? ???? ??

Undresser.AI Undress
???? ?? ??? ??? ?? AI ?? ?

AI Clothes Remover
???? ?? ???? ??? AI ?????.

Clothoff.io
AI ? ???

Video Face Swap
??? ??? AI ?? ?? ??? ???? ?? ???? ??? ?? ????!

?? ??

??? ??

???++7.3.1
???? ?? ?? ?? ???

SublimeText3 ??? ??
??? ??, ???? ?? ????.

???? 13.0.1 ???
??? PHP ?? ?? ??

???? CS6
??? ? ?? ??

SublimeText3 Mac ??
? ??? ?? ?? ?????(SublimeText3)

Elden's Ring??? ?? ??? ??? ? ??? UI ???? ???? ?????. ?? ????? UI? ?? ??? ????? ?? ????. ????? ????? ? ??? ???? ??? ?? ??? ????? ?? ???. ???. ??? UI? ?? ???? ??? ?????? 1. ?? ?? ??? ??? ? [??? ??]? ?????. 2. [????? ? ??? ??] ??????? ?? ????? ??? ?????. 3. ???? ???? ?????.

Vue? ?? ?? ?? ?? ??? ???? ? ??????? ???? ?? ?? JavaScript ????????. Vue ????? ???? ?????? ??? ???? ??? ??? ??? ??? ???? ? ??? ?? ?? UI ?? ?? ?????? ????. ? ????? ? ?? ???? VueUI ?? ?? ?????? ?????. ElementUIElementUI? Ele.me ??? ??? Vue ?? ?? ?????? ????? ????

AI? ?? '???? ??? ?? ?'? ?? ?? ???. ?? ??? ??? ?????(UI)? ???? ????? ? ???. ? ?? ??? ??? ???? ? ?? ???, ??? ???? ?? ??? ??, ??? ???? ??. ?? ???. ??? ??? UI? ???? UI ???? ?? ??? ??-??? ?? ?? ??? ???? ? ??? ? ? ????. ??? UI ???? ?? ?? ??? ????? ??? ? ?? ??? ???? UI? ?? ???? ?? ???? ?? ???? ???? ?? ??? ???? ??? ?????. ??? ? ?? ??? ?? ?????? ??? ? ?? ?? ????. ? ??? ????? ?? ??? ????? ??? ??? ?? ??? ?? ??? ??? ?????

UI? "User Interface"? ??? ?? ??? ???? ?? ??, ?? ?? ? ?????? ???? ?????? ???? ? ?????. UI ???? ??? ????? ??? ?? ?? ???? ??? ?????? ??? ??? ??? ???? ????. ???? UI ???? ??? UI? ?? UI? ?????, ? ? ?? UI? ??? ???? ?? ???? ??.

jQuery ??? UI ?????? ??? ?????? ??? ?? ???, ??? ????? ????? ??? ??? ???? ???? ??? ??? ??? ?????? ???? ??? ? ??? ???. ? ????? ?? ?? ?? jQuery ??? UI ????? ? ??? ???? ??? ??? ?????? ? ? ???? ??? ? ??? ???? ?? ??? ?????. 1.jQueryMobil jQueryMobile? HTML5 ? CSS3 ??? ?? ?? ??? UI ????????.

?? ? Google? ??? ?? Pixel ???? ? ??? ????? Android 15 ?? 4 ????? ????? ??????. ?? Android 15 ?? ??? ??? ?? ??? ????? ????, ?? ?? ??? 2019? ? ?? ????? ?? ??? ??? ?????. ??? ?? ??. ??? ?? ??? ???? ??? ?? ???? ??? ??? ???? OneUI7.0 ??? ?? ????? ??????. 1.[Android15Beta4? Samsung OneUI7.0 ???? ??? ??](https://www.cnbeta.com/articles/tech/1427022.htm) Android15Bet ??

??? ?????? ?? ??? UI? ??? ???? ?? ??, ?? ?? ? ?????? ???? ????? ???? ?????. ??? UI? ?? UI? ?????, ? ? ?? UI? ??? ????? ?? ???? ??. ?? UI ???? ?????? ??? ??? ? ?? ???, ? ????? ?????? ????? ??? ??? ???? ????? ??? ???? ?? ????.

UX? UI ???? ???? ??? ????. 1. UX? ?????? ? ?? ???, UI? ?????? ?? ???? ????. 2. UX? ???? ??? ??? ? ?? ??, UI? ?????? ?? ??? ??? ??????. ??? ???? ??? ????? ????? UI? ??? ????. 4. UI? UX? ???? ?? ????. UX? ????? UX ?? ???, ?? ??, ?? ??, ???? ?? ?? ????? UI? ????? ??? ?????. ????, ?? ???, ??? ???, ?? ???, ???? ??? ? ??? ?? ?? ????.
