FLEXUtility.m 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. //
  2. // FLEXUtility.m
  3. // Flipboard
  4. //
  5. // Created by Ryan Olson on 4/18/14.
  6. // Copyright (c) 2014 Flipboard. All rights reserved.
  7. //
  8. #import "FLEXUtility.h"
  9. #import "FLEXResources.h"
  10. #import <ImageIO/ImageIO.h>
  11. @implementation FLEXUtility
  12. + (UIColor *)consistentRandomColorForObject:(id)object
  13. {
  14. CGFloat hue = (((NSUInteger)object >> 4) % 256) / 255.0;
  15. return [UIColor colorWithHue:hue saturation:1.0 brightness:1.0 alpha:1.0];
  16. }
  17. + (NSString *)descriptionForView:(UIView *)view includingFrame:(BOOL)includeFrame
  18. {
  19. NSString *description = [[view class] description];
  20. NSString *viewControllerDescription = [[[self viewControllerForView:view] class] description];
  21. if ([viewControllerDescription length] > 0) {
  22. description = [description stringByAppendingFormat:@" (%@)", viewControllerDescription];
  23. }
  24. if (includeFrame) {
  25. description = [description stringByAppendingFormat:@" %@", [self stringForCGRect:view.frame]];
  26. }
  27. if ([view.accessibilityLabel length] > 0) {
  28. description = [description stringByAppendingFormat:@" · %@", view.accessibilityLabel];
  29. }
  30. return description;
  31. }
  32. + (NSString *)stringForCGRect:(CGRect)rect
  33. {
  34. return [NSString stringWithFormat:@"{(%g, %g), (%g, %g)}", rect.origin.x, rect.origin.y, rect.size.width, rect.size.height];
  35. }
  36. + (UIViewController *)viewControllerForView:(UIView *)view
  37. {
  38. UIViewController *viewController = nil;
  39. SEL viewDelSel = NSSelectorFromString([NSString stringWithFormat:@"%@ewDelegate", @"_vi"]);
  40. if ([view respondsToSelector:viewDelSel]) {
  41. #pragma clang diagnostic push
  42. #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
  43. viewController = [view performSelector:viewDelSel];
  44. #pragma clang diagnostic pop
  45. }
  46. return viewController;
  47. }
  48. + (NSString *)detailDescriptionForView:(UIView *)view
  49. {
  50. return [NSString stringWithFormat:@"frame %@", [self stringForCGRect:view.frame]];
  51. }
  52. + (UIImage *)circularImageWithColor:(UIColor *)color radius:(CGFloat)radius
  53. {
  54. CGFloat diameter = radius * 2.0;
  55. UIGraphicsBeginImageContextWithOptions(CGSizeMake(diameter, diameter), NO, 0.0);
  56. CGContextRef imageContext = UIGraphicsGetCurrentContext();
  57. CGContextSetFillColorWithColor(imageContext, [color CGColor]);
  58. CGContextFillEllipseInRect(imageContext, CGRectMake(0, 0, diameter, diameter));
  59. UIImage *circularImage = UIGraphicsGetImageFromCurrentImageContext();
  60. UIGraphicsEndImageContext();
  61. return circularImage;
  62. }
  63. + (UIColor *)scrollViewGrayColor
  64. {
  65. return [UIColor colorWithRed:239.0/255.0 green:239.0/255.0 blue:244.0/255.0 alpha:1.0];
  66. }
  67. + (UIColor *)hierarchyIndentPatternColor
  68. {
  69. static UIColor *patternColor = nil;
  70. static dispatch_once_t onceToken;
  71. dispatch_once(&onceToken, ^{
  72. UIImage *indentationPatternImage = [FLEXResources hierarchyIndentPattern];
  73. patternColor = [UIColor colorWithPatternImage:indentationPatternImage];
  74. });
  75. return patternColor;
  76. }
  77. + (NSString *)applicationImageName
  78. {
  79. return [[NSBundle mainBundle] executablePath];
  80. }
  81. + (NSString *)applicationName
  82. {
  83. return [[[FLEXUtility applicationImageName] componentsSeparatedByString:@"/"] lastObject];
  84. }
  85. + (NSString *)safeDescriptionForObject:(id)object
  86. {
  87. // Don't assume that we have an NSObject subclass.
  88. // Check to make sure the object responds to the description methods.
  89. NSString *description = nil;
  90. if ([object respondsToSelector:@selector(debugDescription)]) {
  91. description = [object debugDescription];
  92. } else if ([object respondsToSelector:@selector(description)]) {
  93. description = [object description];
  94. }
  95. return description;
  96. }
  97. + (UIFont *)defaultFontOfSize:(CGFloat)size
  98. {
  99. return [UIFont fontWithName:@"HelveticaNeue" size:size];
  100. }
  101. + (UIFont *)defaultTableViewCellLabelFont
  102. {
  103. return [self defaultFontOfSize:12.0];
  104. }
  105. + (NSString *)stringByEscapingHTMLEntitiesInString:(NSString *)originalString
  106. {
  107. static NSDictionary *escapingDictionary = nil;
  108. static NSRegularExpression *regex = nil;
  109. static dispatch_once_t onceToken;
  110. dispatch_once(&onceToken, ^{
  111. escapingDictionary = @{ @" " : @"&nbsp;",
  112. @">" : @"&gt;",
  113. @"<" : @"&lt;",
  114. @"&" : @"&amp;",
  115. @"'" : @"&apos;",
  116. @"\"" : @"&quot;",
  117. @"«" : @"&laquo;",
  118. @"»" : @"&raquo;"
  119. };
  120. regex = [NSRegularExpression regularExpressionWithPattern:@"(&|>|<|'|\"|«|»)" options:0 error:NULL];
  121. });
  122. NSMutableString *mutableString = [originalString mutableCopy];
  123. NSArray *matches = [regex matchesInString:mutableString options:0 range:NSMakeRange(0, [mutableString length])];
  124. for (NSTextCheckingResult *result in [matches reverseObjectEnumerator]) {
  125. NSString *foundString = [mutableString substringWithRange:result.range];
  126. NSString *replacementString = escapingDictionary[foundString];
  127. if (replacementString) {
  128. [mutableString replaceCharactersInRange:result.range withString:replacementString];
  129. }
  130. }
  131. return [mutableString copy];
  132. }
  133. + (UIInterfaceOrientationMask)infoPlistSupportedInterfaceOrientationsMask
  134. {
  135. NSArray *supportedOrientations = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"UISupportedInterfaceOrientations"];
  136. UIInterfaceOrientationMask supportedOrientationsMask = 0;
  137. if ([supportedOrientations containsObject:@"UIInterfaceOrientationPortrait"]) {
  138. supportedOrientationsMask |= UIInterfaceOrientationMaskPortrait;
  139. }
  140. if ([supportedOrientations containsObject:@"UIInterfaceOrientationMaskLandscapeRight"]) {
  141. supportedOrientationsMask |= UIInterfaceOrientationMaskLandscapeRight;
  142. }
  143. if ([supportedOrientations containsObject:@"UIInterfaceOrientationMaskPortraitUpsideDown"]) {
  144. supportedOrientationsMask |= UIInterfaceOrientationMaskPortraitUpsideDown;
  145. }
  146. if ([supportedOrientations containsObject:@"UIInterfaceOrientationLandscapeLeft"]) {
  147. supportedOrientationsMask |= UIInterfaceOrientationMaskLandscapeLeft;
  148. }
  149. return supportedOrientationsMask;
  150. }
  151. + (NSString *)searchBarPlaceholderText
  152. {
  153. return @"Filter";
  154. }
  155. + (BOOL)isImagePathExtension:(NSString *)extension
  156. {
  157. // https://developer.apple.com/library/ios/documentation/uikit/reference/UIImage_Class/Reference/Reference.html#//apple_ref/doc/uid/TP40006890-CH3-SW3
  158. return [@[@"jpg", @"jpeg", @"png", @"gif", @"tiff", @"tif"] containsObject:extension];
  159. }
  160. + (UIImage *)thumbnailedImageWithMaxPixelDimension:(NSInteger)dimension fromImageData:(NSData *)data
  161. {
  162. UIImage *thumbnail = nil;
  163. CGImageSourceRef imageSource = CGImageSourceCreateWithData((__bridge CFDataRef)data, 0);
  164. if (imageSource) {
  165. NSDictionary *options = @{ (__bridge id)kCGImageSourceCreateThumbnailWithTransform : @YES,
  166. (__bridge id)kCGImageSourceCreateThumbnailFromImageAlways : @YES,
  167. (__bridge id)kCGImageSourceThumbnailMaxPixelSize : @(dimension) };
  168. CGImageRef scaledImageRef = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, (__bridge CFDictionaryRef)options);
  169. if (scaledImageRef) {
  170. thumbnail = [UIImage imageWithCGImage:scaledImageRef];
  171. CFRelease(scaledImageRef);
  172. }
  173. CFRelease(imageSource);
  174. }
  175. return thumbnail;
  176. }
  177. + (NSString *)stringFromRequestDuration:(NSTimeInterval)duration
  178. {
  179. NSString *string = @"0s";
  180. if (duration > 0.0) {
  181. if (duration < 1.0) {
  182. string = [NSString stringWithFormat:@"%dms", (int)(duration * 1000)];
  183. } else if (duration < 10.0) {
  184. string = [NSString stringWithFormat:@"%.2fs", duration];
  185. } else {
  186. string = [NSString stringWithFormat:@"%.1fs", duration];
  187. }
  188. }
  189. return string;
  190. }
  191. + (NSString *)statusCodeStringFromURLResponse:(NSURLResponse *)response
  192. {
  193. NSString *httpResponseString = nil;
  194. if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
  195. NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
  196. NSString *statusCodeDescription = nil;
  197. if (httpResponse.statusCode == 200) {
  198. // Prefer OK to the default "no error"
  199. statusCodeDescription = @"OK";
  200. } else {
  201. statusCodeDescription = [NSHTTPURLResponse localizedStringForStatusCode:httpResponse.statusCode];
  202. }
  203. httpResponseString = [NSString stringWithFormat:@"%ld %@", (long)httpResponse.statusCode, statusCodeDescription];
  204. }
  205. return httpResponseString;
  206. }
  207. + (NSDictionary *)queryDictionaryFromURL:(NSURL *)url
  208. {
  209. NSMutableDictionary *queryDictionary = [NSMutableDictionary dictionary];
  210. // [a=1, b=2, c=3]
  211. NSArray *queryComponents = [url.query componentsSeparatedByString:@"&"];
  212. for (NSString *keyValueString in queryComponents) {
  213. // [a, 1]
  214. NSArray *components = [keyValueString componentsSeparatedByString:@"="];
  215. if ([components count] == 2) {
  216. NSString *key = [[components firstObject] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
  217. NSString *value = [[components lastObject] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
  218. [queryDictionary setObject:value forKey:key];
  219. }
  220. }
  221. return queryDictionary;
  222. }
  223. + (NSString *)prettyJSONStringFromData:(NSData *)data
  224. {
  225. id jsonObject = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
  226. return [[NSString alloc] initWithData:[NSJSONSerialization dataWithJSONObject:jsonObject options:NSJSONWritingPrettyPrinted error:NULL] encoding:NSUTF8StringEncoding];
  227. }
  228. @end