UI基础之UITableView案例QQ聊天界面
数据模型:
#import <Foundation/Foundation.h> typedef enum{ LLMessageTypeMe, LLMessageTypeOther }LLMessageType; @interface LLMessage : NSObject /** * time */ @property (nonatomic, copy) NSString *time; /** * text */ @property (nonatomic, copy) NSString *text; /** * type 定义为枚举类型的好处 */ @property (nonatomic, assign) LLMessageType type; /** * hiddenTime 时间是否隐藏 */ @property (nonatomic, assign, getter=isHiddenTime) BOOL hiddenTime; - (instancetype)initWithDic:(NSDictionary *)dic; + (instancetype)messageWithDic:(NSDictionary *)dic; + (NSMutableArray *)messageList; @end
#import "LLMessage.h" @implementation LLMessage - (instancetype)initWithDic:(NSDictionary *)dic { if (self = [super init]) { [self setValuesForKeysWithDictionary:dic]; } return self; } + (instancetype)messageWithDic:(NSDictionary *)dic { return [[self alloc] initWithDic:dic]; } + (NSMutableArray *)messageList { NSString *path = [[NSBundle mainBundle] pathForResource:@"messages" ofType:@"plist"]; NSArray *dicArr = [NSArray arrayWithContentsOfFile:path]; NSMutableArray *tmpArr = [[NSMutableArray alloc] initWithCapacity:dicArr.count]; for (NSDictionary *dic in dicArr) { LLMessage *endMessage = [tmpArr lastObject]; LLMessage *message = [LLMessage messageWithDic:dic]; if ([message.time isEqualToString:endMessage.time]) { message.hiddenTime = YES; } [tmpArr addObject:message]; } return tmpArr; } @end
frame模型
#import <Foundation/Foundation.h> #import <UIKit/UIKit.h> #define LLTIMEFONT [UIFont systemFontOfSize:12] #define LLTEXTFONT [UIFont systemFontOfSize:13] #define LLPADDING 20 @class LLMessage; @interface LLMessageFrame : NSObject @property (nonatomic, strong) LLMessage *message; @property (nonatomic, assign,readonly) CGRect timeF; @property (nonatomic, assign, readonly) CGRect textF; @property (nonatomic, assign, readonly) CGRect iconF; @property (nonatomic, assign, readonly) CGFloat cellHeight; + (NSMutableArray *)messageFrameList; @end
#import "LLMessageFrame.h" #import "LLMessage.h" #import "NSString+LLNSStringExtension.h" @implementation LLMessageFrame + (NSMutableArray *)messageFrameList { NSMutableArray *messageArr = [LLMessage messageList]; NSMutableArray *tmpArray = [[NSMutableArray alloc] initWithCapacity:messageArr.count]; for (LLMessage *message in messageArr) { LLMessageFrame *frame = [[LLMessageFrame alloc] init]; frame.message = message; [tmpArray addObject:frame]; } return tmpArray; } - (void)setMessage:(LLMessage *)message { _message = message; CGFloat margin = 10; // 时间 CGFloat timeX = 0; CGFloat timeY = 0; CGFloat timeW = 320; CGFloat timeH = 40; if (!message.isHiddenTime){ _timeF = CGRectMake(timeX, timeY, timeW, timeH); } // icon CGFloat iconY = CGRectGetMaxY(_timeF); CGFloat iconWH = 30; CGFloat iconX; if (message.type == LLMessageTypeMe) { // Me iconX = timeW - iconWH - margin; } else { // Other iconX = margin; } _iconF = CGRectMake(iconX, iconY, iconWH, iconWH); // text CGFloat textY = iconY; CGSize textSize = [message.text textOfSize:CGSizeMake(200, MAXFLOAT) font:LLTEXTFONT]; CGSize buttonSize = CGSizeMake(textSize.width + LLPADDING * 2, textSize.height + LLPADDING * 2); CGFloat textX; if (message.type == LLMessageTypeMe) { // Me textX = iconX - margin - buttonSize.width; } else { textX = CGRectGetMaxX(_iconF) + margin; } _textF = (CGRect){{textX, textY} , buttonSize}; CGFloat textMaxY = CGRectGetMaxY(_textF); CGFloat iconMaxY = CGRectGetMaxY(_iconF); _cellHeight = MAX(textMaxY, iconMaxY); } @end
代码自定义cell
#import <UIKit/UIKit.h> @class LLMessageFrame; @interface LLMessageCell : UITableViewCell @property (nonatomic, strong) LLMessageFrame *messageFrame; + (instancetype)messageCellWith:(UITableView *)tableView; @end
#import "LLMessageCell.h" #import "LLMessageFrame.h" #import "LLMessage.h" #import <UIKit/UIKit.h> #import "UIImage+LLUIImageExtension.h" @interface LLMessageCell () @property (nonatomic, weak) UILabel *timeView; @property (nonatomic, weak) UIImageView *iconView; @property (nonatomic, weak) UIButton *textView; @end @implementation LLMessageCell + (instancetype)messageCellWith:(UITableView *)tableView { static NSString *ID = @"messageCell"; LLMessageCell *cell = [tableView dequeueReusableCellWithIdentifier:ID]; if (!cell) { cell = [[LLMessageCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID]; } return cell; } - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) { // 创建cell子控件 self.backgroundColor = [UIColor clearColor]; // time UILabel *timeView = [[UILabel alloc] init]; [self.contentView addSubview:timeView]; timeView.textAlignment = NSTextAlignmentCenter; timeView.font = LLTIMEFONT; self.timeView = timeView; // icon UIImageView *iconView = [[UIImageView alloc] init]; [self.contentView addSubview:iconView]; self.iconView = iconView; // text UIButton *textView = [[UIButton alloc] init]; [self.contentView addSubview: textView]; textView.titleLabel.font = LLTEXTFONT; textView.titleLabel.numberOfLines = 0; textView.contentEdgeInsets = UIEdgeInsetsMake(LLPADDING, LLPADDING, LLPADDING, LLPADDING); self.textView = textView; } return self; } - (void)setMessageFrame:(LLMessageFrame *)messageFrame { _messageFrame = messageFrame; // 1,设置数据 [self setDate]; // 2,设置frame [self setSubviewsFrame]; } - (void)setDate { LLMessage *message = self.messageFrame.message; self.timeView.text = message.time; NSString *iconName = message.type == LLMessageTypeMe ? @"me" : @"other"; self.iconView.image = [UIImage imageNamed:iconName]; // 设置图片背景 #warning 设置按钮上label字体方法 [self.textView setTitle:message.text forState:UIControlStateNormal]; if (message.type == LLMessageTypeMe) { [self.textView setBackgroundImage:[UIImage registerImage:@"chat_send_nor"] forState:UIControlStateNormal]; [self.textView setBackgroundImage:[UIImage registerImage:@"chat_send_press_pic"] forState:UIControlStateHighlighted]; [self.textView setTitleColor:[UIColor redColor] forState:UIControlStateNormal]; } else { [self.textView setTitleColor:[UIColor blueColor] forState:UIControlStateNormal]; [self.textView setBackgroundImage:[UIImage registerImage:@"chat_recive_nor"] forState:UIControlStateNormal]; [self.textView setBackgroundImage:[UIImage registerImage:@"chat_recive_press_pic"] forState:UIControlStateHighlighted]; } } - (void)setSubviewsFrame { self.timeView.frame = self.messageFrame.timeF; self.iconView.frame = self.messageFrame.iconF; self.textView.frame = self.messageFrame.textF; } @end
分类:NSString
#import <Foundation/Foundation.h> #import <UIKit/UIKit.h> @interface NSString (LLNSStringExtension) - (CGSize)textOfSize:(CGSize)maxSize font:(UIFont *)font; @end #import "NSString+LLNSStringExtension.h" @implementation NSString (LLNSStringExtension) - (CGSize)textOfSize:(CGSize)maxSize font:(UIFont *)font { return [self boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : font} context:nil].size; } @end
UIImage分类
#import <UIKit/UIKit.h> @interface UIImage (LLUIImageExtension) + (UIImage *)registerImage:(NSString *)imageName; @end #import "UIImage+LLUIImageExtension.h" #import <UIKit/UIKit.h> @implementation UIImage (LLUIImageExtension) + (UIImage *)registerImage:(NSString *)imageName { UIImage * image = [UIImage imageNamed:imageName]; return [image stretchableImageWithLeftCapWidth:image.size.width * 0.5 topCapHeight:image.size.height * 0.5]; } @end
controller:
#import "ViewController.h" #import "LLMessageFrame.h" #import "LLMessage.h" #import "LLMessageCell.h" @interface ViewController () <UITableViewDataSource, UITableViewDelegate, UITextFieldDelegate> @property (nonatomic, strong) NSMutableArray *messageFrames; @property (weak, nonatomic) IBOutlet UITableView *tableView; @property (weak, nonatomic) IBOutlet UIView *footerView; @property (weak, nonatomic) IBOutlet UITextField *textField; @end @implementation ViewController #pragma mark - 懒加载数据模型 - (NSMutableArray *)messageFrames { if (!_messageFrames) { _messageFrames = [LLMessageFrame messageFrameList]; } return _messageFrames; } - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. // 隐藏分割线 self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone; // 取消tableView的点击 self.tableView.allowsSelection = NO; self.tableView.backgroundColor = [UIColor colorWithRed:240/255.0 green:224/255.0 blue:224/255.0 alpha:1.0]; // 监听通知 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(clickTextFiled:) name:UIKeyboardWillChangeFrameNotification object:nil]; // 设置文本框左边内容默认有间距 self.textField.leftView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 8, 0)]; self.textField.leftViewMode = UITextFieldViewModeAlways; } - (void)clickTextFiled:(NSNotification *)noti { CGRect rect = [noti.userInfo[@"UIKeyboardFrameEndUserInfoKey"] CGRectValue]; CGFloat moveY = rect.origin.y - self.view.frame.size.height; self.view.transform = CGAffineTransformMakeTranslation(0, moveY); } - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; } #pragma mark - 数据源方法 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.messageFrames.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { // 1,创建cell LLMessageCell *cell = [LLMessageCell messageCellWith:tableView]; // 2,设置cell cell.messageFrame = self.messageFrames[indexPath.row]; return cell; } #pragma mark - 代理方法 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { return [self.messageFrames[indexPath.row] cellHeight]; } - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { [self.view endEditing:YES]; } #pragma mark - 文本框代理方法 - (BOOL)textFieldShouldReturn:(UITextField *)textField { LLMessageFrame *frame = [[LLMessageFrame alloc] init]; LLMessage *message = [[LLMessage alloc] init]; NSDate *date = [NSDate date]; NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; formatter.dateFormat = @"hh:mm"; NSString *time = [formatter stringFromDate:date]; message.time = time; message.text = textField.text; message.type = LLMessageTypeMe; LLMessageFrame *lastMF = [self.messageFrames lastObject]; if ([lastMF.message.time isEqualToString:message.time]) { message.hiddenTime = YES; } frame.message = message; [self.messageFrames addObject:frame]; // 刷新 [self.tableView reloadData]; // 自动上滚 NSIndexPath *path = [NSIndexPath indexPathForRow:self.messageFrames.count - 1 inSection:0]; [self.tableView scrollToRowAtIndexPath:path atScrollPosition:UITableViewScrollPositionBottom animated:YES]; textField.text = nil; return YES; } - (BOOL)prefersStatusBarHidden { return YES; } @end
效果;