Skip to content

Commit

Permalink
[ASTableNode][ASCollectionNode] Add content offset bridging property (T…
Browse files Browse the repository at this point in the history
…extureGroup#460)

- Add content offset bridging property to table and collection node
- And use it in `ASCollectionLayout` to avoid measuring unrelated nodes during the first layout.
- Update CHANGELOG and highlight deprecated methods
  • Loading branch information
nguyenhuy authored and bernieperez committed Apr 25, 2018
1 parent 1390d79 commit 13ed5d1
Show file tree
Hide file tree
Showing 15 changed files with 154 additions and 34 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
- Fix an issue that causes calculatedLayoutDidChange being called needlessly. [Huy Nguyen](https://github.com/nguyenhuy) [#490](https://github.com/TextureGroup/Texture/pull/490)
- Negate iOS 11 automatic estimated table row heights. [Christian Selig](https://github.com/christianselig) [#485](https://github.com/TextureGroup/Texture/pull/485)
- Rename ASCellNode.viewModel to ASCellNode.nodeViewModel to reduce collisions with subclass properties implemented by clients. [Adlai Holler](https://github.com/Adlai-Holler) [#499](https://github.com/TextureGroup/Texture/pull/499)
- [Breaking] Add content offset bridging property to ASTableNode and ASCollectionNode. Deprecate related methods in ASTableView and ASCollectionView [Huy Nguyen](https://github.com/nguyenhuy) [#460](https://github.com/TextureGroup/Texture/pull/460)

##2.3.5
- Fix an issue where inserting/deleting sections could lead to inconsistent supplementary element behavior. [Adlai Holler](https://github.com/Adlai-Holler)
Expand Down
14 changes: 14 additions & 0 deletions Source/ASCollectionNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,20 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property (nonatomic, weak) id<ASCollectionViewLayoutInspecting> layoutInspector;

/**
* The offset of the content view's origin from the collection node's origin. Defaults to CGPointZero.
*/
@property (nonatomic, assign) CGPoint contentOffset;

/**
* Sets the offset from the content node’s origin to the collection node’s origin.
*
* @param contentOffset The offset
*
* @param animated YES to animate to this new offset at a constant velocity, NO to not aniamte and immediately make the transition.
*/
- (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated;

/**
* Tuning parameters for a range type in full mode.
*
Expand Down
30 changes: 30 additions & 0 deletions Source/ASCollectionNode.mm
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ @interface _ASCollectionPendingState : NSObject
@property (nonatomic, assign) BOOL usesSynchronousDataLoading;
@property (nonatomic, assign) CGFloat leadingScreensForBatching;
@property (weak, nonatomic) id <ASCollectionViewLayoutInspecting> layoutInspector;
@property (nonatomic, assign) CGPoint contentOffset;
@property (nonatomic, assign) BOOL animatesContentOffset;
@end

@implementation _ASCollectionPendingState
Expand All @@ -61,6 +63,8 @@ - (instancetype)init
_allowsSelection = YES;
_allowsMultipleSelection = NO;
_inverted = NO;
_contentOffset = CGPointZero;
_animatesContentOffset = NO;
}
return self;
}
Expand Down Expand Up @@ -189,6 +193,8 @@ - (void)didLoad
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 Down Expand Up @@ -434,6 +440,30 @@ - (UICollectionViewLayout *)collectionViewLayout
}
}

- (void)setContentOffset:(CGPoint)contentOffset
{
[self setContentOffset:contentOffset animated:NO];
}

- (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated
{
if ([self pendingState]) {
_pendingState.contentOffset = contentOffset;
_pendingState.animatesContentOffset = animated;
} else {
[self.view setContentOffset:contentOffset animated:animated];
}
}

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

- (ASScrollDirection)scrollDirection
{
return [self isNodeLoaded] ? self.view.scrollDirection : ASScrollDirectionNone;
Expand Down
7 changes: 7 additions & 0 deletions Source/ASCollectionView.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,11 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property (nonatomic) BOOL zeroContentInsets ASDISPLAYNODE_DEPRECATED_MSG("Set automaticallyAdjustsScrollViewInsets=NO on your view controller instead.");

/**
* The point at which the origin of the content view is offset from the origin of the collection view.
*/
@property (nonatomic, assign) CGPoint contentOffset ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode property instead.");

/**
* The object that acts as the asynchronous delegate of the collection view
*
Expand Down Expand Up @@ -407,6 +412,8 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (NSArray<__kindof ASCellNode *> *)visibleNodes AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead.");

- (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead.");

@end

ASDISPLAYNODE_DEPRECATED_MSG("Renamed to ASCollectionDataSource.")
Expand Down
14 changes: 14 additions & 0 deletions Source/ASTableNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,20 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property (nonatomic, assign) BOOL inverted;

/**
* The offset of the content view's origin from the table node's origin. Defaults to CGPointZero.
*/
@property (nonatomic, assign) CGPoint contentOffset;

/**
* Sets the offset from the content node’s origin to the table node’s origin.
*
* @param contentOffset The offset
*
* @param animated YES to animate to this new offset at a constant velocity, NO to not aniamte and immediately make the transition.
*/
- (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated;

/**
* YES to automatically adjust the contentOffset when cells are inserted or deleted above
* visible cells, maintaining the users' visible scroll position.
Expand Down
32 changes: 32 additions & 0 deletions Source/ASTableNode.mm
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ @interface _ASTablePendingState : NSObject
@property (nonatomic, assign) BOOL allowsMultipleSelectionDuringEditing;
@property (nonatomic, assign) BOOL inverted;
@property (nonatomic, assign) CGFloat leadingScreensForBatching;
@property (nonatomic, assign) CGPoint contentOffset;
@property (nonatomic, assign) BOOL animatesContentOffset;
@property (nonatomic, assign) BOOL automaticallyAdjustsContentOffset;
@end

Expand All @@ -58,6 +60,8 @@ - (instancetype)init
_allowsMultipleSelectionDuringEditing = NO;
_inverted = NO;
_leadingScreensForBatching = 2;
_contentOffset = CGPointZero;
_animatesContentOffset = NO;
_automaticallyAdjustsContentOffset = NO;
}
return self;
Expand Down Expand Up @@ -120,6 +124,7 @@ - (void)didLoad
if (pendingState.rangeMode != ASLayoutRangeModeUnspecified) {
[view.rangeController updateCurrentRangeWithMode:pendingState.rangeMode];
}
[view setContentOffset:pendingState.contentOffset animated:pendingState.animatesContentOffset];
}
}

Expand Down Expand Up @@ -232,6 +237,33 @@ - (CGFloat)leadingScreensForBatching
}
}

- (void)setContentOffset:(CGPoint)contentOffset
{
[self setContentOffset:contentOffset animated:NO];
}

- (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated
{
_ASTablePendingState *pendingState = self.pendingState;
if (pendingState) {
pendingState.contentOffset = contentOffset;
pendingState.animatesContentOffset = animated;
} else {
ASDisplayNodeAssert(self.nodeLoaded, @"ASTableNode should be loaded if pendingState doesn't exist");
[self.view setContentOffset:contentOffset animated:animated];
}
}

- (CGPoint)contentOffset
{
_ASTablePendingState *pendingState = self.pendingState;
if (pendingState) {
return pendingState.contentOffset;
} else {
return self.view.contentOffset;
}
}

- (void)setAutomaticallyAdjustsContentOffset:(BOOL)automaticallyAdjustsContentOffset
{
_ASTablePendingState *pendingState = self.pendingState;
Expand Down
18 changes: 12 additions & 6 deletions Source/ASTableView.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property (nonatomic, assign) CGFloat leadingScreensForBatching ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode property instead.");

/**
* The offset of the content view's origin from the table node's origin. Defaults to CGPointZero.
*/
@property (nonatomic, assign) CGPoint contentOffset ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode property instead.");

/**
* YES to automatically adjust the contentOffset when cells are inserted or deleted above
Expand All @@ -84,6 +88,12 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property (nonatomic, assign) BOOL inverted ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode property instead.");

@property (nonatomic, readonly, nullable) NSIndexPath *indexPathForSelectedRow ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode property instead.");

@property (nonatomic, readonly, nullable) NSArray<NSIndexPath *> *indexPathsForSelectedRows ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode property instead.");

@property (nonatomic, readonly, nullable) NSArray<NSIndexPath *> *indexPathsForVisibleRows ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode property instead.");

/**
* Tuning parameters for a range type in full mode.
*
Expand Down Expand Up @@ -138,12 +148,6 @@ NS_ASSUME_NONNULL_BEGIN

- (void)selectRowAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated scrollPosition:(UITableViewScrollPosition)scrollPosition ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead.");

@property (nonatomic, readonly, nullable) NSIndexPath *indexPathForSelectedRow ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode property instead.");

@property (nonatomic, readonly, nullable) NSArray<NSIndexPath *> *indexPathsForSelectedRows ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode property instead.");

@property (nonatomic, readonly, nullable) NSArray<NSIndexPath *> *indexPathsForVisibleRows ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode property instead.");

- (nullable NSIndexPath *)indexPathForRowAtPoint:(CGPoint)point ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead.");

- (nullable NSArray<NSIndexPath *> *)indexPathsForRowsInRect:(CGRect)rect ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead.");
Expand Down Expand Up @@ -241,6 +245,8 @@ NS_ASSUME_NONNULL_BEGIN
/// Deprecated in 2.0. You should not call this method.
- (void)clearFetchedData ASDISPLAYNODE_DEPRECATED_MSG("You should not call this method directly. Intead, rely on the Interstate State callback methods.");

- (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead.");

@end

ASDISPLAYNODE_DEPRECATED_MSG("Renamed to ASTableDataSource.")
Expand Down
1 change: 1 addition & 0 deletions Source/Details/ASCollectionLayoutContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ AS_SUBCLASSING_RESTRICTED
@interface ASCollectionLayoutContext : NSObject

@property (nonatomic, assign, readonly) CGSize viewportSize;
@property (nonatomic, assign, readonly) CGPoint initialContentOffset;
@property (nonatomic, assign, readonly) ASScrollDirection scrollableDirections;
@property (nonatomic, weak, readonly) ASElementMap *elements;
@property (nonatomic, strong, readonly, nullable) id additionalInfo;
Expand Down
7 changes: 4 additions & 3 deletions Source/Details/ASCollectionLayoutContext.m
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,11 @@

@implementation ASCollectionLayoutContext {
Class<ASCollectionLayoutDelegate> _layoutDelegateClass;

// This ivar doesn't directly involve in the layout calculation process, i.e contexts can be equal regardless of the layout caches.
// As a result, this ivar is ignored in -isEqualToContext: and -hash.
__weak ASCollectionLayoutCache *_layoutCache;
}

- (instancetype)initWithViewportSize:(CGSize)viewportSize
initialContentOffset:(CGPoint)initialContentOffset
scrollableDirections:(ASScrollDirection)scrollableDirections
elements:(ASElementMap *)elements
layoutDelegateClass:(Class<ASCollectionLayoutDelegate>)layoutDelegateClass
Expand All @@ -38,6 +36,7 @@ - (instancetype)initWithViewportSize:(CGSize)viewportSize
self = [super init];
if (self) {
_viewportSize = viewportSize;
_initialContentOffset = initialContentOffset;
_scrollableDirections = scrollableDirections;
_elements = elements;
_layoutDelegateClass = layoutDelegateClass;
Expand All @@ -57,6 +56,8 @@ - (ASCollectionLayoutCache *)layoutCache
return _layoutCache;
}

// NOTE: Some properties, like initialContentOffset and layoutCache are ignored in -isEqualToContext: and -hash.
// That is because contexts can be equal regardless of the content offsets or layout caches.
- (BOOL)isEqualToContext:(ASCollectionLayoutContext *)context
{
if (context == nil) {
Expand Down
6 changes: 4 additions & 2 deletions Source/Private/ASCollectionLayout.mm
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,13 @@ - (ASCollectionLayoutContext *)layoutContextWithElements:(ASElementMap *)element
{
ASDisplayNodeAssertMainThread();
CGSize viewportSize = [self _viewportSize];
CGPoint contentOffset = _collectionNode.contentOffset;
id additionalInfo = nil;
if (_layoutDelegateFlags.implementsAdditionalInfoForLayoutWithElements) {
additionalInfo = [_layoutDelegate additionalInfoForLayoutWithElements:elements];
}
return [[ASCollectionLayoutContext alloc] initWithViewportSize:viewportSize
initialContentOffset:contentOffset
scrollableDirections:[_layoutDelegate scrollableDirections]
elements:elements
layoutDelegateClass:[_layoutDelegate class]
Expand All @@ -93,8 +95,8 @@ + (ASCollectionLayoutState *)calculateLayoutWithContext:(ASCollectionLayoutConte

// Measure elements in the measure range ahead of time, block on the initial rect as it'll be visible shortly
CGSize viewportSize = context.viewportSize;
// TODO Consider content offset of the collection node
CGRect initialRect = CGRectMake(0, 0, viewportSize.width, viewportSize.height);
CGPoint contentOffset = context.initialContentOffset;
CGRect initialRect = CGRectMake(contentOffset.x, contentOffset.y, viewportSize.width, viewportSize.height);
CGRect measureRect = CGRectExpandToRangeWithScrollableDirections(initialRect,
kASDefaultMeasureRangeTuningParameters,
context.scrollableDirections,
Expand Down
1 change: 1 addition & 0 deletions Source/Private/ASCollectionLayoutContext+Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, weak, readonly) ASCollectionLayoutCache *layoutCache;

- (instancetype)initWithViewportSize:(CGSize)viewportSize
initialContentOffset:(CGPoint)initialContentOffset
scrollableDirections:(ASScrollDirection)scrollableDirections
elements:(ASElementMap *)elements
layoutDelegateClass:(Class<ASCollectionLayoutDelegate>)layoutDelegateClass
Expand Down
4 changes: 4 additions & 0 deletions Source/Private/ASCollectionView+Undeprecated.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ NS_ASSUME_NONNULL_BEGIN

@property (nonatomic, weak) id<ASCollectionViewLayoutInspecting> layoutInspector;

@property (nonatomic, assign) CGPoint contentOffset;

/**
* Tuning parameters for a range type in full mode.
*
Expand Down Expand Up @@ -292,6 +294,8 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (nullable NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode AS_WARN_UNUSED_RESULT;

- (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated;

@end

NS_ASSUME_NONNULL_END
32 changes: 15 additions & 17 deletions Source/Private/ASTableView+Undeprecated.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,19 @@ NS_ASSUME_NONNULL_BEGIN

@property (nonatomic, weak) id<ASTableDelegate> asyncDelegate;
@property (nonatomic, weak) id<ASTableDataSource> asyncDataSource;
@property (nonatomic, assign) CGPoint contentOffset;
@property (nonatomic, assign) BOOL automaticallyAdjustsContentOffset;
@property (nonatomic, assign) BOOL inverted;
@property (nonatomic, readonly, nullable) NSArray<NSIndexPath *> *indexPathsForVisibleRows;
@property (nonatomic, readonly, nullable) NSArray<NSIndexPath *> *indexPathsForSelectedRows;
@property (nonatomic, readonly, nullable) NSIndexPath *indexPathForSelectedRow;

/**
* The number of screens left to scroll before the delegate -tableView:beginBatchFetchingWithContext: is called.
*
* Defaults to two screenfuls.
*/
@property (nonatomic, assign) CGFloat leadingScreensForBatching;

/**
* Initializer.
Expand All @@ -44,10 +57,6 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style;

@property (nonatomic, assign) BOOL automaticallyAdjustsContentOffset;

@property (nonatomic, assign) BOOL inverted;

/**
* Tuning parameters for a range type in full mode.
*
Expand Down Expand Up @@ -109,12 +118,6 @@ NS_ASSUME_NONNULL_BEGIN

- (void)selectRowAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated scrollPosition:(UITableViewScrollPosition)scrollPosition;

@property (nonatomic, readonly, nullable) NSArray<NSIndexPath *> *indexPathsForVisibleRows;

@property (nonatomic, readonly, nullable) NSArray<NSIndexPath *> *indexPathsForSelectedRows;

@property (nonatomic, readonly, nullable) NSIndexPath *indexPathForSelectedRow;

- (nullable NSIndexPath *)indexPathForRowAtPoint:(CGPoint)point;

- (nullable NSArray<NSIndexPath *> *)indexPathsForRowsInRect:(CGRect)rect;
Expand All @@ -135,13 +138,6 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (nullable NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode AS_WARN_UNUSED_RESULT;

/**
* The number of screens left to scroll before the delegate -tableView:beginBatchFetchingWithContext: is called.
*
* Defaults to two screenfuls.
*/
@property (nonatomic, assign) CGFloat leadingScreensForBatching;

/**
* Reload everything from scratch, destroying the working range and all cached nodes.
*
Expand Down Expand Up @@ -311,5 +307,7 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath;

- (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated;

@end
NS_ASSUME_NONNULL_END
Loading

0 comments on commit 13ed5d1

Please sign in to comment.