1. 程式人生 > >獻給初學iOS的小盆友們——微博app專案開發之十二自定義cell程式碼補全

獻給初學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的尋找範圍。

這節課就到這裡吧,如果你執行專案就會看到我們已經得到了下圖所示的微博效果啦。
這裡寫圖片描述

是不是感覺有點像樣了,下節課我們將會講解如何對時間和來源處理,以及增加微博工具欄的處理。