FLEXViewExplorerViewController.m 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. //
  2. // FLEXViewExplorerViewController.m
  3. // Flipboard
  4. //
  5. // Created by Ryan Olson on 6/11/14.
  6. // Copyright (c) 2014 Flipboard. All rights reserved.
  7. //
  8. #import "FLEXViewExplorerViewController.h"
  9. #import "FLEXRuntimeUtility.h"
  10. #import "FLEXUtility.h"
  11. #import "FLEXObjectExplorerFactory.h"
  12. #import "FLEXImagePreviewViewController.h"
  13. #import "FLEXPropertyEditorViewController.h"
  14. typedef NS_ENUM(NSUInteger, FLEXViewExplorerRow) {
  15. FLEXViewExplorerRowViewController,
  16. FLEXViewExplorerRowPreview,
  17. FLEXViewExplorerRowViewControllerForAncestor
  18. };
  19. @interface FLEXViewExplorerViewController ()
  20. // Don't clash with UIViewController's view property
  21. @property (nonatomic, readonly) UIView *viewToExplore;
  22. @end
  23. @implementation FLEXViewExplorerViewController
  24. - (UIView *)viewToExplore
  25. {
  26. return [self.object isKindOfClass:[UIView class]] ? self.object : nil;
  27. }
  28. #pragma mark - Superclass Overrides
  29. - (NSString *)customSectionTitle
  30. {
  31. return @"Shortcuts";
  32. }
  33. - (NSArray *)customSectionRowCookies
  34. {
  35. NSMutableArray *rowCookies = [NSMutableArray array];
  36. if ([FLEXUtility viewControllerForView:self.viewToExplore]) {
  37. [rowCookies addObject:@(FLEXViewExplorerRowViewController)];
  38. }else{
  39. [rowCookies addObject:@(FLEXViewExplorerRowViewControllerForAncestor)];
  40. }
  41. [rowCookies addObject:@(FLEXViewExplorerRowPreview)];
  42. [rowCookies addObjectsFromArray:[self shortcutPropertyNames]];
  43. return rowCookies;
  44. }
  45. - (NSArray *)shortcutPropertyNames
  46. {
  47. NSArray *propertyNames = @[@"frame", @"bounds", @"center", @"transform", @"backgroundColor", @"alpha", @"opaque", @"hidden", @"clipsToBounds", @"userInteractionEnabled", @"layer"];
  48. if ([self.viewToExplore isKindOfClass:[UILabel class]]) {
  49. propertyNames = [@[@"text", @"font", @"textColor"] arrayByAddingObjectsFromArray:propertyNames];
  50. }
  51. return propertyNames;
  52. }
  53. - (NSString *)customSectionTitleForRowCookie:(id)rowCookie
  54. {
  55. NSString *title = nil;
  56. if ([rowCookie isKindOfClass:[NSNumber class]]) {
  57. FLEXViewExplorerRow row = [rowCookie unsignedIntegerValue];
  58. switch (row) {
  59. case FLEXViewExplorerRowViewController:
  60. title = @"View Controller";
  61. break;
  62. case FLEXViewExplorerRowPreview:
  63. title = @"Preview Image";
  64. break;
  65. case FLEXViewExplorerRowViewControllerForAncestor:
  66. title = @"View Controller For Ancestor";
  67. break;
  68. }
  69. } else if ([rowCookie isKindOfClass:[NSString class]]) {
  70. objc_property_t property = [self viewPropertyForName:rowCookie];
  71. if (property) {
  72. NSString *prettyPropertyName = [FLEXRuntimeUtility prettyNameForProperty:property];
  73. // Since we're outside of the "properties" section, prepend @property for clarity.
  74. title = [NSString stringWithFormat:@"@property %@", prettyPropertyName];
  75. }
  76. }
  77. return title;
  78. }
  79. - (NSString *)customSectionSubtitleForRowCookie:(id)rowCookie
  80. {
  81. NSString *subtitle = nil;
  82. if ([rowCookie isKindOfClass:[NSNumber class]]) {
  83. FLEXViewExplorerRow row = [rowCookie unsignedIntegerValue];
  84. switch (row) {
  85. case FLEXViewExplorerRowViewController:
  86. subtitle = [FLEXRuntimeUtility descriptionForIvarOrPropertyValue:[FLEXUtility viewControllerForView:self.viewToExplore]];
  87. break;
  88. case FLEXViewExplorerRowPreview:
  89. break;
  90. case FLEXViewExplorerRowViewControllerForAncestor:
  91. subtitle = [FLEXRuntimeUtility descriptionForIvarOrPropertyValue:[FLEXUtility viewControllerForAncestralView:self.viewToExplore]];
  92. break;
  93. }
  94. } else if ([rowCookie isKindOfClass:[NSString class]]) {
  95. objc_property_t property = [self viewPropertyForName:rowCookie];
  96. if (property) {
  97. id value = [FLEXRuntimeUtility valueForProperty:property onObject:self.viewToExplore];
  98. subtitle = [FLEXRuntimeUtility descriptionForIvarOrPropertyValue:value];
  99. }
  100. }
  101. return subtitle;
  102. }
  103. - (objc_property_t)viewPropertyForName:(NSString *)propertyName
  104. {
  105. return class_getProperty([self.viewToExplore class], [propertyName UTF8String]);
  106. }
  107. - (BOOL)customSectionCanDrillIntoRowWithCookie:(id)rowCookie
  108. {
  109. return YES;
  110. }
  111. - (UIViewController *)customSectionDrillInViewControllerForRowCookie:(id)rowCookie
  112. {
  113. UIViewController *drillInViewController = nil;
  114. if ([rowCookie isKindOfClass:[NSNumber class]]) {
  115. FLEXViewExplorerRow row = [rowCookie unsignedIntegerValue];
  116. switch (row) {
  117. case FLEXViewExplorerRowViewController:
  118. drillInViewController = [FLEXObjectExplorerFactory explorerViewControllerForObject:[FLEXUtility viewControllerForView:self.viewToExplore]];
  119. break;
  120. case FLEXViewExplorerRowPreview:
  121. drillInViewController = [[self class] imagePreviewViewControllerForView:self.viewToExplore];
  122. break;
  123. case FLEXViewExplorerRowViewControllerForAncestor:
  124. drillInViewController = [FLEXObjectExplorerFactory explorerViewControllerForObject:[FLEXUtility viewControllerForAncestralView:self.viewToExplore]];
  125. break;
  126. }
  127. } else if ([rowCookie isKindOfClass:[NSString class]]) {
  128. objc_property_t property = [self viewPropertyForName:rowCookie];
  129. if (property) {
  130. id currentValue = [FLEXRuntimeUtility valueForProperty:property onObject:self.viewToExplore];
  131. if ([FLEXPropertyEditorViewController canEditProperty:property currentValue:currentValue]) {
  132. drillInViewController = [[FLEXPropertyEditorViewController alloc] initWithTarget:self.object property:property];
  133. } else {
  134. drillInViewController = [FLEXObjectExplorerFactory explorerViewControllerForObject:currentValue];
  135. }
  136. }
  137. }
  138. return drillInViewController;
  139. }
  140. + (UIViewController *)imagePreviewViewControllerForView:(UIView *)view
  141. {
  142. UIViewController *imagePreviewViewController = nil;
  143. if (!CGRectIsEmpty(view.bounds)) {
  144. CGSize viewSize = view.bounds.size;
  145. UIGraphicsBeginImageContextWithOptions(viewSize, NO, 0.0);
  146. [view drawViewHierarchyInRect:CGRectMake(0, 0, viewSize.width, viewSize.height) afterScreenUpdates:YES];
  147. UIImage *previewImage = UIGraphicsGetImageFromCurrentImageContext();
  148. UIGraphicsEndImageContext();
  149. imagePreviewViewController = [[FLEXImagePreviewViewController alloc] initWithImage:previewImage];
  150. }
  151. return imagePreviewViewController;
  152. }
  153. #pragma mark - Runtime Adjustment
  154. + (void)initialize
  155. {
  156. // A quirk of UIView: a lot of the "@property"s are not actually properties from the perspective of the runtime.
  157. // We add these properties to the class at runtime if they haven't been added yet.
  158. // This way, we can use our property editor to access and change them.
  159. // The property attributes match the declared attributes in UIView.h
  160. NSDictionary *frameAttributes = @{kFLEXUtilityAttributeTypeEncoding : @(@encode(CGRect)), kFLEXUtilityAttributeNonAtomic : @""};
  161. [FLEXRuntimeUtility tryAddPropertyWithName:"frame" attributes:frameAttributes toClass:[UIView class]];
  162. NSDictionary *alphaAttributes = @{kFLEXUtilityAttributeTypeEncoding : @(@encode(CGFloat)), kFLEXUtilityAttributeNonAtomic : @""};
  163. [FLEXRuntimeUtility tryAddPropertyWithName:"alpha" attributes:alphaAttributes toClass:[UIView class]];
  164. NSDictionary *clipsAttributes = @{kFLEXUtilityAttributeTypeEncoding : @(@encode(BOOL)), kFLEXUtilityAttributeNonAtomic : @""};
  165. [FLEXRuntimeUtility tryAddPropertyWithName:"clipsToBounds" attributes:clipsAttributes toClass:[UIView class]];
  166. NSDictionary *opaqueAttributes = @{kFLEXUtilityAttributeTypeEncoding : @(@encode(BOOL)), kFLEXUtilityAttributeNonAtomic : @"", kFLEXUtilityAttributeCustomGetter : @"isOpaque"};
  167. [FLEXRuntimeUtility tryAddPropertyWithName:"opaque" attributes:opaqueAttributes toClass:[UIView class]];
  168. NSDictionary *hiddenAttributes = @{kFLEXUtilityAttributeTypeEncoding : @(@encode(BOOL)), kFLEXUtilityAttributeNonAtomic : @"", kFLEXUtilityAttributeCustomGetter : @"isHidden"};
  169. [FLEXRuntimeUtility tryAddPropertyWithName:"hidden" attributes:hiddenAttributes toClass:[UIView class]];
  170. NSDictionary *backgroundColorAttributes = @{kFLEXUtilityAttributeTypeEncoding : @(FLEXEncodeClass(UIColor)), kFLEXUtilityAttributeNonAtomic : @"", kFLEXUtilityAttributeCopy : @""};
  171. [FLEXRuntimeUtility tryAddPropertyWithName:"backgroundColor" attributes:backgroundColorAttributes toClass:[UIView class]];
  172. }
  173. @end