Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ASWrapperCellNode] Introduce a new class allowing more control of UIKit passthrough cells. #797

Merged
merged 13 commits into from
Mar 13, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* Add your own contributions to the next release on the line below this with your name.
- [tvOS] Fixes errors when building against tvOS SDK [Alex Hill](https://github.com/alexhillc) [#728](https://github.com/TextureGroup/Texture/pull/728)
- [ASDisplayNode] Add unit tests for layout z-order changes (with an open issue to fix).
- [ASWrapperCellNode] Introduce a new class allowing more control of UIKit passthrough cells.
- [ASDisplayNode] Consolidate main thread initialization and allow apps to invoke it manually instead of +load.
- [ASRunloopQueue] Introduce new runloop queue(ASCATransactionQueue) to coalesce Interface state update calls for view controller transitions.
- [ASRangeController] Fix stability of "minimum" rangeMode if the app has more than one layout before scrolling.
Expand Down
2 changes: 1 addition & 1 deletion Source/ASCellNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ typedef NS_ENUM(NSUInteger, ASCellNodeVisibilityEvent) {

- (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock didLoadBlock:(nullable ASDisplayNodeDidLoadBlock)didLoadBlock __unavailable;

- (void)setLayerBacked:(BOOL)layerBacked AS_UNAVAILABLE("ASCellNode does not support layer-backing");
- (void)setLayerBacked:(BOOL)layerBacked AS_UNAVAILABLE("ASCellNode does not support layer-backing, although subnodes may be layer-backed.");

@end

Expand Down
19 changes: 19 additions & 0 deletions Source/ASCellNode.mm
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,25 @@ - (BOOL)supportsLayerBacking
return NO;
}

- (BOOL)shouldUseUIKitCell
{
return NO;
}

@end


#pragma mark -
#pragma mark ASWrapperCellNode

// TODO: Consider if other calls, such as willDisplayCell, should be bridged to this class.
@implementation ASWrapperCellNode : ASCellNode

- (BOOL)shouldUseUIKitCell
{
return YES;
}

@end


Expand Down
13 changes: 13 additions & 0 deletions Source/ASCollectionNode+Beta.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,19 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property (nonatomic, assign) BOOL usesSynchronousDataLoading;

/**
* Returns YES if the ASCollectionNode contents are completely synchronized with the underlying collection-view layout.
*/
@property (nonatomic, readonly, getter=isSynchronized) BOOL synchronized;

/**
* Schedules a block to be performed (on the main thread) as soon as the completion block is called
* on performBatchUpdates:.
*
* When isSynchronized == YES, the block is run block immediately (before the method returns).
*/
- (void)onDidFinishSynchronizing:(void (^)(void))didFinishSynchronizing;

- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout layoutFacilitator:(nullable id<ASCollectionViewLayoutFacilitatorProtocol>)layoutFacilitator;

- (instancetype)initWithLayoutDelegate:(id<ASCollectionLayoutDelegate>)layoutDelegate layoutFacilitator:(nullable id<ASCollectionViewLayoutFacilitatorProtocol>)layoutFacilitator;
Expand Down
26 changes: 25 additions & 1 deletion Source/ASCollectionNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,30 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property (nonatomic, assign) BOOL allowsMultipleSelection;

/**
* A Boolean value that determines whether bouncing always occurs when vertical scrolling reaches the end of the content.
* The default value of this property is NO.
*/
@property (nonatomic, assign) BOOL alwaysBounceVertical;

/**
* A Boolean value that determines whether bouncing always occurs when horizontal scrolling reaches the end of the content view.
* The default value of this property is NO.
*/
@property (nonatomic, assign) BOOL alwaysBounceHorizontal;

/**
* A Boolean value that controls whether the vertical scroll indicator is visible.
* The default value of this property is YES.
*/
@property (nonatomic, assign) BOOL showsVerticalScrollIndicator;

/**
* A Boolean value that controls whether the horizontal scroll indicator is visible.
* The default value of this property is NO.
*/
@property (nonatomic, assign) BOOL showsHorizontalScrollIndicator;

/**
* The layout used to organize the node's items.
*
Expand Down Expand Up @@ -284,7 +308,7 @@ NS_ASSUME_NONNULL_BEGIN
*
* Calling -waitUntilAllUpdatesAreProcessed is one way to flush any pending update completion blocks.
*/
- (void)onDidFinishProcessingUpdates:(nullable void (^)(void))didFinishProcessingUpdates;
- (void)onDidFinishProcessingUpdates:(void (^)(void))didFinishProcessingUpdates;

/**
* Blocks execution of the main thread until all section and item updates are committed to the view. This method must be called from the main thread.
Expand Down
128 changes: 122 additions & 6 deletions Source/ASCollectionNode.mm
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,13 @@ @interface _ASCollectionPendingState : NSObject
@property (nonatomic, assign) BOOL usesSynchronousDataLoading;
@property (nonatomic, assign) CGFloat leadingScreensForBatching;
@property (weak, nonatomic) id <ASCollectionViewLayoutInspecting> layoutInspector;
@property (nonatomic, assign) BOOL alwaysBounceVertical;
@property (nonatomic, assign) BOOL alwaysBounceHorizontal;
@property (nonatomic, assign) UIEdgeInsets contentInset;
@property (nonatomic, assign) CGPoint contentOffset;
@property (nonatomic, assign) BOOL animatesContentOffset;
@property (nonatomic, assign) BOOL showsVerticalScrollIndicator;
@property (nonatomic, assign) BOOL showsHorizontalScrollIndicator;
@end

@implementation _ASCollectionPendingState
Expand Down Expand Up @@ -203,13 +207,28 @@ - (void)didLoad
view.allowsMultipleSelection = pendingState.allowsMultipleSelection;
view.usesSynchronousDataLoading = pendingState.usesSynchronousDataLoading;
view.layoutInspector = pendingState.layoutInspector;
view.contentInset = pendingState.contentInset;


// Only apply these flags if they're enabled; the view might come with them turned on.
if (pendingState.alwaysBounceVertical) {
view.alwaysBounceVertical = YES;
}
if (pendingState.alwaysBounceHorizontal) {
view.alwaysBounceHorizontal = YES;
}

UIEdgeInsets contentInset = pendingState.contentInset;
if (!UIEdgeInsetsEqualToEdgeInsets(contentInset, UIEdgeInsetsZero)) {
view.contentInset = contentInset;
}

CGPoint contentOffset = pendingState.contentOffset;
if (!CGPointEqualToPoint(contentOffset, CGPointZero)) {
[view setContentOffset:contentOffset animated:pendingState.animatesContentOffset];
}

if (pendingState.rangeMode != ASLayoutRangeModeUnspecified) {
[view.rangeController updateCurrentRangeWithMode:pendingState.rangeMode];
}

[view setContentOffset:pendingState.contentOffset animated:pendingState.animatesContentOffset];

// Don't need to set collectionViewLayout to the view as the layout was already used to init the view in view block.
}
Expand All @@ -235,10 +254,11 @@ - (void)interfaceStateDidChange:(ASInterfaceState)newState fromState:(ASInterfac
- (void)didEnterPreloadState
{
[super didEnterPreloadState];
// ASCollectionNode is often nested inside of other collections. In this case, ASHierarchyState's RangeManaged bit will be set.
// Intentionally allocate the view here and trigger a layout pass on it, which in turn will trigger the intial data load.
// We can get rid of this call later when ASDataController, ASRangeController and ASCollectionLayout can operate without the view.
// TODO (ASCL) If this node supports async layout, kick off the initial data load without allocating the view
if (CGRectEqualToRect(self.bounds, CGRectZero) == NO) {
if (ASHierarchyStateIncludesRangeManaged(self.hierarchyState) && CGRectEqualToRect(self.bounds, CGRectZero) == NO) {
[[self view] layoutIfNeeded];
}
}
Expand Down Expand Up @@ -435,6 +455,82 @@ - (BOOL)allowsMultipleSelection
}
}

- (void)setAlwaysBounceVertical:(BOOL)alwaysBounceVertical
{
if ([self pendingState]) {
_pendingState.alwaysBounceVertical = alwaysBounceVertical;
} else {
ASDisplayNodeAssert([self isNodeLoaded], @"ASCollectionNode should be loaded if pendingState doesn't exist");
self.view.alwaysBounceVertical = alwaysBounceVertical;
}
}

- (BOOL)alwaysBounceVertical
{
if ([self pendingState]) {
return _pendingState.alwaysBounceVertical;
} else {
return self.view.alwaysBounceVertical;
}
}

- (void)setAlwaysBounceHorizontal:(BOOL)alwaysBounceHorizontal
{
if ([self pendingState]) {
_pendingState.alwaysBounceHorizontal = alwaysBounceHorizontal;
} else {
ASDisplayNodeAssert([self isNodeLoaded], @"ASCollectionNode should be loaded if pendingState doesn't exist");
self.view.alwaysBounceHorizontal = alwaysBounceHorizontal;
}
}

- (BOOL)alwaysBounceHorizontal
{
if ([self pendingState]) {
return _pendingState.alwaysBounceHorizontal;
} else {
return self.view.alwaysBounceHorizontal;
}
}

- (void)setShowsVerticalScrollIndicator:(BOOL)showsVerticalScrollIndicator
{
if ([self pendingState]) {
_pendingState.showsVerticalScrollIndicator = showsVerticalScrollIndicator;
} else {
ASDisplayNodeAssert([self isNodeLoaded], @"ASCollectionNode should be loaded if pendingState doesn't exist");
self.view.showsVerticalScrollIndicator = showsVerticalScrollIndicator;
}
}

- (BOOL)showsVerticalScrollIndicator
{
if ([self pendingState]) {
return _pendingState.showsVerticalScrollIndicator;
} else {
return self.view.showsVerticalScrollIndicator;
}
}

- (void)setShowsHorizontalScrollIndicator:(BOOL)showsHorizontalScrollIndicator
{
if ([self pendingState]) {
_pendingState.showsHorizontalScrollIndicator = showsHorizontalScrollIndicator;
} else {
ASDisplayNodeAssert([self isNodeLoaded], @"ASCollectionNode should be loaded if pendingState doesn't exist");
self.view.showsHorizontalScrollIndicator = showsHorizontalScrollIndicator;
}
}

- (BOOL)showsHorizontalScrollIndicator
{
if ([self pendingState]) {
return _pendingState.showsHorizontalScrollIndicator;
} else {
return self.view.showsHorizontalScrollIndicator;
}
}

- (void)setCollectionViewLayout:(UICollectionViewLayout *)layout
{
if ([self pendingState]) {
Expand Down Expand Up @@ -745,15 +841,35 @@ - (BOOL)isProcessingUpdates
return (self.nodeLoaded ? [self.view isProcessingUpdates] : NO);
}

- (void)onDidFinishProcessingUpdates:(nullable void (^)())completion
- (void)onDidFinishProcessingUpdates:(void (^)())completion
{
if (!completion) {
return;
}
if (!self.nodeLoaded) {
completion();
} else {
[self.view onDidFinishProcessingUpdates:completion];
}
}

- (BOOL)isSynchronized
{
return (self.nodeLoaded ? [self.view isSynchronized] : YES);
}

- (void)onDidFinishSynchronizing:(void (^)())completion
{
if (!completion) {
return;
}
if (!self.nodeLoaded) {
completion();
} else {
[self.view onDidFinishSynchronizing:completion];
}
}

- (void)waitUntilAllUpdatesAreProcessed
{
ASDisplayNodeAssertMainThread();
Expand Down
8 changes: 7 additions & 1 deletion Source/ASCollectionView.h
Original file line number Diff line number Diff line change
Expand Up @@ -296,9 +296,15 @@ NS_ASSUME_NONNULL_BEGIN
* See ASCollectionNode.h for full documentation of these methods.
*/
@property (nonatomic, readonly) BOOL isProcessingUpdates;
- (void)onDidFinishProcessingUpdates:(nullable void (^)(void))completion;
- (void)onDidFinishProcessingUpdates:(void (^)(void))completion;
- (void)waitUntilAllUpdatesAreCommitted ASDISPLAYNODE_DEPRECATED_MSG("Use -[ASCollectionNode waitUntilAllUpdatesAreProcessed] instead.");

/**
* See ASCollectionNode.h for full documentation of these methods.
*/
@property (nonatomic, readonly, getter=isSynchronized) BOOL synchronized;
- (void)onDidFinishSynchronizing:(void (^)(void))completion;

/**
* Registers the given kind of supplementary node for use in creating node-backed supplementary views.
*
Expand Down
Loading