献给初学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的寻找范围。

这节课就到这里吧,如果你运行项目就会看到我们已经得到了下图所示的微博效果啦。
这里写图片描述

是不是感觉有点像样了,下节课我们将会讲解如何对时间和来源处理,以及增加微博工具栏的处理。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值