From e657bedd40a18ea019876c36d2c478c3533e187f Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Sun, 2 Aug 2015 18:01:28 +0300 Subject: [PATCH] Automatically relayout cells after orientation changed. --- AsyncDisplayKit/ASCollectionView.mm | 39 ++++++++++++++++++++ AsyncDisplayKit/ASTableView.mm | 38 +++++++++++++++++++ AsyncDisplayKit/Details/ASDataController.h | 6 +++ AsyncDisplayKit/Details/ASDataController.mm | 20 ++++++++++ AsyncDisplayKit/Private/ASInternalHelpers.h | 4 ++ AsyncDisplayKit/Private/ASInternalHelpers.mm | 15 ++++++++ 6 files changed, 122 insertions(+) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 8a0b73ffba..b19d41c355 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -15,6 +15,7 @@ #import "ASDisplayNodeInternal.h" #import "ASBatchFetching.h" #import "UICollectionViewLayout+ASConvenience.h" +#import "ASInternalHelpers.h" const static NSUInteger kASCollectionViewAnimationNone = UITableViewRowAnimationNone; @@ -121,6 +122,8 @@ static BOOL _isInterceptedSelector(SEL sel) BOOL _implementsInsetSection; ASBatchContext *_batchContext; + + BOOL _pendingRelayoutForAllRows; } @property (atomic, assign) BOOL asyncDataSourceLocked; @@ -168,6 +171,11 @@ static BOOL _isInterceptedSelector(SEL sel) [self registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"_ASCollectionViewCell"]; + if (ASSystemVersionLessThan8()) { + [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deviceOrientationDidChange) name:UIDeviceOrientationDidChangeNotification object:nil]; + } + return self; } @@ -177,6 +185,11 @@ static BOOL _isInterceptedSelector(SEL sel) // This bug might be iOS 7-specific. super.delegate = nil; super.dataSource = nil; + + if (ASSystemVersionLessThan8()) { + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications]; + } } #pragma mark - @@ -462,6 +475,32 @@ static BOOL _isInterceptedSelector(SEL sel) } +#pragma mark - +#pragma mark Orientation Change Handling + +- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection +{ + _pendingRelayoutForAllRows = YES; +} + +- (void)deviceOrientationDidChange +{ + _pendingRelayoutForAllRows = YES; +} + +- (void)layoutSubviews +{ + [super layoutSubviews]; + + if (_pendingRelayoutForAllRows) { + _pendingRelayoutForAllRows = NO; + [self performBatchAnimated:NO updates:^{ + [_dataController relayoutAllRows]; + } completion:nil]; + } +} + + #pragma mark - #pragma mark Batch Fetching diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index 6aa33f5141..c32a585a72 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -15,6 +15,7 @@ #import "ASRangeController.h" #import "ASDisplayNodeInternal.h" #import "ASBatchFetching.h" +#import "ASInternalHelpers.h" //#define LOG(...) NSLog(__VA_ARGS__) #define LOG(...) @@ -131,6 +132,8 @@ static BOOL _isInterceptedSelector(SEL sel) NSIndexPath *_contentOffsetAdjustmentTopVisibleRow; CGFloat _contentOffsetAdjustment; + + BOOL _pendingRelayoutForAllRows; } @property (atomic, assign) BOOL asyncDataSourceLocked; @@ -183,6 +186,11 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { _batchContext = [[ASBatchContext alloc] init]; _automaticallyAdjustsContentOffset = NO; + + if (ASSystemVersionLessThan8()) { + [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deviceOrientationDidChange) name:UIDeviceOrientationDidChangeNotification object:nil]; + } } - (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style @@ -220,6 +228,11 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { // This bug might be iOS 7-specific. super.delegate = nil; super.dataSource = nil; + + if (ASSystemVersionLessThan8()) { + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications]; + } } #pragma mark - @@ -343,6 +356,31 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { [_dataController endUpdatesAnimated:animated completion:completion]; } +#pragma mark - +#pragma mark Orientation Change Handling + +- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection +{ + _pendingRelayoutForAllRows = YES; +} + +- (void)deviceOrientationDidChange +{ + _pendingRelayoutForAllRows = YES; +} + +- (void)layoutSubviews +{ + [super layoutSubviews]; + + if (_pendingRelayoutForAllRows) { + _pendingRelayoutForAllRows = NO; + [self beginUpdates]; + [_dataController relayoutAllRows]; + [self endUpdates]; + } +} + #pragma mark - #pragma mark Editing diff --git a/AsyncDisplayKit/Details/ASDataController.h b/AsyncDisplayKit/Details/ASDataController.h index 5cee7b86d0..b2e12b829a 100644 --- a/AsyncDisplayKit/Details/ASDataController.h +++ b/AsyncDisplayKit/Details/ASDataController.h @@ -154,6 +154,12 @@ typedef NSUInteger ASDataControllerAnimationOptions; - (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions; +/** + * Re-measures all loaded nodes. Used for external relayout (relayout that is caused by a change in constrained size of each and every cell node, + * for example, after an orientation change). + */ +- (void)relayoutAllRows; + - (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions; - (void)reloadDataWithAnimationOptions:(ASDataControllerAnimationOptions)animationOptions completion:(void (^)())completion; diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 4ca124a5be..a41f2d84b3 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -555,6 +555,26 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; }]; } +- (void)relayoutAllRows +{ + [self performEditCommandWithBlock:^{ + ASDisplayNodeAssertMainThread(); + LOG(@"Edit Command - relayoutRows"); + [_editingTransactionQueue waitUntilAllOperationsAreFinished]; + + NSArray *indexPaths = ASIndexPathsForMultidimensionalArray(_completedNodes); + NSArray *loadedNodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_completedNodes, indexPaths); + + for (NSUInteger i = 0; i < loadedNodes.count && i < indexPaths.count; i++) { + // TODO: The current implementation does not make use of different constrained sizes per node. + CGSize constrainedSize = [_dataSource dataController:self constrainedSizeForNodeAtIndexPath:indexPaths[i]]; + ASCellNode *node = loadedNodes[i]; + [node measure:constrainedSize]; + node.frame = CGRectMake(0.0f, 0.0f, node.calculatedSize.width, node.calculatedSize.height); + } + }]; +} + - (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { [self performEditCommandWithBlock:^{ diff --git a/AsyncDisplayKit/Private/ASInternalHelpers.h b/AsyncDisplayKit/Private/ASInternalHelpers.h index e20c8c999a..8693440eff 100644 --- a/AsyncDisplayKit/Private/ASInternalHelpers.h +++ b/AsyncDisplayKit/Private/ASInternalHelpers.h @@ -24,4 +24,8 @@ CGFloat ASCeilPixelValue(CGFloat f); CGFloat ASRoundPixelValue(CGFloat f); +BOOL ASSystemVersionLessThan8(); + +BOOL ASSystemVersionLessThanVersion(NSString *version); + ASDISPLAYNODE_EXTERN_C_END diff --git a/AsyncDisplayKit/Private/ASInternalHelpers.mm b/AsyncDisplayKit/Private/ASInternalHelpers.mm index d161523f2b..f230e1e49c 100644 --- a/AsyncDisplayKit/Private/ASInternalHelpers.mm +++ b/AsyncDisplayKit/Private/ASInternalHelpers.mm @@ -61,3 +61,18 @@ CGFloat ASRoundPixelValue(CGFloat f) { return roundf(f * ASScreenScale()) / ASScreenScale(); } + +BOOL ASSystemVersionLessThan8() +{ + static BOOL lessThan8; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + lessThan8 = ASSystemVersionLessThanVersion(@"8"); + }); + return lessThan8; +} + +BOOL ASSystemVersionLessThanVersion(NSString *version) +{ + return [[[UIDevice currentDevice] systemVersion] compare:version options:NSNumericSearch] == NSOrderedAscending; +}