// // WBFeedHelper.m // YYKitExample // // Created by ibireme on 15/9/5. // Copyright (c) 2015 ibireme. All rights reserved. // #import "WBStatusHelper.h" @implementation WBStatusHelper + (NSBundle *)bundle { static NSBundle *bundle; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSString *path = [[NSBundle mainBundle] pathForResource:@"ResourceWeibo" ofType:@"bundle"]; bundle = [NSBundle bundleWithPath:path]; }); return bundle; } + (NSBundle *)emoticonBundle { static NSBundle *bundle; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSString *bundlePath = [[NSBundle mainBundle] pathForResource:@"EmoticonWeibo" ofType:@"bundle"]; bundle = [NSBundle bundleWithPath:bundlePath]; }); return bundle; } + (YYMemoryCache *)imageCache { static YYMemoryCache *cache; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ cache = [YYMemoryCache new]; cache.shouldRemoveAllObjectsOnMemoryWarning = NO; cache.shouldRemoveAllObjectsWhenEnteringBackground = NO; cache.name = @"WeiboImageCache"; }); return cache; } + (UIImage *)imageNamed:(NSString *)name { if (!name) return nil; UIImage *image = [[self imageCache] objectForKey:name]; if (image) return image; NSString *ext = name.pathExtension; if (ext.length == 0) ext = @"png"; NSString *path = [[self bundle] pathForScaledResource:name ofType:ext]; if (!path) return nil; image = [UIImage imageWithContentsOfFile:path]; image = [image imageByDecoded]; if (!image) return nil; [[self imageCache] setObject:image forKey:name]; return image; } + (UIImage *)imageWithPath:(NSString *)path { if (!path) return nil; UIImage *image = [[self imageCache] objectForKey:path]; if (image) return image; if (path.pathScale == 1) { // 查找 @2x @3x 的图片 NSArray *scales = [NSBundle preferredScales]; for (NSNumber *scale in scales) { image = [UIImage imageWithContentsOfFile:[path stringByAppendingPathScale:scale.floatValue]]; if (image) break; } } else { image = [UIImage imageWithContentsOfFile:path]; } if (image) { image = [image imageByDecoded]; [[self imageCache] setObject:image forKey:path]; } return image; } + (YYWebImageManager *)avatarImageManager { static YYWebImageManager *manager; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSString *path = [[UIApplication sharedApplication].cachesPath stringByAppendingPathComponent:@"weibo.avatar"]; YYImageCache *cache = [[YYImageCache alloc] initWithPath:path]; manager = [[YYWebImageManager alloc] initWithCache:cache queue:[YYWebImageManager sharedManager].queue]; manager.sharedTransformBlock = ^(UIImage *image, NSURL *url) { if (!image) return image; return [image imageByRoundCornerRadius:100]; // a large value }; }); return manager; } + (NSString *)stringWithTimelineDate:(NSDate *)date { if (!date) return @""; static NSDateFormatter *formatterYesterday; static NSDateFormatter *formatterSameYear; static NSDateFormatter *formatterFullDate; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ formatterYesterday = [[NSDateFormatter alloc] init]; [formatterYesterday setDateFormat:ASLocalizedString(@"昨天")]; [formatterYesterday setLocale:[NSLocale currentLocale]]; formatterSameYear = [[NSDateFormatter alloc] init]; [formatterSameYear setDateFormat:@"M-d"]; [formatterSameYear setLocale:[NSLocale currentLocale]]; formatterFullDate = [[NSDateFormatter alloc] init]; [formatterFullDate setDateFormat:@"yyyy-MM-dd"]; [formatterFullDate setLocale:[NSLocale currentLocale]]; }); NSDate *now = [NSDate new]; NSTimeInterval delta = now.timeIntervalSince1970 - date.timeIntervalSince1970; if (delta < -60 * 10) { // 本地时间有问题 return [formatterFullDate stringFromDate:date]; } else if (delta < 60 * 10) { // 10分钟内 return ASLocalizedString(@"刚刚"); } else if (delta < 60 * 60) { // 1小时内 return [NSString stringWithFormat:ASLocalizedString(@"%d分钟前"), (int)(delta / 60.0)]; } else if (date.isToday) { return [NSString stringWithFormat:ASLocalizedString(@"%d小时前"), (int)(delta / 60.0 / 60.0)]; } else if (date.isYesterday) { return ASLocalizedString(@"昨天"); } else if (date.month == now.month) { //几天前 return [NSString stringWithFormat:ASLocalizedString(@"%d天前"), (int)(delta / 60 / 60 / 24)]; } else if (date.year == now.year) { return [formatterSameYear stringFromDate:date]; } else { return [formatterFullDate stringFromDate:date]; } } + (NSURL *)defaultURLForImageURL:(id)imageURL { /* 微博 API 提供的图片 URL 有时并不能直接用,需要做一些字符串替换: http://u1.sinaimg.cn/upload/2014/11/04/common_icon_membership_level6.png //input http://u1.sinaimg.cn/upload/2014/11/04/common_icon_membership_level6_default.png //output http://img.t.sinajs.cn/t6/skin/public/feed_cover/star_003_y.png?version=2015080302 //input http://img.t.sinajs.cn/t6/skin/public/feed_cover/star_003_os7.png?version=2015080302 //output */ if (!imageURL) return nil; NSString *link = nil; if ([imageURL isKindOfClass:[NSURL class]]) { link = ((NSURL *)imageURL).absoluteString; } else if ([imageURL isKindOfClass:[NSString class]]) { link = imageURL; } if (link.length == 0) return nil; if ([link hasSuffix:@".png"]) { // add "_default" if (![link hasSuffix:@"_default.png"]) { NSString *sub = [link substringToIndex:link.length - 4]; link = [sub stringByAppendingFormat:@"_default.png"]; } } else { // replace "_y.png" with "_os7.png" NSRange range = [link rangeOfString:@"_y.png?version"]; if (range.location != NSNotFound) { NSMutableString *mutable = link.mutableCopy; [mutable replaceCharactersInRange:NSMakeRange(range.location + 1, 1) withString:@"os7"]; link = mutable; } } return [NSURL URLWithString:link]; } + (NSString *)shortedNumberDesc:(NSUInteger)number { // should be localized if (number <= 9999) return [NSString stringWithFormat:@"%d", (int)number]; if (number <= 9999999) return [NSString stringWithFormat:NSLocalizedString(@"%d万",nil), (int)(number / 10000)]; return [NSString stringWithFormat:NSLocalizedString(@"%d千万",nil), (int)(number / 10000000)]; } + (NSRegularExpression *)regexAt { static NSRegularExpression *regex; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ // 微博的 At 只允许 英文数字下划线连字符,和 unicode 4E00~9FA5 范围内的中文字符,这里保持和微博一致。。 // 目前中文字符范围比这个大 regex = [NSRegularExpression regularExpressionWithPattern:@"@[-_a-zA-Z0-9\u4E00-\u9FA5]+" options:kNilOptions error:NULL]; }); return regex; } + (NSRegularExpression *)regexTopic { static NSRegularExpression *regex; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ regex = [NSRegularExpression regularExpressionWithPattern:@"#[^@#]+?#" options:kNilOptions error:NULL]; }); return regex; } + (NSRegularExpression *)regexEmoticon { static NSRegularExpression *regex; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ regex = [NSRegularExpression regularExpressionWithPattern:@"\\[[^ \\[\\]]+?\\]" options:kNilOptions error:NULL]; }); return regex; } /* weibo.app 里面的正则,有兴趣的可以参考下: HTTP链接 (例如 http://www.weibo.com ): ([hH]ttp[s]{0,1})://[a-zA-Z0-9\.\-]+\.([a-zA-Z]{2,4})(:\d+)?(/[a-zA-Z0-9\-~!@#$%^&*+?:_/=<>.',;]*)? ([hH]ttp[s]{0,1})://[a-zA-Z0-9\.\-]+\.([a-zA-Z]{2,4})(:\d+)?(/[a-zA-Z0-9\-~!@#$%^&*+?:_/=<>]*)? (?i)https?://[a-zA-Z0-9]+(\.[a-zA-Z0-9]+)+([-A-Z0-9a-z_\$\.\+!\*\(\)/,:;@&=\?~#%]*)* ^http?://[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)+(\/[\w-. \/\?%@&+=\u4e00-\u9fa5]*)?$ 链接 (例如 www.baidu.com/s?wd=test ): ^[a-zA-Z0-9]+(\.[a-zA-Z0-9]+)+([-A-Z0-9a-z_\$\.\+!\*\(\)/,:;@&=\?~#%]*)* 邮箱 (例如 sjobs@apple.com ): \b([a-zA-Z0-9%_.+\-]{1,32})@([a-zA-Z0-9.\-]+?\.[a-zA-Z]{2,6})\b \b([a-zA-Z0-9%_.+\-]+)@([a-zA-Z0-9.\-]+?\.[a-zA-Z]{2,6})\b ([a-zA-Z0-9%_.+\-]+)@([a-zA-Z0-9.\-]+?\.[a-zA-Z]{2,6}) 电话号码 (例如 18612345678): ^[1-9][0-9]{4,11}$ At (例如 @王思聪 ): @([\x{4e00}-\x{9fa5}A-Za-z0-9_\-]+) 话题 (例如 #奇葩说# ): #([^@]+?)# 表情 (例如 [呵呵] ): \[([^ \[]*?)] 匹配单个字符 (中英文数字下划线连字符) [\x{4e00}-\x{9fa5}A-Za-z0-9_\-] 匹配回复 (例如 回复@王思聪: ): \x{56de}\x{590d}@([\x{4e00}-\x{9fa5}A-Za-z0-9_\-]+)(\x{0020}\x{7684}\x{8d5e})?: */ @end