FLEXViewExplorerViewController.m 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  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:[super customSectionRowCookies]];
  43. return rowCookies;
  44. }
  45. - (NSArray<NSString *> *)shortcutPropertyNames
  46. {
  47. NSArray *propertyNames = @[@"frame", @"bounds", @"center", @"transform",
  48. @"backgroundColor", @"alpha", @"opaque", @"hidden",
  49. @"clipsToBounds", @"userInteractionEnabled", @"layer"];
  50. if ([self.viewToExplore isKindOfClass:[UILabel class]]) {
  51. propertyNames = [@[@"text", @"font", @"textColor"] arrayByAddingObjectsFromArray:propertyNames];
  52. }
  53. return propertyNames;
  54. }
  55. - (NSString *)customSectionTitleForRowCookie:(id)rowCookie
  56. {
  57. NSString *title = nil;
  58. if ([rowCookie isKindOfClass:[NSNumber class]]) {
  59. FLEXViewExplorerRow row = [rowCookie unsignedIntegerValue];
  60. switch (row) {
  61. case FLEXViewExplorerRowViewController:
  62. title = @"View Controller";
  63. break;
  64. case FLEXViewExplorerRowPreview:
  65. title = @"Preview Image";
  66. break;
  67. case FLEXViewExplorerRowViewControllerForAncestor:
  68. title = @"View Controller For Ancestor";
  69. break;
  70. }
  71. } else if ([rowCookie isKindOfClass:[NSString class]]) {
  72. title = [super customSectionTitleForRowCookie:rowCookie];
  73. }
  74. return title;
  75. }
  76. - (NSString *)customSectionSubtitleForRowCookie:(id)rowCookie
  77. {
  78. NSString *subtitle = nil;
  79. if ([rowCookie isKindOfClass:[NSNumber class]]) {
  80. FLEXViewExplorerRow row = [rowCookie unsignedIntegerValue];
  81. switch (row) {
  82. case FLEXViewExplorerRowViewController:
  83. subtitle = [FLEXRuntimeUtility descriptionForIvarOrPropertyValue:[FLEXUtility viewControllerForView:self.viewToExplore]];
  84. break;
  85. case FLEXViewExplorerRowPreview:
  86. break;
  87. case FLEXViewExplorerRowViewControllerForAncestor:
  88. subtitle = [FLEXRuntimeUtility descriptionForIvarOrPropertyValue:[FLEXUtility viewControllerForAncestralView:self.viewToExplore]];
  89. break;
  90. }
  91. } else if ([rowCookie isKindOfClass:[NSString class]]) {
  92. return [super customSectionSubtitleForRowCookie:rowCookie];
  93. }
  94. return subtitle;
  95. }
  96. - (UIViewController *)customSectionDrillInViewControllerForRowCookie:(id)rowCookie
  97. {
  98. UIViewController *drillInViewController = nil;
  99. if ([rowCookie isKindOfClass:[NSNumber class]]) {
  100. FLEXViewExplorerRow row = [rowCookie unsignedIntegerValue];
  101. switch (row) {
  102. case FLEXViewExplorerRowViewController:
  103. drillInViewController = [FLEXObjectExplorerFactory explorerViewControllerForObject:[FLEXUtility viewControllerForView:self.viewToExplore]];
  104. break;
  105. case FLEXViewExplorerRowPreview:
  106. drillInViewController = [[self class] imagePreviewViewControllerForView:self.viewToExplore];
  107. break;
  108. case FLEXViewExplorerRowViewControllerForAncestor:
  109. drillInViewController = [FLEXObjectExplorerFactory explorerViewControllerForObject:[FLEXUtility viewControllerForAncestralView:self.viewToExplore]];
  110. break;
  111. }
  112. } else if ([rowCookie isKindOfClass:[NSString class]]) {
  113. return [super customSectionDrillInViewControllerForRowCookie:rowCookie];
  114. }
  115. return drillInViewController;
  116. }
  117. + (UIViewController *)imagePreviewViewControllerForView:(UIView *)view
  118. {
  119. UIViewController *imagePreviewViewController = nil;
  120. if (!CGRectIsEmpty(view.bounds)) {
  121. CGSize viewSize = view.bounds.size;
  122. UIGraphicsBeginImageContextWithOptions(viewSize, NO, 0.0);
  123. [view drawViewHierarchyInRect:CGRectMake(0, 0, viewSize.width, viewSize.height) afterScreenUpdates:YES];
  124. UIImage *previewImage = UIGraphicsGetImageFromCurrentImageContext();
  125. UIGraphicsEndImageContext();
  126. imagePreviewViewController = [[FLEXImagePreviewViewController alloc] initWithImage:previewImage];
  127. }
  128. return imagePreviewViewController;
  129. }
  130. #pragma mark - Runtime Adjustment
  131. #define PropertyKey(suffix) kFLEXUtilityAttribute##suffix : @""
  132. #define PropertyKeyGetter(getter) kFLEXUtilityAttributeCustomGetter : NSStringFromSelector(@selector(getter))
  133. #define PropertyKeySetter(setter) kFLEXUtilityAttributeCustomSetter : NSStringFromSelector(@selector(setter))
  134. /// Takes: min iOS version, property name, target class, property type, and a list of attributes
  135. #define FLEXRuntimeUtilityTryAddProperty(iOS_atLeast, name, cls, type, ...) ({ \
  136. if (@available(iOS iOS_atLeast, *)) { \
  137. NSMutableDictionary *attrs = [NSMutableDictionary dictionaryWithDictionary:@{ \
  138. kFLEXUtilityAttributeTypeEncoding : @(type), \
  139. __VA_ARGS__ \
  140. }]; \
  141. [FLEXRuntimeUtility \
  142. tryAddPropertyWithName:#name \
  143. attributes:attrs \
  144. toClass:[cls class] \
  145. ]; \
  146. } \
  147. })
  148. /// Takes: min iOS version, property name, target class, property type, and a list of attributes
  149. #define FLEXRuntimeUtilityTryAddNonatomicProperty(iOS_atLeast, name, cls, type, ...) \
  150. FLEXRuntimeUtilityTryAddProperty(iOS_atLeast, name, cls, @encode(type), PropertyKey(NonAtomic), __VA_ARGS__);
  151. /// Takes: min iOS version, property name, target class, property type (class name), and a list of attributes
  152. #define FLEXRuntimeUtilityTryAddObjectProperty(iOS_atLeast, name, cls, type, ...) \
  153. FLEXRuntimeUtilityTryAddProperty(iOS_atLeast, name, cls, FLEXEncodeClass(type), PropertyKey(NonAtomic), __VA_ARGS__);
  154. + (void)initialize
  155. {
  156. // A quirk of UIView and some other classes: a lot of the `@property`s are
  157. // not actually properties from the perspective of the runtime.
  158. //
  159. // We add these properties to the class at runtime if they haven't been added yet.
  160. // This way, we can use our property editor to access and change them.
  161. // The property attributes match the declared attributes in their headers.
  162. // UIView, public
  163. FLEXRuntimeUtilityTryAddNonatomicProperty(2, frame, UIView, CGRect);
  164. FLEXRuntimeUtilityTryAddNonatomicProperty(2, alpha, UIView, CGFloat);
  165. FLEXRuntimeUtilityTryAddNonatomicProperty(2, clipsToBounds, UIView, BOOL);
  166. FLEXRuntimeUtilityTryAddNonatomicProperty(2, opaque, UIView, BOOL, PropertyKeyGetter(isOpaque));
  167. FLEXRuntimeUtilityTryAddNonatomicProperty(2, hidden, UIView, BOOL, PropertyKeyGetter(isHidden));
  168. FLEXRuntimeUtilityTryAddObjectProperty(2, backgroundColor, UIView, UIColor, PropertyKey(Copy));
  169. FLEXRuntimeUtilityTryAddObjectProperty(6, constraints, UIView, NSArray, PropertyKey(ReadOnly));
  170. FLEXRuntimeUtilityTryAddObjectProperty(2, subviews, UIView, NSArray, PropertyKey(ReadOnly));
  171. FLEXRuntimeUtilityTryAddObjectProperty(2, superview, UIView, UIView, PropertyKey(ReadOnly));
  172. }
  173. @end