Fix Pager Node Issues (#3028)

* Fix pager node and deprecate zeroContentInsets flag

* Do it with the visible state callback

* There we are

* Put viewController in node debug description
This commit is contained in:
Adlai Holler
2017-02-14 14:10:51 -08:00
committed by GitHub
parent aecd36a4df
commit fab98b32ef
14 changed files with 271 additions and 67 deletions

View File

@@ -327,6 +327,10 @@
CC4C2A791D88E3BF0039ACAB /* ASTraceEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = CC4C2A761D88E3BF0039ACAB /* ASTraceEvent.m */; };
CC54A81C1D70079800296A24 /* ASDispatch.h in Headers */ = {isa = PBXBuildFile; fileRef = CC54A81B1D70077A00296A24 /* ASDispatch.h */; settings = {ATTRIBUTES = (Private, ); }; };
CC54A81E1D7008B300296A24 /* ASDispatchTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC54A81D1D7008B300296A24 /* ASDispatchTests.m */; };
CC55A70D1E529FA200594372 /* UIResponder+AsyncDisplayKit.h in Headers */ = {isa = PBXBuildFile; fileRef = CC55A70B1E529FA200594372 /* UIResponder+AsyncDisplayKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
CC55A70E1E529FA200594372 /* UIResponder+AsyncDisplayKit.m in Sources */ = {isa = PBXBuildFile; fileRef = CC55A70C1E529FA200594372 /* UIResponder+AsyncDisplayKit.m */; };
CC55A7111E52A0F200594372 /* ASResponderChainEnumerator.h in Headers */ = {isa = PBXBuildFile; fileRef = CC55A70F1E52A0F200594372 /* ASResponderChainEnumerator.h */; };
CC55A7121E52A0F200594372 /* ASResponderChainEnumerator.m in Sources */ = {isa = PBXBuildFile; fileRef = CC55A7101E52A0F200594372 /* ASResponderChainEnumerator.m */; };
CC57EAF71E3939350034C595 /* ASCollectionView+Undeprecated.h in Headers */ = {isa = PBXBuildFile; fileRef = CC2E317F1DAC353700EEE891 /* ASCollectionView+Undeprecated.h */; settings = {ATTRIBUTES = (Private, ); }; };
CC57EAF81E3939450034C595 /* ASTableView+Undeprecated.h in Headers */ = {isa = PBXBuildFile; fileRef = CC512B841DAC45C60054848E /* ASTableView+Undeprecated.h */; settings = {ATTRIBUTES = (Private, ); }; };
CC58AA4B1E398E1D002C8CB4 /* ASBlockTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = CC58AA4A1E398E1D002C8CB4 /* ASBlockTypes.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -711,6 +715,10 @@
CC512B841DAC45C60054848E /* ASTableView+Undeprecated.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASTableView+Undeprecated.h"; sourceTree = "<group>"; };
CC54A81B1D70077A00296A24 /* ASDispatch.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ASDispatch.h; sourceTree = "<group>"; };
CC54A81D1D7008B300296A24 /* ASDispatchTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = ASDispatchTests.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
CC55A70B1E529FA200594372 /* UIResponder+AsyncDisplayKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIResponder+AsyncDisplayKit.h"; sourceTree = "<group>"; };
CC55A70C1E529FA200594372 /* UIResponder+AsyncDisplayKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIResponder+AsyncDisplayKit.m"; sourceTree = "<group>"; };
CC55A70F1E52A0F200594372 /* ASResponderChainEnumerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASResponderChainEnumerator.h; sourceTree = "<group>"; };
CC55A7101E52A0F200594372 /* ASResponderChainEnumerator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASResponderChainEnumerator.m; sourceTree = "<group>"; };
CC57EAF91E394EA40034C595 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
CC58AA4A1E398E1D002C8CB4 /* ASBlockTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASBlockTypes.h; sourceTree = "<group>"; };
CC7FD9DC1BB5E962005CCB2B /* ASPhotosFrameworkImageRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASPhotosFrameworkImageRequest.h; sourceTree = "<group>"; };
@@ -927,6 +935,8 @@
DB55C2651C641AE4004EDCF5 /* ASContextTransitioning.h */,
68FC85E71CE29C7D00EDD713 /* ASVisibilityProtocols.h */,
68FC85E81CE29C7D00EDD713 /* ASVisibilityProtocols.m */,
CC55A70B1E529FA200594372 /* UIResponder+AsyncDisplayKit.h */,
CC55A70C1E529FA200594372 /* UIResponder+AsyncDisplayKit.m */,
690ED5911E36D118000627C0 /* tvOS */,
DE89C1691DCEB9CC00D49D74 /* Debug */,
058D09E1195D050800B7D73C /* Details */,
@@ -1120,6 +1130,8 @@
058D0A01195D050800B7D73C /* Private */ = {
isa = PBXGroup;
children = (
CC55A70F1E52A0F200594372 /* ASResponderChainEnumerator.h */,
CC55A7101E52A0F200594372 /* ASResponderChainEnumerator.m */,
6947B0BB1E36B4E30007C478 /* Layout */,
CCE04B2A1E313EDA006AEBBB /* Collection Data Adapter */,
058D0A03195D050800B7D73C /* _ASCoreAnimationExtras.h */,
@@ -1398,6 +1410,7 @@
B35062151B010EFD0018CF92 /* ASBatchContext.h in Headers */,
B35061F31B010EFD0018CF92 /* ASCellNode.h in Headers */,
34EFC7631B701CBF00AD841F /* ASCenterLayoutSpec.h in Headers */,
CC55A7111E52A0F200594372 /* ASResponderChainEnumerator.h in Headers */,
18C2ED7F1B9B7DE800F627B3 /* ASCollectionNode.h in Headers */,
B35061F51B010EFD0018CF92 /* ASCollectionView.h in Headers */,
ACE87A2C1D73696800D7FF06 /* ASSectionContext.h in Headers */,
@@ -1441,6 +1454,7 @@
254C6B771BF94DF4003EC431 /* ASTextKitAttributes.h in Headers */,
254C6B7D1BF94DF4003EC431 /* ASTextKitShadower.h in Headers */,
690ED58E1E36BCA6000627C0 /* ASLayoutElementStylePrivate.h in Headers */,
CC55A70D1E529FA200594372 /* UIResponder+AsyncDisplayKit.h in Headers */,
254C6B731BF94DF4003EC431 /* ASTextKitCoreTextAdditions.h in Headers */,
254C6B7A1BF94DF4003EC431 /* ASTextKitRenderer.h in Headers */,
69CB62AC1CB8165900024920 /* _ASDisplayViewAccessiblity.h in Headers */,
@@ -1827,6 +1841,7 @@
18C2ED831B9B7DE800F627B3 /* ASCollectionNode.mm in Sources */,
E55D86331CA8A14000A0C26F /* ASLayoutElement.mm in Sources */,
68FC85EC1CE29C7D00EDD713 /* ASVisibilityProtocols.m in Sources */,
CC55A7121E52A0F200594372 /* ASResponderChainEnumerator.m in Sources */,
68B8A4E41CBDB958007E4543 /* ASWeakProxy.m in Sources */,
9C70F20A1CDBE949007D6C76 /* ASTableNode.mm in Sources */,
69CB62AE1CB8165900024920 /* _ASDisplayViewAccessiblity.mm in Sources */,
@@ -1905,6 +1920,7 @@
254C6B871BF94F8A003EC431 /* ASTextKitEntityAttribute.m in Sources */,
34566CB31BC1213700715E6B /* ASPhotosFrameworkImageRequest.m in Sources */,
254C6B831BF94F8A003EC431 /* ASTextKitCoreTextAdditions.m in Sources */,
CC55A70E1E529FA200594372 /* UIResponder+AsyncDisplayKit.m in Sources */,
697796611D8AC8D3007E93D7 /* ASLayoutSpec+Subclasses.mm in Sources */,
B350623B1B010EFD0018CF92 /* NSMutableAttributedString+TextKitAdditions.m in Sources */,
044284FD1BAA365100D16268 /* UICollectionViewLayout+ASConvenience.m in Sources */,

View File

@@ -111,6 +111,10 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property (nonatomic, assign) BOOL inverted;
@end
@interface ASCollectionView (Deprecated)
/**
* Forces the .contentInset to be UIEdgeInsetsZero.
*
@@ -119,11 +123,7 @@ NS_ASSUME_NONNULL_BEGIN
* automaticallyAdjustsScrollViewInsets, which may not be accessible. ASPagerNode uses this to ensure
* its flow layout behaves predictably and does not log undefined layout warnings.
*/
@property (nonatomic) BOOL zeroContentInsets;
@end
@interface ASCollectionView (Deprecated)
@property (nonatomic) BOOL zeroContentInsets ASDISPLAYNODE_DEPRECATED_MSG("Set automaticallyAdjustsScrollViewInsets=NO on your view controller instead.");
/**
* The object that acts as the asynchronous delegate of the collection view

View File

@@ -26,6 +26,7 @@
#import <AsyncDisplayKit/_ASCollectionViewCell.h>
#import <AsyncDisplayKit/_ASDisplayLayer.h>
#import <AsyncDisplayKit/ASCollectionViewLayoutFacilitatorProtocol.h>
#import <AsyncDisplayKit/ASPagerNode.h>
#import <AsyncDisplayKit/ASSectionContext.h>
#import <AsyncDisplayKit/ASCollectionView+Undeprecated.h>
#import <AsyncDisplayKit/_ASHierarchyChangeSet.h>
@@ -94,6 +95,8 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
CGPoint _deceleratingVelocity;
BOOL _zeroContentInsets;
ASCollectionViewInvalidationStyle _nextLayoutInvalidationStyle;
/**
@@ -584,6 +587,16 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
return [_rangeController tuningParametersForRangeMode:rangeMode rangeType:rangeType];
}
- (void)setZeroContentInsets:(BOOL)zeroContentInsets
{
_zeroContentInsets = zeroContentInsets;
}
- (BOOL)zeroContentInsets
{
return _zeroContentInsets;
}
- (CGSize)calculatedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath
{
return [[self nodeForItemAtIndexPath:indexPath] calculatedSize];
@@ -1911,19 +1924,11 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
BOOL changedInNonScrollingDirection = (fixedHorizontally && newBounds.size.width != lastUsedSize.width) || (fixedVertically && newBounds.size.height != lastUsedSize.height);
if (changedInNonScrollingDirection) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
// This actually doesn't perform an animation, but prevents the transaction block from being processed in the
// data controller's prevent animation block that would interrupt an interrupted relayout happening in an animation block
// ie. ASCollectionView bounds change on rotation or multi-tasking split view resize.
[self performBatchAnimated:YES updates:^{
[_dataController relayoutAllNodes];
} completion:nil];
[_dataController waitUntilAllUpdatesAreCommitted];
// We need to ensure the size requery is done before we update our layout.
[self waitUntilAllUpdatesAreCommitted];
[self.collectionViewLayout invalidateLayout];
}
#pragma clang diagnostic pop
}
}

View File

@@ -36,6 +36,7 @@
#import <AsyncDisplayKit/ASRunLoopQueue.h>
#import <AsyncDisplayKit/ASTraitCollection.h>
#import <AsyncDisplayKit/ASWeakProxy.h>
#import <AsyncDisplayKit/ASResponderChainEnumerator.h>
/**
* Assert if the current thread owns a mutex.
@@ -3784,6 +3785,20 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) {
[result addObject:@{ @"frameInWindow" : [NSValue valueWithCGRect:windowFrame] }];
}
// Attempt to find view controller.
// Note that the convenience method asdk_associatedViewController has an assertion
// that it's run on main. Since this is a debug method, let's bypass the assertion
// and run up the chain ourselves.
if (_view != nil) {
for (UIResponder *responder in [_view asdk_responderChainEnumerator]) {
UIViewController *vc = ASDynamicCast(responder, UIViewController);
if (vc) {
[result addObject:@{ @"viewController" : ASObjectDescriptionMakeTiny(vc) }];
break;
}
}
}
if (_view != nil) {
[result addObject:@{ @"frame" : [NSValue valueWithCGRect:_view.frame] }];
} else if (_layer != nil) {

View File

@@ -11,58 +11,39 @@
//
#import <AsyncDisplayKit/ASPagerFlowLayout.h>
#import <AsyncDisplayKit/ASCellNode.h>
#import <AsyncDisplayKit/ASCollectionView.h>
@interface ASPagerFlowLayout () {
BOOL _didRotate;
CGRect _cachedCollectionViewBounds;
NSIndexPath *_currentIndexPath;
__weak ASCellNode *_currentCellNode;
}
@end
@implementation ASPagerFlowLayout
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity
- (ASCollectionView *)asCollectionView
{
NSInteger currentPage = ceil(proposedContentOffset.x / self.collectionView.bounds.size.width);
_currentIndexPath = [NSIndexPath indexPathForItem:currentPage inSection:0];
return [super targetContentOffsetForProposedContentOffset:proposedContentOffset withScrollingVelocity:velocity];
// Dynamic cast is too slow and not worth it.
return (ASCollectionView *)self.collectionView;
}
- (void)prepareForAnimatedBoundsChange:(CGRect)oldBounds
- (void)prepareLayout
{
// Cache the current page if a rotation did happen. This happens before the rotation animation
// is occuring and the bounds changed so we use this as an opportunity to cache the current index path
if (_cachedCollectionViewBounds.size.width != self.collectionView.bounds.size.width) {
_cachedCollectionViewBounds = self.collectionView.bounds;
// Figurring out current page based on the old bounds visible space
CGRect visibleRect = oldBounds;
CGFloat visibleXCenter = CGRectGetMidX(visibleRect);
NSArray<UICollectionViewLayoutAttributes *> *layoutAttributes = [self layoutAttributesForElementsInRect:visibleRect];
for (UICollectionViewLayoutAttributes *attributes in layoutAttributes) {
if ([attributes representedElementCategory] == UICollectionElementCategoryCell && attributes.center.x == visibleXCenter) {
_currentIndexPath = attributes.indexPath;
break;
[super prepareLayout];
if (_currentCellNode == nil) {
[self _updateCurrentNode];
}
}
_didRotate = YES;
}
[super prepareForAnimatedBoundsChange:oldBounds];
}
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset
{
// Don't mess around if the user is interacting with the page node. Although if just a rotation happened we should
// try to use the current index path to not end up setting the target content offset to something in between pages
if (_didRotate || (!self.collectionView.isDecelerating && !self.collectionView.isTracking)) {
_didRotate = NO;
if (_currentIndexPath) {
return [self _targetContentOffsetForItemAtIndexPath:_currentIndexPath proposedContentOffset:proposedContentOffset];
if (!self.collectionView.decelerating && !self.collectionView.tracking) {
NSIndexPath *indexPath = [self.asCollectionView indexPathForNode:_currentCellNode];
if (indexPath) {
return [self _targetContentOffsetForItemAtIndexPath:indexPath proposedContentOffset:proposedContentOffset];
}
}
@@ -75,7 +56,7 @@
return proposedContentOffset;
}
UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:_currentIndexPath];
UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:indexPath];
if (attributes == nil) {
return proposedContentOffset;
}
@@ -90,4 +71,34 @@
[self.collectionView numberOfItemsInSection:0] == 0);
}
- (void)_updateCurrentNode
{
// Never change node during an animated bounds change (rotation)
// NOTE! Listening for -prepareForAnimatedBoundsChange and -finalizeAnimatedBoundsChange
// isn't sufficient here! It's broken!
NSArray *animKeys = self.collectionView.layer.animationKeys;
for (NSString *key in animKeys) {
if ([key hasPrefix:@"bounds"]) {
return;
}
}
CGRect bounds = self.collectionView.bounds;
CGRect rect = CGRectMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds), 1, 1);
NSIndexPath *indexPath = [self layoutAttributesForElementsInRect:rect].firstObject.indexPath;
if (indexPath) {
ASCellNode *node = [self.asCollectionView nodeForItemAtIndexPath:indexPath];
if (node) {
_currentCellNode = node;
}
}
}
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
[self _updateCurrentNode];
return [super shouldInvalidateLayoutForBoundsChange:newBounds];
}
@end

View File

@@ -120,6 +120,24 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (NSInteger)indexOfPageWithNode:(ASCellNode *)node;
/**
* Tells the pager node to allow its view controller to automatically adjust its content insets.
*
* @see UIViewController.automaticallyAdjustsScrollViewInsets
*
* @discussion ASPagerNode should usually not have its content insets automatically adjusted
* because it scrolls horizontally, and flow layout will log errors because the pages
* do not fit between the top & bottom insets of the collection view.
*
* The default value is NO, which means that ASPagerNode expects that its view controller will
* have automaticallyAdjustsScrollViewInsets=NO.
*
* If this property is NO, but your view controller has automaticallyAdjustsScrollViewInsets=YES,
* the pager node will set the property to NO and log a warning message. In the future,
* the pager node will just log the warning, and you'll need to configure your view controller on your own.
*/
@property (nonatomic, assign) BOOL allowsAutomaticInsetsAdjustment;
@end
NS_ASSUME_NONNULL_END

View File

@@ -12,11 +12,13 @@
#import <AsyncDisplayKit/ASPagerNode.h>
#import <AsyncDisplayKit/ASDelegateProxy.h>
#import <AsyncDisplayKit/ASDisplayNode+FrameworkPrivate.h>
#import <AsyncDisplayKit/ASDisplayNode+Subclasses.h>
#import <AsyncDisplayKit/ASPagerFlowLayout.h>
#import <AsyncDisplayKit/ASAssert.h>
#import <AsyncDisplayKit/ASCellNode.h>
#import <AsyncDisplayKit/ASCollectionView+Undeprecated.h>
#import <AsyncDisplayKit/UIResponder+AsyncDisplayKit.h>
@interface ASPagerNode () <ASCollectionDataSource, ASCollectionDelegate, ASCollectionDelegateFlowLayout, ASDelegateProxyInterceptor>
{
@@ -81,11 +83,6 @@
cv.showsVerticalScrollIndicator = NO;
cv.showsHorizontalScrollIndicator = NO;
// Zeroing contentInset is important, as UIKit will set the top inset for the navigation bar even though
// our view is only horizontally scrollable. This causes UICollectionViewFlowLayout to log a warning.
// From here we cannot disable this directly (UIViewController's automaticallyAdjustsScrollViewInsets).
cv.zeroContentInsets = YES;
ASRangeTuningParameters minimumRenderParams = { .leadingBufferScreenfuls = 0.0, .trailingBufferScreenfuls = 0.0 };
ASRangeTuningParameters minimumPreloadParams = { .leadingBufferScreenfuls = 1.0, .trailingBufferScreenfuls = 1.0 };
[self setTuningParameters:minimumRenderParams forRangeMode:ASLayoutRangeModeMinimum rangeType:ASLayoutRangeTypeDisplay];
@@ -211,4 +208,21 @@
[self setDelegate:nil];
}
- (void)didEnterVisibleState
{
[super didEnterVisibleState];
// Check that our view controller does not automatically set our content insets
// It would be better to have a -didEnterHierarchy hook to put this in, but
// such a hook doesn't currently exist, and in every use case I can imagine,
// the pager is not hosted inside a range-managed node.
if (_allowsAutomaticInsetsAdjustment == NO) {
UIViewController *vc = [self.view asdk_associatedViewController];
if (vc.automaticallyAdjustsScrollViewInsets) {
NSLog(@"AsyncDisplayKit: ASPagerNode is setting automaticallyAdjustsScrollViewInsets=NO on its owning view controller %@. This automatic behavior will be disabled in the future. Set allowsAutomaticInsetsAdjustment=YES on the pager node to suppress this behavior.", vc);
vc.automaticallyAdjustsScrollViewInsets = NO;
}
}
}
@end

View File

@@ -106,6 +106,7 @@
#import <AsyncDisplayKit/UIImage+ASConvenience.h>
#import <AsyncDisplayKit/NSArray+Diffing.h>
#import <AsyncDisplayKit/ASObjectDescriptionHelpers.h>
#import <AsyncDisplayKit/UIResponder+AsyncDisplayKit.h>
#import <AsyncDisplayKit/AsyncDisplayKit+Debug.h>
#import <AsyncDisplayKit/ASDisplayNode+Deprecated.h>

View File

@@ -0,0 +1,28 @@
//
// ASResponderChainEnumerator.h
// AsyncDisplayKit
//
// Created by Adlai Holler on 2/13/17.
// Copyright © 2017 Facebook. All rights reserved.
//
#import <UIKit/UIResponder.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
NS_ASSUME_NONNULL_BEGIN
AS_SUBCLASSING_RESTRICTED
@interface ASResponderChainEnumerator : NSEnumerator
- (instancetype)initWithResponder:(UIResponder *)responder;
@end
@interface UIResponder (ASResponderChainEnumerator)
- (ASResponderChainEnumerator *)asdk_responderChainEnumerator;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,41 @@
//
// ASResponderChainEnumerator.m
// AsyncDisplayKit
//
// Created by Adlai Holler on 2/13/17.
// Copyright © 2017 Facebook. All rights reserved.
//
#import "ASResponderChainEnumerator.h"
@implementation ASResponderChainEnumerator {
UIResponder *_currentResponder;
}
- (instancetype)initWithResponder:(UIResponder *)responder
{
if (self = [super init]) {
_currentResponder = responder;
}
return self;
}
#pragma mark - NSEnumerator
- (id)nextObject
{
id result = [_currentResponder nextResponder];
_currentResponder = result;
return result;
}
@end
@implementation UIResponder (ASResponderChainEnumerator)
- (NSEnumerator *)asdk_responderChainEnumerator
{
return [[ASResponderChainEnumerator alloc] initWithResponder:self];
}
@end

View File

@@ -0,0 +1,24 @@
//
// UIResponder+AsyncDisplayKit.h
// AsyncDisplayKit
//
// Created by Adlai Holler on 2/13/17.
// Copyright © 2017 Facebook. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface UIResponder (AsyncDisplayKit)
/**
* The nearest view controller above this responder, if one exists.
*
* This property must be accessed on the main thread.
*/
@property (nonatomic, nullable, readonly) __kindof UIViewController *asdk_associatedViewController;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,31 @@
//
// UIResponder+AsyncDisplayKit.m
// AsyncDisplayKit
//
// Created by Adlai Holler on 2/13/17.
// Copyright © 2017 Facebook. All rights reserved.
//
#import "UIResponder+AsyncDisplayKit.h"
#import <AsyncDisplayKit/ASAssert.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
#import <AsyncDisplayKit/ASResponderChainEnumerator.h>
@implementation UIResponder (AsyncDisplayKit)
- (__kindof UIViewController *)asdk_associatedViewController
{
ASDisplayNodeAssertMainThread();
for (UIResponder *responder in [self asdk_responderChainEnumerator]) {
UIViewController *vc = ASDynamicCast(responder, UIViewController);
if (vc) {
return vc;
}
}
return nil;
}
@end

View File

@@ -21,9 +21,9 @@
@implementation PageNode
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize
{
return [ASLayout layoutWithLayoutElement:self size:constrainedSize.max];
return constrainedSize;
}
- (void)fetchData

View File

@@ -46,7 +46,7 @@ static UIColor *randomColor() {
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Next" style:UIBarButtonItemStylePlain target:self action:@selector(scrollToNextPage:)];
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Previous" style:UIBarButtonItemStylePlain target:self action:@selector(scrollToPreviousPage:)];
self.automaticallyAdjustsScrollViewInsets = NO;
return self;
}