YYKit是ibireme大神写的一个集model(JSON模型转换)、cache(缓存)、image(图片处理)、text(富文本)等 于一身的优秀第三方开源框架,YYKit的强大是被大多数iOS程序员公认的。直接讲解框架的源代码有些枯燥,那我们就根据demo中的微博的例子来解析一下YYKit的实际用法。
首先看看微博分成哪些模块
1B6EA894-2C75-4FF5-9694-5A6E6B7892BB.pngTimeline:微博列表
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了,控制器只需要处理一下页面的逻辑,这样的项目可读性高,耦合度低,方便别人也方便自己。
网友评论