YYKit是ibireme大神寫的一個集model(JSON模型轉換)、cache(緩存)、image(圖片處理)、text(富文本)等 于一身的優秀第三方開源框架,YYKit的強大是被大多數iOS程序員公認的。直接講解框架的源代碼有些枯燥,那我們就根據demo中的微博的例子來解析一下YYKit的實際用法。
首先看看微博分成哪些模塊
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了,控制器只需要處理一下頁面的邏輯,這樣的項目可讀性高,耦合度低,方便別人也方便自己。