Add a Convenience Property to Get Owning View Controller (#3076)

* Add a convenience property -closestViewController

* Address comments
This commit is contained in:
Adlai Holler 2017-03-02 11:51:16 -08:00 committed by GitHub
parent 012b378a22
commit 93809bd4e7
7 changed files with 94 additions and 5 deletions

View File

@ -307,6 +307,8 @@
CC034A101E60C9BF00626263 /* ASRectTableTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC034A0F1E60C9BF00626263 /* ASRectTableTests.m */; };
CC034A131E649F1300626263 /* AsyncDisplayKit+IGListKitMethods.h in Headers */ = {isa = PBXBuildFile; fileRef = CC034A111E649F1300626263 /* AsyncDisplayKit+IGListKitMethods.h */; };
CC034A141E649F1300626263 /* AsyncDisplayKit+IGListKitMethods.m in Sources */ = {isa = PBXBuildFile; fileRef = CC034A121E649F1300626263 /* AsyncDisplayKit+IGListKitMethods.m */; };
CC034A091E60BEB400626263 /* ASDisplayNode+Convenience.h in Headers */ = {isa = PBXBuildFile; fileRef = CC034A071E60BEB400626263 /* ASDisplayNode+Convenience.h */; settings = {ATTRIBUTES = (Public, ); }; };
CC034A0A1E60BEB400626263 /* ASDisplayNode+Convenience.m in Sources */ = {isa = PBXBuildFile; fileRef = CC034A081E60BEB400626263 /* ASDisplayNode+Convenience.m */; };
CC051F1F1D7A286A006434CB /* ASCALayerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC051F1E1D7A286A006434CB /* ASCALayerTests.m */; };
CC0AEEA41D66316E005D1C78 /* ASUICollectionViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC0AEEA31D66316E005D1C78 /* ASUICollectionViewTests.m */; };
CC0F885B1E42807F00576FED /* ASCollectionViewFlowLayoutInspector.m in Sources */ = {isa = PBXBuildFile; fileRef = CC0F88591E42807F00576FED /* ASCollectionViewFlowLayoutInspector.m */; };
@ -697,6 +699,8 @@
CC034A0F1E60C9BF00626263 /* ASRectTableTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASRectTableTests.m; sourceTree = "<group>"; };
CC034A111E649F1300626263 /* AsyncDisplayKit+IGListKitMethods.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "AsyncDisplayKit+IGListKitMethods.h"; sourceTree = "<group>"; };
CC034A121E649F1300626263 /* AsyncDisplayKit+IGListKitMethods.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "AsyncDisplayKit+IGListKitMethods.m"; sourceTree = "<group>"; };
CC034A071E60BEB400626263 /* ASDisplayNode+Convenience.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASDisplayNode+Convenience.h"; sourceTree = "<group>"; };
CC034A081E60BEB400626263 /* ASDisplayNode+Convenience.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "ASDisplayNode+Convenience.m"; sourceTree = "<group>"; };
CC051F1E1D7A286A006434CB /* ASCALayerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCALayerTests.m; sourceTree = "<group>"; };
CC0AEEA31D66316E005D1C78 /* ASUICollectionViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASUICollectionViewTests.m; sourceTree = "<group>"; };
CC0F88591E42807F00576FED /* ASCollectionViewFlowLayoutInspector.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCollectionViewFlowLayoutInspector.m; sourceTree = "<group>"; };
@ -900,6 +904,8 @@
90FC784E1E4BFE1B00383C5A /* ASDisplayNode+Yoga.mm */,
683489271D70DE3400327501 /* ASDisplayNode+Deprecated.h */,
058D09DA195D050800B7D73C /* ASDisplayNode+Subclasses.h */,
CC034A071E60BEB400626263 /* ASDisplayNode+Convenience.h */,
CC034A081E60BEB400626263 /* ASDisplayNode+Convenience.m */,
058D09DB195D050800B7D73C /* ASDisplayNodeExtras.h */,
058D09DC195D050800B7D73C /* ASDisplayNodeExtras.mm */,
0587F9BB1A7309ED00AFF0BA /* ASEditableTextNode.h */,
@ -1453,6 +1459,7 @@
698DFF471E36B7E9002891F1 /* ASLayoutSpecUtilities.h in Headers */,
9C70F20D1CDBE9CB007D6C76 /* ASDefaultPlayButton.h in Headers */,
DE7EF4F81DFF77720082B84A /* ASDisplayNode+FrameworkSubclasses.h in Headers */,
CC034A091E60BEB400626263 /* ASDisplayNode+Convenience.h in Headers */,
254C6B7E1BF94DF4003EC431 /* ASTextKitTailTruncater.h in Headers */,
B35062491B010EFD0018CF92 /* _ASCoreAnimationExtras.h in Headers */,
68EE0DBE1C1B4ED300BA1B99 /* ASMainSerialQueue.h in Headers */,
@ -1916,6 +1923,7 @@
696F01EE1DD2AF450049FBD5 /* ASEventLog.mm in Sources */,
9C70F2051CDA4F06007D6C76 /* ASTraitCollection.m in Sources */,
83A7D95B1D44547700BF333E /* ASWeakMap.m in Sources */,
CC034A0A1E60BEB400626263 /* ASDisplayNode+Convenience.m in Sources */,
DE84918E1C8FFF9F003D89E9 /* ASRunLoopQueue.mm in Sources */,
68FC85E51CE29B7E00EDD713 /* ASTabBarController.m in Sources */,
34EFC7741B701D0A00AD841F /* ASAbsoluteLayoutSpec.mm in Sources */,

View File

@ -0,0 +1,27 @@
//
// ASDisplayNode+Convenience.h
// AsyncDisplayKit
//
// Created by Adlai Holler on 2/24/17.
// Copyright © 2017 Facebook. All rights reserved.
//
#import <AsyncDisplayKit/ASDisplayNode.h>
NS_ASSUME_NONNULL_BEGIN
@class UIViewController;
@interface ASDisplayNode (Convenience)
/**
* @abstract Returns the view controller nearest to this node in the view hierarchy.
*
* @warning This property may only be accessed on the main thread. This property may
* be @c nil until the node's view is actually hosted in the view hierarchy.
*/
@property (nonatomic, nullable, readonly) __kindof UIViewController *closestViewController;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,39 @@
//
// ASDisplayNode+Convenience.m
// AsyncDisplayKit
//
// Created by Adlai Holler on 2/24/17.
// Copyright © 2017 Facebook. All rights reserved.
//
#import "ASDisplayNode+Convenience.h"
#import <UIKit/UIViewController.h>
#import <AsyncDisplayKit/ASDisplayNodeExtras.h>
#import <AsyncDisplayKit/ASResponderChainEnumerator.h>
@implementation ASDisplayNode (Convenience)
- (__kindof UIViewController *)closestViewController
{
ASDisplayNodeAssertMainThread();
// Careful not to trigger node loading here.
if (!self.nodeLoaded) {
return nil;
}
// Get the closest view.
UIView *view = ASFindClosestViewOfLayer(self.layer);
// Travel up the responder chain to find a view controller.
for (UIResponder *responder in [view asdk_responderChainEnumerator]) {
UIViewController *vc = ASDynamicCast(responder, UIViewController);
if (vc != nil) {
return vc;
}
}
return nil;
}
@end

View File

@ -130,6 +130,11 @@ extern __kindof ASDisplayNode * _Nullable ASDisplayNodeFindFirstSupernodeOfClass
*/
extern UIWindow * _Nullable ASFindWindowOfLayer(CALayer *layer) AS_WARN_UNUSED_RESULT;
/**
* Given a layer, find the closest view it lives in, if any.
*/
extern UIView * _Nullable ASFindClosestViewOfLayer(CALayer *layer) AS_WARN_UNUSED_RESULT;
/**
* Given two nodes, finds their most immediate common parent. Used for geometry conversion methods.
* NOTE: It is an error to try to convert between nodes which do not share a common ancestor. This behavior is

View File

@ -250,14 +250,20 @@ static inline BOOL _ASDisplayNodeIsAncestorOfDisplayNode(ASDisplayNode *possible
}
extern UIWindow * _Nullable ASFindWindowOfLayer(CALayer *layer)
{
UIView *view = ASFindClosestViewOfLayer(layer);
if (UIWindow *window = ASDynamicCast(view, UIWindow)) {
return window;
} else {
return view.window;
}
}
extern UIView * _Nullable ASFindClosestViewOfLayer(CALayer *layer)
{
while (layer != nil) {
if (UIView *view = ASDynamicCast(layer.delegate, UIView)) {
if ([view isKindOfClass:[UIWindow class]]) {
return (UIWindow *)view;
} else {
return view.window;
}
return view;
}
layer = layer.superlayer;
}

View File

@ -9,6 +9,7 @@
//
#import <AsyncDisplayKit/ASDisplayNode.h>
#import <AsyncDisplayKit/ASDisplayNode+Convenience.h>
#import <AsyncDisplayKit/ASDisplayNodeExtras.h>
#import <AsyncDisplayKit/ASControlNode.h>

View File

@ -7,6 +7,7 @@
//
#import "ASResponderChainEnumerator.h"
#import <AsyncDisplayKit/ASAssert.h>
@implementation ASResponderChainEnumerator {
UIResponder *_currentResponder;
@ -14,6 +15,7 @@
- (instancetype)initWithResponder:(UIResponder *)responder
{
ASDisplayNodeAssertMainThread();
if (self = [super init]) {
_currentResponder = responder;
}
@ -24,6 +26,7 @@
- (id)nextObject
{
ASDisplayNodeAssertMainThread();
id result = [_currentResponder nextResponder];
_currentResponder = result;
return result;