獻給初學iOS的小盆友們——微博app專案開發之十二自定義cell程式碼補全
上節課我們主要講解了我們是怎麼一步一步把自定義cell打通的,主要方法就是引入了MVVM檢視模型,這樣我們就可以利用檢視模型就提前把各個子控制元件的frame都計算好了。今天我們就是講如何計運算元控制元件frame,以及補全原創微博和轉發微博的設計。
本節內容
- 計算cell子控制元件frame
- YGStatusCell程式碼補全
- 原創微博YGOriginalView程式碼補全
- 轉發微博YGRetweetView 程式碼補全
本節資料
12.1 計算cell子控制元件frame
在YGStatusFrame檢視模型執行檔案內,我們重寫setStatus方法,表明一接到YGStatus資料模型,我們就把各個子控制元件計算好,程式碼如下:
- (void)setStatus:(YGStatus *)status
{
_status = status;
// 計算原創微博
[self setUpOriginalViewFrame];
CGFloat toolBarY = CGRectGetMaxY(_originalViewFrame);
if (status.retweeted_status) {
// 計算轉發微博
[self setUpRetweetViewFrame];
toolBarY = CGRectGetMaxY(_retweetViewFrame);
}
// 計算工具條
CGFloat toolBarX = 0;
CGFloat toolBarW = YGScreenW;
CGFloat toolBarH = 35;
_toolBarFrame = CGRectMake(toolBarX, toolBarY, toolBarW, toolBarH);
// 計算cell高度
_cellHeight = CGRectGetMaxY(_toolBarFrame);
}
我們首先計算了原創微博子控制元件的frame,然後判斷微博是否有轉發微博,如果有轉發微博,我們就計算轉發微博子控制元件的frame。最後計算工具條的縱座標Y值時,也要考慮是否有轉發微博。
下面就貼上原創微博怎麼設定frame的:
#pragma mark - 計算原創微博
- (void)setUpOriginalViewFrame
{
// 頭像
CGFloat imageX = YGStatusCellMargin;
CGFloat imageY = imageX;
CGFloat imageWH = 35;
_originalIconFrame = CGRectMake(imageX, imageY, imageWH, imageWH);
// 暱稱
CGFloat nameX = CGRectGetMaxX(_originalIconFrame) + YGStatusCellMargin;
CGFloat nameY = imageY;
CGSize nameSize = [_status.user.name sizeWithFont:YGNameFont];
_originalNameFrame = (CGRect){{nameX,nameY},nameSize};
// vip
if (_status.user.vip) {
CGFloat vipX = CGRectGetMaxX(_originalNameFrame) + YGStatusCellMargin;
CGFloat vipY = nameY;
CGFloat vipWH = 14;
_originalVipFrame = CGRectMake(vipX, vipY, vipWH, vipWH);
}
// 時間
CGFloat timeX = nameX;
CGFloat timeY = CGRectGetMaxY(_originalNameFrame) + YGStatusCellMargin * 0.5;
CGSize timeSize = [_status.created_at sizeWithFont:YGTimeFont];
_originalTimeFrame = (CGRect){{timeX,timeY},timeSize};
// 來源
CGFloat sourceX = CGRectGetMaxX(_originalTimeFrame) + YGStatusCellMargin;
CGFloat sourceY = timeY;
CGSize sourceSize = [_status.source sizeWithFont:YGSourceFont];
_originalSourceFrame = (CGRect){{sourceX,sourceY},sourceSize};
// 正文
CGFloat textX = imageX;
CGFloat textY = CGRectGetMaxY(_originalIconFrame) + YGStatusCellMargin;
CGFloat textW = YGScreenW - 2 * YGStatusCellMargin;
CGSize textSize = [_status.text sizeWithFont:YGTextFont constrainedToSize:CGSizeMake(textW, MAXFLOAT)];
_originalTextFrame = (CGRect){{textX,textY},textSize};
// 原創微博的frame
CGFloat originX = 0;
//設定間隙
CGFloat originY = 10;
CGFloat originW = YGScreenW;
CGFloat originH = CGRectGetMaxY(_originalTextFrame) + YGStatusCellMargin;
_originalViewFrame = CGRectMake(originX, originY, originW, originH);
}
這裡需要說明的是:在計算會員屬性的frame時,需要判斷使用者是否是會員,但是以前我們設計的YGUser模型裡沒有VIP屬性,所以我們現在在YGUser增加幾個屬性,用來接受返回的資料,然後判斷是否是vip,標頭檔案增加如下程式碼:
/** 會員型別 > 2代表是會員 */
@property (nonatomic, assign) int mbtype;
/** 會員等級 */
@property (nonatomic, assign) int mbrank;
@property (nonatomic, assign,getter=isVip) BOOL vip;
執行檔案增加如下程式碼:
- (void)setMbtype:(int)mbtype
{
_mbtype = mbtype;
_vip = mbtype > 2;
}
這裡的意思就是當返回的資料mbtype大於2時,使用者就是VIP,具體會員等級由mbrank決定。
如何計算轉發微博frame:
#pragma mark - 計算轉發微博
- (void)setUpRetweetViewFrame
{
// 暱稱frame
// 暱稱
CGFloat nameX = YGStatusCellMargin;
CGFloat nameY = nameX;
// 注意:一定要是轉發微博的使用者暱稱
CGSize nameSize = [_status.retweeted_status.user.name sizeWithFont:YGNameFont];
_retweetNameFrame = (CGRect){{nameX,nameY},nameSize};
// 正文
CGFloat textX = nameX;
CGFloat textY = CGRectGetMaxY(_retweetNameFrame) + YGStatusCellMargin;
CGFloat textW = YGScreenW - 2 * YGStatusCellMargin;
// 注意:一定要是轉發微博的正文
CGSize textSize = [_status.retweeted_status.text sizeWithFont:YGTextFont constrainedToSize:CGSizeMake(textW, MAXFLOAT)];
_retweetTextFrame = (CGRect){{textX,textY},textSize};
// 原創微博的frame
CGFloat retweetX = 0;
CGFloat retweetY = CGRectGetMaxY(_originalViewFrame);
CGFloat retweetW = YGScreenW;
CGFloat retweetH = CGRectGetMaxY(_retweetTextFrame) + YGStatusCellMargin;
_retweetViewFrame = CGRectMake(retweetX, retweetY, retweetW, retweetH);
}
以上程式碼所用的巨集定義我們設定在了weibo.pch檔案內了:
/* cell */
#define YGStatusCellMargin 10
#define YGNameFont [UIFont systemFontOfSize:13]
#define YGTimeFont [UIFont systemFontOfSize:12]
#define YGSourceFont YGTimeFont
#define YGTextFont [UIFont systemFontOfSize:15]
#define YGScreenW [UIScreen mainScreen].bounds.size.width
12.2 YGStatusCell程式碼補全
重寫setStatusFrame方法,設定cell內三個子控制元件的位置和大小:
- (void)setStatusFrame:(YGStatusFrame *)statusFrame{
_statusFrame = statusFrame;
// 設定原創微博frame
_originalView.frame = statusFrame.originalViewFrame;
_originalView.statusFrame = statusFrame;
// 設定原創微博frame
_retweetView.frame = statusFrame.retweetViewFrame;
_retweetView.statusFrame = statusFrame;
// 設定工具條frame
_toolBar.frame = statusFrame.toolBarFrame;
}
這裡不要忘記給YGOriginalView 和YGRetweetView新增YGStatus Frame屬性,以便傳遞檢視模型。
12.3 原創微博YGOriginalView程式碼補全
這裡我們在取得檢視模型後,要重寫setStatusFrame方法,來設定原創微博子控制元件的大小位置以及內容:
-(void)setStatusFrame:(YGStatusFrame *)statusFrame
{
_statusFrame = statusFrame;
// 設定frame
[self setUpFrame];
// 設定data
[self setUpData];
}
這裡我們抽取兩給方法,一個專門設定子控制元件位置和大小,一盒專門設定內容,設定frame程式碼如下:
- (void)setUpFrame
{
// 頭像
_iconView.frame = _statusFrame.originalIconFrame;
// 暱稱
_nameView.frame = _statusFrame.originalNameFrame;
// vip
if (_statusFrame.status.user.vip) { // 是vip
_vipView.hidden = NO;
_vipView.frame = _statusFrame.originalVipFrame;
}else{
_vipView.hidden = YES;
}
// 時間
_timeView.frame = _statusFrame.originalTimeFrame;
// 來源
_sourceView.frame = _statusFrame.originalSourceFrame;
// 正文
_textView.frame = _statusFrame.originalTextFrame;
}
設定子控制元件內容如下:
- (void)setUpData
{
YGStatus *status = _statusFrame.status;
// 頭像
[_iconView sd_setImageWithURL:status.user.profile_image_url placeholderImage:[UIImage imageNamed:@"timeline_image_placeholder"]];
// 暱稱
if (status.user.vip) {
_nameView.textColor = [UIColor redColor];
}else{
_nameView.textColor = [UIColor blackColor];
}
_nameView.text = status.user.name;
// vip
NSString *imageName = [NSString stringWithFormat:@"common_icon_membership_level%d",status.user.mbrank];
UIImage *image = [UIImage imageNamed:imageName];
_vipView.image = image;
// 時間
_timeView.text = status.created_at;
// 來源
_sourceView.text = status.source;
// 正文
_textView.text = status.text;
}
最後還有幾點需要注意,我們要給暱稱,來源,時間和正文設定合適的字型大小和顏色,正文還要設定跨行,具體請參考資料程式碼。
12.4 轉發微博YGRetweetView 程式碼補全
轉發微博的子控制元件設定原理就跟原創微博一樣了,首先,重寫initWithFrame方法:
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
// 新增所有子控制元件
[self setUpAllChildView];
}
return self;
}
然後抽取的新增所有子控制元件的方法為:
// 新增所有子控制元件
- (void)setUpAllChildView
{
// 暱稱
UILabel *nameView = [[UILabel alloc] init];
nameView.font = YGNameFont;
[self addSubview:nameView];
_nameView = nameView;
// 正文
UILabel *textView = [[UILabel alloc] init];
textView.font = YGTextFont;
textView.numberOfLines = 0;
[self addSubview:textView];
_textView = textView;
}
這裡只需要新增兩個子控制元件,比原創微博的要簡單些。
最後就是重寫setStatusFrame方法,設定其子控制元件大小位置和內容:
- (void)setStatusFrame:(YGStatusFrame *)statusFrame
{
_statusFrame = statusFrame;
YGStatus *status = statusFrame.status;
// 暱稱
_nameView.frame = statusFrame.retweetNameFrame;
_nameView.text = status.retweeted_status.user.name;
// 正文
_textView.frame = statusFrame.retweetTextFrame;
_textView.text = status.retweeted_status.text;
}
是不是感覺這些程式碼敲起來巨爽,直接用點語法賦值即可,但願你此時可以深刻理解到面向物件程式設計思想是多麼方便。
這裡還有兩點需要說明:
第一:我們為了設定原創微博和轉發微博子控制元件的背景圖片,需要把兩者繼承的類改為UIImageView,這樣才能設定圖片。然後在各自的初始化方法裡,新增其背景圖片,並把使用者互動開啟,程式碼如下:
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
// 新增所有子控制元件
[self setUpAllChildView];
self.userInteractionEnabled = YES;
self.image = [UIImage imageWithStretchableName:@"timeline_card_top_background"];
}
return self;
}
第二:為了讓cell產生間隙效果,我們這裡把原始微博的縱座標往下移了10point。仔細在程式碼資源裡找找在那裡設定的吧。
第三:我在寫程式碼的時候,不小心產生了一個錯誤叫(null): Linker command failed with exit code 1 (use -v to see invocation)
這個錯誤我找了好半天才找到,原來是我在某個地方錯誤的引用了.m檔案引起的,這裡指出來希望對大家有所幫助。而且以後在開發中,注意多儲存檔案,一個功能建立一個檔案,多執行,這樣就能縮小bug的尋找範圍。
這節課就到這裡吧,如果你執行專案就會看到我們已經得到了下圖所示的微博效果啦。
是不是感覺有點像樣了,下節課我們將會講解如何對時間和來源處理,以及增加微博工具欄的處理。