WBStatusCell.m 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993
  1. //
  2. // WBFeedCell.m
  3. // YYKitExample
  4. //
  5. // Created by ibireme on 15/9/5.
  6. // Copyright (c) 2015 ibireme. All rights reserved.
  7. //
  8. #import "WBStatusCell.h"
  9. #import "YYControl.h"
  10. @implementation WBStatusTitleView
  11. - (instancetype)initWithFrame:(CGRect)frame {
  12. if (frame.size.width == 0 && frame.size.height == 0) {
  13. frame.size.width = kScreenWidth;
  14. frame.size.height = kWBCellTitleHeight;
  15. }
  16. self = [super initWithFrame:frame];
  17. _titleLabel = [YYLabel new];
  18. _titleLabel.size = CGSizeMake(kScreenWidth - 100, self.height);
  19. _titleLabel.left = kWBCellPadding;
  20. _titleLabel.displaysAsynchronously = YES;
  21. _titleLabel.ignoreCommonProperties = YES;
  22. _titleLabel.fadeOnHighlight = NO;
  23. _titleLabel.fadeOnAsynchronouslyDisplay = NO;
  24. [self addSubview:_titleLabel];
  25. CALayer *line = [CALayer layer];
  26. line.size = CGSizeMake(self.width, CGFloatFromPixel(1));
  27. line.bottom = self.height;
  28. line.backgroundColor = kWBCellLineColor.CGColor;
  29. [self.layer addSublayer:line];
  30. self.exclusiveTouch = YES;
  31. return self;
  32. }
  33. @end
  34. @implementation WBStatusProfileView {
  35. BOOL _trackingTouch;
  36. }
  37. - (instancetype)initWithFrame:(CGRect)frame {
  38. if (frame.size.width == 0 && frame.size.height == 0) {
  39. frame.size.width = kScreenWidth;
  40. frame.size.height = kWBCellProfileHeight;
  41. }
  42. self = [super initWithFrame:frame];
  43. self.exclusiveTouch = YES;
  44. @weakify(self);
  45. _avatarView = [UIImageView new];
  46. _avatarView.size = CGSizeMake(40, 40);
  47. _avatarView.origin = CGPointMake(kWBCellPadding, kWBCellPadding + 3);
  48. _avatarView.contentMode = UIViewContentModeScaleAspectFill;
  49. [self addSubview:_avatarView];
  50. CALayer *avatarBorder = [CALayer layer];
  51. avatarBorder.frame = _avatarView.bounds;
  52. avatarBorder.borderWidth = CGFloatFromPixel(1);
  53. avatarBorder.borderColor = [UIColor colorWithWhite:0.000 alpha:0.090].CGColor;
  54. avatarBorder.cornerRadius = _avatarView.height / 2;
  55. avatarBorder.shouldRasterize = YES;
  56. avatarBorder.rasterizationScale = kScreenScale;
  57. [_avatarView.layer addSublayer:avatarBorder];
  58. _avatarBadgeView = [UIImageView new];
  59. _avatarBadgeView.size = CGSizeMake(14, 14);
  60. _avatarBadgeView.center = CGPointMake(_avatarView.right - 6, _avatarView.bottom - 6);
  61. _avatarBadgeView.contentMode = UIViewContentModeScaleAspectFit;
  62. [self addSubview:_avatarBadgeView];
  63. _nameLabel = [YYLabel new];
  64. _nameLabel.size = CGSizeMake(kWBCellNameWidth, 24);
  65. _nameLabel.left = _avatarView.right + kWBCellNamePaddingLeft;
  66. _nameLabel.centerY = 27;
  67. _nameLabel.displaysAsynchronously = YES;
  68. _nameLabel.ignoreCommonProperties = YES;
  69. _nameLabel.fadeOnAsynchronouslyDisplay = NO;
  70. _nameLabel.fadeOnHighlight = NO;
  71. _nameLabel.lineBreakMode = NSLineBreakByClipping;
  72. _nameLabel.textVerticalAlignment = YYTextVerticalAlignmentCenter;
  73. [self addSubview:_nameLabel];
  74. _sourceLabel = [YYLabel new];
  75. _sourceLabel.frame = _nameLabel.frame;
  76. _sourceLabel.centerY = 47;
  77. _sourceLabel.displaysAsynchronously = YES;
  78. _sourceLabel.ignoreCommonProperties = YES;
  79. _sourceLabel.fadeOnAsynchronouslyDisplay = NO;
  80. _sourceLabel.fadeOnHighlight = NO;
  81. _sourceLabel.highlightTapAction = ^(UIView *containerView, NSAttributedString *text, NSRange range, CGRect rect) {
  82. if ([weak_self.cell.delegate respondsToSelector:@selector(cell:didClickInLabel:textRange:)]) {
  83. [weak_self.cell.delegate cell:weak_self.cell didClickInLabel:(YYLabel *)containerView textRange:range];
  84. }
  85. };
  86. [self addSubview:_sourceLabel];
  87. return self;
  88. }
  89. - (void)setVerifyType:(WBUserVerifyType)verifyType {
  90. _verifyType = verifyType;
  91. switch (verifyType) {
  92. case WBUserVerifyTypeStandard: {
  93. _avatarBadgeView.hidden = NO;
  94. _avatarBadgeView.image = [WBStatusHelper imageNamed:@"avatar_vip"];
  95. } break;
  96. case WBUserVerifyTypeClub: {
  97. _avatarBadgeView.hidden = NO;
  98. _avatarBadgeView.image = [WBStatusHelper imageNamed:@"avatar_grassroot"];
  99. } break;
  100. default: {
  101. _avatarBadgeView.hidden = YES;
  102. } break;
  103. }
  104. }
  105. - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
  106. _trackingTouch = NO;
  107. UITouch *t = touches.anyObject;
  108. CGPoint p = [t locationInView:_avatarView];
  109. if (CGRectContainsPoint(_avatarView.bounds, p)) {
  110. _trackingTouch = YES;
  111. }
  112. p = [t locationInView:_nameLabel];
  113. if (CGRectContainsPoint(_nameLabel.bounds, p) && _nameLabel.textLayout.textBoundingRect.size.width > p.x) {
  114. _trackingTouch = YES;
  115. }
  116. if (!_trackingTouch) {
  117. [super touchesBegan:touches withEvent:event];
  118. }
  119. }
  120. - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
  121. if (!_trackingTouch) {
  122. [super touchesEnded:touches withEvent:event];
  123. } else {
  124. if ([_cell.delegate respondsToSelector:@selector(cell:didClickUser:)]) {
  125. [_cell.delegate cell:_cell didClickUser:_cell.statusView.layout.status.user];
  126. }
  127. }
  128. }
  129. - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
  130. if (!_trackingTouch) {
  131. [super touchesCancelled:touches withEvent:event];
  132. }
  133. }
  134. @end
  135. @implementation WBStatusCardView {
  136. BOOL _isRetweet;
  137. }
  138. - (instancetype)initWithFrame:(CGRect)frame {
  139. if (frame.size.width == 0 && frame.size.height == 0){
  140. frame.size.width = kScreenWidth;
  141. frame.origin.x = kWBCellPadding;
  142. }
  143. self = [super initWithFrame:frame];
  144. self.exclusiveTouch = YES;
  145. _imageView = [UIImageView new];
  146. _imageView.clipsToBounds = YES;
  147. _imageView.contentMode = UIViewContentModeScaleAspectFill;
  148. _badgeImageView = [UIImageView new];
  149. _badgeImageView.clipsToBounds = YES;
  150. _badgeImageView.contentMode = UIViewContentModeScaleAspectFit;
  151. _label = [YYLabel new];
  152. _label.textVerticalAlignment = YYTextVerticalAlignmentCenter;
  153. _label.numberOfLines = 3;
  154. _label.ignoreCommonProperties = YES;
  155. _label.displaysAsynchronously = YES;
  156. _label.fadeOnAsynchronouslyDisplay = NO;
  157. _label.fadeOnHighlight = NO;
  158. _button = [UIButton buttonWithType:UIButtonTypeCustom];
  159. [self addSubview:_imageView];
  160. [self addSubview:_badgeImageView];
  161. [self addSubview:_label];
  162. [self addSubview:_button];
  163. self.backgroundColor = kWBCellInnerViewColor;
  164. self.layer.borderWidth = CGFloatFromPixel(1);
  165. self.layer.borderColor = [UIColor colorWithWhite:0.000 alpha:0.070].CGColor;
  166. return self;
  167. }
  168. - (void)setWithLayout:(WBStatusLayout *)layout isRetweet:(BOOL)isRetweet {
  169. WBPageInfo *pageInfo = isRetweet ? layout.status.retweetedStatus.pageInfo : layout.status.pageInfo;
  170. if (!pageInfo) return;
  171. self.height = isRetweet ? layout.retweetCardHeight : layout.cardHeight;
  172. /*
  173. badge: 25,25 左上角 (42)
  174. image: 70,70 方形
  175. 100, 70 矩形
  176. btn: 60,70
  177. lineheight 20
  178. padding 10
  179. */
  180. _isRetweet = isRetweet;
  181. switch (isRetweet ? layout.retweetCardType : layout.cardType) {
  182. case WBStatusCardTypeNone: {
  183. } break;
  184. case WBStatusCardTypeNormal: {
  185. self.width = kWBCellContentWidth;
  186. if (pageInfo.typeIcon) {
  187. _badgeImageView.hidden = NO;
  188. _badgeImageView.frame = CGRectMake(0, 0, 25, 25);
  189. [_badgeImageView setImageWithURL:pageInfo.typeIcon placeholder:nil];
  190. } else {
  191. _badgeImageView.hidden = YES;
  192. }
  193. if (pageInfo.pagePic) {
  194. _imageView.hidden = NO;
  195. if (pageInfo.typeIcon) {
  196. _imageView.frame = CGRectMake(0, 0, 100, 70);
  197. } else {
  198. _imageView.frame = CGRectMake(0, 0, 70, 70);
  199. }
  200. [_imageView setImageWithURL:pageInfo.pagePic placeholder:nil];
  201. } else {
  202. _imageView.hidden = YES;
  203. }
  204. _label.hidden = NO;
  205. _label.frame = isRetweet ? layout.retweetCardTextRect : layout.cardTextRect;
  206. _label.textLayout = isRetweet ? layout.retweetCardTextLayout : layout.cardTextLayout;
  207. WBButtonLink *button = pageInfo.buttons.firstObject;
  208. if (button.pic && button.name) {
  209. _button.hidden = NO;
  210. _button.size = CGSizeMake(60, 70);
  211. _button.top = 0;
  212. _button.right = self.width;
  213. [_button setTitle:button.name forState:UIControlStateNormal];
  214. [_button setImageWithURL:button.pic forState:UIControlStateNormal placeholder:nil];
  215. } else {
  216. _button.hidden = YES;
  217. }
  218. }break;
  219. case WBStatusCardTypeVideo: {
  220. self.width = self.height;
  221. _badgeImageView.hidden = YES;
  222. _label.hidden = YES;
  223. _imageView.frame = self.bounds;
  224. [_imageView setImageWithURL:pageInfo.pagePic options:kNilOptions];
  225. _button.hidden = NO;
  226. _button.frame = self.bounds;
  227. [_button setTitle:nil forState:UIControlStateNormal];
  228. [_button cancelImageRequestForState:UIControlStateNormal];
  229. [_button setImage:[WBStatusHelper imageNamed:@"multimedia_videocard_play"] forState:UIControlStateNormal];
  230. } break;
  231. default: {
  232. } break;
  233. }
  234. self.backgroundColor = isRetweet ? [UIColor whiteColor] : kWBCellInnerViewColor;
  235. }
  236. - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
  237. self.backgroundColor = kWBCellInnerViewHighlightColor;
  238. }
  239. - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
  240. self.backgroundColor = _isRetweet ? [UIColor whiteColor] : kWBCellInnerViewColor;
  241. if ([_cell.delegate respondsToSelector:@selector(cellDidClickCard:)]) {
  242. [_cell.delegate cellDidClickCard:_cell];
  243. }
  244. }
  245. - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
  246. self.backgroundColor = _isRetweet ? [UIColor whiteColor] : kWBCellInnerViewColor;
  247. }
  248. @end
  249. @implementation WBStatusToolbarView
  250. - (instancetype)initWithFrame:(CGRect)frame {
  251. if (frame.size.width == 0 && frame.size.height == 0) {
  252. frame.size.width = kScreenWidth;
  253. frame.size.height = kWBCellToolbarHeight;
  254. }
  255. self = [super initWithFrame:frame];
  256. self.exclusiveTouch = YES;
  257. _repostButton = [UIButton buttonWithType:UIButtonTypeCustom];
  258. _repostButton.exclusiveTouch = YES;
  259. _repostButton.size = CGSizeMake(CGFloatPixelRound(self.width / 3.0), self.height);
  260. [_repostButton setBackgroundImage:[UIImage imageWithColor:kWBCellHighlightColor] forState:UIControlStateHighlighted];
  261. _commentButton = [UIButton buttonWithType:UIButtonTypeCustom];
  262. _commentButton.exclusiveTouch = YES;
  263. _commentButton.size = CGSizeMake(CGFloatPixelRound(self.width / 3.0), self.height);
  264. _commentButton.left = CGFloatPixelRound(self.width / 3.0);
  265. [_commentButton setBackgroundImage:[UIImage imageWithColor:kWBCellHighlightColor] forState:UIControlStateHighlighted];
  266. _likeButton = [UIButton buttonWithType:UIButtonTypeCustom];
  267. _likeButton.exclusiveTouch = YES;
  268. _likeButton.size = CGSizeMake(CGFloatPixelRound(self.width / 3.0), self.height);
  269. _likeButton.left = CGFloatPixelRound(self.width / 3.0 * 2.0);
  270. [_likeButton setBackgroundImage:[UIImage imageWithColor:kWBCellHighlightColor] forState:UIControlStateHighlighted];
  271. _repostImageView = [[UIImageView alloc] initWithImage:[WBStatusHelper imageNamed:@"timeline_icon_retweet"]];
  272. _repostImageView.centerY = self.height / 2;
  273. [_repostButton addSubview:_repostImageView];
  274. _commentImageView = [[UIImageView alloc] initWithImage:[WBStatusHelper imageNamed:@"timeline_icon_comment"]];
  275. _commentImageView.centerY = self.height / 2;
  276. [_commentButton addSubview:_commentImageView];
  277. _likeImageView = [[UIImageView alloc] initWithImage:[WBStatusHelper imageNamed:@"timeline_icon_unlike"]];
  278. _likeImageView.centerY = self.height / 2;
  279. [_likeButton addSubview:_likeImageView];
  280. _repostLabel = [YYLabel new];
  281. _repostLabel.userInteractionEnabled = NO;
  282. _repostLabel.height = self.height;
  283. _repostLabel.textVerticalAlignment = YYTextVerticalAlignmentCenter;
  284. _repostLabel.displaysAsynchronously = YES;
  285. _repostLabel.ignoreCommonProperties = YES;
  286. _repostLabel.fadeOnHighlight = NO;
  287. _repostLabel.fadeOnAsynchronouslyDisplay = NO;
  288. [_repostButton addSubview:_repostLabel];
  289. _commentLabel = [YYLabel new];
  290. _commentLabel.userInteractionEnabled = NO;
  291. _commentLabel.height = self.height;
  292. _commentLabel.textVerticalAlignment = YYTextVerticalAlignmentCenter;
  293. _commentLabel.displaysAsynchronously = YES;
  294. _commentLabel.ignoreCommonProperties = YES;
  295. _commentLabel.fadeOnHighlight = NO;
  296. _commentLabel.fadeOnAsynchronouslyDisplay = NO;
  297. [_commentButton addSubview:_commentLabel];
  298. _likeLabel = [YYLabel new];
  299. _likeLabel.userInteractionEnabled = NO;
  300. _likeLabel.height = self.height;
  301. _likeLabel.textVerticalAlignment = YYTextVerticalAlignmentCenter;
  302. _likeLabel.displaysAsynchronously = YES;
  303. _likeLabel.ignoreCommonProperties = YES;
  304. _likeLabel.fadeOnHighlight = NO;
  305. _likeLabel.fadeOnAsynchronouslyDisplay = NO;
  306. [_likeButton addSubview:_likeLabel];
  307. UIColor *dark = [UIColor colorWithWhite:0 alpha:0.2];
  308. UIColor *clear = [UIColor colorWithWhite:0 alpha:0];
  309. NSArray *colors = @[(id)clear.CGColor,(id)dark.CGColor, (id)clear.CGColor];
  310. NSArray *locations = @[@0.2, @0.5, @0.8];
  311. _line1 = [CAGradientLayer layer];
  312. _line1.colors = colors;
  313. _line1.locations = locations;
  314. _line1.startPoint = CGPointMake(0, 0);
  315. _line1.endPoint = CGPointMake(0, 1);
  316. _line1.size = CGSizeMake(CGFloatFromPixel(1), self.height);
  317. _line1.left = _repostButton.right;
  318. _line2 = [CAGradientLayer layer];
  319. _line2.colors = colors;
  320. _line2.locations = locations;
  321. _line2.startPoint = CGPointMake(0, 0);
  322. _line2.endPoint = CGPointMake(0, 1);
  323. _line2.size = CGSizeMake(CGFloatFromPixel(1), self.height);
  324. _line2.left = _commentButton.right;
  325. _topLine = [CALayer layer];
  326. _topLine.size = CGSizeMake(self.width, CGFloatFromPixel(1));
  327. _topLine.backgroundColor = kWBCellLineColor.CGColor;
  328. _bottomLine = [CALayer layer];
  329. _bottomLine.size = _topLine.size;
  330. _bottomLine.bottom = self.height;
  331. _bottomLine.backgroundColor = UIColorHex(e8e8e8).CGColor;
  332. [self addSubview:_repostButton];
  333. [self addSubview:_commentButton];
  334. [self addSubview:_likeButton];
  335. [self.layer addSublayer:_line1];
  336. [self.layer addSublayer:_line2];
  337. [self.layer addSublayer:_topLine];
  338. [self.layer addSublayer:_bottomLine];
  339. @weakify(self);
  340. [_repostButton addBlockForControlEvents:UIControlEventTouchUpInside block:^(id sender) {
  341. WBStatusCell *cell = weak_self.cell;
  342. if ([cell.delegate respondsToSelector:@selector(cellDidClickRepost:)]) {
  343. [cell.delegate cellDidClickRepost:cell];
  344. }
  345. }];
  346. [_commentButton addBlockForControlEvents:UIControlEventTouchUpInside block:^(id sender) {
  347. WBStatusCell *cell = weak_self.cell;
  348. if ([cell.delegate respondsToSelector:@selector(cellDidClickComment:)]) {
  349. [cell.delegate cellDidClickComment:cell];
  350. }
  351. }];
  352. [_likeButton addBlockForControlEvents:UIControlEventTouchUpInside block:^(id sender) {
  353. WBStatusCell *cell = weak_self.cell;
  354. if ([cell.delegate respondsToSelector:@selector(cellDidClickLike:)]) {
  355. [cell.delegate cellDidClickLike:cell];
  356. }
  357. }];
  358. return self;
  359. }
  360. - (void)setWithLayout:(WBStatusLayout *)layout {
  361. _repostLabel.width = layout.toolbarRepostTextWidth;
  362. _commentLabel.width = layout.toolbarCommentTextWidth;
  363. _likeLabel.width = layout.toolbarLikeTextWidth;
  364. _repostLabel.textLayout = layout.toolbarRepostTextLayout;
  365. _commentLabel.textLayout = layout.toolbarCommentTextLayout;
  366. _likeLabel.textLayout = layout.toolbarLikeTextLayout;
  367. [self adjustImage:_repostImageView label:_repostLabel inButton:_repostButton];
  368. [self adjustImage:_commentImageView label:_commentLabel inButton:_commentButton];
  369. [self adjustImage:_likeImageView label:_likeLabel inButton:_likeButton];
  370. _likeImageView.image = layout.status.attitudesStatus ? [self likeImage] : [self unlikeImage];
  371. }
  372. - (UIImage *)likeImage {
  373. static UIImage *img;
  374. static dispatch_once_t onceToken;
  375. dispatch_once(&onceToken, ^{
  376. img = [WBStatusHelper imageNamed:@"timeline_icon_like"];
  377. });
  378. return img;
  379. }
  380. - (UIImage *)unlikeImage {
  381. static UIImage *img;
  382. static dispatch_once_t onceToken;
  383. dispatch_once(&onceToken, ^{
  384. img = [WBStatusHelper imageNamed:@"timeline_icon_unlike"];
  385. });
  386. return img;
  387. }
  388. - (void)adjustImage:(UIImageView *)image label:(YYLabel *)label inButton:(UIButton *)button {
  389. CGFloat imageWidth = image.bounds.size.width;
  390. CGFloat labelWidth = label.width;
  391. CGFloat paddingMid = 5;
  392. CGFloat paddingSide = (button.width - imageWidth - labelWidth - paddingMid) / 2.0;
  393. image.centerX = CGFloatPixelRound(paddingSide + imageWidth / 2);
  394. label.right = CGFloatPixelRound(button.width - paddingSide);
  395. }
  396. - (void)setLiked:(BOOL)liked withAnimation:(BOOL)animation {
  397. WBStatusLayout *layout = _cell.statusView.layout;
  398. if (layout.status.attitudesStatus == liked) return;
  399. UIImage *image = liked ? [self likeImage] : [self unlikeImage];
  400. int newCount = layout.status.attitudesCount;
  401. newCount = liked ? newCount + 1 : newCount - 1;
  402. if (newCount < 0) newCount = 0;
  403. if (liked && newCount < 1) newCount = 1;
  404. NSString *newCountDesc = newCount > 0 ? [WBStatusHelper shortedNumberDesc:newCount] : @"赞";
  405. UIFont *font = [UIFont systemFontOfSize:kWBCellToolbarFontSize];
  406. YYTextContainer *container = [YYTextContainer containerWithSize:CGSizeMake(kScreenWidth, kWBCellToolbarHeight)];
  407. container.maximumNumberOfRows = 1;
  408. NSMutableAttributedString *likeText = [[NSMutableAttributedString alloc] initWithString:newCountDesc];
  409. likeText.font = font;
  410. likeText.color = liked ? kWBCellToolbarTitleHighlightColor : kWBCellToolbarTitleColor;
  411. YYTextLayout *textLayout = [YYTextLayout layoutWithContainer:container text:likeText];
  412. layout.status.attitudesStatus = liked;
  413. layout.status.attitudesCount = newCount;
  414. layout.toolbarLikeTextLayout = textLayout;
  415. if (!animation) {
  416. _likeImageView.image = image;
  417. _likeLabel.width = CGFloatPixelRound(textLayout.textBoundingRect.size.width);
  418. _likeLabel.textLayout = layout.toolbarLikeTextLayout;
  419. [self adjustImage:_likeImageView label:_likeLabel inButton:_likeButton];
  420. return;
  421. }
  422. [UIView animateWithDuration:0.2 delay:0 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseInOut animations:^{
  423. _likeImageView.layer.transformScale = 1.7;
  424. } completion:^(BOOL finished) {
  425. _likeImageView.image = image;
  426. _likeLabel.width = CGFloatPixelRound(textLayout.textBoundingRect.size.width);
  427. _likeLabel.textLayout = layout.toolbarLikeTextLayout;
  428. [self adjustImage:_likeImageView label:_likeLabel inButton:_likeButton];
  429. [UIView animateWithDuration:0.2 delay:0 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseInOut animations:^{
  430. _likeImageView.layer.transformScale = 0.9;
  431. } completion:^(BOOL finished) {
  432. [UIView animateWithDuration:0.2 delay:0 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseInOut animations:^{
  433. _likeImageView.layer.transformScale = 1;
  434. } completion:^(BOOL finished) {
  435. }];
  436. }];
  437. }];
  438. }
  439. @end
  440. @implementation WBStatusTagView
  441. - (instancetype)initWithFrame:(CGRect)frame {
  442. self = [super initWithFrame:frame];
  443. @weakify(self);
  444. _button = [UIButton buttonWithType:UIButtonTypeCustom];
  445. [_button setBackgroundImage:[UIImage imageWithColor:kWBCellBackgroundColor] forState:UIControlStateNormal];
  446. [_button setBackgroundImage:[UIImage imageWithColor:[UIColor colorWithWhite:0.000 alpha:0.200]] forState:UIControlStateHighlighted];
  447. [_button addBlockForControlEvents:UIControlEventTouchUpInside block:^(id sender) {
  448. if ([weak_self.cell.delegate respondsToSelector:@selector(cellDidClickTag:)]) {
  449. [weak_self.cell.delegate cellDidClickTag:weak_self.cell];
  450. }
  451. }];
  452. _button.hidden = YES;
  453. [self addSubview:_button];
  454. _label = [YYLabel new];
  455. _label.textVerticalAlignment = YYTextVerticalAlignmentCenter;
  456. _label.displaysAsynchronously = YES;
  457. _label.ignoreCommonProperties = YES;
  458. _label.fadeOnHighlight = NO;
  459. _label.fadeOnAsynchronouslyDisplay = NO;
  460. _label.highlightTapAction = ^(UIView *containerView, NSAttributedString *text, NSRange range, CGRect rect) {
  461. if ([weak_self.cell.delegate respondsToSelector:@selector(cell:didClickInLabel:textRange:)]) {
  462. [weak_self.cell.delegate cell:weak_self.cell didClickInLabel:(YYLabel *)containerView textRange:range];
  463. }
  464. };
  465. [self addSubview:_label];
  466. _imageView = [UIImageView new];
  467. _imageView.size = CGSizeMake(kWBCellTagPlaceHeight, kWBCellTagPlaceHeight);
  468. _imageView.image = [WBStatusHelper imageNamed:@"timeline_icon_locate"];
  469. _imageView.hidden = YES;
  470. [self addSubview:_imageView];
  471. _label.height = kWBCellTagPlaceHeight;
  472. _button.height = kWBCellTagPlaceHeight;
  473. self.height = kWBCellTagPlaceHeight;
  474. return self;
  475. }
  476. - (void)setWithLayout:(WBStatusLayout *)layout {
  477. if (layout.tagType == WBStatusTagTypePlace) {
  478. _label.height = kWBCellTagPlaceHeight;
  479. _imageView.hidden = NO;
  480. _button.hidden = NO;
  481. _label.left = _imageView.right + 6;
  482. _label.width = layout.tagTextLayout.textBoundingRect.size.width + 6;
  483. _label.textLayout = layout.tagTextLayout;
  484. _label.userInteractionEnabled = NO;
  485. self.width = _label.right;
  486. _label.width = self.width;
  487. _button.width = self.width;
  488. } else if (layout.tagType == WBStatusTagTypeNormal) {
  489. _imageView.hidden = YES;
  490. _button.hidden = YES;
  491. _label.left = 0;
  492. _label.width = layout.tagTextLayout.textBoundingRect.size.width + 1;
  493. _label.userInteractionEnabled = YES;
  494. _label.textLayout = layout.tagTextLayout;
  495. }
  496. }
  497. @end
  498. @implementation WBStatusView {
  499. BOOL _touchRetweetView;
  500. }
  501. - (instancetype)initWithFrame:(CGRect)frame {
  502. if (frame.size.width == 0 && frame.size.height == 0) {
  503. frame.size.width = kScreenWidth;
  504. frame.size.height = 1;
  505. }
  506. self = [super initWithFrame:frame];
  507. self.backgroundColor = [UIColor clearColor];
  508. self.exclusiveTouch = YES;
  509. @weakify(self);
  510. _contentView = [UIView new];
  511. _contentView.width = kScreenWidth;
  512. _contentView.height = 1;
  513. _contentView.backgroundColor = [UIColor whiteColor];
  514. static UIImage *topLineBG, *bottomLineBG;
  515. static dispatch_once_t onceToken;
  516. dispatch_once(&onceToken, ^{
  517. topLineBG = [UIImage imageWithSize:CGSizeMake(1, 3) drawBlock:^(CGContextRef context) {
  518. CGContextSetFillColorWithColor(context, [UIColor blackColor].CGColor);
  519. CGContextSetShadowWithColor(context, CGSizeMake(0, 0), 0.8, [UIColor colorWithWhite:0 alpha:0.08].CGColor);
  520. CGContextAddRect(context, CGRectMake(-2, 3, 4, 4));
  521. CGContextFillPath(context);
  522. }];
  523. bottomLineBG = [UIImage imageWithSize:CGSizeMake(1, 3) drawBlock:^(CGContextRef context) {
  524. CGContextSetFillColorWithColor(context, [UIColor blackColor].CGColor);
  525. CGContextSetShadowWithColor(context, CGSizeMake(0, 0.4), 2, [UIColor colorWithWhite:0 alpha:0.08].CGColor);
  526. CGContextAddRect(context, CGRectMake(-2, -2, 4, 2));
  527. CGContextFillPath(context);
  528. }];
  529. });
  530. UIImageView *topLine = [[UIImageView alloc] initWithImage:topLineBG];
  531. topLine.width = _contentView.width;
  532. topLine.bottom = 0;
  533. topLine.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleBottomMargin;
  534. [_contentView addSubview:topLine];
  535. UIImageView *bottomLine = [[UIImageView alloc] initWithImage:bottomLineBG];
  536. bottomLine.width = _contentView.width;
  537. bottomLine.top = _contentView.height;
  538. bottomLine.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin;
  539. [_contentView addSubview:bottomLine];
  540. [self addSubview:_contentView];
  541. _titleView = [WBStatusTitleView new];
  542. _titleView.hidden = YES;
  543. [_contentView addSubview:_titleView];
  544. _profileView = [WBStatusProfileView new];
  545. [_contentView addSubview:_profileView];
  546. _vipBackgroundView = [UIImageView new];
  547. _vipBackgroundView.size = CGSizeMake(kScreenWidth, 14.0);
  548. _vipBackgroundView.top = -2;
  549. _vipBackgroundView.contentMode = UIViewContentModeTopRight;
  550. [_contentView addSubview:_vipBackgroundView];
  551. _menuButton = [UIButton buttonWithType:UIButtonTypeCustom];
  552. _menuButton.size = CGSizeMake(30, 30);
  553. [_menuButton setImage:[WBStatusHelper imageNamed:@"timeline_icon_more"] forState:UIControlStateNormal];
  554. [_menuButton setImage:[WBStatusHelper imageNamed:@"timeline_icon_more_highlighted"] forState:UIControlStateHighlighted];
  555. _menuButton.centerX = self.width - 20;
  556. _menuButton.centerY = 18;
  557. [_menuButton addBlockForControlEvents:UIControlEventTouchUpInside block:^(id sender) {
  558. if ([weak_self.cell.delegate respondsToSelector:@selector(cellDidClickMenu:)]) {
  559. [weak_self.cell.delegate cellDidClickMenu:weak_self.cell];
  560. }
  561. }];
  562. [_contentView addSubview:_menuButton];
  563. _retweetBackgroundView = [UIView new];
  564. _retweetBackgroundView.backgroundColor = kWBCellInnerViewColor;
  565. _retweetBackgroundView.width = kScreenWidth;
  566. [_contentView addSubview:_retweetBackgroundView];
  567. _textLabel = [YYLabel new];
  568. _textLabel.left = kWBCellPadding;
  569. _textLabel.width = kWBCellContentWidth;
  570. _textLabel.textVerticalAlignment = YYTextVerticalAlignmentTop;
  571. _textLabel.displaysAsynchronously = YES;
  572. _textLabel.ignoreCommonProperties = YES;
  573. _textLabel.fadeOnAsynchronouslyDisplay = NO;
  574. _textLabel.fadeOnHighlight = NO;
  575. _textLabel.highlightTapAction = ^(UIView *containerView, NSAttributedString *text, NSRange range, CGRect rect) {
  576. if ([weak_self.cell.delegate respondsToSelector:@selector(cell:didClickInLabel:textRange:)]) {
  577. [weak_self.cell.delegate cell:weak_self.cell didClickInLabel:(YYLabel *)containerView textRange:range];
  578. }
  579. };
  580. [_contentView addSubview:_textLabel];
  581. _retweetTextLabel = [YYLabel new];
  582. _retweetTextLabel.left = kWBCellPadding;
  583. _retweetTextLabel.width = kWBCellContentWidth;
  584. _retweetTextLabel.textVerticalAlignment = YYTextVerticalAlignmentTop;
  585. _retweetTextLabel.displaysAsynchronously = YES;
  586. _retweetTextLabel.ignoreCommonProperties = YES;
  587. _retweetTextLabel.fadeOnAsynchronouslyDisplay = NO;
  588. _retweetTextLabel.fadeOnHighlight = NO;
  589. _retweetTextLabel.highlightTapAction = ^(UIView *containerView, NSAttributedString *text, NSRange range, CGRect rect) {
  590. if ([weak_self.cell.delegate respondsToSelector:@selector(cell:didClickInLabel:textRange:)]) {
  591. [weak_self.cell.delegate cell:weak_self.cell didClickInLabel:(YYLabel *)containerView textRange:range];
  592. }
  593. };
  594. [_contentView addSubview:_retweetTextLabel];
  595. NSMutableArray *picViews = [NSMutableArray new];
  596. for (int i = 0; i < 9; i++) {
  597. YYControl *imageView = [YYControl new];
  598. imageView.size = CGSizeMake(100, 100);
  599. imageView.hidden = YES;
  600. imageView.clipsToBounds = YES;
  601. imageView.backgroundColor = kWBCellHighlightColor;
  602. imageView.exclusiveTouch = YES;
  603. imageView.touchBlock = ^(YYControl *view, YYGestureRecognizerState state, NSSet *touches, UIEvent *event) {
  604. if (![weak_self.cell.delegate respondsToSelector:@selector(cell:didClickImageAtIndex:)]) return;
  605. if (state == YYGestureRecognizerStateEnded) {
  606. UITouch *touch = touches.anyObject;
  607. CGPoint p = [touch locationInView:view];
  608. if (CGRectContainsPoint(view.bounds, p)) {
  609. [weak_self.cell.delegate cell:weak_self.cell didClickImageAtIndex:i];
  610. }
  611. }
  612. };
  613. UIView *badge = [UIImageView new];
  614. badge.userInteractionEnabled = NO;
  615. badge.contentMode = UIViewContentModeScaleAspectFit;
  616. badge.size = CGSizeMake(56 / 2, 36 / 2);
  617. badge.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin;
  618. badge.right = imageView.width;
  619. badge.bottom = imageView.height;
  620. badge.hidden = YES;
  621. [imageView addSubview:badge];
  622. [picViews addObject:imageView];
  623. [_contentView addSubview:imageView];
  624. }
  625. _picViews = picViews;
  626. _cardView = [WBStatusCardView new];
  627. _cardView.hidden = YES;
  628. [_contentView addSubview:_cardView];
  629. _tagView = [WBStatusTagView new];
  630. _tagView.left = kWBCellPadding;
  631. _tagView.hidden = YES;
  632. [_contentView addSubview:_tagView];
  633. _toolbarView = [WBStatusToolbarView new];
  634. [_contentView addSubview:_toolbarView];
  635. return self;
  636. }
  637. - (void)setLayout:(WBStatusLayout *)layout {
  638. _layout = layout;
  639. self.height = layout.height;
  640. _contentView.top = layout.marginTop;
  641. _contentView.height = layout.height - layout.marginTop - layout.marginBottom;
  642. CGFloat top = 0;
  643. if (layout.titleHeight > 0) {
  644. _titleView.hidden = NO;
  645. _titleView.height = layout.titleHeight;
  646. _titleView.titleLabel.textLayout = layout.titleTextLayout;
  647. top = layout.titleHeight;
  648. } else {
  649. _titleView.hidden = YES;
  650. }
  651. /// 圆角头像
  652. [_profileView.avatarView setImageWithURL:layout.status.user.avatarLarge //profileImageURL
  653. placeholder:nil
  654. options:kNilOptions
  655. manager:[WBStatusHelper avatarImageManager] //< 圆角头像manager,内置圆角处理
  656. progress:nil
  657. transform:nil
  658. completion:nil];
  659. _profileView.nameLabel.textLayout = layout.nameTextLayout;
  660. _profileView.sourceLabel.textLayout = layout.sourceTextLayout;
  661. _profileView.verifyType = layout.status.user.userVerifyType;
  662. _profileView.height = layout.profileHeight;
  663. _profileView.top = top;
  664. top += layout.profileHeight;
  665. NSURL *picBg = [WBStatusHelper defaultURLForImageURL:layout.status.picBg];
  666. __weak typeof(_vipBackgroundView) vipBackgroundView = _vipBackgroundView;
  667. [_vipBackgroundView setImageWithURL:picBg placeholder:nil options:YYWebImageOptionAvoidSetImage completion:^(UIImage *image, NSURL *url, YYWebImageFromType from, YYWebImageStage stage, NSError *error) {
  668. if (image) {
  669. image = [UIImage imageWithCGImage:image.CGImage scale:2.0 orientation:image.imageOrientation];
  670. vipBackgroundView.image = image;
  671. }
  672. }];
  673. _textLabel.top = top;
  674. _textLabel.height = layout.textHeight;
  675. _textLabel.textLayout = layout.textLayout;
  676. top += layout.textHeight;
  677. _retweetBackgroundView.hidden = YES;
  678. _retweetTextLabel.hidden = YES;
  679. _cardView.hidden = YES;
  680. if (layout.picHeight == 0 && layout.retweetPicHeight == 0) {
  681. [self _hideImageViews];
  682. }
  683. //优先级是 转发->图片->卡片
  684. if (layout.retweetHeight > 0) {
  685. _retweetBackgroundView.top = top;
  686. _retweetBackgroundView.height = layout.retweetHeight;
  687. _retweetBackgroundView.hidden = NO;
  688. _retweetTextLabel.top = top;
  689. _retweetTextLabel.height = layout.retweetTextHeight;
  690. _retweetTextLabel.textLayout = layout.retweetTextLayout;
  691. _retweetTextLabel.hidden = NO;
  692. if (layout.retweetPicHeight > 0) {
  693. [self _setImageViewWithTop:_retweetTextLabel.bottom isRetweet:YES];
  694. } else {
  695. [self _hideImageViews];
  696. if (layout.retweetCardHeight > 0) {
  697. _cardView.top = _retweetTextLabel.bottom;
  698. _cardView.hidden = NO;
  699. [_cardView setWithLayout:layout isRetweet:YES];
  700. }
  701. }
  702. } else if (layout.picHeight > 0) {
  703. [self _setImageViewWithTop:top isRetweet:NO];
  704. } else if (layout.cardHeight > 0) {
  705. _cardView.top = top;
  706. _cardView.hidden = NO;
  707. [_cardView setWithLayout:layout isRetweet:NO];
  708. }
  709. if (layout.tagHeight > 0) {
  710. _tagView.hidden = NO;
  711. [_tagView setWithLayout:layout];
  712. _tagView.centerY = _contentView.height - kWBCellToolbarHeight - layout.tagHeight / 2;
  713. } else {
  714. _tagView.hidden = YES;
  715. }
  716. _toolbarView.bottom = _contentView.height;
  717. [_toolbarView setWithLayout:layout];
  718. }
  719. - (void)_hideImageViews {
  720. for (UIImageView *imageView in _picViews) {
  721. imageView.hidden = YES;
  722. }
  723. }
  724. - (void)_setImageViewWithTop:(CGFloat)imageTop isRetweet:(BOOL)isRetweet {
  725. CGSize picSize = isRetweet ? _layout.retweetPicSize : _layout.picSize;
  726. NSArray *pics = isRetweet ? _layout.status.retweetedStatus.pics : _layout.status.pics;
  727. int picsCount = (int)pics.count;
  728. for (int i = 0; i < 9; i++) {
  729. UIView *imageView = _picViews[i];
  730. if (i >= picsCount) {
  731. [imageView.layer cancelCurrentImageRequest];
  732. imageView.hidden = YES;
  733. } else {
  734. CGPoint origin = {0};
  735. switch (picsCount) {
  736. case 1: {
  737. origin.x = kWBCellPadding;
  738. origin.y = imageTop;
  739. } break;
  740. case 4: {
  741. origin.x = kWBCellPadding + (i % 2) * (picSize.width + kWBCellPaddingPic);
  742. origin.y = imageTop + (int)(i / 2) * (picSize.height + kWBCellPaddingPic);
  743. } break;
  744. default: {
  745. origin.x = kWBCellPadding + (i % 3) * (picSize.width + kWBCellPaddingPic);
  746. origin.y = imageTop + (int)(i / 3) * (picSize.height + kWBCellPaddingPic);
  747. } break;
  748. }
  749. imageView.frame = (CGRect){.origin = origin, .size = picSize};
  750. imageView.hidden = NO;
  751. [imageView.layer removeAnimationForKey:@"contents"];
  752. WBPicture *pic = pics[i];
  753. UIView *badge = imageView.subviews.firstObject;
  754. switch (pic.largest.badgeType) {
  755. case WBPictureBadgeTypeNone: {
  756. if (badge.layer.contents) {
  757. badge.layer.contents = nil;
  758. badge.hidden = YES;
  759. }
  760. } break;
  761. case WBPictureBadgeTypeLong: {
  762. badge.layer.contents = (__bridge id)([WBStatusHelper imageNamed:@"timeline_image_longimage"].CGImage);
  763. badge.hidden = NO;
  764. } break;
  765. case WBPictureBadgeTypeGIF: {
  766. badge.layer.contents = (__bridge id)([WBStatusHelper imageNamed:@"timeline_image_gif"].CGImage);
  767. badge.hidden = NO;
  768. } break;
  769. }
  770. @weakify(imageView);
  771. [imageView.layer setImageWithURL:pic.bmiddle.url
  772. placeholder:nil
  773. options:YYWebImageOptionAvoidSetImage
  774. completion:^(UIImage *image, NSURL *url, YYWebImageFromType from, YYWebImageStage stage, NSError *error) {
  775. @strongify(imageView);
  776. if (!imageView) return;
  777. if (image && stage == YYWebImageStageFinished) {
  778. int width = pic.bmiddle.width;
  779. int height = pic.bmiddle.height;
  780. CGFloat scale = (height / width) / (imageView.height / imageView.width);
  781. if (scale < 0.99 || isnan(scale)) { // 宽图把左右两边裁掉
  782. imageView.contentMode = UIViewContentModeScaleAspectFill;
  783. imageView.layer.contentsRect = CGRectMake(0, 0, 1, 1);
  784. } else { // 高图只保留顶部
  785. imageView.contentMode = UIViewContentModeScaleToFill;
  786. imageView.layer.contentsRect = CGRectMake(0, 0, 1, (float)width / height);
  787. }
  788. ((YYControl *)imageView).image = image;
  789. if (from != YYWebImageFromMemoryCacheFast) {
  790. CATransition *transition = [CATransition animation];
  791. transition.duration = 0.15;
  792. transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
  793. transition.type = kCATransitionFade;
  794. [imageView.layer addAnimation:transition forKey:@"contents"];
  795. }
  796. }
  797. }];
  798. }
  799. }
  800. }
  801. - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
  802. UITouch *touch = touches.anyObject;
  803. CGPoint p = [touch locationInView:_retweetBackgroundView];
  804. BOOL insideRetweet = CGRectContainsPoint(_retweetBackgroundView.bounds, p);
  805. if (!_retweetBackgroundView.hidden && insideRetweet) {
  806. [(_retweetBackgroundView) performSelector:@selector(setBackgroundColor:) withObject:kWBCellHighlightColor afterDelay:0.15];
  807. _touchRetweetView = YES;
  808. } else {
  809. [(_contentView) performSelector:@selector(setBackgroundColor:) withObject:kWBCellHighlightColor afterDelay:0.15];
  810. _touchRetweetView = NO;
  811. }
  812. }
  813. - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
  814. [self touchesRestoreBackgroundColor];
  815. if (_touchRetweetView) {
  816. if ([_cell.delegate respondsToSelector:@selector(cellDidClickRetweet:)]) {
  817. [_cell.delegate cellDidClickRetweet:_cell];
  818. }
  819. } else {
  820. if ([_cell.delegate respondsToSelector:@selector(cellDidClick:)]) {
  821. [_cell.delegate cellDidClick:_cell];
  822. }
  823. }
  824. }
  825. - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
  826. [self touchesRestoreBackgroundColor];
  827. }
  828. - (void)touchesRestoreBackgroundColor {
  829. [NSObject cancelPreviousPerformRequestsWithTarget:_retweetBackgroundView selector:@selector(setBackgroundColor:) object:kWBCellHighlightColor];
  830. [NSObject cancelPreviousPerformRequestsWithTarget:_contentView selector:@selector(setBackgroundColor:) object:kWBCellHighlightColor];
  831. _contentView.backgroundColor = [UIColor whiteColor];
  832. _retweetBackgroundView.backgroundColor = kWBCellInnerViewColor;
  833. }
  834. @end
  835. @implementation WBStatusCell
  836. - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
  837. self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
  838. _statusView = [WBStatusView new];
  839. _statusView.cell = self;
  840. _statusView.titleView.cell = self;
  841. _statusView.profileView.cell = self;
  842. _statusView.cardView.cell = self;
  843. _statusView.toolbarView.cell = self;
  844. _statusView.tagView.cell = self;
  845. [self.contentView addSubview:_statusView];
  846. return self;
  847. }
  848. - (void)prepareForReuse {
  849. // ignore
  850. }
  851. - (void)setLayout:(WBStatusLayout *)layout {
  852. self.height = layout.height;
  853. self.contentView.height = layout.height;
  854. _statusView.layout = layout;
  855. }
  856. @end