QQChat Layout - 第二季
- 本來第二季是快寫好了, 也花了點功夫, 結果gitbook出了點問題, 給沒掉了。有些細節可能會一帶而過, 如有疑問, 相互交流進步~.
在第一季中我們完成了QQ聊天界面的基本框架.但是相對於iphone上手機QQ的聊天界面還存在以下差距。
- 第二季中的工程源文件下載地址:點擊到百度雲下載
- 聊天消息沒有左右區分。
- 聊天內容沒有背景圖片。
- 菜單欄還沒做出, 不能發消息。
1.設置ImageView距離右邊10 , 寬高為40==》確定了x,和寬高
2.設置ImageView的上面和timeLabel下面對齊 ==》確定了Y, 確定了Button
3.設置Button右邊距離ImageView左邊10, 固定寬高。
4.設置Button的上面與ImageView上面對齊。
SB圖片如下: 
- 修改完SB的cell界面后開始編碼了。我們主要需要在setMessage這個setter方法中, 加入判斷聊天消息是屬於左邊還是屬於右邊。具體邏輯如下:
- (void)setMessage:(Message *)message {
_message = message;
MessageWhoIsMe ==
_message.type?
[self setShowButton: _rightText andIcon: _rightIcon withMessage:message]:
[self setShowButton: _text andIcon: _icon withMessage:message];
}
/**
* 設置要展示的消息內容與頭像
*
* @param button 設置要顯示的信息。
* @param icon 設置要顯示的頭像
*/
- (void)setShowButton:(UIButton *)text andIcon:(UIImageView *)icon withMessage:(Message*)message{
_rightIcon.hidden = _rightText.hidden = (text != _rightText);
_icon.hidden = _text.hidden = (text != _text);
// 1.給控件裝數據
icon.image = [UIImage imageNamed:[self getPicture: message.type]];
[text setTitle:message.text forState:UIControlStateNormal];
_timeLabel.text = message.time;
// 2.裝完數據強制布局, 使得設置按鈕高度的值准確, 並且更新約束
[text layoutIfNeeded];
// 要先強制布局, 這時候更新約束才准確
// 更新約束, 使得按鈕的高度此時等於文本的高度。
[text updateConstraints:^(MASConstraintMaker *make) {
CGFloat textH = CGRectGetHeight(text.titleLabel.frame); //+ 30;
make.height.equalTo(textH);
}];
// 3.再次強制布局, 使得約束生效, 這樣獲取到的按鈕高度才准確
[text layoutIfNeeded];
CGFloat textH = CGRectGetMaxY(text.frame);
CGFloat iconH = CGRectGetMaxY(icon.frame);
CGFloat cellH = MAX(textH, iconH) + 10;
// 4.更新cell的高度到模型中
message.height = cellH;
}
效果圖如下(已經實現了左右排列):
- 第五步:
- 我們已經完成了QQ消息的可以分左右排列, 接下去我們需要給消息上背景圖, 也就是設置按鈕的背景圖。由於按鈕有狀態,所以你不能操作它的圖片控件來給它設置圖, 應該用其提供的設置圖片的接口。具體的邏輯比較簡單。你可以直接看代碼,我主要講下
[UIImage resizeWithImageName:
的知識點。
.....省略N行代碼
_timeLabel.text = message.time;
// 1.2 改進背景圖。
if (!_rightText.hidden) { // 當前顯示的是右邊, 則設置右邊的背景
[_rightText setBackgroundImage:[UIImage resizeWithImageName:@"chat_send_nor"] forState:UIControlStateNormal];
[_rightText setBackgroundImage:[UIImage resizeWithImageName:@"chat_send_press_pic"] forState:UIControlStateHighlighted];
}else { // 顯示的是左邊
[_text setBackgroundImage:[UIImage resizeWithImageName:@"chat_recive_press_pic"] forState:UIControlStateNormal];
[_text setBackgroundImage:[UIImage resizeWithImageName:@"chat_recive_nor"] forState:UIControlStateHighlighted];
}
// 2.裝完數據強制布局, 使得設置按鈕高度的值准確, 並且更新約束
.....省略N行代碼
- 具體的邏輯很簡單,但是我要介紹下圖片拉伸的知識。在實際項目中, 我們使用的資源圖片不可能是十分大的, 能小則小。不然很消耗內存。你可以打開工程看到, 這個工程中的資源圖片中消息背景圖。你可以嘗試把
UIImage resizeWithImageName:
替換成UIImage imageNamed:
來加載圖片, 會發現,當按鈕中的文字變大時候,圖片還是那么大,也就說圖片沒有隨着按鈕的尺寸進行伸縮。好在蘋果已經為我們提供了一個方法來伸縮圖片。其實UIImage resizeWithImageName:
只是我對Apple官方的方法的一個封裝, 並將它做成UIImage的分類 - 細說
-(UIImage *)resizableImageWithCapInsets:
這個方法是Apple提供的Image類的實例方法。為了不誤導大家, 特意查了官方解釋如下:
1.簡單來說該方法是用來返回一個可隨着Button尺寸自動伸縮的圖片,並且能保留住原來圖片的四個邊角, 也就說你要把它設置成Button的背景圖。
2.該方法主要通過保護區域是不是有寬、高,和保護區域的大小來決定渲染的方式。當保護區域有高該圖片就是豎直可伸縮, 有寬則是水平可伸縮。當寬高都是1px時候,選用的渲染方式是直接把這1px的圖片扯大, 渲染的效率十分高。
那么究竟如何確定保護區域呢?根據官方文檔,我們最好將保護區域設置成1*1的大小。這樣水平、豎直方向都可以進行拉伸, 並且渲染方式也高, 還有一點就是這個區域我們最好選擇的是最靠近正中間的, 因為一般來說這樣才能盡可能把圖片邊緣切掉, 保證渲染出來的圖片和遠圖片看上去是放大后的效果。否則可能出現,圖片存在菱角。當然我們還能通過resizableImageWithCapInsets:resizingMode:
來說明圖片渲染的模式,一種是使用拉伸來resize圖片, 一種是使用平鋪的方式來resize圖片.說了那么多, 你應該懂得了原理, 那么直接看我給UIImage擴充的分類方法吧。
#import "UIImage+Resizingable.h"
@implementation UIImage (Resizingable)
+ (UIImage *)resizeWithImageName:(NSString *)imageName {
UIImage * image = [UIImage imageNamed: imageName];
int W = image.size.width * 0.5;
int H = image.size.height * 0.5;
return [image resizableImageWithCapInsets: UIEdgeInsetsMake(H, W, image.size.height - H - 1 , image.size.width - W - 1)];
}
@end
第五步-1效果圖:
- 你一定會看出還存在一點問題。問什么文字沒有全部被背景包括着,這是因為很多時候美工給的圖片背景都會有留白問題。也就是不是沒有被圖片包括着,是因為圖片旁邊有一些空白,顯示出來就成這樣了。這是后我們又可以利用按鈕的內邊距來把Label向左右推,這樣Label就會變高了,會超出Button,所以我們需要再給Button加點高度。應為給Button設置內邊距只需要設置一次,所以我一般將這種操作方法
- (void)awakeFromNib
方法中
- (void)awakeFromNib {
// 設置自動換行
_text.titleLabel.numberOfLines = 0;
_rightText.titleLabel.numberOfLines = 0;
// 設置button的內邊距
_text.contentEdgeInsets = UIEdgeInsetsMake(0, 30, 0, 30);
_rightText.contentEdgeInsets = UIEdgeInsetsMake(0, 30, 0, 30);
}
更改Button的高度約束,多加30
[text updateConstraints:^(MASConstraintMaker *make) {
CGFloat textH = CGRectGetHeight(text.titleLabel.frame)+ 30;
make.height.equalTo(textH);
}];
我覺得你會對內邊距存在很大的疑惑, 請看我的博客有專門介紹了下內邊距。
最終效果圖如下:
時間有限,菜單欄還介紹, 敬請關注第三季。