Procházet zdrojové kódy

Add FLEXActionShortcut , refactor FLEXViewShortcuts

Tanner Bennett před 6 roky
rodič
revize
9790c7b0bc

+ 26 - 5
Classes/ObjectExplorers/Sections/Shortcuts/FLEXShortcut.h

@@ -8,6 +8,8 @@
 
 #import <UIKit/UIKit.h>
 
+NS_ASSUME_NONNULL_BEGIN
+
 /// Represents a row in a shortcut section.
 ///
 /// The purpsoe of this protocol is to allow delegating a small
@@ -18,21 +20,23 @@
 /// them to the existing list of shortcuts for a class.
 @protocol FLEXShortcut <NSObject>
 
-- (NSString *)titleWith:(id)object;
-- (NSString *)subtitleWith:(id)object;
+- (nonnull  NSString *)titleWith:(id)object;
+- (nullable NSString *)subtitleWith:(id)object;
 //- (void (^)(UIViewController *))didSelectAction:(id)object;
 /// Called when the row is selected
-- (UIViewController *)viewerWith:(id)object;
+- (nullable UIViewController *)viewerWith:(id)object;
 /// Basically, whether or not to show a detail disclosure indicator
 - (UITableViewCellAccessoryType)accessoryTypeWith:(id)object;
 
 @optional
-/// Called when the (i) button is pressed
+/// Called when the (i) button is pressed if the accessory type includes it
 - (UIViewController *)editorWith:(id)object;
 
 @end
 
-/// Provides default behavior for FLEX metadata objects.
+
+/// Provides default behavior for FLEX metadata objects. Also works in a limited way with strings.
+/// Used internally. If you wish to use this object, only pass in \c FLEX* metadata objects.
 @interface FLEXShortcut : NSObject <FLEXShortcut>
 
 /// @param item An \c NSString or \c FLEX* metadata object.
@@ -41,3 +45,20 @@
 + (id<FLEXShortcut>)shortcutFor:(id)item;
 
 @end
+
+
+/// Provides a quick and dirty implementation of the \c FLEXShortcut protocol,
+/// allowing you to specify a static title and dynamic atttributes for everything else.
+/// The object passed into each block is the object passed to each \c FLEXShortcut method.
+///
+/// Does not support the \c -editorWith: method.
+@interface FLEXActionShortcut : NSObject <FLEXShortcut>
+
++ (instancetype)title:(NSString *)title
+             subtitle:(nullable NSString *(^)(id object))subtitleFuture
+               viewer:(nullable UIViewController *(^)(id object))viewerFuture
+        accessoryType:(nullable UITableViewCellAccessoryType(^)(id object))accessoryTypeFuture;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 58 - 0
Classes/ObjectExplorers/Sections/Shortcuts/FLEXShortcut.m

@@ -17,6 +17,9 @@
 #import "FLEXMethodCallingViewController.h"
 #import "FLEXMetadataSection.h"
 
+
+#pragma mark - FLEXShortcut
+
 @interface FLEXShortcut () {
     id _item;
 }
@@ -116,3 +119,58 @@
 - (id<FLEXRuntimeMetadata>)metadata { return _item; }
 
 @end
+
+
+#pragma mark - FLEXActionShortcut
+
+@interface FLEXActionShortcut ()
+@property (nonatomic, readonly) NSString *title;
+@property (nonatomic, readonly) NSString *(^subtitleFuture)(id);
+@property (nonatomic, readonly) UIViewController *(^viewerFuture)(id);
+@property (nonatomic, readonly) UITableViewCellAccessoryType (^accessoryTypeFuture)(id);
+@end
+
+@implementation FLEXActionShortcut
+
++ (instancetype)title:(NSString *)title
+             subtitle:(NSString *(^)(id))subtitle
+               viewer:(UIViewController *(^)(id))viewer
+        accessoryType:(UITableViewCellAccessoryType (^)(id))type {
+    return [[self alloc] initWithTitle:title subtitle:subtitle viewer:viewer accessoryType:type];
+}
+
+- (id)initWithTitle:(NSString *)title
+           subtitle:(id)subtitleFuture
+             viewer:(id)viewerFuture
+      accessoryType:(id)accessoryTypeFuture {
+    NSParameterAssert(title.length);
+
+    self = [super init];
+    if (self) {
+        id nilBlock = ^id (id obj) { return nil; };
+        _title = title;
+        _subtitleFuture = subtitleFuture ?: nilBlock;
+        _viewerFuture = viewerFuture ?: nilBlock;
+        _accessoryTypeFuture = accessoryTypeFuture ?: nilBlock;
+    }
+
+    return self;
+}
+
+- (NSString *)titleWith:(id)object {
+    return self.title;
+}
+
+- (NSString *)subtitleWith:(id)object {
+    return self.subtitleFuture(object);
+}
+
+- (UIViewController *)viewerWith:(id)object {
+    return self.viewerFuture(object);
+}
+
+- (UITableViewCellAccessoryType)accessoryTypeWith:(id)object {
+    return self.accessoryTypeFuture(object);
+}
+
+@end

+ 1 - 0
Classes/ObjectExplorers/Sections/Shortcuts/FLEXViewShortcuts.h

@@ -8,6 +8,7 @@
 
 #import "FLEXShortcutsSection.h"
 
+/// Adds "Nearest View Controller" and "Preview Image" shortcuts to all views
 @interface FLEXViewShortcuts : FLEXShortcutsSection
 
 @end

+ 38 - 41
Classes/ObjectExplorers/Sections/Shortcuts/FLEXViewShortcuts.m

@@ -7,12 +7,13 @@
 //
 
 #import "FLEXViewShortcuts.h"
+#import "FLEXShortcut.h"
+#import "FLEXRuntimeUtility.h"
 #import "FLEXObjectExplorerFactory.h"
 #import "FLEXImagePreviewViewController.h"
 
 @interface FLEXViewShortcuts ()
 @property (nonatomic, readonly) UIView *view;
-@property (nonatomic, readonly) BOOL showsViewControllerRow;
 @end
 
 @implementation FLEXViewShortcuts
@@ -41,16 +42,15 @@
     return nil;
 }
 
-- (UIViewController *)viewControllerForView {
-    return [[self class] viewControllerForView:self.view] ?:
-        [[self class] viewControllerForAncestralView:self.view];
++ (UIViewController *)nearestViewControllerForView:(UIView *)view {
+    return [self viewControllerForView:view] ?: [self viewControllerForAncestralView:view];
 }
 
-- (UIViewController *)imagePreviewViewController {
-    if (!CGRectIsEmpty(self.view.bounds)) {
-        CGSize viewSize = self.view.bounds.size;
++ (UIViewController *)imagePreviewViewControllerForView:(UIView *)view {
+    if (!CGRectIsEmpty(view.bounds)) {
+        CGSize viewSize = view.bounds.size;
         UIGraphicsBeginImageContextWithOptions(viewSize, NO, 0.0);
-        [self.view drawViewHierarchyInRect:CGRectMake(0, 0, viewSize.width, viewSize.height) afterScreenUpdates:YES];
+        [view drawViewHierarchyInRect:CGRectMake(0, 0, viewSize.width, viewSize.height) afterScreenUpdates:YES];
         UIImage *previewImage = UIGraphicsGetImageFromCurrentImageContext();
         UIGraphicsEndImageContext();
         return [FLEXImagePreviewViewController forImage:previewImage];
@@ -62,43 +62,40 @@
 #pragma mark - Overrides
 
 + (instancetype)forObject:(UIView *)view {
-    // Views without a superview don't need the "View Controller for Ancestor" row
-    BOOL hasViewController = [self viewControllerForView:view] != nil;
-    BOOL hasAncestralVC = [self viewControllerForAncestralView:view] != nil;
-    NSString *vcRowTitle = hasViewController ? @"View Controller" :
-        hasAncestralVC ? @"View Controller for Ancestor" : nil;
-
-    // These additional rows will appear at the beginning of the shortcuts section.
-    // The methods below are written in such a way that they will not interfere
-    // with properties/etc being registered alongside these
-    FLEXViewShortcuts *shortcuts = [self forObject:view additionalRows:(
-        vcRowTitle ? @[vcRowTitle, @"Preview"] : @[@"Preview"]
-    )];
-    shortcuts->_showsViewControllerRow = hasViewController || hasAncestralVC;
-    return shortcuts;
-}
-
-- (UIViewController *)viewControllerToPushForRow:(NSInteger)row {
-    switch (row) {
-        case 0:
-            if (self.showsViewControllerRow) {
-                return [FLEXObjectExplorerFactory
-                    explorerViewControllerForObject:[self viewControllerForView]
-                ];
-            } else {
-                return [self imagePreviewViewController];
+    // In the past, FLEX would not hold a strong reference to something like this.
+    // After using FLEX for so long, I am certain it is more useful to eagerly
+    // reference something as useful as a view controller so that the reference
+    // is not lost and swept out from under you before you can access it.
+    //
+    // The alternative here is to use a future in place of `controller` which would
+    // dynamically grab a reference to the view controller. 99% of the time, however,
+    // it is not all that useful. If you need it to refresh, you can simply go back
+    // and go forward again and it will show if the view controller is nil or changed.
+    UIViewController *controller = [FLEXViewShortcuts nearestViewControllerForView:view];
+
+    return [self forObject:view additionalRows:@[
+        [FLEXActionShortcut title:@"Nearest View Controller"
+            subtitle:^NSString *(id view) {
+                return [FLEXRuntimeUtility safeDescriptionForObject:controller];
             }
-        case 1:
-            if (self.showsViewControllerRow) {
-                return [self imagePreviewViewController];
+            viewer:^UIViewController *(id view) {
+                return [FLEXObjectExplorerFactory explorerViewControllerForObject:controller];
             }
-
-        default:
-            return [super viewControllerToPushForRow:row];
-    }
+            accessoryType:^UITableViewCellAccessoryType(id view) {
+                return controller ? UITableViewCellAccessoryDisclosureIndicator : 0;
+            }
+        ],
+        [FLEXActionShortcut title:@"Preview Image" subtitle:nil
+            viewer:^UIViewController *(id view) {
+                return [FLEXViewShortcuts imagePreviewViewControllerForView:view];
+            }
+            accessoryType:^UITableViewCellAccessoryType(id view) {
+                return UITableViewCellAccessoryDisclosureIndicator;
+            }
+        ]
+    ]];
 }
 
-
 #pragma mark - Runtime Adjustment
 
 #define PropertyKey(suffix) kFLEXPropertyAttributeKey##suffix : @""