美文网首页iOS Dev
解析YYKit中微博列表的代码

解析YYKit中微博列表的代码

作者: hanryChen | 来源:发表于2017-03-02 16:32 被阅读738次

    YYKit是ibireme大神写的一个集model(JSON模型转换)、cache(缓存)、image(图片处理)、text(富文本)等 于一身的优秀第三方开源框架,YYKit的强大是被大多数iOS程序员公认的。直接讲解框架的源代码有些枯燥,那我们就根据demo中的微博的例子来解析一下YYKit的实际用法。

    首先看看微博分成哪些模块
    1B6EA894-2C75-4FF5-9694-5A6E6B7892BB.png

    Timeline:微博列表
    Compose:转发评论
    Helper:与项目高耦合的工具
    API Dump:数据源

    Timeline是这里面最核心的模块了,我们就来解析一下Timeline的代码

    我们本着由浅入深的原则,在解析WBStatusTimelineViewController类之前,先看看其他几个类的内容
    WBStatusLayout:cell的布局model
    WBStatusCell:微博列表的cell
    WBModel:数据model

    很明显,这个微博列表用了MVVM模式。WBModel是模块的基础,这里用YYModel中延展的方法对接口返回的数据重命名和做一些简单的修改,一个WBStatus对应的是一个cell的数据
    WBStatusLayout 一个cell的布局model,在[self _layout]里计算布局。
    [self _layoutTitle]计算title的布局

    这里最重要的一个技术点就是把图片和文字拼在一起,以富文本的形式显示出来

    - (NSAttributedString *)_attachmentWithFontSize:(CGFloat)fontSize imageURL:(NSString *)imageURL shrink:(BOOL)shrink {
        /*
         微博 URL 嵌入的图片,比临近的字体要小一圈。。
         这里模拟一下 Heiti SC 字体,然后把图片缩小一下。
         */
        CGFloat ascent = fontSize * 0.86;
        CGFloat descent = fontSize * 0.14;
        CGRect bounding = CGRectMake(0, -0.14 * fontSize, fontSize, fontSize);
        UIEdgeInsets contentInsets = UIEdgeInsetsMake(ascent - (bounding.size.height + bounding.origin.y), 0, descent + bounding.origin.y, 0);
        CGSize size = CGSizeMake(fontSize, fontSize);
        
        if (shrink) {
            // 缩小~
            CGFloat scale = 1 / 10.0;
            contentInsets.top += fontSize * scale;
            contentInsets.bottom += fontSize * scale;
            contentInsets.left += fontSize * scale;
            contentInsets.right += fontSize * scale;
            contentInsets = UIEdgeInsetPixelFloor(contentInsets);
            size = CGSizeMake(fontSize - fontSize * scale * 2, fontSize - fontSize * scale * 2);
            size = CGSizePixelRound(size);
        }
        
        YYTextRunDelegate *delegate = [YYTextRunDelegate new];
        delegate.ascent = ascent;
        delegate.descent = descent;
        delegate.width = bounding.size.width;
        
        WBTextImageViewAttachment *attachment = [WBTextImageViewAttachment new];
        attachment.contentMode = UIViewContentModeScaleAspectFit;
        attachment.contentInsets = contentInsets;
        attachment.size = size;
        attachment.imageURL = [WBStatusHelper defaultURLForImageURL:imageURL];
        
        NSMutableAttributedString *atr = [[NSMutableAttributedString alloc] initWithString:YYTextAttachmentToken];
        [atr setTextAttachment:attachment range:NSMakeRange(0, atr.length)];
        CTRunDelegateRef ctDelegate = delegate.CTRunDelegate;
        [atr setRunDelegate:ctDelegate range:NSMakeRange(0, atr.length)];
        if (ctDelegate) CFRelease(ctDelegate);
        
        return atr;
    }
    

    YYTextAttachmentToken = @"\uFFFC"; 是一个占位符
    YYTextRunDelegate 包含元素的宽度,行距,间距
    设置CTRunDelegateRef 并用kCTRunDelegateAttributeName标记这个区段会有特殊元素混入,ctDelegate不含特殊元素,但是可以通过CTRunDelegateGetRefCon方法反取母体YYTextRunDelegate
    WBTextImageViewAttachment 继承于 YYTextAttachment ,而YYTextAttachment的主要功能是实现图片和文字的混排,具体可参考NSTextAttachment
    生成NSMutableAttributedString的atr便是图文富文本了。

    [self _layoutProfile]; 计算名称头像栏的布局
    [self _layoutPics];计算引用的图片文件的布局
    [self _layoutTag];计算tag的布局
    [self _layoutToolbar];计算下发转发,评论的toolbar的布局

    model是原材料,经过加工成有布局数据的WBStatusLayout,WBStatusCell是显示的内容,现在我们需要把加工好的WBStatusLayout显示到WBStatusCell,这个操作就要在WBStatusTimelineViewController里进行了

    if ([self respondsToSelector:@selector( setAutomaticallyAdjustsScrollViewInsets:)]) {
            self.automaticallyAdjustsScrollViewInsets = NO;
        }
    

    UIScrollView会在有navigation bar时自动下移64位,关闭这个属性,我们可以自己设置UIScrollView的布局。

    数据加载
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            for (int i = 0; i <= 7; i++) {
                NSData *data = [NSData dataNamed:[NSString stringWithFormat:@"weibo_%d.json",i]];
                WBTimelineItem *item = [WBTimelineItem modelWithJSON:data];
                for (WBStatus *status in item.statuses) {
                    WBStatusLayout *layout = [[WBStatusLayout alloc] initWithStatus:status style:WBLayoutStyleTimeline];
    //                [layout layout];
                    [_layouts addObject:layout];
                }
            }
            
            // 复制一下,让列表长一些,不至于滑两下就到底了
            [_layouts addObjectsFromArray:_layouts];
            
            dispatch_async(dispatch_get_main_queue(), ^{
                self.title = [NSString stringWithFormat:@"Weibo (loaded:%d)", (int)_layouts.count];
                [indicator removeFromSuperview];
                self.navigationController.view.userInteractionEnabled = YES;
                [_tableView reloadData];
            });
        });
    

    开启后台线程:
    NSData *data = [NSData dataNamed:[NSString stringWithFormat:@"weibo_%d.json",i]]; json格式的数据源
    WBTimelineItem *item = [WBTimelineItem modelWithJSON:data]; 转成model
    WBStatusLayout *layout = [[WBStatusLayout alloc] initWithStatus:status style:WBLayoutStyleTimeline]; model转成cell的布局VM
    回到主线程开始布局。
    整个微博列表WBStatusTimelineViewController不过三百多行,繁杂的布局和数据处理工作都交给WBStatusLayout了,控制器只需要处理一下页面的逻辑,这样的项目可读性高,耦合度低,方便别人也方便自己。

    相关文章

      网友评论

        本文标题:解析YYKit中微博列表的代码

        本文链接:https://www.haomeiwen.com/subject/odtfgttx.html