WBStatusHelper.m 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. //
  2. // WBFeedHelper.m
  3. // YYKitExample
  4. //
  5. // Created by ibireme on 15/9/5.
  6. // Copyright (c) 2015 ibireme. All rights reserved.
  7. //
  8. #import "WBStatusHelper.h"
  9. @implementation WBStatusHelper
  10. + (NSBundle *)bundle {
  11. static NSBundle *bundle;
  12. static dispatch_once_t onceToken;
  13. dispatch_once(&onceToken, ^{
  14. NSString *path = [[NSBundle mainBundle] pathForResource:@"ResourceWeibo" ofType:@"bundle"];
  15. bundle = [NSBundle bundleWithPath:path];
  16. });
  17. return bundle;
  18. }
  19. + (NSBundle *)emoticonBundle {
  20. static NSBundle *bundle;
  21. static dispatch_once_t onceToken;
  22. dispatch_once(&onceToken, ^{
  23. NSString *bundlePath = [[NSBundle mainBundle] pathForResource:@"EmoticonWeibo" ofType:@"bundle"];
  24. bundle = [NSBundle bundleWithPath:bundlePath];
  25. });
  26. return bundle;
  27. }
  28. + (YYMemoryCache *)imageCache {
  29. static YYMemoryCache *cache;
  30. static dispatch_once_t onceToken;
  31. dispatch_once(&onceToken, ^{
  32. cache = [YYMemoryCache new];
  33. cache.shouldRemoveAllObjectsOnMemoryWarning = NO;
  34. cache.shouldRemoveAllObjectsWhenEnteringBackground = NO;
  35. cache.name = @"WeiboImageCache";
  36. });
  37. return cache;
  38. }
  39. + (UIImage *)imageNamed:(NSString *)name {
  40. if (!name) return nil;
  41. UIImage *image = [[self imageCache] objectForKey:name];
  42. if (image) return image;
  43. NSString *ext = name.pathExtension;
  44. if (ext.length == 0) ext = @"png";
  45. NSString *path = [[self bundle] pathForScaledResource:name ofType:ext];
  46. if (!path) return nil;
  47. image = [UIImage imageWithContentsOfFile:path];
  48. image = [image imageByDecoded];
  49. if (!image) return nil;
  50. [[self imageCache] setObject:image forKey:name];
  51. return image;
  52. }
  53. + (UIImage *)imageWithPath:(NSString *)path {
  54. if (!path) return nil;
  55. UIImage *image = [[self imageCache] objectForKey:path];
  56. if (image) return image;
  57. if (path.pathScale == 1) {
  58. // 查找 @2x @3x 的图片
  59. NSArray *scales = [NSBundle preferredScales];
  60. for (NSNumber *scale in scales) {
  61. image = [UIImage imageWithContentsOfFile:[path stringByAppendingPathScale:scale.floatValue]];
  62. if (image) break;
  63. }
  64. } else {
  65. image = [UIImage imageWithContentsOfFile:path];
  66. }
  67. if (image) {
  68. image = [image imageByDecoded];
  69. [[self imageCache] setObject:image forKey:path];
  70. }
  71. return image;
  72. }
  73. + (YYWebImageManager *)avatarImageManager {
  74. static YYWebImageManager *manager;
  75. static dispatch_once_t onceToken;
  76. dispatch_once(&onceToken, ^{
  77. NSString *path = [[UIApplication sharedApplication].cachesPath stringByAppendingPathComponent:@"weibo.avatar"];
  78. YYImageCache *cache = [[YYImageCache alloc] initWithPath:path];
  79. manager = [[YYWebImageManager alloc] initWithCache:cache queue:[YYWebImageManager sharedManager].queue];
  80. manager.sharedTransformBlock = ^(UIImage *image, NSURL *url) {
  81. if (!image) return image;
  82. return [image imageByRoundCornerRadius:100]; // a large value
  83. };
  84. });
  85. return manager;
  86. }
  87. + (NSString *)stringWithTimelineDate:(NSDate *)date {
  88. if (!date) return @"";
  89. static NSDateFormatter *formatterYesterday;
  90. static NSDateFormatter *formatterSameYear;
  91. static NSDateFormatter *formatterFullDate;
  92. static dispatch_once_t onceToken;
  93. dispatch_once(&onceToken, ^{
  94. formatterYesterday = [[NSDateFormatter alloc] init];
  95. [formatterYesterday setDateFormat:ASLocalizedString(@"昨天")];
  96. [formatterYesterday setLocale:[NSLocale currentLocale]];
  97. formatterSameYear = [[NSDateFormatter alloc] init];
  98. [formatterSameYear setDateFormat:@"M-d"];
  99. [formatterSameYear setLocale:[NSLocale currentLocale]];
  100. formatterFullDate = [[NSDateFormatter alloc] init];
  101. [formatterFullDate setDateFormat:@"yyyy-MM-dd"];
  102. [formatterFullDate setLocale:[NSLocale currentLocale]];
  103. });
  104. NSDate *now = [NSDate new];
  105. NSTimeInterval delta = now.timeIntervalSince1970 - date.timeIntervalSince1970;
  106. if (delta < -60 * 10) { // 本地时间有问题
  107. return [formatterFullDate stringFromDate:date];
  108. } else if (delta < 60 * 10) { // 10分钟内
  109. return ASLocalizedString(@"刚刚");
  110. } else if (delta < 60 * 60) { // 1小时内
  111. return [NSString stringWithFormat:ASLocalizedString(@"%d分钟前"), (int)(delta / 60.0)];
  112. } else if (date.isToday) {
  113. return [NSString stringWithFormat:ASLocalizedString(@"%d小时前"), (int)(delta / 60.0 / 60.0)];
  114. } else if (date.isYesterday) {
  115. return ASLocalizedString(@"昨天");
  116. } else if (date.month == now.month) {
  117. //几天前
  118. return [NSString stringWithFormat:ASLocalizedString(@"%d天前"), (int)(delta / 60 / 60 / 24)];
  119. } else if (date.year == now.year) {
  120. return [formatterSameYear stringFromDate:date];
  121. } else {
  122. return [formatterFullDate stringFromDate:date];
  123. }
  124. }
  125. + (NSURL *)defaultURLForImageURL:(id)imageURL {
  126. /*
  127. 微博 API 提供的图片 URL 有时并不能直接用,需要做一些字符串替换:
  128. http://u1.sinaimg.cn/upload/2014/11/04/common_icon_membership_level6.png //input
  129. http://u1.sinaimg.cn/upload/2014/11/04/common_icon_membership_level6_default.png //output
  130. http://img.t.sinajs.cn/t6/skin/public/feed_cover/star_003_y.png?version=2015080302 //input
  131. http://img.t.sinajs.cn/t6/skin/public/feed_cover/star_003_os7.png?version=2015080302 //output
  132. */
  133. if (!imageURL) return nil;
  134. NSString *link = nil;
  135. if ([imageURL isKindOfClass:[NSURL class]]) {
  136. link = ((NSURL *)imageURL).absoluteString;
  137. } else if ([imageURL isKindOfClass:[NSString class]]) {
  138. link = imageURL;
  139. }
  140. if (link.length == 0) return nil;
  141. if ([link hasSuffix:@".png"]) {
  142. // add "_default"
  143. if (![link hasSuffix:@"_default.png"]) {
  144. NSString *sub = [link substringToIndex:link.length - 4];
  145. link = [sub stringByAppendingFormat:@"_default.png"];
  146. }
  147. } else {
  148. // replace "_y.png" with "_os7.png"
  149. NSRange range = [link rangeOfString:@"_y.png?version"];
  150. if (range.location != NSNotFound) {
  151. NSMutableString *mutable = link.mutableCopy;
  152. [mutable replaceCharactersInRange:NSMakeRange(range.location + 1, 1) withString:@"os7"];
  153. link = mutable;
  154. }
  155. }
  156. return [NSURL URLWithString:link];
  157. }
  158. + (NSString *)shortedNumberDesc:(NSUInteger)number {
  159. // should be localized
  160. if (number <= 9999) return [NSString stringWithFormat:@"%d", (int)number];
  161. if (number <= 9999999) return [NSString stringWithFormat:NSLocalizedString(@"%d万",nil), (int)(number / 10000)];
  162. return [NSString stringWithFormat:NSLocalizedString(@"%d千万",nil), (int)(number / 10000000)];
  163. }
  164. + (NSRegularExpression *)regexAt {
  165. static NSRegularExpression *regex;
  166. static dispatch_once_t onceToken;
  167. dispatch_once(&onceToken, ^{
  168. // 微博的 At 只允许 英文数字下划线连字符,和 unicode 4E00~9FA5 范围内的中文字符,这里保持和微博一致。。
  169. // 目前中文字符范围比这个大
  170. regex = [NSRegularExpression regularExpressionWithPattern:@"@[-_a-zA-Z0-9\u4E00-\u9FA5]+" options:kNilOptions error:NULL];
  171. });
  172. return regex;
  173. }
  174. + (NSRegularExpression *)regexTopic {
  175. static NSRegularExpression *regex;
  176. static dispatch_once_t onceToken;
  177. dispatch_once(&onceToken, ^{
  178. regex = [NSRegularExpression regularExpressionWithPattern:@"#[^@#]+?#" options:kNilOptions error:NULL];
  179. });
  180. return regex;
  181. }
  182. + (NSRegularExpression *)regexEmoticon {
  183. static NSRegularExpression *regex;
  184. static dispatch_once_t onceToken;
  185. dispatch_once(&onceToken, ^{
  186. regex = [NSRegularExpression regularExpressionWithPattern:@"\\[[^ \\[\\]]+?\\]" options:kNilOptions error:NULL];
  187. });
  188. return regex;
  189. }
  190. /*
  191. weibo.app 里面的正则,有兴趣的可以参考下:
  192. HTTP链接 (例如 http://www.weibo.com ):
  193. ([hH]ttp[s]{0,1})://[a-zA-Z0-9\.\-]+\.([a-zA-Z]{2,4})(:\d+)?(/[a-zA-Z0-9\-~!@#$%^&*+?:_/=<>.',;]*)?
  194. ([hH]ttp[s]{0,1})://[a-zA-Z0-9\.\-]+\.([a-zA-Z]{2,4})(:\d+)?(/[a-zA-Z0-9\-~!@#$%^&*+?:_/=<>]*)?
  195. (?i)https?://[a-zA-Z0-9]+(\.[a-zA-Z0-9]+)+([-A-Z0-9a-z_\$\.\+!\*\(\)/,:;@&=\?~#%]*)*
  196. ^http?://[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)+(\/[\w-. \/\?%@&+=\u4e00-\u9fa5]*)?$
  197. 链接 (例如 www.baidu.com/s?wd=test ):
  198. ^[a-zA-Z0-9]+(\.[a-zA-Z0-9]+)+([-A-Z0-9a-z_\$\.\+!\*\(\)/,:;@&=\?~#%]*)*
  199. 邮箱 (例如 sjobs@apple.com ):
  200. \b([a-zA-Z0-9%_.+\-]{1,32})@([a-zA-Z0-9.\-]+?\.[a-zA-Z]{2,6})\b
  201. \b([a-zA-Z0-9%_.+\-]+)@([a-zA-Z0-9.\-]+?\.[a-zA-Z]{2,6})\b
  202. ([a-zA-Z0-9%_.+\-]+)@([a-zA-Z0-9.\-]+?\.[a-zA-Z]{2,6})
  203. 电话号码 (例如 18612345678):
  204. ^[1-9][0-9]{4,11}$
  205. At (例如 @王思聪 ):
  206. @([\x{4e00}-\x{9fa5}A-Za-z0-9_\-]+)
  207. 话题 (例如 #奇葩说# ):
  208. #([^@]+?)#
  209. 表情 (例如 [呵呵] ):
  210. \[([^ \[]*?)]
  211. 匹配单个字符 (中英文数字下划线连字符)
  212. [\x{4e00}-\x{9fa5}A-Za-z0-9_\-]
  213. 匹配回复 (例如 回复@王思聪: ):
  214. \x{56de}\x{590d}@([\x{4e00}-\x{9fa5}A-Za-z0-9_\-]+)(\x{0020}\x{7684}\x{8d5e})?:
  215. */
  216. @end