• 【iOS基础控件


    A.需求
    做出一个类似于QQ、微信的聊天界面
    1.每个cell包含发送时间、发送人(头像)、发送信息
    2.使用对方头像放在左边,我方头像在右边
    3.对方信息使用白色背景对话框,我方信息使用蓝色背景对话框
    4.隐藏相同的发送时间
    5.底部功能按钮:语音按钮、消息输入框、表情按钮、附加按钮
    6.响应键盘事件,呼出键盘、隐藏键盘时对上述的视图作出上移操作
    7.键盘的发送事件处理
     
    Code Source: 
     
     
    Image(108)
     
    B.实现点
    1.底层视图搭建
    上部分聊天信息框:UITableView
    下部分功能区:UIButton
    信息输入框使用无边框,然后使用自带背景图片,以保证在不同版本的iOS中样式一致
    9CEC2AC9-325F-44AA-8CBC-068833C5BD63
     
     
    2.构建框架
    依照“微博展示”的代码框架,设计:
    • 自定义message模型
    • 自定义cell
    • 装载了message模型和cell子控件位置尺寸的frame
     
    Image(109)
     
    3.使用扩展,给NSString加上文本size计算的功能
    复制代码
     1 @implementation NSString (Extension)
     2 
     3 /** 测量文本的尺寸 */
     4 - (CGSize)sizeWithFont:(UIFont *)font maxSize:(CGSize)maxSize {
     5     NSDictionary *attrs = @{NSFontAttributeName: font};
     6     CGSize size =  [self boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:attrs context:nil].size;
     7    
     8     return size;
     9 }
    10 
    11 @end
    复制代码
     
    给信息文本框计算位置和尺寸
    复制代码
     1     // 3.信息,尺寸可变
     2     CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
     3     // 3.1 设置最大尺寸
     4     CGSize textMaxSize = CGSizeMake(screenWidth - iconWidth - padding * 5, MAXFLOAT);
     5     // 3.2 计算真实尺寸
     6     CGSize textRealSize = [message.text sizeWithFont:MESSAGE_TEXT_FONT maxSize:textMaxSize];
     7 
     8     // 3.3 调整信息的位置
     9     CGFloat textX;
    10     if (MessageTypeMe == message.type) {
    11         // 我方,放在靠右
    12         textX = CGRectGetMinX(_iconFrame) - textRealSize.width - padding;
    13     } else {
    14         // 对方,放在靠左
    15         textX = CGRectGetMaxX(_iconFrame) + padding;
    16     }
    17    
    18     CGFloat textY = iconY;
    19     _textFrame = CGRectMake(textX, textY, textRealSize.width, textRealSize.height);
    复制代码
     
    4.使用数据中的信息类型,判断是我方发出的信息还是对方发出的信息,计算头像和信息的位置
    复制代码
     1     // 2.头像
     2     CGFloat iconWidth = 40;
     3     CGFloat iconHeight = 40;
     4    
     5     // 2.1 根据信息的发送方调整头像位置
     6     CGFloat iconX;
     7     if (MessageTypeMe == message.type) {
     8         // 我方,放在右边
     9         iconX = [UIScreen mainScreen].bounds.size.width - padding - iconWidth;
    10     } else {
    11         // 对方,放在左边
    12         iconX = padding;
    13     }
    14    
    15     CGFloat iconY = CGRectGetMaxY(_timeFrame) + padding;
    16     _iconFrame = CGRectMake(iconX, iconY, iconWidth, iconHeight);
    复制代码
     
    Image(110)
     
    5.加上信息背景框
    我方:使用蓝色背景聊天框
    对方:使用白色背景聊天框
    重点:图片的中心拉伸,利用图片分别水平、垂直方向某个区域进行拉伸,保持其他部分的图形 —>对UIImage使用扩展,返回具备了特定拉伸方式属性的图片
     
    拉伸图片方式属性:
    复制代码
     1 @implementation UIImage (Extension)
     2 
     3 + (UIImage *) resizableImage:(NSString *) imageName {
     4     UIImage *image = [UIImage imageNamed:imageName];
     5     // 取图片中部的1 x 1进行拉伸
     6     UIEdgeInsets insets = UIEdgeInsetsMake(image.size.height/2, image.size.width/2, image.size.height/2 + 1, image.size.width/2 + 1);
     7     return [image resizableImageWithCapInsets:insets];
     8 }
     9 
    10 @end
    复制代码
     
    设置图片:
    复制代码
     1     // 3.1 设置聊天框
     2     NSString *chatImageNormalName;
     3     NSString *chatImageHighlightedName;
     4     if (MessageTypeMe == messageFrame.message.type) {
     5         chatImageNormalName = @"chat_send_nor";
     6         chatImageHighlightedName = @"chat_send_press_pic";
     7     } else {
     8         chatImageNormalName = @"chat_receive_nor";
     9         chatImageHighlightedName = @"chat_receive_press_pic";
    10     }
    11    
    12     UIImage *chatImageNormal = [UIImage resizableImage:chatImageNormalName];
    13     UIImage *chatImageHighlighted = [UIImage resizableImage:chatImageHighlightedName];
    14     [self.textView setBackgroundImage:chatImageNormal forState:UIControlStateNormal];
    15     [self.textView setBackgroundImage:chatImageHighlighted forState:UIControlStateHighlighted];
    复制代码
     
    Image(111)
     
    6.调整信息文字内边距,让文字被“包裹”在聊天框内
    1     // 3.2 调整文字的内边距
    2     textView.contentEdgeInsets = UIEdgeInsetsMake(TEXT_INSET, TEXT_INSET, TEXT_INSET, TEXT_INSET);
     
    因为背景图片边缘有空白,改变了文字内边距之后,高度会变高,需要对装载信息的view的frame尺寸做出相应改变:
    复制代码
     1     // 3.信息,尺寸可变
     2     CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
     3     // 3.1 设置文本最大尺寸
     4     CGSize textMaxSize = CGSizeMake(screenWidth - iconWidth - padding * 10, MAXFLOAT);
     5     // 3.2 计算文本真实尺寸
     6     CGSize textRealSize = [message.text sizeWithFont:MESSAGE_TEXT_FONT maxSize:textMaxSize];
     7    
     8     // 3.3 按钮尺寸
     9     CGSize btnSize = CGSizeMake(textRealSize.width + TEXT_INSET*2, textRealSize.height + TEXT_INSET*2);
    10 
    11     // 3.4 调整信息的位置
    12     CGFloat textX;
    13     if (MessageTypeMe == message.type) {
    14         // 我方,放在靠右
    15         textX = CGRectGetMinX(_iconFrame) - btnSize.width - padding;
    16     } else {
    17         // 对方,放在靠左
    18         textX = CGRectGetMaxX(_iconFrame) + padding;
    19     }
    20    
    21     CGFloat textY = iconY;
    22     _textFrame = CGRectMake(textX, textY, btnSize.width, btnSize.height);
    复制代码
     
    Image(112)
     
    7.屏蔽相同的发送时间
    (1)在message模型中定义一个标志
    1 /** 是否隐藏发送时间 */
    2 @property(nonatomic, assign) BOOL hideTime;
     
    (2)当控制器从plist文件装载信息的时候初始化此标志
    1 // 判断是否发送时间与上一条信息的发送时间相同,若是则不用显示了
    2 MessageFrame *lastMessageFrame = [mdictArray lastObject];
    3 if (lastMessageFrame && [message.time isEqualToString:lastMessageFrame.message.time]) {
    4      message.hideTime = YES;
    5 }
                  
    (3)只有hideTime ==  NO,计算frame的时候,长期需要计算发送时间的frame
    复制代码
    1     // 1.发送时间
    2     if (NO == message.hideTime) {
    3         CGFloat timeWidth = [UIScreen mainScreen].bounds.size.width;
    4         CGFloat timeHeight = 40;
    5         CGFloat timeX = 0;
    6         CGFloat timeY = 0;
    7         _timeFrame = CGRectMake(timeX, timeY, timeWidth, timeHeight);
    8     }
    复制代码
     
    Image(113)
     
    8.响应键盘呼出缩回事件,上移或下移恢复整个版面(聊天区和功能区)
    (1)设置控制器为键盘监听器
        // 设置虚拟键盘监听器
    1     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];
     
    (2)编写监听方法
    复制代码
     1 /** 点击拖曳聊天区的时候,缩回键盘 */
     2 - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
     3     // 1.缩回键盘
     4     [self.view endEditing:YES];
     5 }
     6 
     7 
     8 #pragma mark - 监听事件
     9 - (void) keyboardWillChangeFrame:(NSNotification *) note {
    10     // 1.取得弹出后的键盘frame
    11     CGRect keyboardFrame = [note.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
    12    
    13     // 2.键盘弹出的耗时时间
    14     CGFloat duration = [note.userInfo[UIKeyboardAnimationDurationUserInfoKey] floatValue];
    15    
    16     // 3.键盘变化时,view的位移,包括了上移/恢复下移
    17     CGFloat transformY = keyboardFrame.origin.y - self.view.frame.size.height;
    18    
    19     [UIView animateWithDuration:duration animations:^{
    20         self.view.transform = CGAffineTransformMakeTranslation(0, transformY);
    21     }];
    22    
    23   
    24 }
    复制代码
     
    Image(114)
     
    9.设置数据框TextField,改变键盘
    (1)当没有文字的时候禁用回车,设置回车样式
    Image(115)
     
    Image(116)
     
    (2)调不出中文键盘
    虽然在设置里面添加了中文键盘,但是依然找不到进入中文键盘的按钮
     
     
    10.发送消息
    (1)拖入信息输入框到控制器,设置控制器为输入框TextField的代理
    1     // 设置信息输入框的代理
    2     self.inputView.delegate = self;
     
     
    (2)响应回车事件
    复制代码
     1 #pragma mark - TextField 代理方法
     2 /** 回车响应事件 */
     3 - (BOOL)textFieldShouldReturn:(UITextField *)textField {
     4     // 我方发出信息
     5     [self sendMessageWithContent:textField.text andType:MessageTypeMe];
     6    
     7     // 自动回复
     8     [self sendMessageWithContent:[NSString stringWithFormat:@"%@
    %@", textField.text, @"你妹!!!"] andType:MessageTypeOhter];
     9    
    10     // 消除消息框内容
    11     self.inputView.text = nil;
    12    
    13     [self.tableView reloadData];
    14    
    15     // 滚动到最新的消息
    16     NSIndexPath *lastIndexPath = [NSIndexPath indexPathForRow:self.messages.count - 1 inSection:0];
    17     [self.tableView scrollToRowAtIndexPath:lastIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
    18    
    19     return YES; // 返回值意义不明
    20 }
    21 
    22 // 发送消息
    23 - (void) sendMessageWithContent:(NSString *) text andType:(MessageType) type {
    24     // 获取当前时间
    25     NSDate *date = [NSDate date];
    26     NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    27     formatter.dateFormat = @"yyyy-MMM-dd hh:mm:ss";
    28     NSString *dateStr = [formatter stringFromDate:date];
    29    
    30     // 我方发出信息
    31     NSDictionary *dict = @{@"text":text,
    32                            @"time":dateStr,
    33                            @"type":[NSString stringWithFormat:@"%d", type]};
    34    
    35     Message *message = [[Message alloc] init];
    36     [message setValuesForKeysWithDictionary:dict];
    37     MessageFrame *messageFrame = [[MessageFrame alloc] init];
    38     messageFrame.message = message;
    39    
    40     [self.messages addObject:messageFrame];
    41 }
    42  
    复制代码
    (3)自动滚动在最底端
    1     // 滚动到最新的消息
    2     NSIndexPath *lastIndexPath = [NSIndexPath indexPathForRow:self.messages.count - 1 inSection:0];
    3     [self.tableView scrollToRowAtIndexPath:lastIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
     
     
    Image(117)
  • 相关阅读:
    汇编中的String
    对于C语言可移植性的思考【转】【补充】
    AT&T Mnemonic Conventions(AT&T汇编助记法的规则)
    Macro和Procedure的比较(汇编中的宏与函数)
    File Descriptor和Sys_call number
    #include <sys/types.h>在哪里?
    IIS7 与 WCF 问题总结
    非常不错的WCF入门文章,来自Artech
    WCF 部署问题 小总结 (HTTP 不能注册的解决方法)
    IIS7 aspx出现500.21错误
  • 原文地址:https://www.cnblogs.com/kengsir/p/4281750.html
Copyright © 2020-2023  润新知