mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-24 07:05:35 +00:00
Merge commit 'aec8dbfb33832cafdd183f5818ddb01c8b1c3997' into debug-drawrect
This commit is contained in:
@@ -30,12 +30,6 @@ Pod::Spec.new do |spec|
|
||||
'AsyncDisplayKit/TextKit/ASTextKitComponents.h'
|
||||
]
|
||||
|
||||
# ASDealloc2MainObject must be compiled with MRR
|
||||
core.exclude_files = [
|
||||
'AsyncDisplayKit/Private/_AS-objc-internal.h',
|
||||
'AsyncDisplayKit/Details/ASDealloc2MainObject.h',
|
||||
'AsyncDisplayKit/Details/ASDealloc2MainObject.m',
|
||||
]
|
||||
core.source_files = [
|
||||
'AsyncDisplayKit/**/*.{h,m,mm}',
|
||||
'Base/*.{h,m}',
|
||||
@@ -46,21 +40,11 @@ Pod::Spec.new do |spec|
|
||||
# See https://github.com/facebook/AsyncDisplayKit/issues/1153
|
||||
'AsyncDisplayKit/TextKit/*.h',
|
||||
]
|
||||
core.dependency 'AsyncDisplayKit/ASDealloc2MainObject'
|
||||
end
|
||||
|
||||
spec.subspec 'ASDealloc2MainObject' do |mrr|
|
||||
mrr.requires_arc = false
|
||||
mrr.source_files = [
|
||||
'AsyncDisplayKit/Private/_AS-objc-internal.h',
|
||||
'AsyncDisplayKit/Details/ASDealloc2MainObject.h',
|
||||
'AsyncDisplayKit/Details/ASDealloc2MainObject.m',
|
||||
]
|
||||
end
|
||||
|
||||
spec.subspec 'PINRemoteImage' do |pin|
|
||||
pin.xcconfig = { 'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) PIN_REMOTE_IMAGE=1' }
|
||||
pin.dependency 'PINRemoteImage/iOS', '= 3.0.0-beta.6'
|
||||
pin.dependency 'PINRemoteImage/iOS', '= 3.0.0-beta.7'
|
||||
pin.dependency 'PINRemoteImage/PINCache'
|
||||
pin.dependency 'AsyncDisplayKit/Core'
|
||||
end
|
||||
|
||||
@@ -125,7 +125,8 @@
|
||||
{
|
||||
__weak __typeof__(self) weakSelf = self;
|
||||
ASDisplayNodeViewBlock collectionViewBlock = ^UIView *{
|
||||
__typeof__(self) strongSelf = weakSelf;
|
||||
// Variable will be unused if event logging is off.
|
||||
__unused __typeof__(self) strongSelf = weakSelf;
|
||||
return [[ASCollectionView alloc] _initWithFrame:frame collectionViewLayout:layout layoutFacilitator:layoutFacilitator eventLog:ASDisplayNodeGetEventLog(strongSelf)];
|
||||
};
|
||||
|
||||
|
||||
@@ -360,6 +360,13 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
||||
[self reloadDataWithCompletion:nil];
|
||||
}
|
||||
|
||||
- (void)scrollToItemAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UICollectionViewScrollPosition)scrollPosition animated:(BOOL)animated
|
||||
{
|
||||
if ([self validateIndexPath:indexPath]) {
|
||||
[super scrollToItemAtIndexPath:indexPath atScrollPosition:scrollPosition animated:animated];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)reloadDataImmediately
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
@@ -622,8 +629,35 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the index path is a valid view-index-path, and returns it if so, nil otherwise.
|
||||
*/
|
||||
- (nullable NSIndexPath *)validateIndexPath:(nullable NSIndexPath *)indexPath
|
||||
{
|
||||
if (indexPath == nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSInteger section = indexPath.section;
|
||||
if (section >= self.numberOfSections) {
|
||||
ASDisplayNodeFailAssert(@"Collection view index path has invalid section %lu, section count = %lu", (unsigned long)section, (unsigned long)self.numberOfSections);
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (indexPath.item >= [self numberOfItemsInSection:section]) {
|
||||
ASDisplayNodeFailAssert(@"Collection view index path has invalid item %lu in section %lu, item count = %lu", (unsigned long)indexPath.item, (unsigned long)section, (unsigned long)[self numberOfItemsInSection:section]);
|
||||
return nil;
|
||||
}
|
||||
|
||||
return indexPath;
|
||||
}
|
||||
|
||||
- (NSIndexPath *)convertIndexPathToCollectionNode:(NSIndexPath *)indexPath
|
||||
{
|
||||
if ([self validateIndexPath:indexPath] == nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
// If this is a section index path, we don't currently have a method
|
||||
// to do a mapping.
|
||||
if (indexPath.item == NSNotFound) {
|
||||
@@ -658,7 +692,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
||||
|
||||
- (NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode
|
||||
{
|
||||
return [_dataController completedIndexPathForNode:cellNode];
|
||||
return [self validateIndexPath:[_dataController completedIndexPathForNode:cellNode]];
|
||||
}
|
||||
|
||||
- (NSArray *)visibleNodes
|
||||
@@ -1710,6 +1744,12 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
||||
[_rangeController setNeedsUpdate];
|
||||
[_rangeController updateIfNeeded];
|
||||
}
|
||||
|
||||
// When we aren't visible, we will only fetch up to the visible area. Now that we are visible,
|
||||
// we will fetch visible area + leading screens, so we need to check.
|
||||
if (visible) {
|
||||
[self _checkForBatchFetching];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark ASCALayerExtendedDelegate
|
||||
|
||||
@@ -893,6 +893,9 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
||||
|
||||
if (_calculatedDisplayNodeLayout->isValidForConstrainedSizeParentSize(constrainedSize, parentSize)) {
|
||||
ASDisplayNodeAssertNotNil(_calculatedDisplayNodeLayout->layout, @"-[ASDisplayNode layoutThatFits:parentSize:] _calculatedDisplayNodeLayout->layout should not be nil! %@", self);
|
||||
// Our calculated layout is suitable for this constrainedSize, so keep using it and
|
||||
// invalidate any pending layout that has been generated in the past.
|
||||
_pendingDisplayNodeLayout = nullptr;
|
||||
return _calculatedDisplayNodeLayout->layout ?: [ASLayout layoutWithLayoutElement:self size:{0, 0}];
|
||||
}
|
||||
|
||||
@@ -2990,12 +2993,10 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
|
||||
- (void)didExitPreloadState
|
||||
{
|
||||
if (_methodOverrides & ASDisplayNodeMethodOverrideClearFetchedData) {
|
||||
if ([self supportsRangeManagedInterfaceState]) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
[self clearFetchedData];
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3065,7 +3066,11 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
|
||||
if (nowPreload) {
|
||||
[self didEnterPreloadState];
|
||||
} else {
|
||||
[self didExitPreloadState];
|
||||
// We don't want to call -didExitPreloadState on nodes that aren't being managed by a range controller.
|
||||
// Otherwise we get flashing behavior from normal UIKit manipulations like navigation controller push / pop.
|
||||
if ([self supportsRangeManagedInterfaceState]) {
|
||||
[self didExitPreloadState];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -83,7 +83,8 @@
|
||||
{
|
||||
__weak __typeof__(self) weakSelf = self;
|
||||
ASDisplayNodeViewBlock tableViewBlock = ^UIView *{
|
||||
__typeof__(self) strongSelf = weakSelf;
|
||||
// Variable will be unused if event logging is off.
|
||||
__unused __typeof__(self) strongSelf = weakSelf;
|
||||
return [[ASTableView alloc] _initWithFrame:frame style:style dataControllerClass:dataControllerClass eventLog:ASDisplayNodeGetEventLog(strongSelf)];
|
||||
};
|
||||
|
||||
|
||||
@@ -469,6 +469,13 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
[super reloadData];
|
||||
}
|
||||
|
||||
- (void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated
|
||||
{
|
||||
if ([self validateIndexPath:indexPath]) {
|
||||
[super scrollToRowAtIndexPath:indexPath atScrollPosition:scrollPosition animated:animated];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)relayoutItems
|
||||
{
|
||||
[_dataController relayoutAllNodes];
|
||||
@@ -523,6 +530,10 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
|
||||
- (NSIndexPath *)convertIndexPathToTableNode:(NSIndexPath *)indexPath
|
||||
{
|
||||
if ([self validateIndexPath:indexPath] == nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
// If this is a section index path, we don't currently have a method
|
||||
// to do a mapping.
|
||||
if (indexPath.row == NSNotFound) {
|
||||
@@ -555,12 +566,37 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
return [self indexPathForNode:cellNode waitingIfNeeded:NO];
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the index path is a valid view-index-path, and returns it if so, nil otherwise.
|
||||
*/
|
||||
- (nullable NSIndexPath *)validateIndexPath:(nullable NSIndexPath *)indexPath
|
||||
{
|
||||
if (indexPath == nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSInteger section = indexPath.section;
|
||||
if (section >= self.numberOfSections) {
|
||||
ASDisplayNodeFailAssert(@"Table view index path has invalid section %lu, section count = %lu", (unsigned long)section, (unsigned long)self.numberOfSections);
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (indexPath.item >= [self numberOfRowsInSection:section]) {
|
||||
ASDisplayNodeFailAssert(@"Table view index path has invalid item %lu in section %lu, item count = %lu", (unsigned long)indexPath.item, (unsigned long)section, (unsigned long)[self numberOfRowsInSection:section]);
|
||||
return nil;
|
||||
}
|
||||
|
||||
return indexPath;
|
||||
}
|
||||
|
||||
- (nullable NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode waitingIfNeeded:(BOOL)wait
|
||||
{
|
||||
NSIndexPath *indexPath = [_dataController completedIndexPathForNode:cellNode];
|
||||
indexPath = [self validateIndexPath:indexPath];
|
||||
if (indexPath == nil && wait) {
|
||||
[_dataController waitUntilAllUpdatesAreCommitted];
|
||||
indexPath = [_dataController completedIndexPathForNode:cellNode];
|
||||
indexPath = [self validateIndexPath:indexPath];
|
||||
}
|
||||
return indexPath;
|
||||
}
|
||||
@@ -1727,6 +1763,12 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
[_rangeController setNeedsUpdate];
|
||||
[_rangeController updateIfNeeded];
|
||||
}
|
||||
|
||||
// When we aren't visible, we will only fetch up to the visible area. Now that we are visible,
|
||||
// we will fetch visible area + leading screens, so we need to check.
|
||||
if (visible) {
|
||||
[self _checkForBatchFetching];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -22,7 +22,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@class ASRangeController;
|
||||
|
||||
@interface ASCollectionView ()
|
||||
- (instancetype)_initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout layoutFacilitator:(nullable id<ASCollectionViewLayoutFacilitatorProtocol>)layoutFacilitator eventLog:(ASEventLog*)eventLog;
|
||||
- (instancetype)_initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout layoutFacilitator:(nullable id<ASCollectionViewLayoutFacilitatorProtocol>)layoutFacilitator eventLog:(nullable ASEventLog *)eventLog;
|
||||
|
||||
@property (nonatomic, weak, readwrite) ASCollectionNode *collectionNode;
|
||||
@property (nonatomic, strong, readonly) ASDataController *dataController;
|
||||
|
||||
@@ -47,6 +47,7 @@ BOOL ASDisplayShouldFetchBatchForScrollView(UIScrollView<ASBatchFetchingScrollVi
|
||||
@param contentSize The content size of the scrollview.
|
||||
@param targetOffset The offset that the scrollview will scroll to.
|
||||
@param leadingScreens How many screens in the remaining distance will trigger batch fetching.
|
||||
@param visible Whether the view is visible or not.
|
||||
@return Whether or not the current state should proceed with batch fetching.
|
||||
@discussion This method is broken into a category for unit testing purposes and should be used with the ASTableView and
|
||||
* ASCollectionView batch fetching API.
|
||||
@@ -57,7 +58,8 @@ extern BOOL ASDisplayShouldFetchBatchForContext(ASBatchContext *context,
|
||||
CGRect bounds,
|
||||
CGSize contentSize,
|
||||
CGPoint targetOffset,
|
||||
CGFloat leadingScreens);
|
||||
CGFloat leadingScreens,
|
||||
BOOL visible);
|
||||
|
||||
ASDISPLAYNODE_EXTERN_C_END
|
||||
|
||||
|
||||
@@ -24,7 +24,8 @@ BOOL ASDisplayShouldFetchBatchForScrollView(UIScrollView<ASBatchFetchingScrollVi
|
||||
CGRect bounds = scrollView.bounds;
|
||||
CGSize contentSize = scrollView.contentSize;
|
||||
CGFloat leadingScreens = scrollView.leadingScreensForBatching;
|
||||
return ASDisplayShouldFetchBatchForContext(context, scrollDirection, scrollableDirections, bounds, contentSize, contentOffset, leadingScreens);
|
||||
BOOL visible = (scrollView.window != nil);
|
||||
return ASDisplayShouldFetchBatchForContext(context, scrollDirection, scrollableDirections, bounds, contentSize, contentOffset, leadingScreens, visible);
|
||||
}
|
||||
|
||||
BOOL ASDisplayShouldFetchBatchForContext(ASBatchContext *context,
|
||||
@@ -33,7 +34,8 @@ BOOL ASDisplayShouldFetchBatchForContext(ASBatchContext *context,
|
||||
CGRect bounds,
|
||||
CGSize contentSize,
|
||||
CGPoint targetOffset,
|
||||
CGFloat leadingScreens)
|
||||
CGFloat leadingScreens,
|
||||
BOOL visible)
|
||||
{
|
||||
// Do not allow fetching if a batch is already in-flight and hasn't been completed or cancelled
|
||||
if ([context isFetching]) {
|
||||
@@ -57,18 +59,27 @@ BOOL ASDisplayShouldFetchBatchForContext(ASBatchContext *context,
|
||||
contentLength = contentSize.width;
|
||||
}
|
||||
|
||||
// target offset will always be 0 if the content size is smaller than the viewport
|
||||
BOOL hasSmallContent = offset == 0.0 && contentLength < viewLength;
|
||||
BOOL hasSmallContent = contentLength < viewLength;
|
||||
if (hasSmallContent) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
BOOL isScrollingTowardEnd = (ASScrollDirectionContainsDown(scrollDirection) || ASScrollDirectionContainsRight(scrollDirection));
|
||||
// If we are not visible, but we do have enough content to fill visible area,
|
||||
// don't batch fetch.
|
||||
if (visible == NO) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
// If they are scrolling toward the head of content, don't batch fetch.
|
||||
BOOL isScrollingTowardHead = (ASScrollDirectionContainsUp(scrollDirection) || ASScrollDirectionContainsLeft(scrollDirection));
|
||||
if (isScrollingTowardHead) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
CGFloat triggerDistance = viewLength * leadingScreens;
|
||||
CGFloat remainingDistance = contentLength - viewLength - offset;
|
||||
|
||||
return isScrollingTowardEnd && remainingDistance <= triggerDistance;
|
||||
return remainingDistance <= triggerDistance;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -30,26 +30,26 @@
|
||||
|
||||
- (void)testBatchNullState {
|
||||
ASBatchContext *context = [[ASBatchContext alloc] init];
|
||||
BOOL shouldFetch = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionDown, ASScrollDirectionVerticalDirections, CGRectZero, CGSizeZero, CGPointZero, 0.0);
|
||||
BOOL shouldFetch = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionDown, ASScrollDirectionVerticalDirections, CGRectZero, CGSizeZero, CGPointZero, 0.0, YES);
|
||||
XCTAssert(shouldFetch == NO, @"Should not fetch in the null state");
|
||||
}
|
||||
|
||||
- (void)testBatchAlreadyFetching {
|
||||
ASBatchContext *context = [[ASBatchContext alloc] init];
|
||||
[context beginBatchFetching];
|
||||
BOOL shouldFetch = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionDown, ASScrollDirectionVerticalDirections, PASSING_RECT, PASSING_SIZE, PASSING_POINT, 1.0);
|
||||
BOOL shouldFetch = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionDown, ASScrollDirectionVerticalDirections, PASSING_RECT, PASSING_SIZE, PASSING_POINT, 1.0, YES);
|
||||
XCTAssert(shouldFetch == NO, @"Should not fetch when context is already fetching");
|
||||
}
|
||||
|
||||
- (void)testUnsupportedScrollDirections {
|
||||
ASBatchContext *context = [[ASBatchContext alloc] init];
|
||||
BOOL fetchRight = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionRight, ASScrollDirectionHorizontalDirections, PASSING_RECT, PASSING_SIZE, PASSING_POINT, 1.0);
|
||||
BOOL fetchRight = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionRight, ASScrollDirectionHorizontalDirections, PASSING_RECT, PASSING_SIZE, PASSING_POINT, 1.0, YES);
|
||||
XCTAssert(fetchRight == YES, @"Should fetch for scrolling right");
|
||||
BOOL fetchDown = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionDown, ASScrollDirectionVerticalDirections, PASSING_RECT, PASSING_SIZE, PASSING_POINT, 1.0);
|
||||
BOOL fetchDown = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionDown, ASScrollDirectionVerticalDirections, PASSING_RECT, PASSING_SIZE, PASSING_POINT, 1.0, YES);
|
||||
XCTAssert(fetchDown == YES, @"Should fetch for scrolling down");
|
||||
BOOL fetchUp = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionUp, ASScrollDirectionVerticalDirections, PASSING_RECT, PASSING_SIZE, PASSING_POINT, 1.0);
|
||||
BOOL fetchUp = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionUp, ASScrollDirectionVerticalDirections, PASSING_RECT, PASSING_SIZE, PASSING_POINT, 1.0, YES);
|
||||
XCTAssert(fetchUp == NO, @"Should not fetch for scrolling up");
|
||||
BOOL fetchLeft = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionLeft, ASScrollDirectionHorizontalDirections, PASSING_RECT, PASSING_SIZE, PASSING_POINT, 1.0);
|
||||
BOOL fetchLeft = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionLeft, ASScrollDirectionHorizontalDirections, PASSING_RECT, PASSING_SIZE, PASSING_POINT, 1.0, YES);
|
||||
XCTAssert(fetchLeft == NO, @"Should not fetch for scrolling left");
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
CGFloat screen = 1.0;
|
||||
ASBatchContext *context = [[ASBatchContext alloc] init];
|
||||
// scroll to 1-screen top offset, height is 1 screen, so bottom is 1 screen away from end of content
|
||||
BOOL shouldFetch = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionDown, ASScrollDirectionVerticalDirections, VERTICAL_RECT(screen), VERTICAL_SIZE(screen * 3.0), VERTICAL_OFFSET(screen * 1.0), 1.0);
|
||||
BOOL shouldFetch = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionDown, ASScrollDirectionVerticalDirections, VERTICAL_RECT(screen), VERTICAL_SIZE(screen * 3.0), VERTICAL_OFFSET(screen * 1.0), 1.0, YES);
|
||||
XCTAssert(shouldFetch == YES, @"Fetch should begin when vertically scrolling to exactly 1 leading screen away");
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
CGFloat screen = 1.0;
|
||||
ASBatchContext *context = [[ASBatchContext alloc] init];
|
||||
// 3 screens of content, scroll only 1/2 of one screen
|
||||
BOOL shouldFetch = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionDown, ASScrollDirectionVerticalDirections, VERTICAL_RECT(screen), VERTICAL_SIZE(screen * 3.0), VERTICAL_OFFSET(screen * 0.5), 1.0);
|
||||
BOOL shouldFetch = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionDown, ASScrollDirectionVerticalDirections, VERTICAL_RECT(screen), VERTICAL_SIZE(screen * 3.0), VERTICAL_OFFSET(screen * 0.5), 1.0, YES);
|
||||
XCTAssert(shouldFetch == NO, @"Fetch should not begin when vertically scrolling less than the leading distance away");
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
CGFloat screen = 1.0;
|
||||
ASBatchContext *context = [[ASBatchContext alloc] init];
|
||||
// 3 screens of content, top offset to 3-screens, height 1 screen, so its 1 screen past the leading
|
||||
BOOL shouldFetch = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionDown, ASScrollDirectionVerticalDirections, VERTICAL_RECT(screen), VERTICAL_SIZE(screen * 3.0), VERTICAL_OFFSET(screen * 3.0), 1.0);
|
||||
BOOL shouldFetch = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionDown, ASScrollDirectionVerticalDirections, VERTICAL_RECT(screen), VERTICAL_SIZE(screen * 3.0), VERTICAL_OFFSET(screen * 3.0), 1.0, YES);
|
||||
XCTAssert(shouldFetch == YES, @"Fetch should begin when vertically scrolling past the content size");
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@
|
||||
CGFloat screen = 1.0;
|
||||
ASBatchContext *context = [[ASBatchContext alloc] init];
|
||||
// scroll to 1-screen left offset, width is 1 screen, so right is 1 screen away from end of content
|
||||
BOOL shouldFetch = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionRight, ASScrollDirectionVerticalDirections, HORIZONTAL_RECT(screen), HORIZONTAL_SIZE(screen * 3.0), HORIZONTAL_OFFSET(screen * 1.0), 1.0);
|
||||
BOOL shouldFetch = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionRight, ASScrollDirectionVerticalDirections, HORIZONTAL_RECT(screen), HORIZONTAL_SIZE(screen * 3.0), HORIZONTAL_OFFSET(screen * 1.0), 1.0, YES);
|
||||
XCTAssert(shouldFetch == YES, @"Fetch should begin when horizontally scrolling to exactly 1 leading screen away");
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
CGFloat screen = 1.0;
|
||||
ASBatchContext *context = [[ASBatchContext alloc] init];
|
||||
// 3 screens of content, scroll only 1/2 of one screen
|
||||
BOOL shouldFetch = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionLeft, ASScrollDirectionHorizontalDirections, HORIZONTAL_RECT(screen), HORIZONTAL_SIZE(screen * 3.0), HORIZONTAL_OFFSET(screen * 0.5), 1.0);
|
||||
BOOL shouldFetch = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionLeft, ASScrollDirectionHorizontalDirections, HORIZONTAL_RECT(screen), HORIZONTAL_SIZE(screen * 3.0), HORIZONTAL_OFFSET(screen * 0.5), 1.0, YES);
|
||||
XCTAssert(shouldFetch == NO, @"Fetch should not begin when horizontally scrolling less than the leading distance away");
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@
|
||||
CGFloat screen = 1.0;
|
||||
ASBatchContext *context = [[ASBatchContext alloc] init];
|
||||
// 3 screens of content, left offset to 3-screens, width 1 screen, so its 1 screen past the leading
|
||||
BOOL shouldFetch = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionDown, ASScrollDirectionHorizontalDirections, HORIZONTAL_RECT(screen), HORIZONTAL_SIZE(screen * 3.0), HORIZONTAL_OFFSET(screen * 3.0), 1.0);
|
||||
BOOL shouldFetch = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionDown, ASScrollDirectionHorizontalDirections, HORIZONTAL_RECT(screen), HORIZONTAL_SIZE(screen * 3.0), HORIZONTAL_OFFSET(screen * 3.0), 1.0, YES);
|
||||
XCTAssert(shouldFetch == YES, @"Fetch should begin when vertically scrolling past the content size");
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@
|
||||
CGFloat screen = 1.0;
|
||||
ASBatchContext *context = [[ASBatchContext alloc] init];
|
||||
// when the content size is < screen size, the target offset will always be 0
|
||||
BOOL shouldFetch = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionDown, ASScrollDirectionVerticalDirections, VERTICAL_RECT(screen), VERTICAL_SIZE(screen * 0.5), VERTICAL_OFFSET(0.0), 1.0);
|
||||
BOOL shouldFetch = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionDown, ASScrollDirectionVerticalDirections, VERTICAL_RECT(screen), VERTICAL_SIZE(screen * 0.5), VERTICAL_OFFSET(0.0), 1.0, YES);
|
||||
XCTAssert(shouldFetch == YES, @"Fetch should begin when the target is 0 and the content size is smaller than the scree");
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@
|
||||
CGFloat screen = 1.0;
|
||||
ASBatchContext *context = [[ASBatchContext alloc] init];
|
||||
// when the content size is < screen size, the target offset will always be 0
|
||||
BOOL shouldFetch = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionRight, ASScrollDirectionHorizontalDirections, HORIZONTAL_RECT(screen), HORIZONTAL_SIZE(screen * 0.5), HORIZONTAL_OFFSET(0.0), 1.0);
|
||||
BOOL shouldFetch = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionRight, ASScrollDirectionHorizontalDirections, HORIZONTAL_RECT(screen), HORIZONTAL_SIZE(screen * 0.5), HORIZONTAL_OFFSET(0.0), 1.0, YES);
|
||||
XCTAssert(shouldFetch == YES, @"Fetch should begin when the target is 0 and the content size is smaller than the scree");
|
||||
}
|
||||
|
||||
|
||||
@@ -891,4 +891,94 @@
|
||||
[self waitForExpectationsWithTimeout:3 handler:nil];
|
||||
}
|
||||
|
||||
- (void)testThatWeBatchFetchUntilContentRequirementIsMet_Animated
|
||||
{
|
||||
[self _primitiveBatchFetchingFillTestAnimated:YES visible:YES controller:nil];
|
||||
}
|
||||
|
||||
- (void)testThatWeBatchFetchUntilContentRequirementIsMet_Nonanimated
|
||||
{
|
||||
[self _primitiveBatchFetchingFillTestAnimated:NO visible:YES controller:nil];
|
||||
}
|
||||
|
||||
- (void)testThatWeBatchFetchUntilContentRequirementIsMet_Invisible
|
||||
{
|
||||
[self _primitiveBatchFetchingFillTestAnimated:NO visible:NO controller:nil];
|
||||
}
|
||||
|
||||
- (void)testThatWhenWeBecomeVisibleWeWillFetchAdditionalContent
|
||||
{
|
||||
ASCollectionViewTestController *ctrl = [[ASCollectionViewTestController alloc] initWithNibName:nil bundle:nil];
|
||||
// Start with 1 empty section
|
||||
ctrl.asyncDelegate->_itemCounts = {0};
|
||||
[self _primitiveBatchFetchingFillTestAnimated:NO visible:NO controller:ctrl];
|
||||
XCTAssertGreaterThan([ctrl.collectionNode numberOfItemsInSection:0], 0);
|
||||
[self _primitiveBatchFetchingFillTestAnimated:NO visible:YES controller:ctrl];
|
||||
}
|
||||
|
||||
- (void)_primitiveBatchFetchingFillTestAnimated:(BOOL)animated visible:(BOOL)visible controller:(nullable ASCollectionViewTestController *)testController
|
||||
{
|
||||
if (testController == nil) {
|
||||
testController = [[ASCollectionViewTestController alloc] initWithNibName:nil bundle:nil];
|
||||
// Start with 1 empty section
|
||||
testController.asyncDelegate->_itemCounts = {0};
|
||||
}
|
||||
ASCollectionNode *cn = testController.collectionNode;
|
||||
|
||||
UIWindow *window = nil;
|
||||
UIView *view = nil;
|
||||
if (visible) {
|
||||
window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
||||
view = window;
|
||||
} else {
|
||||
view = cn.view;
|
||||
view.frame = [UIScreen mainScreen].bounds;
|
||||
}
|
||||
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:@"Completed all batch fetches"];
|
||||
__weak ASCollectionViewTestController *weakController = testController;
|
||||
__block NSInteger batchFetchCount = 0;
|
||||
testController.asyncDelegate.willBeginBatchFetch = ^(ASBatchContext *context) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
NSInteger fetchIndex = batchFetchCount++;
|
||||
|
||||
NSInteger itemCount = weakController.asyncDelegate->_itemCounts[0];
|
||||
weakController.asyncDelegate->_itemCounts[0] = (itemCount + 1);
|
||||
if (animated) {
|
||||
[cn insertItemsAtIndexPaths:@[ [NSIndexPath indexPathForItem:itemCount inSection:0] ]];
|
||||
} else {
|
||||
[cn performBatchAnimated:NO updates:^{
|
||||
[cn insertItemsAtIndexPaths:@[ [NSIndexPath indexPathForItem:itemCount inSection:0] ]];
|
||||
} completion:nil];
|
||||
}
|
||||
|
||||
[context completeBatchFetching:YES];
|
||||
|
||||
// If no more batch fetches have happened in 1 second, assume we're done.
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
if (fetchIndex == batchFetchCount - 1) {
|
||||
[expectation fulfill];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
window.rootViewController = testController;
|
||||
|
||||
[window makeKeyAndVisible];
|
||||
[view layoutIfNeeded];
|
||||
|
||||
[self waitForExpectationsWithTimeout:60 handler:nil];
|
||||
CGFloat contentHeight = cn.view.contentSize.height;
|
||||
CGFloat requiredContentHeight;
|
||||
CGFloat itemHeight = [cn.view layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]].size.height;
|
||||
if (visible) {
|
||||
requiredContentHeight = CGRectGetMaxY(cn.bounds) + CGRectGetHeight(cn.bounds) * cn.view.leadingScreensForBatching;
|
||||
} else {
|
||||
requiredContentHeight = CGRectGetMaxY(cn.bounds);
|
||||
}
|
||||
XCTAssertGreaterThan(batchFetchCount, 2);
|
||||
XCTAssertGreaterThanOrEqual(contentHeight, requiredContentHeight, @"Loaded too little content.");
|
||||
XCTAssertLessThanOrEqual(contentHeight, requiredContentHeight + 2 * itemHeight, @"Loaded too much content.");
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -1941,6 +1941,7 @@ static bool stringContainsPointer(NSString *description, id p) {
|
||||
- (void)testDidExitPreloadIsCalledWhenNodesExitPreloadRange
|
||||
{
|
||||
ASTestDisplayNode *node = [[ASTestDisplayNode alloc] init];
|
||||
[node setHierarchyState:ASHierarchyStateRangeManaged];
|
||||
|
||||
[node recursivelySetInterfaceState:ASInterfaceStatePreload];
|
||||
[node recursivelySetInterfaceState:ASInterfaceStateDisplay];
|
||||
|
||||
@@ -13,46 +13,48 @@
|
||||
#import <Foundation/NSException.h>
|
||||
#import <pthread.h>
|
||||
|
||||
#define ASDisplayNodeAssertWithSignalAndLogFunction(condition, description, logFunction, ...) NSAssert(condition, description, ##__VA_ARGS__);
|
||||
#define ASDisplayNodeCAssertWithSignalAndLogFunction(condition, description, logFunction, ...) NSCAssert(condition, description, ##__VA_ARGS__);
|
||||
#define ASDisplayNodeAssertWithSignal(condition, description, ...) NSAssert(condition, description, ##__VA_ARGS__)
|
||||
#define ASDisplayNodeCAssertWithSignal(condition, description, ...) NSCAssert(condition, description, ##__VA_ARGS__)
|
||||
|
||||
#define ASDISPLAYNODE_ASSERTIONS_ENABLED (!defined(NS_BLOCK_ASSERTIONS))
|
||||
|
||||
#define ASDisplayNodeAssert(...) NSAssert(__VA_ARGS__)
|
||||
#define ASDisplayNodeCAssert(...) NSCAssert(__VA_ARGS__)
|
||||
/**
|
||||
* Note: In some cases it would be sufficient to do e.g.:
|
||||
* ASDisplayNodeAssert(...) NSAssert(__VA_ARGS__)
|
||||
* but we prefer not to, because we want to match the autocomplete behavior of NSAssert.
|
||||
* The construction listed above does not show the user what arguments are required and what are optional.
|
||||
*/
|
||||
|
||||
#define ASDisplayNodeAssertNil(condition, description, ...) ASDisplayNodeAssertWithSignal(!(condition), nil, (description), ##__VA_ARGS__)
|
||||
#define ASDisplayNodeCAssertNil(condition, description, ...) ASDisplayNodeCAssertWithSignal(!(condition), nil, (description), ##__VA_ARGS__)
|
||||
#define ASDisplayNodeAssert(condition, desc, ...) NSAssert(condition, desc, ##__VA_ARGS__)
|
||||
#define ASDisplayNodeCAssert(condition, desc, ...) NSCAssert(condition, desc, ##__VA_ARGS__)
|
||||
|
||||
#define ASDisplayNodeAssertNotNil(condition, description, ...) ASDisplayNodeAssertWithSignal((condition), nil, (description), ##__VA_ARGS__)
|
||||
#define ASDisplayNodeCAssertNotNil(condition, description, ...) ASDisplayNodeCAssertWithSignal((condition), nil, (description), ##__VA_ARGS__)
|
||||
#define ASDisplayNodeAssertNil(condition, desc, ...) ASDisplayNodeAssert((condition) == nil, desc, ##__VA_ARGS__)
|
||||
#define ASDisplayNodeCAssertNil(condition, desc, ...) ASDisplayNodeCAssert((condition) == nil, desc, ##__VA_ARGS__)
|
||||
|
||||
#define ASDisplayNodeAssertImplementedBySubclass() ASDisplayNodeAssertWithSignal(NO, nil, @"This method must be implemented by subclass %@", [self class]);
|
||||
#define ASDisplayNodeAssertNotInstantiable() ASDisplayNodeAssertWithSignal(NO, nil, @"This class is not instantiable.");
|
||||
#define ASDisplayNodeAssertNotSupported() ASDisplayNodeAssertWithSignal(NO, nil, @"This method is not supported by class %@", [self class]);
|
||||
#define ASDisplayNodeAssertNotNil(condition, desc, ...) ASDisplayNodeAssert((condition) != nil, desc, ##__VA_ARGS__)
|
||||
#define ASDisplayNodeCAssertNotNil(condition, desc, ...) ASDisplayNodeCAssert((condition) != nil, desc, ##__VA_ARGS__)
|
||||
|
||||
#define ASDisplayNodeAssertMainThread() ASDisplayNodeAssertWithSignal(0 != pthread_main_np(), nil, @"This method must be called on the main thread")
|
||||
#define ASDisplayNodeCAssertMainThread() ASDisplayNodeCAssertWithSignal(0 != pthread_main_np(), nil, @"This function must be called on the main thread")
|
||||
#define ASDisplayNodeAssertImplementedBySubclass() ASDisplayNodeAssert(NO, @"This method must be implemented by subclass %@", [self class]);
|
||||
#define ASDisplayNodeAssertNotInstantiable() ASDisplayNodeAssert(NO, nil, @"This class is not instantiable.");
|
||||
#define ASDisplayNodeAssertNotSupported() ASDisplayNodeAssert(NO, nil, @"This method is not supported by class %@", [self class]);
|
||||
|
||||
#define ASDisplayNodeAssertNotMainThread() ASDisplayNodeAssertWithSignal(0 == pthread_main_np(), nil, @"This method must be called off the main thread")
|
||||
#define ASDisplayNodeCAssertNotMainThread() ASDisplayNodeCAssertWithSignal(0 == pthread_main_np(), nil, @"This function must be called off the main thread")
|
||||
#define ASDisplayNodeAssertMainThread() ASDisplayNodeAssert(0 != pthread_main_np(), @"This method must be called on the main thread")
|
||||
#define ASDisplayNodeCAssertMainThread() ASDisplayNodeCAssert(0 != pthread_main_np(), @"This function must be called on the main thread")
|
||||
|
||||
#define ASDisplayNodeAssertFlag(X) ASDisplayNodeAssertWithSignal((1 == __builtin_popcount(X)), nil, nil)
|
||||
#define ASDisplayNodeCAssertFlag(X) ASDisplayNodeCAssertWithSignal((1 == __builtin_popcount(X)), nil, nil)
|
||||
#define ASDisplayNodeAssertNotMainThread() ASDisplayNodeAssert(0 == pthread_main_np(), @"This method must be called off the main thread")
|
||||
#define ASDisplayNodeCAssertNotMainThread() ASDisplayNodeCAssert(0 == pthread_main_np(), @"This function must be called off the main thread")
|
||||
|
||||
#define ASDisplayNodeAssertTrue(condition) ASDisplayNodeAssertWithSignal((condition), nil, nil)
|
||||
#define ASDisplayNodeCAssertTrue(condition) ASDisplayNodeCAssertWithSignal((condition), nil, nil)
|
||||
#define ASDisplayNodeAssertFlag(X, desc, ...) ASDisplayNodeAssert((1 == __builtin_popcount(X)), desc, ##__VA_ARGS__)
|
||||
#define ASDisplayNodeCAssertFlag(X, desc, ...) ASDisplayNodeCAssert((1 == __builtin_popcount(X)), desc, ##__VA_ARGS__)
|
||||
|
||||
#define ASDisplayNodeAssertFalse(condition) ASDisplayNodeAssertWithSignal(!(condition), nil, nil)
|
||||
#define ASDisplayNodeCAssertFalse(condition) ASDisplayNodeCAssertWithSignal(!(condition), nil, nil)
|
||||
#define ASDisplayNodeAssertTrue(condition) ASDisplayNodeAssert((condition), @"Expected %s to be true.", #condition)
|
||||
#define ASDisplayNodeCAssertTrue(condition) ASDisplayNodeCAssert((condition), @"Expected %s to be true.", #condition)
|
||||
|
||||
#define ASDisplayNodeFailAssert(description, ...) ASDisplayNodeAssertWithSignal(NO, (description), ##__VA_ARGS__)
|
||||
#define ASDisplayNodeCFailAssert(description, ...) ASDisplayNodeCAssertWithSignal(NO, (description), ##__VA_ARGS__)
|
||||
#define ASDisplayNodeAssertFalse(condition) ASDisplayNodeAssert(!(condition), @"Expected %s to be false.", #condition)
|
||||
#define ASDisplayNodeCAssertFalse(condition) ASDisplayNodeCAssert(!(condition), @"Expected %s to be false.", #condition)
|
||||
|
||||
#define ASDisplayNodeConditionalAssert(shouldTestCondition, condition, description, ...) ASDisplayNodeAssert((!(shouldTestCondition) || (condition)), nil, (description), ##__VA_ARGS__)
|
||||
#define ASDisplayNodeConditionalCAssert(shouldTestCondition, condition, description, ...) ASDisplayNodeCAssert((!(shouldTestCondition) || (condition)), nil, (description), ##__VA_ARGS__)
|
||||
#define ASDisplayNodeFailAssert(desc, ...) ASDisplayNodeAssert(NO, desc, ##__VA_ARGS__)
|
||||
#define ASDisplayNodeCFailAssert(desc, ...) ASDisplayNodeCAssert(NO, desc, ##__VA_ARGS__)
|
||||
|
||||
#define ASDisplayNodeConditionalAssert(shouldTestCondition, condition, desc, ...) ASDisplayNodeAssert((!(shouldTestCondition) || (condition)), desc, ##__VA_ARGS__)
|
||||
#define ASDisplayNodeConditionalCAssert(shouldTestCondition, condition, desc, ...) ASDisplayNodeCAssert((!(shouldTestCondition) || (condition)), desc, ##__VA_ARGS__)
|
||||
|
||||
#define ASDisplayNodeCAssertPositiveReal(description, num) ASDisplayNodeCAssert(num >= 0 && num <= CGFLOAT_MAX, @"%@ must be a real positive integer.", description)
|
||||
#define ASDisplayNodeCAssertInfOrPositiveReal(description, num) ASDisplayNodeCAssert(isinf(num) || (num >= 0 && num <= CGFLOAT_MAX), @"%@ must be infinite or a real positive integer.", description)
|
||||
|
||||
10
README.md
10
README.md
@@ -1,4 +1,4 @@
|
||||

|
||||

|
||||
|
||||
[](http://cocoapods.org/pods/AsyncDisplayKit)
|
||||
[](http://cocoapods.org/pods/AsyncDisplayKit)
|
||||
@@ -10,10 +10,10 @@
|
||||
[](https://github.com/Carthage/Carthage)
|
||||
[](https://travis-ci.org/facebook/AsyncDisplayKit)
|
||||
[](https://github.com/facebook/AsyncDisplayKit/blob/master/LICENSE)
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
ASDK is available via CocoaPods or Carthage. See our [Installation](http://asyncdisplaykit.org/docs/installation.html) guide for instructions.
|
||||
ASDK is available via CocoaPods or Carthage. See our [Installation](http://asyncdisplaykit.org/docs/installation.html) guide for instructions.
|
||||
|
||||
## Performance Gains
|
||||
|
||||
@@ -21,7 +21,7 @@ AsyncDisplayKit's basic unit is the `node`. An ASDisplayNode is an abstraction o
|
||||
|
||||
To keep its user interface smooth and responsive, your app should render at 60 frames per second — the gold standard on iOS. This means the main thread has one-sixtieth of a second to push each frame. That's 16 milliseconds to execute all layout and drawing code! And because of system overhead, your code usually has less than ten milliseconds to run before it causes a frame drop.
|
||||
|
||||
AsyncDisplayKit lets you move image decoding, text sizing and rendering, layout, and other expensive UI operations off the main thread, to keep the main thread available to respond to user interaction.
|
||||
AsyncDisplayKit lets you move image decoding, text sizing and rendering, layout, and other expensive UI operations off the main thread, to keep the main thread available to respond to user interaction.
|
||||
|
||||
## Advanced Developer Features
|
||||
|
||||
@@ -35,7 +35,7 @@ As the framework has grown, many features have been added that can save develope
|
||||
|
||||
## Getting Help
|
||||
|
||||
We use Slack for real-time debugging, community updates, and general talk about ASDK. [Signup](http://asdk-slack-auto-invite.herokuapp.com) youself or email AsyncDisplayKit(at)gmail.com to get an invite.
|
||||
We use Slack for real-time debugging, community updates, and general talk about ASDK. [Signup](http://asdk-slack-auto-invite.herokuapp.com) yourself or email AsyncDisplayKit(at)gmail.com to get an invite.
|
||||
|
||||
## Contributing
|
||||
|
||||
|
||||
@@ -200,9 +200,9 @@
|
||||
|
||||
#pragma mark - Instance Methods
|
||||
|
||||
- (void)fetchData
|
||||
- (void)didEnterPreloadState
|
||||
{
|
||||
[super fetchData];
|
||||
[super didEnterPreloadState];
|
||||
|
||||
[_photoModel.commentFeed refreshFeedWithCompletionBlock:^(NSArray *newComments) {
|
||||
[self loadCommentsForPhoto:_photoModel];
|
||||
|
||||
Reference in New Issue
Block a user