Merge branch 'master' into ASCornerRounding

This commit is contained in:
Scott Goodson
2017-09-17 22:47:57 -07:00
86 changed files with 1265 additions and 1287 deletions
+4 -12
View File
@@ -96,6 +96,7 @@
3917EBD41E9C2FC400D04A01 /* _ASCollectionReusableView.h in Headers */ = {isa = PBXBuildFile; fileRef = 3917EBD21E9C2FC400D04A01 /* _ASCollectionReusableView.h */; settings = {ATTRIBUTES = (Private, ); }; };
3917EBD51E9C2FC400D04A01 /* _ASCollectionReusableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3917EBD31E9C2FC400D04A01 /* _ASCollectionReusableView.m */; };
3C9C128519E616EF00E942A0 /* ASTableViewTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3C9C128419E616EF00E942A0 /* ASTableViewTests.mm */; };
4E9127691F64157600499623 /* ASRunLoopQueueTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E9127681F64157600499623 /* ASRunLoopQueueTests.m */; };
509E68601B3AED8E009B9150 /* ASScrollDirection.m in Sources */ = {isa = PBXBuildFile; fileRef = 205F0E111B371BD7007741D0 /* ASScrollDirection.m */; };
509E68611B3AEDA0009B9150 /* ASAbstractLayoutController.h in Headers */ = {isa = PBXBuildFile; fileRef = 205F0E171B37339C007741D0 /* ASAbstractLayoutController.h */; };
509E68621B3AEDA5009B9150 /* ASAbstractLayoutController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 205F0E181B37339C007741D0 /* ASAbstractLayoutController.mm */; };
@@ -106,7 +107,6 @@
636EA1A41C7FF4EC00EE152F /* NSArray+Diffing.m in Sources */ = {isa = PBXBuildFile; fileRef = DBC452DA1C5BF64600B16017 /* NSArray+Diffing.m */; };
636EA1A51C7FF4EF00EE152F /* ASDefaultPlayButton.m in Sources */ = {isa = PBXBuildFile; fileRef = AEB7B0191C5962EA00662EF4 /* ASDefaultPlayButton.m */; };
680346941CE4052A0009FEB4 /* ASNavigationController.h in Headers */ = {isa = PBXBuildFile; fileRef = 68FC85DC1CE29AB700EDD713 /* ASNavigationController.h */; settings = {ATTRIBUTES = (Public, ); }; };
683489281D70DE3400327501 /* ASDisplayNode+Deprecated.h in Headers */ = {isa = PBXBuildFile; fileRef = 683489271D70DE3400327501 /* ASDisplayNode+Deprecated.h */; settings = {ATTRIBUTES = (Public, ); }; };
68355B341CB579B9001D4E68 /* ASImageNode+AnimatedImage.mm in Sources */ = {isa = PBXBuildFile; fileRef = 68355B2E1CB5799E001D4E68 /* ASImageNode+AnimatedImage.mm */; };
68355B3E1CB57A60001D4E68 /* ASPINRemoteImageDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 68355B361CB57A5A001D4E68 /* ASPINRemoteImageDownloader.m */; };
68355B401CB57A69001D4E68 /* ASImageContainerProtocolCategories.m in Sources */ = {isa = PBXBuildFile; fileRef = 68355B381CB57A5A001D4E68 /* ASImageContainerProtocolCategories.m */; };
@@ -129,8 +129,6 @@
6907C25A1DC4ECFE00374C66 /* ASObjectDescriptionHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = 6907C2571DC4ECFE00374C66 /* ASObjectDescriptionHelpers.m */; };
690C35621E055C5D00069B91 /* ASDimensionInternal.mm in Sources */ = {isa = PBXBuildFile; fileRef = 690C35601E055C5D00069B91 /* ASDimensionInternal.mm */; };
690C35641E055C7B00069B91 /* ASDimensionInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 690C35631E055C7B00069B91 /* ASDimensionInternal.h */; settings = {ATTRIBUTES = (Public, ); }; };
690C35671E0567C600069B91 /* ASDimensionDeprecated.mm in Sources */ = {isa = PBXBuildFile; fileRef = 690C35651E0567C600069B91 /* ASDimensionDeprecated.mm */; };
690C356B1E05680300069B91 /* ASDimensionDeprecated.h in Headers */ = {isa = PBXBuildFile; fileRef = 690C356A1E05680300069B91 /* ASDimensionDeprecated.h */; settings = {ATTRIBUTES = (Public, ); }; };
690ED58E1E36BCA6000627C0 /* ASLayoutElementStylePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 690ED58D1E36BCA6000627C0 /* ASLayoutElementStylePrivate.h */; settings = {ATTRIBUTES = (Private, ); }; };
690ED5961E36D118000627C0 /* ASControlNode+tvOS.h in Headers */ = {isa = PBXBuildFile; fileRef = 690ED5921E36D118000627C0 /* ASControlNode+tvOS.h */; settings = {ATTRIBUTES = (Private, ); }; };
690ED5981E36D118000627C0 /* ASControlNode+tvOS.m in Sources */ = {isa = PBXBuildFile; fileRef = 690ED5931E36D118000627C0 /* ASControlNode+tvOS.m */; };
@@ -627,7 +625,7 @@
4640521B1A3F83C40061C0BA /* ASTableLayoutController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASTableLayoutController.h; sourceTree = "<group>"; };
4640521C1A3F83C40061C0BA /* ASTableLayoutController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASTableLayoutController.m; sourceTree = "<group>"; };
4640521D1A3F83C40061C0BA /* ASLayoutController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASLayoutController.h; sourceTree = "<group>"; };
683489271D70DE3400327501 /* ASDisplayNode+Deprecated.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASDisplayNode+Deprecated.h"; sourceTree = "<group>"; };
4E9127681F64157600499623 /* ASRunLoopQueueTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASRunLoopQueueTests.m; sourceTree = "<group>"; };
68355B2E1CB5799E001D4E68 /* ASImageNode+AnimatedImage.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "ASImageNode+AnimatedImage.mm"; sourceTree = "<group>"; };
68355B361CB57A5A001D4E68 /* ASPINRemoteImageDownloader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASPINRemoteImageDownloader.m; sourceTree = "<group>"; };
68355B371CB57A5A001D4E68 /* ASImageContainerProtocolCategories.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASImageContainerProtocolCategories.h; sourceTree = "<group>"; };
@@ -652,8 +650,6 @@
6907C2571DC4ECFE00374C66 /* ASObjectDescriptionHelpers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASObjectDescriptionHelpers.m; sourceTree = "<group>"; };
690C35601E055C5D00069B91 /* ASDimensionInternal.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASDimensionInternal.mm; sourceTree = "<group>"; };
690C35631E055C7B00069B91 /* ASDimensionInternal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASDimensionInternal.h; sourceTree = "<group>"; };
690C35651E0567C600069B91 /* ASDimensionDeprecated.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASDimensionDeprecated.mm; sourceTree = "<group>"; };
690C356A1E05680300069B91 /* ASDimensionDeprecated.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASDimensionDeprecated.h; sourceTree = "<group>"; };
690ED58D1E36BCA6000627C0 /* ASLayoutElementStylePrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASLayoutElementStylePrivate.h; sourceTree = "<group>"; };
690ED5921E36D118000627C0 /* ASControlNode+tvOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASControlNode+tvOS.h"; sourceTree = "<group>"; };
690ED5931E36D118000627C0 /* ASControlNode+tvOS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "ASControlNode+tvOS.m"; sourceTree = "<group>"; };
@@ -1081,7 +1077,6 @@
058D09D9195D050800B7D73C /* ASDisplayNode.mm */,
68B027791C1A79CC0041016B /* ASDisplayNode+Beta.h */,
90FC784E1E4BFE1B00383C5A /* ASDisplayNode+Yoga.mm */,
683489271D70DE3400327501 /* ASDisplayNode+Deprecated.h */,
058D09DA195D050800B7D73C /* ASDisplayNode+Subclasses.h */,
CC034A071E60BEB400626263 /* ASDisplayNode+Convenience.h */,
CC034A081E60BEB400626263 /* ASDisplayNode+Convenience.m */,
@@ -1226,6 +1221,7 @@
69FEE53C1D95A9AF0086F066 /* ASLayoutElementStyleTests.m */,
695BE2541DC1245C008E6EA5 /* ASWrapperSpecSnapshotTests.mm */,
699B83501E3C1BA500433FA4 /* ASLayoutSpecTests.m */,
4E9127681F64157600499623 /* ASRunLoopQueueTests.m */,
);
path = Tests;
sourceTree = "<group>";
@@ -1524,8 +1520,6 @@
ACF6ED041B17843500DA7C62 /* ASCenterLayoutSpec.mm */,
ACF6ED071B17843500DA7C62 /* ASDimension.h */,
ACF6ED081B17843500DA7C62 /* ASDimension.mm */,
690C356A1E05680300069B91 /* ASDimensionDeprecated.h */,
690C35651E0567C600069B91 /* ASDimensionDeprecated.mm */,
690C35631E055C7B00069B91 /* ASDimensionInternal.h */,
690C35601E055C5D00069B91 /* ASDimensionInternal.mm */,
ACF6ED091B17843500DA7C62 /* ASInsetLayoutSpec.h */,
@@ -1731,8 +1725,6 @@
696F01EC1DD2AF450049FBD5 /* ASEventLog.h in Headers */,
690C35641E055C7B00069B91 /* ASDimensionInternal.h in Headers */,
3917EBD41E9C2FC400D04A01 /* _ASCollectionReusableView.h in Headers */,
690C356B1E05680300069B91 /* ASDimensionDeprecated.h in Headers */,
683489281D70DE3400327501 /* ASDisplayNode+Deprecated.h in Headers */,
698371DB1E4379CD00437585 /* ASNodeController+Beta.h in Headers */,
6907C2581DC4ECFE00374C66 /* ASObjectDescriptionHelpers.h in Headers */,
69E0E8A71D356C9400627613 /* ASEqualityHelpers.h in Headers */,
@@ -2151,6 +2143,7 @@
696FCB311D6E46050093471E /* ASBackgroundLayoutSpecSnapshotTests.mm in Sources */,
CC583AD81EF9BDC300134156 /* OCMockObject+ASAdditions.m in Sources */,
69FEE53D1D95A9AF0086F066 /* ASLayoutElementStyleTests.m in Sources */,
4E9127691F64157600499623 /* ASRunLoopQueueTests.m in Sources */,
CC4981B31D1A02BE004E13CC /* ASTableViewThrashTests.m in Sources */,
CC54A81E1D7008B300296A24 /* ASDispatchTests.m in Sources */,
CCE4F9B31F0D60AC00062E4E /* ASIntegerMapTests.m in Sources */,
@@ -2204,7 +2197,6 @@
CCCCCCE41EC3EF060087FE10 /* NSParagraphStyle+ASText.m in Sources */,
8BBBAB8D1CEBAF1E00107FC6 /* ASDefaultPlaybackButton.m in Sources */,
B30BF6541C59D889004FCD53 /* ASLayoutManager.m in Sources */,
690C35671E0567C600069B91 /* ASDimensionDeprecated.mm in Sources */,
92DD2FE71BF4D0850074C9DD /* ASMapNode.mm in Sources */,
CCA282B91E9EA8E40037E8B7 /* AsyncDisplayKit+Tips.m in Sources */,
636EA1A51C7FF4EF00EE152F /* ASDefaultPlayButton.m in Sources */,
+19 -2
View File
@@ -2,13 +2,30 @@
* Add your own contributions to the next release on the line below this with your name.
- [ASCornerRounding] Introduce .cornerRoundingType: CALayer, Precomposited, or Clip Corners. [Scott Goodson](https://github.com/appleguy) [#465](https://github.com/TextureGroup/Texture/pull/465)
- [Yoga] Add insertYogaNode:atIndex: method. Improve handling of relayouts. [Scott Goodson](https://github.com/appleguy)
- [ASCollectionNode] Add -isProcessingUpdates and -onDidFinishProcessingUpdates: APIs. [#522](https://github.com/TextureGroup/Texture/pull/522) [Scott Goodson](https://github.com/appleguy)
- [Accessibility] Add .isAccessibilityContainer property, allowing automatic aggregation of children's a11y labels. [#468][Scott Goodson](https://github.com/appleguy)
- [ASImageNode] Enabled .clipsToBounds by default, fixing the use of .cornerRadius and clipping of GIFs. [Scott Goodson](https://github.com/appleguy) [#466](https://github.com/TextureGroup/Texture/pull/466)
- Fix an issue in layout transition that causes it to unexpectedly use the old layout [Huy Nguyen](https://github.com/nguyenhuy) [#464](https://github.com/TextureGroup/Texture/pull/464)
- Add -[ASDisplayNode detailedLayoutDescription] property to aid debugging. [Adlai Holler](https://github.com/Adlai-Holler) [#476](https://github.com/TextureGroup/Texture/pull/476)
- 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)
- [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)
- Add content inset and offset bridging properties to ASTableNode and ASCollectionNode. Deprecate related properties and methods in ASTableView and ASCollectionView [Huy Nguyen](https://github.com/nguyenhuy) [#460](https://github.com/TextureGroup/Texture/pull/460) [#560](https://github.com/TextureGroup/Texture/pull/560)
- Remove re-entrant access to self.view when applying initial pending state. [Adlai Holler](https://github.com/Adlai-Holler) [#510](https://github.com/TextureGroup/Texture/pull/510)
- Small improvements in ASCollectionLayout [Huy Nguyen](https://github.com/nguyenhuy) [#509](https://github.com/TextureGroup/Texture/pull/509) [#513](https://github.com/TextureGroup/Texture/pull/513)
- Small improvements in ASCollectionLayout [Huy Nguyen](https://github.com/nguyenhuy) [#509](https://github.com/TextureGroup/Texture/pull/509) [#513](https://github.com/TextureGroup/Texture/pull/513) [#562]((https://github.com/TextureGroup/Texture/pull/562)
- Fix retain cycle between ASImageNode and PINAnimatedImage [Phil Larson](https://github.com/plarson) [#520](https://github.com/TextureGroup/Texture/pull/520)
- Change the API for disabling logging from a compiler flag to a runtime C function ASDisableLogging(). [Adlai Holler](https://github.com/Adlai-Holler) [#528](https://github.com/TextureGroup/Texture/pull/528)
- Table and collection views to consider content inset when calculating (default) element size range [Huy Nguyen](https://github.com/nguyenhuy) [#525](https://github.com/TextureGroup/Texture/pull/525)
- [ASEditableTextNode] added -editableTextNodeShouldBeginEditing to ASEditableTextNodeDelegate to mirror the corresponding method from UITextViewDelegate. [Yan S.](https://github.com/yans) [#535](https://github.com/TextureGroup/Texture/pull/535)
- [Breaking] Remove APIs that have been deprecated since 2.0 and/or for at least 6 months [Huy Nguyen](https://github.com/nguyenhuy) [#529](https://github.com/TextureGroup/Texture/pull/529)
- [ASDisplayNode] Ensure `-displayWillStartAsynchronously:` and `-displayDidFinish` are invoked on rasterized subnodes. [Eric Scheers](https://github.com/smeis) [#532](https://github.com/TextureGroup/Texture/pull/532)
- Fixed a memory corruption issue in the ASImageNode display system. [Adlai Holler](https://github.com/Adlai-Holler) [#555](https://github.com/TextureGroup/Texture/pull/555)
- [Breaking] Rename ASCollectionGalleryLayoutSizeProviding to ASCollectionGalleryLayoutPropertiesProviding. Besides a fixed item size, it now can provide interitem and line spacings, as well as section inset [Huy Nguyen](https://github.com/nguyenhuy) [#496](https://github.com/TextureGroup/Texture/pull/496) [#533](https://github.com/TextureGroup/Texture/pull/533)
- Deprecate `-[ASDisplayNode displayWillStart]` in favor of `-displayWillStartAsynchronously:` [Huy Nguyen](https://github.com/nguyenhuy) [#536](https:/
/github.com/TextureGroup/Texture/pull/536)
- Add support for URLs on ASNetworkImageNode. [Garrett Moon](https://github.com/garrettmoon)
- [ASImageNode] Always dealloc images in a background queue [Huy Nguyen](https://github.com/nguyenhuy) [#561](https://github.com/TextureGroup/Texture/pull/561)
- Mark ASRunLoopQueue as drained if it contains only NULLs [Cesar Estebanez](https://github.com/cesteban) [#558](https://github.com/TextureGroup/Texture/pull/558)
##2.4
- Fix an issue where inserting/deleting sections could lead to inconsistent supplementary element behavior. [Adlai Holler](https://github.com/Adlai-Holler)
+1 -1
View File
@@ -1,2 +1,2 @@
github "pinterest/PINRemoteImage" "3.0.0-beta.11"
github "pinterest/PINRemoteImage" "3.0.0-beta.12"
github "pinterest/PINCache" "3.0.1-beta.5"
+36 -10
View File
@@ -128,6 +128,11 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property (nonatomic, weak) id<ASCollectionViewLayoutInspecting> layoutInspector;
/**
* The distance that the content view is inset from the collection node edges. Defaults to UIEdgeInsetsZero.
*/
@property (nonatomic, assign) UIEdgeInsets contentInset;
/**
* The offset of the content view's origin from the collection node's origin. Defaults to CGPointZero.
*/
@@ -252,10 +257,39 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (void)performBatchUpdates:(nullable AS_NOESCAPE void (^)())updates completion:(nullable void (^)(BOOL finished))completion;
/**
* Returns YES if the ASCollectionNode is still processing changes from performBatchUpdates:.
* This is typically the concurrent allocation (calling nodeBlocks) and layout of newly inserted
* ASCellNodes. If YES is returned, then calling -waitUntilAllUpdatesAreProcessed may take tens of
* milliseconds to return as it blocks on these concurrent operations.
*
* Returns NO if ASCollectionNode is fully synchronized with the underlying UICollectionView. This
* means that until the next performBatchUpdates: is called, it is safe to compare UIKit values
* (such as from UICollectionViewLayout) with your app's data source.
*
* This method will always return NO if called immediately after -waitUntilAllUpdatesAreProcessed.
*/
@property (nonatomic, readonly) BOOL isProcessingUpdates;
/**
* Schedules a block to be performed (on the main thread) after processing of performBatchUpdates:
* is finished (completely synchronized to UIKit). The blocks will be run at the moment that
* -isProcessingUpdates changes from YES to NO;
*
* When isProcessingUpdates == NO, the block is run block immediately (before the method returns).
*
* Blocks scheduled by this mechanism are NOT guaranteed to run in the order they are scheduled.
* They may also be delayed if performBatchUpdates continues to be called; the blocks will wait until
* all running updates are finished.
*
* Calling -waitUntilAllUpdatesAreProcessed is one way to flush any pending update completion blocks.
*/
- (void)onDidFinishProcessingUpdates:(nullable 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.
*/
- (void)waitUntilAllUpdatesAreCommitted;
- (void)waitUntilAllUpdatesAreProcessed;
/**
* Inserts one or more sections.
@@ -495,15 +529,7 @@ NS_ASSUME_NONNULL_BEGIN
@interface ASCollectionNode (Deprecated)
/**
* Reload everything from scratch, destroying the working range and all cached nodes.
*
* @warning This method is substantially more expensive than UICollectionView's version.
*
* @deprecated This method is deprecated in 2.0. Use @c reloadDataWithCompletion: and
* then @c waitUntilAllUpdatesAreCommitted instead.
*/
- (void)reloadDataImmediately ASDISPLAYNODE_DEPRECATED_MSG("Use -reloadData / -reloadDataWithCompletion: followed by -waitUntilAllUpdatesAreCommitted instead.");
- (void)waitUntilAllUpdatesAreCommitted ASDISPLAYNODE_DEPRECATED_MSG("This method has been renamed to -waitUntilAllUpdatesAreProcessed.");
@end
+44 -9
View File
@@ -49,6 +49,7 @@
@property (nonatomic, assign) BOOL usesSynchronousDataLoading;
@property (nonatomic, assign) CGFloat leadingScreensForBatching;
@property (weak, nonatomic) id <ASCollectionViewLayoutInspecting> layoutInspector;
@property (nonatomic, assign) UIEdgeInsets contentInset;
@property (nonatomic, assign) CGPoint contentOffset;
@property (nonatomic, assign) BOOL animatesContentOffset;
@end
@@ -63,6 +64,7 @@
_allowsSelection = YES;
_allowsMultipleSelection = NO;
_inverted = NO;
_contentInset = UIEdgeInsetsZero;
_contentOffset = CGPointZero;
_animatesContentOffset = NO;
}
@@ -181,6 +183,7 @@
if (_pendingState) {
_ASCollectionPendingState *pendingState = _pendingState;
self.pendingState = nil;
view.asyncDelegate = pendingState.delegate;
view.asyncDataSource = pendingState.dataSource;
view.inverted = pendingState.inverted;
@@ -188,7 +191,7 @@
view.allowsMultipleSelection = pendingState.allowsMultipleSelection;
view.usesSynchronousDataLoading = pendingState.usesSynchronousDataLoading;
view.layoutInspector = pendingState.layoutInspector;
self.pendingState = nil;
view.contentInset = pendingState.contentInset;
if (pendingState.rangeMode != ASLayoutRangeModeUnspecified) {
[view.rangeController updateCurrentRangeWithMode:pendingState.rangeMode];
@@ -440,6 +443,25 @@
}
}
- (void)setContentInset:(UIEdgeInsets)contentInset
{
if ([self pendingState]) {
_pendingState.contentInset = contentInset;
} else {
ASDisplayNodeAssert([self isNodeLoaded], @"ASCollectionNode should be loaded if pendingState doesn't exist");
self.view.contentInset = contentInset;
}
}
- (UIEdgeInsets)contentInset
{
if ([self pendingState]) {
return _pendingState.contentInset;
} else {
return self.view.contentInset;
}
}
- (void)setContentOffset:(CGPoint)contentOffset
{
[self setContentOffset:contentOffset animated:NO];
@@ -451,6 +473,7 @@
_pendingState.contentOffset = contentOffset;
_pendingState.animatesContentOffset = animated;
} else {
ASDisplayNodeAssert([self isNodeLoaded], @"ASCollectionNode should be loaded if pendingState doesn't exist");
[self.view setContentOffset:contentOffset animated:animated];
}
}
@@ -705,7 +728,21 @@
[self performBatchAnimated:UIView.areAnimationsEnabled updates:updates completion:completion];
}
- (void)waitUntilAllUpdatesAreCommitted
- (BOOL)isProcessingUpdates
{
return (self.nodeLoaded ? [self.view isProcessingUpdates] : NO);
}
- (void)onDidFinishProcessingUpdates:(nullable void (^)())completion
{
if (!self.nodeLoaded) {
completion();
} else {
[self.view onDidFinishProcessingUpdates:completion];
}
}
- (void)waitUntilAllUpdatesAreProcessed
{
ASDisplayNodeAssertMainThread();
if (self.nodeLoaded) {
@@ -713,6 +750,11 @@
}
}
- (void)waitUntilAllUpdatesAreCommitted
{
[self waitUntilAllUpdatesAreProcessed];
}
- (void)reloadDataWithCompletion:(void (^)())completion
{
ASDisplayNodeAssertMainThread();
@@ -734,13 +776,6 @@
[self reloadDataWithCompletion:nil];
}
- (void)reloadDataImmediately
{
ASDisplayNodeAssertMainThread();
[self reloadData];
[self waitUntilAllUpdatesAreCommitted];
}
- (void)relayoutItems
{
ASDisplayNodeAssertMainThread();
+9 -10
View File
@@ -141,6 +141,11 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property (nonatomic) BOOL zeroContentInsets ASDISPLAYNODE_DEPRECATED_MSG("Set automaticallyAdjustsScrollViewInsets=NO on your view controller instead.");
/**
* The distance that the content view is inset from the collection view edges. Defaults to UIEdgeInsetsZero.
*/
@property (nonatomic, assign) UIEdgeInsets contentInset ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode property instead");
/**
* The point at which the origin of the content view is offset from the origin of the collection view.
*/
@@ -280,14 +285,6 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (void)reloadData AS_UNAVAILABLE("Use ASCollectionNode method instead.");
/**
* Reload everything from scratch entirely on the main thread, destroying the working range and all cached nodes.
*
* @warning This method is substantially more expensive than UICollectionView's version and will block the main thread
* while all the cells load.
*/
- (void)reloadDataImmediately AS_UNAVAILABLE("Use ASCollectionNode method instead.");
/**
* Triggers a relayout of all nodes.
*
@@ -296,9 +293,11 @@ NS_ASSUME_NONNULL_BEGIN
- (void)relayoutItems ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead.");
/**
* Blocks execution of the main thread until all section and row updates are committed. This method must be called from the main thread.
* See ASCollectionNode.h for full documentation of these methods.
*/
- (void)waitUntilAllUpdatesAreCommitted ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead.");
@property (nonatomic, readonly) BOOL isProcessingUpdates;
- (void)onDidFinishProcessingUpdates:(nullable void (^)())completion;
- (void)waitUntilAllUpdatesAreCommitted ASDISPLAYNODE_DEPRECATED_MSG("Use -[ASCollectionNode waitUntilAllUpdatesAreProcessed] instead.");
/**
* Registers the given kind of supplementary node for use in creating node-backed supplementary views.
+13 -7
View File
@@ -359,6 +359,16 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
[_dataController relayoutAllNodes];
}
- (BOOL)isProcessingUpdates
{
return [_dataController isProcessingUpdates];
}
- (void)onDidFinishProcessingUpdates:(nullable void (^)())completion
{
[_dataController onDidFinishProcessingUpdates:completion];
}
- (void)waitUntilAllUpdatesAreCommitted
{
ASDisplayNodeAssertMainThread();
@@ -367,8 +377,8 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
// ASDisplayNodeFailAssert(@"Should not call %@ during batch update", NSStringFromSelector(_cmd));
return;
}
[_dataController waitUntilAllUpdatesAreCommitted];
[_dataController waitUntilAllUpdatesAreProcessed];
}
- (void)setDataSource:(id<UICollectionViewDataSource>)dataSource
@@ -1536,10 +1546,6 @@ minimumLineSpacingForSectionAtIndex:(NSInteger)section
return [self.layoutInspector scrollableDirections];
}
- (ASScrollDirection)flowLayoutScrollableDirections:(UICollectionViewFlowLayout *)flowLayout {
return (flowLayout.scrollDirection == UICollectionViewScrollDirectionHorizontal) ? ASScrollDirectionHorizontalDirections : ASScrollDirectionVerticalDirections;
}
- (void)layoutSubviews
{
if (_cellsForLayoutUpdates.count > 0) {
@@ -2197,7 +2203,7 @@ minimumLineSpacingForSectionAtIndex:(NSInteger)section
if (changedInNonScrollingDirection) {
[_dataController relayoutAllNodes];
[_dataController waitUntilAllUpdatesAreCommitted];
[_dataController waitUntilAllUpdatesAreProcessed];
// We need to ensure the size requery is done before we update our layout.
[self.collectionViewLayout invalidateLayout];
}
+17 -14
View File
@@ -61,19 +61,6 @@ typedef struct {
@interface ASDisplayNode (Beta)
/**
* ASTableView and ASCollectionView now throw exceptions on invalid updates
* like their UIKit counterparts. If YES, these classes will log messages
* on invalid updates rather than throwing exceptions.
*
* Note that even if AsyncDisplayKit's exception is suppressed, the app may still crash
* as it proceeds with an invalid update.
*
* This property defaults to NO. It will be removed in a future release.
*/
+ (BOOL)suppressesInvalidCollectionUpdateExceptions AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED_MSG("Collection update exceptions are thrown if assertions are enabled.");
+ (void)setSuppressesInvalidCollectionUpdateExceptions:(BOOL)suppresses ASDISPLAYNODE_DEPRECATED_MSG("Collection update exceptions are thrown if assertions are enabled.");
/**
* @abstract Recursively ensures node and all subnodes are displayed.
* @see Full documentation in ASDisplayNode+FrameworkPrivate.h
@@ -112,6 +99,19 @@ typedef struct {
@property (nonatomic, strong, readonly) ASEventLog *eventLog;
#endif
/**
* @abstract Whether this node acts as an accessibility container. If set to YES, then this node's accessibility label will represent
* an aggregation of all child nodes' accessibility labels. Nodes in this node's subtree that are also accessibility containers will
* not be included in this aggregation, and will be exposed as separate accessibility elements to UIKit.
*/
@property (nonatomic, assign) BOOL isAccessibilityContainer;
/**
* @abstract Invoked when a user performs a custom action on an accessible node. Nodes that are children of accessibility containers, have
* an accessibity label and have an interactive UIAccessibilityTrait will automatically receive custom-action handling.
*/
- (void)performAccessibilityCustomAction:(UIAccessibilityCustomAction *)action;
/**
* @abstract Currently used by ASNetworkImageNode and ASMultiplexImageNode to allow their placeholders to stay if they are loading an image from the network.
* Otherwise, a display pass is scheduled and completes, but does not actually draw anything - and ASDisplayNode considers the element finished.
@@ -175,12 +175,15 @@ extern void ASDisplayNodePerformBlockOnEveryYogaChild(ASDisplayNode * _Nullable
- (void)addYogaChild:(ASDisplayNode *)child;
- (void)removeYogaChild:(ASDisplayNode *)child;
- (void)insertYogaChild:(ASDisplayNode *)child atIndex:(NSUInteger)index;
- (void)semanticContentAttributeDidChange:(UISemanticContentAttribute)attribute;
@property (nonatomic, assign) BOOL yogaLayoutInProgress;
@property (nonatomic, strong, nullable) ASLayout *yogaCalculatedLayout;
// These methods should not normally be called directly.
// These methods are intended to be used internally to Texture, and should not be called directly.
- (BOOL)shouldHaveYogaMeasureFunc;
- (void)invalidateCalculatedYogaLayout;
- (void)calculateLayoutFromYogaRoot:(ASSizeRange)rootConstrainedSize;
-142
View File
@@ -1,142 +0,0 @@
//
// ASDisplayNode+Deprecated.h
// Texture
//
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional
// grant of patent rights can be found in the PATENTS file in the same directory.
//
// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present,
// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
#pragma once
#import <AsyncDisplayKit/ASDisplayNode.h>
@interface ASDisplayNode (Deprecated)
/**
* @abstract The name of this node, which will be displayed in `description`. The default value is nil.
*
* @deprecated Deprecated in version 2.0: Use .debugName instead. This value will display in
* results of the -asciiArtString method (@see ASLayoutElementAsciiArtProtocol).
*/
@property (nullable, nonatomic, copy) NSString *name ASDISPLAYNODE_DEPRECATED_MSG("Use .debugName instead.");
/**
* @abstract Provides a default intrinsic content size for calculateSizeThatFits:. This is useful when laying out
* a node that either has no intrinsic content size or should be laid out at a different size than its intrinsic content
* size. For example, this property could be set on an ASImageNode to display at a size different from the underlying
* image size.
*
* @return Try to create a CGSize for preferredFrameSize of this node from the width and height property of this node. It will return CGSizeZero if width and height dimensions are not of type ASDimensionUnitPoints.
*
* @deprecated Deprecated in version 2.0: Just calls through to set the height and width property of the node. Convert to use sizing properties instead: height, minHeight, maxHeight, width, minWidth, maxWidth.
*/
@property (nonatomic, assign, readwrite) CGSize preferredFrameSize ASDISPLAYNODE_DEPRECATED_MSG("Use .style.preferredSize instead OR set individual values with .style.height and .style.width.");
/**
* @abstract Asks the node to measure and return the size that best fits its subnodes.
*
* @param constrainedSize The maximum size the receiver should fit in.
*
* @return A new size that fits the receiver's subviews.
*
* @discussion Though this method does not set the bounds of the view, it does have side effects--caching both the
* constraint and the result.
*
* @warning Subclasses must not override this; it calls -measureWithSizeRange: with zero min size.
* -measureWithSizeRange: caches results from -calculateLayoutThatFits:. Calling this method may
* be expensive if result is not cached.
*
* @see measureWithSizeRange:
* @see [ASDisplayNode(Subclassing) calculateLayoutThatFits:]
*
* @deprecated Deprecated in version 2.0: Use layoutThatFits: with a constrained size of (CGSizeZero, constrainedSize) and call size on the returned ASLayout
*/
- (CGSize)measure:(CGSize)constrainedSize ASDISPLAYNODE_DEPRECATED_MSG("Use layoutThatFits: with a constrained size of (CGSizeZero, constrainedSize) and call size on the returned ASLayout.");
ASLayoutElementStyleForwardingDeclaration
/**
* @abstract Called whenever the visiblity of the node changed.
*
* @discussion Subclasses may use this to monitor when they become visible.
*
* @deprecated @see didEnterVisibleState @see didExitVisibleState
*/
- (void)visibilityDidChange:(BOOL)isVisible ASDISPLAYNODE_REQUIRES_SUPER ASDISPLAYNODE_DEPRECATED_MSG("Use -didEnterVisibleState / -didExitVisibleState instead.");
/**
* @abstract Called whenever the visiblity of the node changed.
*
* @discussion Subclasses may use this to monitor when they become visible.
*
* @deprecated @see didEnterVisibleState @see didExitVisibleState
*/
- (void)visibleStateDidChange:(BOOL)isVisible ASDISPLAYNODE_REQUIRES_SUPER ASDISPLAYNODE_DEPRECATED_MSG("Use -didEnterVisibleState / -didExitVisibleState instead.");
/**
* @abstract Called whenever the the node has entered or exited the display state.
*
* @discussion Subclasses may use this to monitor when a node should be rendering its content.
*
* @note This method can be called from any thread and should therefore be thread safe.
*
* @deprecated @see didEnterDisplayState @see didExitDisplayState
*/
- (void)displayStateDidChange:(BOOL)inDisplayState ASDISPLAYNODE_REQUIRES_SUPER ASDISPLAYNODE_DEPRECATED_MSG("Use -didEnterDisplayState / -didExitDisplayState instead.");
/**
* @abstract Called whenever the the node has entered or left the load state.
*
* @discussion Subclasses may use this to monitor data for a node should be loaded, either from a local or remote source.
*
* @note This method can be called from any thread and should therefore be thread safe.
*
* @deprecated @see didEnterPreloadState @see didExitPreloadState
*/
- (void)loadStateDidChange:(BOOL)inLoadState ASDISPLAYNODE_REQUIRES_SUPER ASDISPLAYNODE_DEPRECATED_MSG("Use -didEnterPreloadState / -didExitPreloadState instead.");
/**
* @abstract Cancels all performing layout transitions. Can be called on any thread.
*
* @deprecated Deprecated in version 2.0: Use cancelLayoutTransition
*/
- (void)cancelLayoutTransitionsInProgress ASDISPLAYNODE_DEPRECATED_MSG("Use -cancelLayoutTransition instead.");
/**
* @abstract A boolean that shows whether the node automatically inserts and removes nodes based on the presence or
* absence of the node and its subnodes is completely determined in its layoutSpecThatFits: method.
*
* @discussion If flag is YES the node no longer require addSubnode: or removeFromSupernode method calls. The presence
* or absence of subnodes is completely determined in its layoutSpecThatFits: method.
*
* @deprecated Deprecated in version 2.0: Use automaticallyManagesSubnodes
*/
@property (nonatomic, assign) BOOL usesImplicitHierarchyManagement ASDISPLAYNODE_DEPRECATED_MSG("Set .automaticallyManagesSubnodes instead.");
/**
* @abstract Indicates that the node should fetch any external data, such as images.
*
* @discussion Subclasses may override this method to be notified when they should begin to preload. Fetching
* should be done asynchronously. The node is also responsible for managing the memory of any data.
* The data may be remote and accessed via the network, but could also be a local database query.
*/
- (void)fetchData ASDISPLAYNODE_REQUIRES_SUPER ASDISPLAYNODE_DEPRECATED_MSG("Use -didEnterPreloadState instead.");
/**
* Provides an opportunity to clear any fetched data (e.g. remote / network or database-queried) on the current node.
*
* @discussion This will not clear data recursively for all subnodes. Either call -recursivelyClearPreloadedData or
* selectively clear fetched data.
*/
- (void)clearFetchedData ASDISPLAYNODE_REQUIRES_SUPER ASDISPLAYNODE_DEPRECATED_MSG("Use -didExitPreloadState instead.");
@end
+1 -12
View File
@@ -57,15 +57,6 @@
#pragma mark Measurement Pass
- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
// For now we just call the deprecated measureWithSizeRange: method to not break old API
return [self measureWithSizeRange:constrainedSize];
#pragma clang diagnostic pop
}
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
{
return [self layoutThatFits:constrainedSize parentSize:constrainedSize.max];
}
@@ -127,8 +118,6 @@ ASLayoutElementStyleExtensibilityForwarding
return [ASTraitCollection traitCollectionWithASPrimitiveTraitCollection:self.primitiveTraitCollection];
}
ASPrimitiveTraitCollectionDeprecatedImplementation
#pragma mark - ASLayoutElementAsciiArtProtocol
- (NSString *)asciiArtString
@@ -938,7 +927,7 @@ ASPrimitiveTraitCollectionDeprecatedImplementation
// Grab lock after calling out to subclass
ASDN::MutexLocker l(__instanceLock__);
// We generate placeholders at measureWithSizeRange: time so that a node is guaranteed to have a placeholder ready to go.
// We generate placeholders at -layoutThatFits: time so that a node is guaranteed to have a placeholder ready to go.
// This is also because measurement is usually asynchronous, but placeholders need to be set up synchronously.
// First measurement is guaranteed to be before the node is onscreen, so we can create the image async. but still have it appear sync.
if (_placeholderEnabled && !_placeholderImage && [self _locked_displaysAsynchronously]) {
+16 -5
View File
@@ -116,12 +116,12 @@ NS_ASSUME_NONNULL_BEGIN
* @discussion For node subclasses that implement manual layout (e.g., they have a custom -layout method),
* calculatedLayout may be accessed on subnodes to retrieved cached information about their size.
* This allows -layout to be very fast, saving time on the main thread.
* Note: .calculatedLayout will only be set for nodes that have had -measure: called on them.
* For manual layout, make sure you call -measure: in your implementation of -calculateSizeThatFits:.
* Note: .calculatedLayout will only be set for nodes that have had -layoutThatFits: called on them.
* For manual layout, make sure you call -layoutThatFits: in your implementation of -calculateSizeThatFits:.
*
* For node subclasses that use automatic layout (e.g., they implement -layoutSpecThatFits:),
* it is typically not necessary to use .calculatedLayout at any point. For these nodes,
* the ASLayoutSpec implementation will automatically call -measureWithSizeRange: on all of the subnodes,
* the ASLayoutSpec implementation will automatically call -layoutThatFits: on all of the subnodes,
* and the ASDisplayNode base class implementation of -layout will automatically make use of .calculatedLayout on the subnodes.
*
* @return Layout that wraps calculated size returned by -calculateSizeThatFits: (in manual layout mode),
@@ -183,7 +183,7 @@ NS_ASSUME_NONNULL_BEGIN
* or -calculateSizeThatFits:, whichever method is overriden. Subclasses rarely need to override this method,
* override -layoutSpecThatFits: or -calculateSizeThatFits: instead.
*
* @note This method should not be called directly outside of ASDisplayNode; use -measure: or -calculatedLayout instead.
* @note This method should not be called directly outside of ASDisplayNode; use -layoutThatFits: or -calculatedLayout instead.
*/
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize;
@@ -315,6 +315,18 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (nullable id<NSObject>)drawParametersForAsyncLayer:(_ASDisplayLayer *)layer;
/**
* @abstract Indicates that the receiver is about to display.
*
* @discussion Deprecated in 2.5.
*
* @discussion Subclasses may override this method to be notified when display (asynchronous or synchronous) is
* about to begin.
*
* @note Called on the main thread only
*/
- (void)displayWillStart ASDISPLAYNODE_REQUIRES_SUPER ASDISPLAYNODE_DEPRECATED_MSG("Use displayWillStartAsynchronously: instead.");
/**
* @abstract Indicates that the receiver is about to display.
*
@@ -323,7 +335,6 @@ NS_ASSUME_NONNULL_BEGIN
*
* @note Called on the main thread only
*/
- (void)displayWillStart ASDISPLAYNODE_REQUIRES_SUPER;
- (void)displayWillStartAsynchronously:(BOOL)asynchronously ASDISPLAYNODE_REQUIRES_SUPER;
/**
+75 -20
View File
@@ -19,6 +19,7 @@
#if YOGA /* YOGA */
#import <AsyncDisplayKit/_ASDisplayViewAccessiblity.h>
#import <AsyncDisplayKit/ASYogaLayoutSpec.h>
#import <AsyncDisplayKit/ASYogaUtilities.h>
#import <AsyncDisplayKit/ASDisplayNode+Beta.h>
@@ -58,20 +59,7 @@
- (void)addYogaChild:(ASDisplayNode *)child
{
if (child == nil) {
return;
}
if (_yogaChildren == nil) {
_yogaChildren = [NSMutableArray array];
}
// Clean up state in case this child had another parent.
[self removeYogaChild:child];
[_yogaChildren addObject:child];
// YGNodeRef insertion is done in setParent:
child.yogaParent = self;
[self insertYogaChild:child atIndex:_yogaChildren.count];
}
- (void)removeYogaChild:(ASDisplayNode *)child
@@ -86,6 +74,24 @@
child.yogaParent = nil;
}
- (void)insertYogaChild:(ASDisplayNode *)child atIndex:(NSUInteger)index
{
if (child == nil) {
return;
}
if (_yogaChildren == nil) {
_yogaChildren = [NSMutableArray array];
}
// Clean up state in case this child had another parent.
[self removeYogaChild:child];
[_yogaChildren insertObject:child atIndex:index];
// YGNodeRef insertion is done in setParent:
child.yogaParent = self;
}
- (void)semanticContentAttributeDidChange:(UISemanticContentAttribute)attribute
{
if (AS_AT_LEAST_IOS9) {
@@ -167,28 +173,72 @@
CGSize size = CGSizeMake(YGNodeLayoutGetWidth(yogaNode), YGNodeLayoutGetHeight(yogaNode));
ASLayout *layout = [ASLayout layoutWithLayoutElement:self size:size sublayouts:sublayouts];
self.yogaCalculatedLayout = layout;
#if ASDISPLAYNODE_ASSERTIONS_ENABLED
// Assert that the sublayout is already flattened.
for (ASLayout *sublayout in layout.sublayouts) {
if (sublayout.sublayouts.count > 0 || ASDynamicCast(sublayout.layoutElement, ASDisplayNode) == nil) {
ASDisplayNodeAssert(NO, @"Yoga sublayout is not flattened! %@, %@", self, sublayout);
}
}
#endif
// Because this layout won't go through the rest of the logic in calculateLayoutThatFits:, flatten it now.
layout = [layout filteredNodeLayoutTree];
if ([self.yogaCalculatedLayout isEqual:layout] == NO) {
self.yogaCalculatedLayout = layout;
} else {
layout = self.yogaCalculatedLayout;
ASYogaLog("-setupYogaCalculatedLayout: applying identical ASLayout: %@", layout);
}
// Setup _pendingDisplayNodeLayout to reference the Yoga-calculated ASLayout, *unless* we are a leaf node.
// Leaf yoga nodes may have their own .sublayouts, if they use a layout spec (such as ASButtonNode).
// Their _pending variable is set after passing the Yoga checks at the start of -calculateLayoutThatFits:
// For other Yoga nodes, there is no code that will set _pending unless we do it here. Why does it need to be set?
// When CALayer triggers the -[ASDisplayNode __layout] call, we will check if our current _pending layout
// has a size which matches our current bounds size. If it does, that layout will be used without recomputing it.
// NOTE: Yoga does not make the constrainedSize available to intermediate nodes in the tree (e.g. not root or leaves).
// Although the size range provided here is not accurate, this will only affect caching of calls to layoutThatFits:
// These calls will behave as if they are not cached, starting a new Yoga layout pass, but this will tap into Yoga's
// own internal cache.
if ([self shouldHaveYogaMeasureFunc] == NO) {
YGNodeRef parentNode = YGNodeGetParent(yogaNode);
CGSize parentSize = CGSizeZero;
if (parentNode) {
parentSize.width = YGNodeLayoutGetWidth(parentNode);
parentSize.height = YGNodeLayoutGetHeight(parentNode);
}
_pendingDisplayNodeLayout = std::make_shared<ASDisplayNodeLayout>(layout, ASSizeRangeUnconstrained, parentSize, 0);
}
}
- (void)updateYogaMeasureFuncIfNeeded
- (BOOL)shouldHaveYogaMeasureFunc
{
// Size calculation via calculateSizeThatFits: or layoutSpecThatFits:
// This will be used for ASTextNode, as well as any other node that has no Yoga children
BOOL isLeafNode = (self.yogaChildren.count == 0);
BOOL definesCustomLayout = [self implementsLayoutMethod];
return (isLeafNode && definesCustomLayout);
}
- (void)updateYogaMeasureFuncIfNeeded
{
// We set the measure func only during layout. Otherwise, a cycle is created:
// The YGNodeRef Context will retain the ASDisplayNode, which retains the style, which owns the YGNodeRef.
BOOL shouldHaveMeasureFunc = (isLeafNode && definesCustomLayout && checkFlag(YogaLayoutInProgress));
BOOL shouldHaveMeasureFunc = ([self shouldHaveYogaMeasureFunc] && checkFlag(YogaLayoutInProgress));
ASLayoutElementYogaUpdateMeasureFunc(self.style.yogaNode, shouldHaveMeasureFunc ? self : nil);
}
- (void)invalidateCalculatedYogaLayout
{
// Yoga internally asserts that this method may only be called on nodes with a measurement function.
YGNodeRef yogaNode = self.style.yogaNode;
if (yogaNode && YGNodeGetMeasureFunc(yogaNode)) {
// Yoga internally asserts that MarkDirty() may only be called on nodes with a measurement function.
YGNodeMarkDirty(yogaNode);
}
self.yogaCalculatedLayout = nil;
@@ -199,7 +249,7 @@
ASDisplayNode *yogaParent = self.yogaParent;
if (yogaParent) {
ASYogaLog(@"ESCALATING to Yoga root: %@", self);
ASYogaLog("ESCALATING to Yoga root: %@", self);
// TODO(appleguy): Consider how to get the constrainedSize for the yogaRoot when escalating manually.
[yogaParent calculateLayoutFromYogaRoot:ASSizeRangeUnconstrained];
return;
@@ -216,7 +266,7 @@
rootConstrainedSize = [self _locked_constrainedSizeForLayoutPass];
}
ASYogaLog(@"CALCULATING at Yoga root with constraint = {%@, %@}: %@",
ASYogaLog("CALCULATING at Yoga root with constraint = {%@, %@}: %@",
NSStringFromCGSize(rootConstrainedSize.min), NSStringFromCGSize(rootConstrainedSize.max), self);
YGNodeRef rootYogaNode = self.style.yogaNode;
@@ -235,6 +285,11 @@
yogaFloatForCGFloat(rootConstrainedSize.max.height),
YGDirectionInherit);
// Reset accessible elements, since layout may have changed.
ASPerformBlockOnMainThread(^{
[(_ASDisplayView *)self.view setAccessibleElements:nil];
});
ASDisplayNodePerformBlockOnEveryYogaChild(self, ^(ASDisplayNode * _Nonnull node) {
[node setupYogaCalculatedLayout];
node.yogaLayoutInProgress = NO;
+1 -1
View File
@@ -814,7 +814,7 @@ extern NSInteger const ASDefaultDrawingPriority;
* @abstract Return the calculated size.
*
* @discussion Ideal for use by subclasses in -layout, having already prompted their subnodes to calculate their size by
* calling -measure: on them in -calculateLayoutThatFits.
* calling -layoutThatFits: on them in -calculateLayoutThatFits.
*
* @return Size already calculated by -calculateLayoutThatFits:.
*
+35 -144
View File
@@ -20,7 +20,6 @@
#import <AsyncDisplayKit/ASDisplayNode+Ancestry.h>
#import <AsyncDisplayKit/ASDisplayNode+FrameworkSubclasses.h>
#import <AsyncDisplayKit/ASDisplayNode+Beta.h>
#import <AsyncDisplayKit/ASDisplayNode+Deprecated.h>
#import <AsyncDisplayKit/AsyncDisplayKit+Debug.h>
#import <AsyncDisplayKit/ASLayoutSpec+Subclasses.h>
#import <AsyncDisplayKit/ASCellNode+Internal.h>
@@ -77,19 +76,8 @@ NSInteger const ASDefaultDrawingPriority = ASDefaultTransactionPriority;
@synthesize threadSafeBounds = _threadSafeBounds;
static BOOL suppressesInvalidCollectionUpdateExceptions = NO;
static std::atomic_bool storesUnflattenedLayouts = ATOMIC_VAR_INIT(NO);
+ (BOOL)suppressesInvalidCollectionUpdateExceptions
{
return suppressesInvalidCollectionUpdateExceptions;
}
+ (void)setSuppressesInvalidCollectionUpdateExceptions:(BOOL)suppresses
{
suppressesInvalidCollectionUpdateExceptions = suppresses;
}
BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector)
{
return ASSubclassOverridesSelector([ASDisplayNode class], subclass, selector);
@@ -182,12 +170,6 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
if (ASDisplayNodeSubclassOverridesSelector(c, @selector(calculateSizeThatFits:))) {
overrides |= ASDisplayNodeMethodOverrideCalcSizeThatFits;
}
if (ASDisplayNodeSubclassOverridesSelector(c, @selector(fetchData))) {
overrides |= ASDisplayNodeMethodOverrideFetchData;
}
if (ASDisplayNodeSubclassOverridesSelector(c, @selector(clearFetchedData))) {
overrides |= ASDisplayNodeMethodOverrideClearFetchedData;
}
return overrides;
}
@@ -202,8 +184,6 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(calculatedSize)), @"Subclass %@ must not override calculatedSize method.", classString);
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(calculatedLayout)), @"Subclass %@ must not override calculatedLayout method.", classString);
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(measure:)), @"Subclass %@ must not override measure: method", classString);
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(measureWithSizeRange:)), @"Subclass %@ must not override measureWithSizeRange: method. Instead override calculateLayoutThatFits:", classString);
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(layoutThatFits:)), @"Subclass %@ must not override layoutThatFits: method. Instead override calculateLayoutThatFits:.", classString);
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(layoutThatFits:parentSize:)), @"Subclass %@ must not override layoutThatFits:parentSize method. Instead override calculateLayoutThatFits:.", classString);
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(recursivelyClearContents)), @"Subclass %@ must not override recursivelyClearContents method.", classString);
@@ -906,7 +886,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
if (CGRectEqualToRect(bounds, CGRectZero)) {
// Performing layout on a zero-bounds view often results in frame calculations
// with negative sizes after applying margins, which will cause
// measureWithSizeRange: on subnodes to assert.
// layoutThatFits: on subnodes to assert.
as_log_debug(OS_LOG_DISABLED, "Warning: No size given for node before node was trying to layout itself: %@. Please provide a frame for the node.", self);
return;
}
@@ -988,23 +968,30 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
// - This node is a Yoga tree root: it has no yogaParent, but has yogaChildren.
// - This node is a Yoga tree node: it has both a yogaParent and yogaChildren.
// - This node is a Yoga tree leaf: it has a yogaParent, but no yogaChidlren.
// If we're a leaf node, we are probably being called by a measure function and proceed as normal.
// If we're a yoga root or tree node, initiate a new Yoga calculation pass from root.
YGNodeRef yogaNode = _style.yogaNode;
BOOL hasYogaParent = (_yogaParent != nil);
BOOL hasYogaChildren = (_yogaChildren.count > 0);
BOOL usesYoga = (yogaNode != NULL && (hasYogaParent || hasYogaChildren));
if (usesYoga && (_yogaParent == nil || _yogaChildren.count > 0)) {
if (usesYoga) {
// This node has some connection to a Yoga tree.
ASDN::MutexUnlocker ul(__instanceLock__);
if (self.yogaLayoutInProgress == NO) {
[self calculateLayoutFromYogaRoot:constrainedSize];
if ([self shouldHaveYogaMeasureFunc] == NO) {
// If we're a yoga root, tree node, or leaf with no measure func (e.g. spacer), then
// initiate a new Yoga calculation pass from root.
ASDN::MutexUnlocker ul(__instanceLock__);
as_activity_create_for_scope("Yoga layout calculation");
if (self.yogaLayoutInProgress == NO) {
ASYogaLog("Calculating yoga layout from root %@, %@", self, NSStringFromASSizeRange(constrainedSize));
[self calculateLayoutFromYogaRoot:constrainedSize];
} else {
ASYogaLog("Reusing existing yoga layout %@", _yogaCalculatedLayout);
}
ASDisplayNodeAssert(_yogaCalculatedLayout, @"Yoga node should have a non-nil layout at this stage: %@", self);
return _yogaCalculatedLayout;
} else {
// If we're a yoga leaf node with custom measurement function, proceed with normal layout so layoutSpecs can run (e.g. ASButtonNode).
ASYogaLog("PROCEEDING past Yoga check to calculate ASLayout for: %@", self);
}
ASDisplayNodeAssert(_yogaCalculatedLayout, @"Yoga node should have a non-nil layout at this stage: %@", self);
return _yogaCalculatedLayout;
}
ASYogaLog(@"PROCEEDING past Yoga check to calculate ASLayout for: %@", self);
#endif /* YOGA */
// Manual size calculation via calculateSizeThatFits:
@@ -1695,7 +1682,11 @@ static void _recursivelySetDisplaySuspended(ASDisplayNode *node, CALayer *layer,
- (void)willDisplayAsyncLayer:(_ASDisplayLayer *)layer asynchronously:(BOOL)asynchronously
{
// Subclass hook.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[self displayWillStart];
#pragma clang diagnostic pop
[self displayWillStartAsynchronously:asynchronously];
}
@@ -1708,7 +1699,6 @@ static void _recursivelySetDisplaySuspended(ASDisplayNode *node, CALayer *layer,
- (void)displayWillStart {}
- (void)displayWillStartAsynchronously:(BOOL)asynchronously
{
[self displayWillStart]; // Subclass override
ASDisplayNodeAssertMainThread();
ASDisplayNodeLogEvent(self, @"displayWillStart");
@@ -3077,13 +3067,6 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) {
ASDisplayNodeAssertMainThread();
ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__);
[_interfaceStateDelegate didEnterPreloadState];
if (_methodOverrides & ASDisplayNodeMethodOverrideFetchData) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[self fetchData];
#pragma clang diagnostic pop
}
}
- (void)didExitPreloadState
@@ -3091,13 +3074,6 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) {
ASDisplayNodeAssertMainThread();
ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__);
[_interfaceStateDelegate didExitPreloadState];
if (_methodOverrides & ASDisplayNodeMethodOverrideClearFetchedData) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[self clearFetchedData];
#pragma clang diagnostic pop
}
}
- (void)clearContents
@@ -3313,6 +3289,19 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) {
return measurements;
}
#pragma mark - Accessibility
- (void)setIsAccessibilityContainer:(BOOL)isAccessibilityContainer
{
ASDN::MutexLocker l(__instanceLock__);
_isAccessibilityContainer = isAccessibilityContainer;
}
- (BOOL)isAccessibilityContainer
{
ASDN::MutexLocker l(__instanceLock__);
return _isAccessibilityContainer;
}
#pragma mark - Debugging (Private)
@@ -3633,101 +3622,3 @@ static const char *ASDisplayNodeAssociatedNodeKey = "ASAssociatedNode";
}
@end
#pragma mark - ASDisplayNode (Deprecated)
@implementation ASDisplayNode (Deprecated)
- (NSString *)name
{
return self.debugName;
}
- (void)setName:(NSString *)name
{
self.debugName = name;
}
- (void)setPreferredFrameSize:(CGSize)preferredFrameSize
{
// Deprecated preferredFrameSize just calls through to set width and height
self.style.preferredSize = preferredFrameSize;
[self setNeedsLayout];
}
- (CGSize)preferredFrameSize
{
ASLayoutSize size = self.style.preferredLayoutSize;
BOOL isPoints = (size.width.unit == ASDimensionUnitPoints && size.height.unit == ASDimensionUnitPoints);
return isPoints ? CGSizeMake(size.width.value, size.height.value) : CGSizeZero;
}
- (BOOL)usesImplicitHierarchyManagement
{
return self.automaticallyManagesSubnodes;
}
- (void)setUsesImplicitHierarchyManagement:(BOOL)enabled
{
self.automaticallyManagesSubnodes = enabled;
}
- (CGSize)measure:(CGSize)constrainedSize
{
return [self layoutThatFits:ASSizeRangeMake(CGSizeZero, constrainedSize)].size;
}
ASLayoutElementStyleForwarding
- (void)visibilityDidChange:(BOOL)isVisible
{
if (isVisible) {
[self didEnterVisibleState];
} else {
[self didExitVisibleState];
}
}
- (void)visibleStateDidChange:(BOOL)isVisible
{
if (isVisible) {
[self didEnterVisibleState];
} else {
[self didExitVisibleState];
}
}
- (void)displayStateDidChange:(BOOL)inDisplayState
{
if (inDisplayState) {
[self didEnterVisibleState];
} else {
[self didExitVisibleState];
}
}
- (void)loadStateDidChange:(BOOL)inLoadState
{
if (inLoadState) {
[self didEnterPreloadState];
} else {
[self didExitPreloadState];
}
}
- (void)fetchData
{
// subclass override
}
- (void)clearFetchedData
{
// subclass override
}
- (void)cancelLayoutTransitionsInProgress
{
[self cancelLayoutTransition];
}
@end
+7
View File
@@ -158,6 +158,13 @@ NS_ASSUME_NONNULL_BEGIN
@protocol ASEditableTextNodeDelegate <NSObject>
@optional
/**
@abstract Asks the delegate if editing should begin for the text node.
@param editableTextNode An editable text node.
@discussion YES if editing should begin; NO if editing should not begin -- the default returns YES.
*/
- (BOOL)editableTextNodeShouldBeginEditing:(ASEditableTextNode *)editableTextNode;
/**
@abstract Indicates to the delegate that the text node began editing.
@param editableTextNode An editable text node.
+14
View File
@@ -699,6 +699,12 @@
}
#pragma mark - UITextView Delegate
- (BOOL)textViewShouldBeginEditing:(UITextView *)textView
{
// Delegateify.
return [self _delegateShouldBeginEditing];
}
- (void)textViewDidBeginEditing:(UITextView *)textView
{
// Delegateify.
@@ -793,6 +799,14 @@
}
#pragma mark -
- (BOOL)_delegateShouldBeginEditing
{
if ([_delegate respondsToSelector:@selector(editableTextNodeShouldBeginEditing:)]) {
return [_delegate editableTextNodeShouldBeginEditing:self];
}
return YES;
}
- (void)_delegateDidBeginEditing
{
if ([_delegate respondsToSelector:@selector(editableTextNodeDidBeginEditing:)])
+1 -1
View File
@@ -70,7 +70,7 @@ NSString *const ASAnimatedImageDefaultRunLoopMode = NSRunLoopCommonModes;
} else {
animatedImage.playbackReadyCallback = ^{
// In this case the lock is already gone we have to call the unlocked version therefore
[self setShouldAnimate:YES];
[weakSelf setShouldAnimate:YES];
};
}
}
+16 -4
View File
@@ -41,6 +41,8 @@
#include <functional>
static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
typedef void (^ASImageNodeDrawParametersBlock)(ASWeakMapEntry *entry);
@interface ASImageNodeDrawParameters : NSObject {
@@ -175,7 +177,8 @@ typedef void (^ASImageNodeDrawParametersBlock)(ASWeakMapEntry *entry);
self.contentsScale = ASScreenScale();
self.contentMode = UIViewContentModeScaleAspectFill;
self.opaque = NO;
self.clipsToBounds = YES;
// If no backgroundColor is set to the image node and it's a subview of UITableViewCell, UITableView is setting
// the opaque value of all subviews to YES if highlighting / selection is happening and does not set it back to the
// initial value. With setting a explicit backgroundColor we can prevent that change.
@@ -247,11 +250,11 @@ typedef void (^ASImageNodeDrawParametersBlock)(ASWeakMapEntry *entry);
if (ASObjectIsEqual(_image, image)) {
return;
}
UIImage *oldImage = _image;
_image = image;
if (image != nil) {
// We explicitly call setNeedsDisplay in this case, although we know setNeedsDisplay will be called with lock held.
// Therefore we have to be careful in methods that are involved with setNeedsDisplay to not run into a deadlock
[self setNeedsDisplay];
@@ -264,10 +267,19 @@ typedef void (^ASImageNodeDrawParametersBlock)(ASWeakMapEntry *entry);
[self addSubnode:_debugLabelNode];
});
}
} else {
self.contents = nil;
}
// Destruction of bigger images on the main thread can be expensive
// and can take some time, so we dispatch onto a bg queue to
// actually dealloc.
CGSize oldImageSize = oldImage.size;
BOOL shouldReleaseImageOnBackgroundThread = oldImageSize.width > kMinReleaseImageOnBackgroundSize.width
|| oldImageSize.height > kMinReleaseImageOnBackgroundSize.height;
if (shouldReleaseImageOnBackgroundThread) {
ASPerformBackgroundDeallocation(oldImage);
}
}
- (UIImage *)image
+3 -3
View File
@@ -261,11 +261,11 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
return (self.image == nil && self.animatedImage == nil && self.imageIdentifiers.count > 0);
}
/* displayWillStart in ASNetworkImageNode has a very similar implementation. Changes here are likely necessary
/* displayWillStartAsynchronously in ASNetworkImageNode has a very similar implementation. Changes here are likely necessary
in ASNetworkImageNode as well. */
- (void)displayWillStart
- (void)displayWillStartAsynchronously:(BOOL)asynchronously
{
[super displayWillStart];
[super displayWillStartAsynchronously:asynchronously];
[self didEnterPreloadState];
+9
View File
@@ -82,6 +82,15 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property (nullable, nonatomic, strong, readwrite) NSURL *URL;
/**
* An array of URLs of increasing cost to download.
*
* @discussion By setting an array of URLs, the image property of this node will be managed internally. This means previously
* directly set images to the image property will be cleared out and replaced by the placeholder (<defaultImage>) image
* while loading and the final image after the new image data was downloaded and processed.
*/
@property (nullable, nonatomic, strong, readwrite) NSArray <NSURL *> *URLs;
/**
* Download and display a new image.
*
+120 -80
View File
@@ -32,14 +32,12 @@
#import <AsyncDisplayKit/ASPINRemoteImageDownloader.h>
#endif
static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
@interface ASNetworkImageNode ()
{
// Only access any of these with __instanceLock__.
__weak id<ASNetworkImageNodeDelegate> _delegate;
NSURL *_URL;
NSArray *_URLs;
UIImage *_defaultImage;
NSUUID *_cacheUUID;
@@ -68,6 +66,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
unsigned int downloaderImplementsSetPriority:1;
unsigned int downloaderImplementsAnimatedImage:1;
unsigned int downloaderImplementsCancelWithResume:1;
unsigned int downloaderImplementsDownloadURLs:1;
} _downloaderFlags;
// Immutable and set on init only. We don't need to lock in this case.
@@ -75,6 +74,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
struct {
unsigned int cacheSupportsClearing:1;
unsigned int cacheSupportsSynchronousFetch:1;
unsigned int cacheSupportsCachedURLs:1;
} _cacheFlags;
}
@@ -96,9 +96,11 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
_downloaderFlags.downloaderImplementsSetPriority = [downloader respondsToSelector:@selector(setPriority:withDownloadIdentifier:)];
_downloaderFlags.downloaderImplementsAnimatedImage = [downloader respondsToSelector:@selector(animatedImageWithData:)];
_downloaderFlags.downloaderImplementsCancelWithResume = [downloader respondsToSelector:@selector(cancelImageDownloadWithResumePossibilityForIdentifier:)];
_downloaderFlags.downloaderImplementsDownloadURLs = [downloader respondsToSelector:@selector(downloadImageWithURLs:callbackQueue:downloadProgress:completion:)];
_cacheFlags.cacheSupportsClearing = [cache respondsToSelector:@selector(clearFetchedImageFromCacheWithURL:)];
_cacheFlags.cacheSupportsSynchronousFetch = [cache respondsToSelector:@selector(synchronouslyFetchedCachedImageWithURL:)];
_cacheFlags.cacheSupportsCachedURLs = [cache respondsToSelector:@selector(cachedImageWithURLs:callbackQueue:completion:)];
_shouldCacheImage = YES;
_shouldRenderProgressImages = YES;
@@ -136,8 +138,8 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
BOOL shouldCancelAndClear = imageWasSetExternally && (imageWasSetExternally != _imageWasSetExternally);
_imageWasSetExternally = imageWasSetExternally;
if (shouldCancelAndClear) {
ASDisplayNodeAssertNil(_URL, @"Directly setting an image on an ASNetworkImageNode causes it to behave like an ASImageNode instead of an ASNetworkImageNode. If this is what you want, set the URL to nil first.");
_URL = nil;
ASDisplayNodeAssert(_URLs == nil || _URLs.count == 0, @"Directly setting an image on an ASNetworkImageNode causes it to behave like an ASImageNode instead of an ASNetworkImageNode. If this is what you want, set the URL to nil first.");
_URLs = nil;
[self _locked_cancelDownloadAndClearImageWithResumePossibility:NO];
}
@@ -158,15 +160,38 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
- (void)setURL:(NSURL *)URL
{
[self setURL:URL resetToDefault:YES];
if (URL) {
[self setURLs:@[URL]];
} else {
[self setURLs:nil];
}
}
- (void)setURL:(NSURL *)URL resetToDefault:(BOOL)reset
{
if (URL) {
[self setURLs:@[URL] resetToDefault:reset];
} else {
[self setURLs:nil resetToDefault:reset];
}
}
- (NSURL *)URL
{
return [self.URLs lastObject];
}
- (void)setURLs:(NSArray <NSURL *> *)URLs
{
[self setURLs:URLs resetToDefault:YES];
}
- (void)setURLs:(NSArray <NSURL *> *)URLs resetToDefault:(BOOL)reset
{
{
ASDN::MutexLocker l(__instanceLock__);
if (ASObjectIsEqual(URL, _URL)) {
if (ASObjectIsEqual(URLs, _URLs)) {
return;
}
@@ -175,25 +200,25 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
_imageWasSetExternally = NO;
[self _locked_cancelImageDownloadWithResumePossibility:NO];
_imageLoaded = NO;
_URL = URL;
_URLs = URLs;
BOOL hasURL = (_URL == nil);
BOOL hasURL = (_URLs.count == 0);
if (reset || hasURL) {
[self _locked_setCurrentImageQuality:(hasURL ? 0.0 : 1.0)];
[self _locked__setImage:_defaultImage];
}
}
[self setNeedsPreload];
}
- (NSURL *)URL
- (NSArray <NSURL *>*)URLs
{
ASDN::MutexLocker l(__instanceLock__);
return _URL;
return _URLs;
}
- (void)setDefaultImage:(UIImage *)defaultImage
@@ -212,7 +237,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
_defaultImage = defaultImage;
if (!_imageLoaded) {
[self _locked_setCurrentImageQuality:((_URL == nil) ? 0.0 : 1.0)];
[self _locked_setCurrentImageQuality:((_URLs.count == 0) ? 0.0 : 1.0)];
[self _locked__setImage:defaultImage];
}
@@ -310,10 +335,10 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
- (BOOL)placeholderShouldPersist
{
ASDN::MutexLocker l(__instanceLock__);
return (self.image == nil && self.animatedImage == nil && _URL != nil);
return (self.image == nil && self.animatedImage == nil && _URLs.count != 0);
}
/* displayWillStart in ASMultiplexImageNode has a very similar implementation. Changes here are likely necessary
/* displayWillStartAsynchronously: in ASMultiplexImageNode has a very similar implementation. Changes here are likely necessary
in ASMultiplexImageNode as well. */
- (void)displayWillStartAsynchronously:(BOOL)asynchronously
{
@@ -322,13 +347,16 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
if (asynchronously == NO && _cacheFlags.cacheSupportsSynchronousFetch) {
ASDN::MutexLocker l(__instanceLock__);
if (_imageLoaded == NO && _URL && _downloadIdentifier == nil) {
UIImage *result = [[_cache synchronouslyFetchedCachedImageWithURL:_URL] asdk_image];
if (result) {
[self _locked_setCurrentImageQuality:1.0];
[self _locked__setImage:result];
_imageLoaded = YES;
if (_imageLoaded == NO && _URLs.count > 0 && _downloadIdentifier == nil) {
for (NSURL *url in [_URLs reverseObjectEnumerator]) {
UIImage *result = [[_cache synchronouslyFetchedCachedImageWithURL:url] asdk_image];
if (result) {
[self _locked_setCurrentImageQuality:1.0];
[self _locked__setImage:result];
_imageLoaded = YES;
break;
}
}
}
}
@@ -491,28 +519,18 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
{
[self _locked_cancelImageDownloadWithResumePossibility:storeResume];
// Destruction of bigger images on the main thread can be expensive
// and can take some time, so we dispatch onto a bg queue to
// actually dealloc.
UIImage *image = [self _locked_Image];
UIImage *defaultImage = _defaultImage;
CGSize imageSize = image.size;
BOOL shouldReleaseImageOnBackgroundThread = imageSize.width > kMinReleaseImageOnBackgroundSize.width ||
imageSize.height > kMinReleaseImageOnBackgroundSize.height;
if (shouldReleaseImageOnBackgroundThread) {
ASPerformBackgroundDeallocation(image);
}
[self _locked_setAnimatedImage:nil];
[self _locked_setCurrentImageQuality:0.0];
[self _locked__setImage:defaultImage];
[self _locked__setImage:_defaultImage];
_imageLoaded = NO;
if (_cacheFlags.cacheSupportsClearing) {
if (_URL != nil) {
as_log_verbose(ASImageLoadingLog(), "Clearing cached image for %@ url: %@", self, _URL);
[_cache clearFetchedImageFromCacheWithURL:_URL];
if (_URLs.count != 0) {
as_log_verbose(ASImageLoadingLog(), "Clearing cached image for %@ url: %@", self, _URLs);
for (NSURL *url in _URLs) {
[_cache clearFetchedImageFromCacheWithURL:url];
}
}
}
}
@@ -546,7 +564,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
- (void)_downloadImageWithCompletion:(void (^)(id <ASImageContainerProtocol> imageContainer, NSError*, id downloadIdentifier))finished
{
ASPerformBlockOnBackgroundThread(^{
NSURL *url;
NSArray <NSURL *> *urls;
id downloadIdentifier;
BOOL cancelAndReattempt = NO;
@@ -555,23 +573,34 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
// it and try again.
{
ASDN::MutexLocker l(__instanceLock__);
url = _URL;
urls = _URLs;
}
downloadIdentifier = [_downloader downloadImageWithURL:url
callbackQueue:dispatch_get_main_queue()
downloadProgress:NULL
completion:^(id <ASImageContainerProtocol> _Nullable imageContainer, NSError * _Nullable error, id _Nullable downloadIdentifier) {
if (finished != NULL) {
finished(imageContainer, error, downloadIdentifier);
}
}];
if (_downloaderFlags.downloaderImplementsDownloadURLs) {
downloadIdentifier = [_downloader downloadImageWithURLs:urls
callbackQueue:dispatch_get_main_queue()
downloadProgress:NULL
completion:^(id <ASImageContainerProtocol> _Nullable imageContainer, NSError * _Nullable error, id _Nullable downloadIdentifier) {
if (finished != NULL) {
finished(imageContainer, error, downloadIdentifier);
}
}];
} else {
downloadIdentifier = [_downloader downloadImageWithURL:[urls lastObject]
callbackQueue:dispatch_get_main_queue()
downloadProgress:NULL
completion:^(id <ASImageContainerProtocol> _Nullable imageContainer, NSError * _Nullable error, id _Nullable downloadIdentifier) {
if (finished != NULL) {
finished(imageContainer, error, downloadIdentifier);
}
}];
}
as_log_verbose(ASImageLoadingLog(), "Downloading image for %@ url: %@", self, url);
{
ASDN::MutexLocker l(__instanceLock__);
if (ASObjectIsEqual(_URL, url)) {
if (ASObjectIsEqual(_URLs, urls)) {
// The download we kicked off is correct, no need to do any more work.
_downloadIdentifier = downloadIdentifier;
} else {
@@ -600,34 +629,36 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
__weak id<ASNetworkImageNodeDelegate> delegate = _delegate;
BOOL delegateDidStartFetchingData = _delegateFlags.delegateDidStartFetchingData;
BOOL isImageLoaded = _imageLoaded;
NSURL *URL = _URL;
NSArray <NSURL *>*URLs = _URLs;
id currentDownloadIdentifier = _downloadIdentifier;
__instanceLock__.unlock();
if (!isImageLoaded && URL != nil && currentDownloadIdentifier == nil) {
if (!isImageLoaded && URLs.count > 0 && currentDownloadIdentifier == nil) {
if (delegateDidStartFetchingData) {
[delegate imageNodeDidStartFetchingData:self];
}
if (URL.isFileURL) {
// We only support file URLs if there is one URL currently
if (URLs.count == 1 && [URLs lastObject].isFileURL) {
dispatch_async(dispatch_get_main_queue(), ^{
ASDN::MutexLocker l(__instanceLock__);
// Bail out if not the same URL anymore
if (!ASObjectIsEqual(URL, _URL)) {
if (!ASObjectIsEqual(URLs, _URLs)) {
return;
}
NSURL *URL = [URLs lastObject];
if (_shouldCacheImage) {
[self _locked__setImage:[UIImage imageNamed:_URL.path.lastPathComponent]];
[self _locked__setImage:[UIImage imageNamed:URL.path.lastPathComponent]];
} else {
// First try to load the path directly, for efficiency assuming a developer who
// doesn't want caching is trying to be as minimal as possible.
UIImage *nonAnimatedImage = [UIImage imageWithContentsOfFile:_URL.path];
UIImage *nonAnimatedImage = [UIImage imageWithContentsOfFile:URL.path];
if (nonAnimatedImage == nil) {
// If we couldn't find it, execute an -imageNamed:-like search so we can find resources even if the
// extension is not provided in the path. This allows the same path to work regardless of shouldCacheImage.
NSString *filename = [[NSBundle mainBundle] pathForResource:_URL.path.lastPathComponent ofType:nil];
NSString *filename = [[NSBundle mainBundle] pathForResource:URL.path.lastPathComponent ofType:nil];
if (filename != nil) {
nonAnimatedImage = [UIImage imageWithContentsOfFile:filename];
}
@@ -636,7 +667,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
// If the file may be an animated gif and then created an animated image.
id<ASAnimatedImageProtocol> animatedImage = nil;
if (_downloaderFlags.downloaderImplementsAnimatedImage) {
NSData *data = [NSData dataWithContentsOfURL:_URL];
NSData *data = [NSData dataWithContentsOfURL:URL];
if (data != nil) {
animatedImage = [_downloader animatedImageWithData:data];
@@ -671,7 +702,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
return;
}
as_log_verbose(ASImageLoadingLog(), "Downloaded image for %@ img: %@ url: %@", self, [imageContainer asdk_image], URL);
as_log_verbose(ASImageLoadingLog(), "Downloaded image for %@ img: %@ urls: %@", self, [imageContainer asdk_image], URLs);
// Grab the lock for the rest of the block
ASDN::MutexLocker l(strongSelf->__instanceLock__);
@@ -714,26 +745,35 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
_cacheUUID = cacheUUID;
__instanceLock__.unlock();
as_log_verbose(ASImageLoadingLog(), "Decaching image for %@ url: %@", self, URL);
[_cache cachedImageWithURL:URL
callbackQueue:dispatch_get_main_queue()
completion:^(id <ASImageContainerProtocol> imageContainer) {
// If the cache UUID changed, that means this request was cancelled.
__instanceLock__.lock();
NSUUID *currentCacheUUID = _cacheUUID;
__instanceLock__.unlock();
if (!ASObjectIsEqual(currentCacheUUID, cacheUUID)) {
return;
}
if ([imageContainer asdk_image] == nil && _downloader != nil) {
[self _downloadImageWithCompletion:finished];
} else {
as_log_verbose(ASImageLoadingLog(), "Decached image for %@ img: %@ url: %@", self, [imageContainer asdk_image], URL);
finished(imageContainer, nil, nil);
}
}];
as_log_verbose(ASImageLoadingLog(), "Decaching image for %@ urls: %@", self, URLs);
ASImageCacherCompletion completion = ^(id <ASImageContainerProtocol> imageContainer) {
// If the cache UUID changed, that means this request was cancelled.
__instanceLock__.lock();
NSUUID *currentCacheUUID = _cacheUUID;
__instanceLock__.unlock();
if (!ASObjectIsEqual(currentCacheUUID, cacheUUID)) {
return;
}
if ([imageContainer asdk_image] == nil && _downloader != nil) {
[self _downloadImageWithCompletion:finished];
} else {
as_log_verbose(ASImageLoadingLog(), "Decached image for %@ img: %@ urls: %@", self, [imageContainer asdk_image], URLs);
finished(imageContainer, nil, nil);
}
};
if (_cacheFlags.cacheSupportsCachedURLs) {
[_cache cachedImageWithURLs:URLs
callbackQueue:dispatch_get_main_queue()
completion:completion];
} else {
[_cache cachedImageWithURL:[URLs lastObject]
callbackQueue:dispatch_get_main_queue()
completion:completion];
}
} else {
[self _downloadImageWithCompletion:finished];
}
+5 -12
View File
@@ -61,20 +61,11 @@ NS_ASSUME_NONNULL_BEGIN
@end
@protocol ASPagerDelegate <ASCollectionDelegate>
@optional
/**
* Provides the constrained size range for measuring the node at the index.
*
* @param pagerNode The sender.
* @param index The index of the node.
* @return A constrained size range for layout the node at this index.
*/
- (ASSizeRange)pagerNode:(ASPagerNode *)pagerNode constrainedSizeForNodeAtIndex:(NSInteger)index ASDISPLAYNODE_DEPRECATED_MSG("Pages in a pager node should be the exact size of the collection node (default behavior).");
@end
/**
* A horizontal, paging collection node.
*/
@interface ASPagerNode : ASCollectionNode
/**
@@ -84,6 +75,8 @@ NS_ASSUME_NONNULL_BEGIN
/**
* Initializer with custom-configured flow layout properties.
*
* NOTE: The flow layout must have a horizontal scroll direction.
*/
- (instancetype)initWithCollectionViewLayout:(ASPagerFlowLayout *)flowLayout;
+13 -22
View File
@@ -39,9 +39,6 @@
} _pagerDataSourceFlags;
__weak id <ASPagerDelegate> _pagerDelegate;
struct {
unsigned constrainedSizeForNode:1;
} _pagerDelegateFlags;
ASPagerNodeProxy *_proxyDelegate;
}
@@ -66,6 +63,7 @@
- (instancetype)initWithCollectionViewLayout:(ASPagerFlowLayout *)flowLayout;
{
ASDisplayNodeAssert([flowLayout isKindOfClass:[ASPagerFlowLayout class]], @"ASPagerNode requires a flow layout.");
ASDisplayNodeAssertTrue(flowLayout.scrollDirection == UICollectionViewScrollDirectionHorizontal);
self = [super initWithCollectionViewLayout:flowLayout];
return self;
}
@@ -112,7 +110,15 @@
- (NSInteger)currentPageIndex
{
return (self.view.contentOffset.x / CGRectGetWidth(self.view.bounds));
return (self.view.contentOffset.x / [self pageSize].width);
}
- (CGSize)pageSize
{
UIEdgeInsets contentInset = self.contentInset;
CGSize pageSize = self.bounds.size;
pageSize.height -= (contentInset.top + contentInset.bottom);
return pageSize;
}
#pragma mark - Helpers
@@ -139,10 +145,10 @@
#pragma mark - ASCollectionGalleryLayoutPropertiesProviding
- (CGSize)sizeForElements:(ASElementMap *)elements
- (CGSize)galleryLayoutDelegate:(nonnull ASCollectionGalleryLayoutDelegate *)delegate sizeForElements:(nonnull ASElementMap *)elements
{
ASDisplayNodeAssertMainThread();
return self.bounds.size;
return [self pageSize];
}
#pragma mark - ASCollectionDataSource
@@ -172,14 +178,7 @@
- (ASSizeRange)collectionNode:(ASCollectionNode *)collectionNode constrainedSizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
if (_pagerDelegateFlags.constrainedSizeForNode) {
return [_pagerDelegate pagerNode:self constrainedSizeForNodeAtIndex:indexPath.item];
}
#pragma clang diagnostic pop
return ASSizeRangeMake(self.bounds.size);
return ASSizeRangeMake([self pageSize]);
}
#pragma mark - Data Source Proxy
@@ -211,15 +210,7 @@
{
if (delegate != _pagerDelegate) {
_pagerDelegate = delegate;
if (delegate == nil) {
memset(&_pagerDelegateFlags, 0, sizeof(_pagerDelegateFlags));
} else {
_pagerDelegateFlags.constrainedSizeForNode = [_pagerDelegate respondsToSelector:@selector(pagerNode:constrainedSizeForNodeAtIndex:)];
}
_proxyDelegate = delegate ? [[ASPagerNodeProxy alloc] initWithTarget:delegate interceptor:self] : nil;
super.delegate = (id <ASCollectionDelegate>)_proxyDelegate;
}
}
+2
View File
@@ -41,6 +41,8 @@ AS_SUBCLASSING_RESTRICTED
- (void)enqueue:(ObjectType)object;
@property (nonatomic, readonly) BOOL isEmpty;
@property (nonatomic, assign) NSUInteger batchSize; // Default == 1.
@property (nonatomic, assign) BOOL ensureExclusiveMembership; // Default == YES. Set-like behavior.
+17 -3
View File
@@ -348,12 +348,12 @@ typedef enum {
{
ASDN::MutexLocker l(_internalQueueLock);
// Early-exit if the queue is empty.
NSInteger internalQueueCount = _internalQueue.count;
// Early-exit if the queue is empty.
if (internalQueueCount == 0) {
return;
}
ASSignpostStart(ASSignpostRunLoopQueueBatch);
// Snatch the next batch of items.
@@ -382,6 +382,14 @@ typedef enum {
}
}
if (foundItemCount == 0) {
// If _internalQueue holds weak references, and all of them just become NULL, then the array
// is never marked as needsCompletion, and compact will return early, not removing the NULL's.
// Inserting a NULL here ensures the compaction will take place.
// See http://www.openradar.me/15396578 and https://stackoverflow.com/a/40274426/1136669
[_internalQueue addPointer:NULL];
}
[_internalQueue compact];
if (_internalQueue.count == 0) {
isQueueDrained = YES;
@@ -434,10 +442,16 @@ typedef enum {
if (!foundObject) {
[_internalQueue addPointer:(__bridge void *)object];
CFRunLoopSourceSignal(_runLoopSource);
CFRunLoopWakeUp(_runLoop);
}
}
- (BOOL)isEmpty
{
ASDN::MutexLocker l(_internalQueueLock);
return _internalQueue.count == 0;
}
@end
+42 -2
View File
@@ -55,6 +55,11 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property (nonatomic, assign) BOOL inverted;
/**
* The distance that the content view is inset from the table node edges. Defaults to UIEdgeInsetsZero.
*/
@property (nonatomic, assign) UIEdgeInsets contentInset;
/**
* The offset of the content view's origin from the table node's origin. Defaults to CGPointZero.
*/
@@ -207,9 +212,38 @@ NS_ASSUME_NONNULL_BEGIN
- (void)performBatchUpdates:(nullable AS_NOESCAPE void (^)())updates completion:(nullable void (^)(BOOL finished))completion;
/**
* Blocks execution of the main thread until all section and row updates are committed. This method must be called from the main thread.
* Returns YES if the ASCollectionNode is still processing changes from performBatchUpdates:.
* This is typically the concurrent allocation (calling nodeBlocks) and layout of newly inserted
* ASCellNodes. If YES is returned, then calling -waitUntilAllUpdatesAreProcessed may take tens of
* milliseconds to return as it blocks on these concurrent operations.
*
* Returns NO if ASCollectionNode is fully synchronized with the underlying UICollectionView. This
* means that until the next performBatchUpdates: is called, it is safe to compare UIKit values
* (such as from UICollectionViewLayout) with your app's data source.
*
* This method will always return NO if called immediately after -waitUntilAllUpdatesAreProcessed.
*/
- (void)waitUntilAllUpdatesAreCommitted;
@property (nonatomic, readonly) BOOL isProcessingUpdates;
/**
* Schedules a block to be performed (on the main thread) after processing of performBatchUpdates:
* is finished (completely synchronized to UIKit). The blocks will be run at the moment that
* -isProcessingUpdates changes from YES to NO;
*
* When isProcessingUpdates == NO, the block is run block immediately (before the method returns).
*
* Blocks scheduled by this mechanism are NOT guaranteed to run in the order they are scheduled.
* They may also be delayed if performBatchUpdates continues to be called; the blocks will wait until
* all running updates are finished.
*
* Calling -waitUntilAllUpdatesAreProcessed is one way to flush any pending update completion blocks.
*/
- (void)onDidFinishProcessingUpdates:(nullable 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.
*/
- (void)waitUntilAllUpdatesAreProcessed;
/**
* Inserts one or more sections, with an option to animate the insertion.
@@ -699,4 +733,10 @@ NS_ASSUME_NONNULL_BEGIN
@end
@interface ASTableNode (Deprecated)
- (void)waitUntilAllUpdatesAreCommitted ASDISPLAYNODE_DEPRECATED_MSG("This method has been renamed to -waitUntilAllUpdatesAreProcessed.");
@end
NS_ASSUME_NONNULL_END
+53 -8
View File
@@ -43,6 +43,7 @@
@property (nonatomic, assign) BOOL allowsMultipleSelectionDuringEditing;
@property (nonatomic, assign) BOOL inverted;
@property (nonatomic, assign) CGFloat leadingScreensForBatching;
@property (nonatomic, assign) UIEdgeInsets contentInset;
@property (nonatomic, assign) CGPoint contentOffset;
@property (nonatomic, assign) BOOL animatesContentOffset;
@property (nonatomic, assign) BOOL automaticallyAdjustsContentOffset;
@@ -60,6 +61,7 @@
_allowsMultipleSelectionDuringEditing = NO;
_inverted = NO;
_leadingScreensForBatching = 2;
_contentInset = UIEdgeInsetsZero;
_contentOffset = CGPointZero;
_animatesContentOffset = NO;
_automaticallyAdjustsContentOffset = NO;
@@ -113,17 +115,20 @@
if (_pendingState) {
_ASTablePendingState *pendingState = _pendingState;
self.pendingState = nil;
view.asyncDelegate = pendingState.delegate;
view.asyncDataSource = pendingState.dataSource;
view.inverted = pendingState.inverted;
view.allowsSelection = pendingState.allowsSelection;
view.allowsSelectionDuringEditing = pendingState.allowsSelectionDuringEditing;
view.allowsMultipleSelection = pendingState.allowsMultipleSelection;
view.asyncDelegate = pendingState.delegate;
view.asyncDataSource = pendingState.dataSource;
view.inverted = pendingState.inverted;
view.allowsSelection = pendingState.allowsSelection;
view.allowsSelectionDuringEditing = pendingState.allowsSelectionDuringEditing;
view.allowsMultipleSelection = pendingState.allowsMultipleSelection;
view.allowsMultipleSelectionDuringEditing = pendingState.allowsMultipleSelectionDuringEditing;
view.contentInset = pendingState.contentInset;
self.pendingState = nil;
if (pendingState.rangeMode != ASLayoutRangeModeUnspecified) {
[view.rangeController updateCurrentRangeWithMode:pendingState.rangeMode];
}
[view setContentOffset:pendingState.contentOffset animated:pendingState.animatesContentOffset];
}
}
@@ -237,6 +242,27 @@
}
}
- (void)setContentInset:(UIEdgeInsets)contentInset
{
_ASTablePendingState *pendingState = self.pendingState;
if (pendingState) {
pendingState.contentInset = contentInset;
} else {
ASDisplayNodeAssert(self.nodeLoaded, @"ASTableNode should be loaded if pendingState doesn't exist");
self.view.contentInset = contentInset;
}
}
- (UIEdgeInsets)contentInset
{
_ASTablePendingState *pendingState = self.pendingState;
if (pendingState) {
return pendingState.contentInset;
} else {
return self.view.contentInset;
}
}
- (void)setContentOffset:(CGPoint)contentOffset
{
[self setContentOffset:contentOffset animated:NO];
@@ -733,7 +759,21 @@ ASLayoutElementCollectionTableSetTraitCollection(_environmentStateLock)
}
}
- (void)waitUntilAllUpdatesAreCommitted
- (BOOL)isProcessingUpdates
{
return (self.nodeLoaded ? [self.view isProcessingUpdates] : NO);
}
- (void)onDidFinishProcessingUpdates:(nullable void (^)())completion
{
if (!self.nodeLoaded) {
completion();
} else {
[self.view onDidFinishProcessingUpdates:completion];
}
}
- (void)waitUntilAllUpdatesAreProcessed
{
ASDisplayNodeAssertMainThread();
if (self.nodeLoaded) {
@@ -741,6 +781,11 @@ ASLayoutElementCollectionTableSetTraitCollection(_environmentStateLock)
}
}
- (void)waitUntilAllUpdatesAreCommitted
{
[self waitUntilAllUpdatesAreProcessed];
}
#pragma mark - Debugging (Private)
- (NSMutableArray<NSDictionary *> *)propertiesForDebugDescription
+9 -16
View File
@@ -67,6 +67,11 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property (nonatomic, assign) CGFloat leadingScreensForBatching ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode property instead.");
/**
* The distance that the content view is inset from the table view edges. Defaults to UIEdgeInsetsZero.
*/
@property (nonatomic, assign) UIEdgeInsets contentInset ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode property instead");
/**
* The offset of the content view's origin from the table node's origin. Defaults to CGPointZero.
*/
@@ -184,14 +189,6 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (void)reloadData ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead.");
/**
* Reload everything from scratch entirely on the main thread, destroying the working range and all cached nodes.
*
* @warning This method is substantially more expensive than UITableView's version and will block the main thread while
* all the cells load.
*/
- (void)reloadDataImmediately ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode's reloadDataWithCompletion: followed by ASTableNode's -waitUntilAllUpdatesAreCommitted instead.");
/**
* Triggers a relayout of all nodes.
*
@@ -219,9 +216,11 @@ NS_ASSUME_NONNULL_BEGIN
- (void)endUpdatesAnimated:(BOOL)animated completion:(void (^ _Nullable)(BOOL completed))completion ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode's -performBatchUpdates:completion: instead.");
/**
* Blocks execution of the main thread until all section and row updates are committed. This method must be called from the main thread.
* See ASTableNode.h for full documentation of these methods.
*/
- (void)waitUntilAllUpdatesAreCommitted ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead.");
@property (nonatomic, readonly) BOOL isProcessingUpdates;
- (void)onDidFinishProcessingUpdates:(nullable void (^)())completion;
- (void)waitUntilAllUpdatesAreCommitted ASDISPLAYNODE_DEPRECATED_MSG("Use -[ASTableNode waitUntilAllUpdatesAreProcessed] instead.");
- (void)insertSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead.");
@@ -239,12 +238,6 @@ NS_ASSUME_NONNULL_BEGIN
- (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead.");
/// Deprecated in 2.0. You should not call this method.
- (void)clearContents ASDISPLAYNODE_DEPRECATED_MSG("You should not call this method directly. Intead, rely on the Interstate State callback methods.");
/// 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
+14 -10
View File
@@ -544,13 +544,6 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
[self reloadDataWithCompletion:nil];
}
- (void)reloadDataImmediately
{
ASDisplayNodeAssertMainThread();
[self reloadData];
[_dataController waitUntilAllUpdatesAreCommitted];
}
- (void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated
{
if ([self validateIndexPath:indexPath]) {
@@ -735,6 +728,16 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
}
}
- (BOOL)isProcessingUpdates
{
return [_dataController isProcessingUpdates];
}
- (void)onDidFinishProcessingUpdates:(nullable void (^)())completion
{
[_dataController onDidFinishProcessingUpdates:completion];
}
- (void)waitUntilAllUpdatesAreCommitted
{
ASDisplayNodeAssertMainThread();
@@ -743,15 +746,16 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
// ASDisplayNodeFailAssert(@"Should not call %@ during batch update", NSStringFromSelector(_cmd));
return;
}
[_dataController waitUntilAllUpdatesAreCommitted];
[_dataController waitUntilAllUpdatesAreProcessed];
}
- (void)layoutSubviews
{
// Remeasure all rows if our row width has changed.
_remeasuringCellNodes = YES;
CGFloat constrainedWidth = self.bounds.size.width - [self sectionIndexWidth];
UIEdgeInsets contentInset = self.contentInset;
CGFloat constrainedWidth = self.bounds.size.width - [self sectionIndexWidth] - contentInset.left - contentInset.right;
if (constrainedWidth > 0 && _nodesConstrainedWidth != constrainedWidth) {
_nodesConstrainedWidth = constrainedWidth;
-8
View File
@@ -44,8 +44,6 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, assign) BOOL controlsDisabled;
@property (nonatomic, assign, readonly) BOOL loadAssetWhenNodeBecomesVisible ASDISPLAYNODE_DEPRECATED_MSG("Asset is always loaded when this node enters preload state. This flag does nothing.");
#pragma mark - ASVideoNode property proxy
/**
* When shouldAutoplay is set to true, a video node will play when it has both loaded and entered the "visible" interfaceState.
@@ -79,12 +77,6 @@ NS_ASSUME_NONNULL_BEGIN
- (instancetype)initWithAsset:(AVAsset *)asset;
- (instancetype)initWithAsset:(AVAsset *)asset videoComposition:(AVVideoComposition *)videoComposition audioMix:(AVAudioMix *)audioMix;
#pragma mark Lifecycle Deprecated
- (instancetype)initWithUrl:(NSURL *)url ASDISPLAYNODE_DEPRECATED_MSG("Asset is always loaded when this node enters preload state, therefore loadAssetWhenNodeBecomesVisible is deprecated and not used anymore.");
- (instancetype)initWithUrl:(NSURL *)url loadAssetWhenNodeBecomesVisible:(BOOL)loadAssetWhenNodeBecomesVisible ASDISPLAYNODE_DEPRECATED_MSG("Asset is always loaded when this node enters preload state, therefore loadAssetWhenNodeBecomesVisible is deprecated and not used anymore.");
- (instancetype)initWithAsset:(AVAsset *)asset loadAssetWhenNodeBecomesVisible:(BOOL)loadAssetWhenNodeBecomesVisible ASDISPLAYNODE_DEPRECATED_MSG("Asset is always loaded when this node enters preload state, therefore loadAssetWhenNodeBecomesVisible is deprecated and not used anymore.");
- (instancetype)initWithAsset:(AVAsset *)asset videoComposition:(AVVideoComposition *)videoComposition audioMix:(AVAudioMix *)audioMix loadAssetWhenNodeBecomesVisible:(BOOL)loadAssetWhenNodeBecomesVisible ASDISPLAYNODE_DEPRECATED_MSG("Asset is always loaded when this node enters preload state, therefore loadAssetWhenNodeBecomesVisible is deprecated and not used anymore.");
#pragma mark - Public API
- (void)seekToTime:(CGFloat)percentComplete;
- (void)play;
-22
View File
@@ -154,28 +154,6 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
[self addSubnode:_videoNode];
}
#pragma mark Deprecated
- (instancetype)initWithUrl:(NSURL *)url
{
return [self initWithURL:url];
}
- (instancetype)initWithUrl:(NSURL *)url loadAssetWhenNodeBecomesVisible:(BOOL)loadAssetWhenNodeBecomesVisible
{
return [self initWithURL:url];
}
- (instancetype)initWithAsset:(AVAsset *)asset loadAssetWhenNodeBecomesVisible:(BOOL)loadAssetWhenNodeBecomesVisible
{
return [self initWithAsset:asset];
}
- (instancetype)initWithAsset:(AVAsset *)asset videoComposition:(AVVideoComposition *)videoComposition audioMix:(AVAudioMix *)audioMix loadAssetWhenNodeBecomesVisible:(BOOL)loadAssetWhenNodeBecomesVisible
{
return [self initWithAsset:asset videoComposition:videoComposition audioMix:audioMix];
}
#pragma mark - Setter / Getter
- (void)setAssetURL:(NSURL *)assetURL
-13
View File
@@ -92,17 +92,4 @@ NS_ASSUME_NONNULL_BEGIN
@end
@interface ASViewController (Deprecated)
/**
* The constrained size used to measure the backing node.
*
* @discussion Defaults to providing a size range that uses the view controller view's bounds as
* both the min and max definitions. Override this method to provide a custom size range to the
* backing node.
*/
- (ASSizeRange)nodeConstrainedSize AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED_MSG("Set the size directly to the view's frame");
@end
NS_ASSUME_NONNULL_END
-6
View File
@@ -146,12 +146,9 @@
[self propagateNewTraitCollection:traitCollection];
}];
} else {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
// Call layoutThatFits: to let the node prepare for a layout that will happen shortly in the layout pass of the view.
// If the node's constrained size didn't change between the last layout pass it's a no-op
[_node layoutThatFits:[self nodeConstrainedSize]];
#pragma clang diagnostic pop
}
}
@@ -296,13 +293,10 @@ ASVisibilityDepthImplementation;
ASTraitCollectionPropagateDown(child, traitCollection);
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
// Once we've propagated all the traits, layout this node.
// Remeasure the node with the latest constrained size – old constrained size may be incorrect.
as_activity_scope_verbose(as_activity_create("Layout ASViewController node with new traits", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT));
[_node layoutThatFits:[self nodeConstrainedSize]];
#pragma clang diagnostic pop
}
}
-2
View File
@@ -75,7 +75,6 @@
#import <AsyncDisplayKit/ASLayout.h>
#import <AsyncDisplayKit/ASDimension.h>
#import <AsyncDisplayKit/ASDimensionInternal.h>
#import <AsyncDisplayKit/ASDimensionDeprecated.h>
#import <AsyncDisplayKit/ASLayoutElement.h>
#import <AsyncDisplayKit/ASLayoutSpec.h>
#import <AsyncDisplayKit/ASBackgroundLayoutSpec.h>
@@ -125,7 +124,6 @@
#import <AsyncDisplayKit/AsyncDisplayKit+Debug.h>
#import <AsyncDisplayKit/AsyncDisplayKit+Tips.h>
#import <AsyncDisplayKit/ASDisplayNode+Deprecated.h>
#import <AsyncDisplayKit/IGListAdapter+AsyncDisplayKit.h>
#import <AsyncDisplayKit/AsyncDisplayKit+IGListKitMethods.h>
+15 -4
View File
@@ -21,16 +21,27 @@
#import <os/log.h>
#import <os/activity.h>
#ifndef ASEnableLogs
#define ASEnableLogs 1
#endif
#ifndef ASEnableVerboseLogging
#define ASEnableVerboseLogging 0
#endif
ASDISPLAYNODE_EXTERN_C_BEGIN
/**
* Disable all logging.
*
* You should only use this function if the default log level is
* annoying during development. By default, logging is run at
* the appropriate system log level (see the os_log_* functions),
* so you do not need to worry generally about the performance
* implications of log messages.
*
* For example, virtually all log messages generated by Texture
* are at the `debug` log level, which the system
* disables in production.
*/
void ASDisableLogging();
/// Log for general node events e.g. interfaceState, didLoad.
#define ASNodeLogEnabled 1
os_log_t ASNodeLog();
+20 -6
View File
@@ -11,27 +11,41 @@
//
#import <AsyncDisplayKit/ASLog.h>
#import <stdatomic.h>
static atomic_bool __ASLogEnabled = ATOMIC_VAR_INIT(YES);
void ASDisableLogging() {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
atomic_store(&__ASLogEnabled, NO);
});
}
ASDISPLAYNODE_INLINE BOOL ASLoggingIsEnabled() {
return atomic_load(&__ASLogEnabled);
}
os_log_t ASNodeLog() {
return ASCreateOnce((ASEnableLogs && ASNodeLogEnabled) ? as_log_create("org.TextureGroup.Texture", "Node") : OS_LOG_DISABLED);
return (ASNodeLogEnabled && ASLoggingIsEnabled()) ? ASCreateOnce(as_log_create("org.TextureGroup.Texture", "Node")) : OS_LOG_DISABLED;
}
os_log_t ASLayoutLog() {
return ASCreateOnce((ASEnableLogs && ASLayoutLogEnabled) ? as_log_create("org.TextureGroup.Texture", "Layout") : OS_LOG_DISABLED);
return (ASLayoutLogEnabled && ASLoggingIsEnabled()) ? ASCreateOnce(as_log_create("org.TextureGroup.Texture", "Layout")) : OS_LOG_DISABLED;
}
os_log_t ASCollectionLog() {
return ASCreateOnce((ASEnableLogs && ASCollectionLogEnabled) ? as_log_create("org.TextureGroup.Texture", "Collection") : OS_LOG_DISABLED);
return (ASCollectionLogEnabled && ASLoggingIsEnabled()) ?ASCreateOnce(as_log_create("org.TextureGroup.Texture", "Collection")) : OS_LOG_DISABLED;
}
os_log_t ASDisplayLog() {
return ASCreateOnce((ASEnableLogs && ASDisplayLogEnabled) ? as_log_create("org.TextureGroup.Texture", "Display") : OS_LOG_DISABLED);
return (ASDisplayLogEnabled && ASLoggingIsEnabled()) ?ASCreateOnce(as_log_create("org.TextureGroup.Texture", "Display")) : OS_LOG_DISABLED;
}
os_log_t ASImageLoadingLog() {
return ASCreateOnce((ASEnableLogs && ASImageLoadingLogEnabled) ? as_log_create("org.TextureGroup.Texture", "ImageLoading") : OS_LOG_DISABLED);
return (ASImageLoadingLogEnabled && ASLoggingIsEnabled()) ? ASCreateOnce(as_log_create("org.TextureGroup.Texture", "ImageLoading")) : OS_LOG_DISABLED;
}
os_log_t ASMainThreadDeallocationLog() {
return ASCreateOnce((ASEnableLogs && ASMainThreadDeallocationLogEnabled) ? as_log_create("org.TextureGroup.Texture", "MainDealloc") : OS_LOG_DISABLED);
return (ASMainThreadDeallocationLogEnabled && ASLoggingIsEnabled()) ? ASCreateOnce(as_log_create("org.TextureGroup.Texture", "MainDealloc")) : OS_LOG_DISABLED;
}
@@ -14,6 +14,7 @@
#import <AsyncDisplayKit/ASScrollDirection.h>
@class ASElementMap;
@class ASCollectionGalleryLayoutDelegate;
NS_ASSUME_NONNULL_BEGIN
@@ -24,11 +25,13 @@ NS_ASSUME_NONNULL_BEGIN
*
* @discussion This method will only be called on main thread.
*
* @param delegate The calling object.
*
* @param elements All elements to be sized.
*
* @return The elements' size
*/
- (CGSize)sizeForElements:(ASElementMap *)elements;
- (CGSize)galleryLayoutDelegate:(ASCollectionGalleryLayoutDelegate *)delegate sizeForElements:(ASElementMap *)elements;
@optional
@@ -42,11 +45,13 @@ NS_ASSUME_NONNULL_BEGIN
* It is not applied between the first line and the header, or between the last line and the footer.
* This is the same behavior as UICollectionViewFlowLayout's minimumLineSpacing.
*
* @param delegate The calling object.
*
* @param elements All elements in the layout.
*
* @return The interitem spacing
*/
- (CGFloat)minimumLineSpacingForElements:(ASElementMap *)elements;
- (CGFloat)galleryLayoutDelegate:(ASCollectionGalleryLayoutDelegate *)delegate minimumLineSpacingForElements:(ASElementMap *)elements;
/**
* Returns the minumum spacing to use between items in the same row or column, depending on the scroll directions.
@@ -58,22 +63,26 @@ NS_ASSUME_NONNULL_BEGIN
* It is considered while fitting items into lines, but the actual final spacing between some items might be larger.
* This is the same behavior as UICollectionViewFlowLayout's minimumInteritemSpacing.
*
* @param delegate The calling object.
*
* @param elements All elements in the layout.
*
* @return The interitem spacing
*/
- (CGFloat)minimumInteritemSpacingForElements:(ASElementMap *)elements;
- (CGFloat)galleryLayoutDelegate:(ASCollectionGalleryLayoutDelegate *)delegate minimumInteritemSpacingForElements:(ASElementMap *)elements;
/**
* Returns the margins of each section.
*
* @discussion This method will only be called on main thread.
*
* @param delegate The calling object.
*
* @param elements All elements in the layout.
*
* @return The margins used to layout content in a section
*/
- (UIEdgeInsets)sectionInsetForElements:(ASElementMap *)elements;
- (UIEdgeInsets)galleryLayoutDelegate:(ASCollectionGalleryLayoutDelegate *)delegate sectionInsetForElements:(ASElementMap *)elements;
@end
@@ -66,9 +66,9 @@
_propertiesProviderFlags = {};
} else {
_propertiesProvider = propertiesProvider;
_propertiesProviderFlags.minimumLineSpacingForElements = [_propertiesProvider respondsToSelector:@selector(minimumLineSpacingForElements:)];
_propertiesProviderFlags.minimumInteritemSpacingForElements = [_propertiesProvider respondsToSelector:@selector(minimumInteritemSpacingForElements:)];
_propertiesProviderFlags.sectionInsetForElements = [_propertiesProvider respondsToSelector:@selector(sectionInsetForElements:)];
_propertiesProviderFlags.minimumLineSpacingForElements = [_propertiesProvider respondsToSelector:@selector(galleryLayoutDelegate:minimumLineSpacingForElements:)];
_propertiesProviderFlags.minimumInteritemSpacingForElements = [_propertiesProvider respondsToSelector:@selector(galleryLayoutDelegate:minimumInteritemSpacingForElements:)];
_propertiesProviderFlags.sectionInsetForElements = [_propertiesProvider respondsToSelector:@selector(galleryLayoutDelegate:sectionInsetForElements:)];
}
}
@@ -80,10 +80,10 @@
return nil;
}
CGSize itemSize = [propertiesProvider sizeForElements:elements];
UIEdgeInsets sectionInset = _propertiesProviderFlags.sectionInsetForElements ? [propertiesProvider sectionInsetForElements:elements] : UIEdgeInsetsZero;
CGFloat lineSpacing = _propertiesProviderFlags.minimumLineSpacingForElements ? [propertiesProvider minimumLineSpacingForElements:elements] : 0.0;
CGFloat interitemSpacing = _propertiesProviderFlags.minimumInteritemSpacingForElements ? [propertiesProvider minimumInteritemSpacingForElements:elements] : 0.0;
CGSize itemSize = [propertiesProvider galleryLayoutDelegate:self sizeForElements:elements];
UIEdgeInsets sectionInset = _propertiesProviderFlags.sectionInsetForElements ? [propertiesProvider galleryLayoutDelegate:self sectionInsetForElements:elements] : UIEdgeInsetsZero;
CGFloat lineSpacing = _propertiesProviderFlags.minimumLineSpacingForElements ? [propertiesProvider galleryLayoutDelegate:self minimumLineSpacingForElements:elements] : 0.0;
CGFloat interitemSpacing = _propertiesProviderFlags.minimumInteritemSpacingForElements ? [propertiesProvider galleryLayoutDelegate:self minimumInteritemSpacingForElements:elements] : 0.0;
return [[_ASCollectionGalleryLayoutInfo alloc] initWithItemSize:itemSize
minimumLineSpacing:lineSpacing
minimumInteritemSpacing:interitemSpacing
@@ -86,9 +86,6 @@ extern ASSizeRange NodeConstrainedSizeForScrollDirection(ASCollectionView *colle
* @warning This class is not meant to be subclassed and will be restricted in the future.
*/
@interface ASCollectionViewLayoutInspector : NSObject <ASCollectionViewLayoutInspecting>
- (instancetype)initWithCollectionView:(ASCollectionView *)collectionView ASDISPLAYNODE_DEPRECATED_MSG("Use -init instead.");
@end
NS_ASSUME_NONNULL_END
@@ -27,9 +27,12 @@
// of the collection view
ASSizeRange NodeConstrainedSizeForScrollDirection(ASCollectionView *collectionView) {
CGSize maxSize = collectionView.bounds.size;
UIEdgeInsets contentInset = collectionView.contentInset;
if (ASScrollDirectionContainsHorizontalDirection(collectionView.scrollableDirections)) {
maxSize.width = CGFLOAT_MAX;
maxSize.height -= (contentInset.top + contentInset.bottom);
} else {
maxSize.width -= (contentInset.left + contentInset.right);
maxSize.height = CGFLOAT_MAX;
}
return ASSizeRangeMake(CGSizeZero, maxSize);
@@ -44,13 +47,6 @@ ASSizeRange NodeConstrainedSizeForScrollDirection(ASCollectionView *collectionVi
} _delegateFlags;
}
#pragma mark Lifecycle
- (instancetype)initWithCollectionView:(ASCollectionView *)collectionView
{
return [self init];
}
#pragma mark ASCollectionViewLayoutInspecting
- (void)didChangeCollectionViewDelegate:(id<ASCollectionDelegate>)delegate
+6 -1
View File
@@ -254,7 +254,12 @@ extern NSString * const ASCollectionInvalidUpdateException;
*/
- (void)relayoutNodes:(id<NSFastEnumeration>)nodes nodesSizeChanged:(NSMutableArray * _Nonnull)nodesSizesChanged;
- (void)waitUntilAllUpdatesAreCommitted;
/**
* See ASCollectionNode.h for full documentation of these methods.
*/
@property (nonatomic, readonly) BOOL isProcessingUpdates;
- (void)onDidFinishProcessingUpdates:(nullable void (^)())completion;
- (void)waitUntilAllUpdatesAreProcessed;
/**
* Notifies the data controller object that its environment has changed. The object will request its environment delegate for new information
+31 -2
View File
@@ -430,13 +430,42 @@ typedef dispatch_block_t ASDataControllerCompletionBlock;
#pragma mark - Batching (External API)
- (void)waitUntilAllUpdatesAreCommitted
- (void)waitUntilAllUpdatesAreProcessed
{
// Schedule block in main serial queue to wait until all operations are finished that are
// where scheduled while waiting for the _editingTransactionQueue to finish
[self _scheduleBlockOnMainSerialQueue:^{ }];
}
- (BOOL)isProcessingUpdates
{
ASDisplayNodeAssertMainThread();
if (_mainSerialQueue.numberOfScheduledBlocks > 0) {
return YES;
} else if (dispatch_group_wait(_editingTransactionGroup, DISPATCH_TIME_NOW) != 0) {
// After waiting for zero duration, a nonzero value is returned if blocks are still running.
return YES;
}
// Both the _mainSerialQueue and _editingTransactionQueue are drained; we are fully quiesced.
return NO;
}
- (void)onDidFinishProcessingUpdates:(nullable void (^)())completion
{
ASDisplayNodeAssertMainThread();
if ([self isProcessingUpdates] == NO) {
ASPerformBlockOnMainThread(completion);
} else {
dispatch_async(_editingTransactionQueue, ^{
// Retry the block. If we're done processing updates, it'll run immediately, otherwise
// wait again for updates to quiesce completely.
[_mainSerialQueue performBlockOnMainThread:^{
[self onDidFinishProcessingUpdates:completion];
}];
});
}
}
- (void)updateWithChangeSet:(_ASHierarchyChangeSet *)changeSet
{
ASDisplayNodeAssertMainThread();
@@ -563,7 +592,7 @@ typedef dispatch_block_t ASDataControllerCompletionBlock;
});
if (_usesSynchronousDataLoading) {
[self waitUntilAllUpdatesAreCommitted];
[self waitUntilAllUpdatesAreProcessed];
}
}
+29 -1
View File
@@ -37,7 +37,7 @@ typedef void(^ASImageCacherCompletion)(id <ASImageContainerProtocol> _Nullable i
@param URL The URL of the image to retrieve from the cache.
@param callbackQueue The queue to call `completion` on.
@param completion The block to be called when the cache has either hit or missed.
@discussion If `URL` is nil, `completion` will be invoked immediately with a nil image. This method should not block
@discussion If `URL` is nil, `completion` should be invoked immediately with a nil image. This method should not block
the calling thread as it is likely to be called from the main thread.
*/
- (void)cachedImageWithURL:(NSURL *)URL
@@ -66,6 +66,19 @@ typedef void(^ASImageCacherCompletion)(id <ASImageContainerProtocol> _Nullable i
*/
- (void)clearFetchedImageFromCacheWithURL:(NSURL *)URL;
/**
@abstract Attempts to fetch an image with the given URLs from the cache in reverse order.
@param URLs The URLs of the image to retrieve from the cache.
@param callbackQueue The queue to call `completion` on.
@param completion The block to be called when the cache has either hit or missed.
@discussion If `URLs` is nil or empty, `completion` should be invoked immediately with a nil image. This method should not block
the calling thread as it is likely to be called from the main thread.
@see downloadImageWithURLs:callbackQueue:downloadProgress:completion:
*/
- (void)cachedImageWithURLs:(NSArray <NSURL *> *)URLs
callbackQueue:(dispatch_queue_t)callbackQueue
completion:(ASImageCacherCompletion)completion;
@end
/**
@@ -154,6 +167,21 @@ typedef NS_ENUM(NSUInteger, ASImageDownloaderPriority) {
- (void)setPriority:(ASImageDownloaderPriority)priority
withDownloadIdentifier:(id)downloadIdentifier;
/**
@abstract Downloads an image from a list of URLs depending on previously observed network speed conditions.
@param URLs An array of URLs ordered by the cost of downloading them, the URL at index 0 being the lowest cost.
@param callbackQueue The queue to call `downloadProgressBlock` and `completion` on.
@param downloadProgress The block to be invoked when the download of `URL` progresses.
@param completion The block to be invoked when the download has completed, or has failed.
@discussion This method is likely to be called on the main thread, so any custom implementations should make sure to background any expensive download operations.
@result An opaque identifier to be used in canceling the download, via `cancelImageDownloadForIdentifier:`. You must
retain the identifier if you wish to use it later.
*/
- (nullable id)downloadImageWithURLs:(NSArray <NSURL *> *)URLs
callbackQueue:(dispatch_queue_t)callbackQueue
downloadProgress:(nullable ASImageDownloaderProgress)downloadProgress
completion:(ASImageDownloaderCompletion)completion;
@end
@protocol ASAnimatedImageProtocol <NSObject>
-3
View File
@@ -74,6 +74,3 @@ typedef NS_ENUM(NSInteger, ASLayoutRangeType) {
};
static NSInteger const ASLayoutRangeTypeCount = 2;
#define ASLayoutRangeTypeRender ASLayoutRangeTypeDisplay
#define ASLayoutRangeTypeFetchData ASLayoutRangeTypePreload
+1
View File
@@ -21,6 +21,7 @@
AS_SUBCLASSING_RESTRICTED
@interface ASMainSerialQueue : NSObject
@property (nonatomic, readonly) NSUInteger numberOfScheduledBlocks;
- (void)performBlockOnMainThread:(dispatch_block_t)block;
@end
+6
View File
@@ -40,6 +40,12 @@
return self;
}
- (NSUInteger)numberOfScheduledBlocks
{
ASDN::MutexLocker l(_serialQueueLock);
return _blocks.count;
}
- (void)performBlockOnMainThread:(dispatch_block_t)block
{
ASDN::MutexLocker l(_serialQueueLock);
+69 -32
View File
@@ -196,6 +196,23 @@ static ASPINRemoteImageDownloader *sharedDownloader = nil;
}
}
- (void)cachedImageWithURLs:(NSArray <NSURL *> *)URLs
callbackQueue:(dispatch_queue_t)callbackQueue
completion:(ASImageCacherCompletion)completion
{
[self cachedImageWithURL:[URLs lastObject]
callbackQueue:callbackQueue
completion:^(id<ASImageContainerProtocol> _Nullable imageFromCache) {
if (imageFromCache.asdk_image == nil && URLs.count > 1) {
[self cachedImageWithURLs:[URLs subarrayWithRange:NSMakeRange(0, URLs.count - 1)]
callbackQueue:callbackQueue
completion:completion];
} else {
completion(imageFromCache);
}
}];
}
- (void)clearFetchedImageFromCacheWithURL:(NSURL *)URL
{
if ([self sharedImageManagerSupportsMemoryRemoval]) {
@@ -210,43 +227,63 @@ static ASPINRemoteImageDownloader *sharedDownloader = nil;
downloadProgress:(ASImageDownloaderProgress)downloadProgress
completion:(ASImageDownloaderCompletion)completion;
{
return [[self sharedPINRemoteImageManager] downloadImageWithURL:URL options:PINRemoteImageManagerDownloadOptionsSkipDecode progressDownload:^(int64_t completedBytes, int64_t totalBytes) {
if (downloadProgress == nil) { return; }
NSArray <NSURL *>*URLs = nil;
if (URL) {
URLs = @[URL];
}
return [self downloadImageWithURLs:URLs callbackQueue:callbackQueue downloadProgress:downloadProgress completion:completion];
}
/// If we're targeting the main queue and we're on the main thread, call immediately.
if (ASDisplayNodeThreadIsMain() && callbackQueue == dispatch_get_main_queue()) {
downloadProgress(completedBytes / (CGFloat)totalBytes);
} else {
dispatch_async(callbackQueue, ^{
downloadProgress(completedBytes / (CGFloat)totalBytes);
});
}
} completion:^(PINRemoteImageManagerResult * _Nonnull result) {
/// If we're targeting the main queue and we're on the main thread, complete immediately.
if (ASDisplayNodeThreadIsMain() && callbackQueue == dispatch_get_main_queue()) {
#if PIN_ANIMATED_AVAILABLE
if (result.alternativeRepresentation) {
completion(result.alternativeRepresentation, result.error, result.UUID);
} else {
completion(result.image, result.error, result.UUID);
}
#else
completion(result.image, result.error, result.UUID);
#endif
} else {
dispatch_async(callbackQueue, ^{
#if PIN_ANIMATED_AVAILABLE
if (result.alternativeRepresentation) {
completion(result.alternativeRepresentation, result.error, result.UUID);
- (nullable id)downloadImageWithURLs:(NSArray <NSURL *> *)URLs
callbackQueue:(dispatch_queue_t)callbackQueue
downloadProgress:(nullable ASImageDownloaderProgress)downloadProgress
completion:(ASImageDownloaderCompletion)completion
{
PINRemoteImageManagerProgressDownload progressDownload = ^(int64_t completedBytes, int64_t totalBytes) {
if (downloadProgress == nil) { return; }
/// If we're targeting the main queue and we're on the main thread, call immediately.
if (ASDisplayNodeThreadIsMain() && callbackQueue == dispatch_get_main_queue()) {
downloadProgress(completedBytes / (CGFloat)totalBytes);
} else {
completion(result.image, result.error, result.UUID);
dispatch_async(callbackQueue, ^{
downloadProgress(completedBytes / (CGFloat)totalBytes);
});
}
};
PINRemoteImageManagerImageCompletion imageCompletion = ^(PINRemoteImageManagerResult * _Nonnull result) {
/// If we're targeting the main queue and we're on the main thread, complete immediately.
if (ASDisplayNodeThreadIsMain() && callbackQueue == dispatch_get_main_queue()) {
#if PIN_ANIMATED_AVAILABLE
if (result.alternativeRepresentation) {
completion(result.alternativeRepresentation, result.error, result.UUID);
} else {
completion(result.image, result.error, result.UUID);
}
#else
completion(result.image, result.error, result.UUID);
completion(result.image, result.error, result.UUID);
#endif
});
}
}];
} else {
dispatch_async(callbackQueue, ^{
#if PIN_ANIMATED_AVAILABLE
if (result.alternativeRepresentation) {
completion(result.alternativeRepresentation, result.error, result.UUID);
} else {
completion(result.image, result.error, result.UUID);
}
#else
completion(result.image, result.error, result.UUID);
#endif
});
}
};
return [[self sharedPINRemoteImageManager] downloadImageWithURLs:URLs
options:PINRemoteImageManagerDownloadOptionsSkipDecode
progressImage:nil
progressDownload:progressDownload
completion:imageCompletion];
}
- (void)cancelImageDownloadForIdentifier:(id)downloadIdentifier
-21
View File
@@ -66,10 +66,6 @@ extern NSString *NSStringFromASPrimitiveTraitCollection(ASPrimitiveTraitCollecti
*/
extern void ASTraitCollectionPropagateDown(id<ASLayoutElement> element, ASPrimitiveTraitCollection traitCollection);
/// For backward compatibility reasons we redefine the old layout element trait collection struct name
#define ASEnvironmentTraitCollection ASPrimitiveTraitCollection
#define ASEnvironmentTraitCollectionMakeDefault ASPrimitiveTraitCollectionMakeDefault
ASDISPLAYNODE_EXTERN_C_END
/**
@@ -92,13 +88,6 @@ ASDISPLAYNODE_EXTERN_C_END
*/
- (ASTraitCollection *)asyncTraitCollection;
/**
* Deprecated and should be replaced by the methods from above
*/
- (ASEnvironmentTraitCollection)environmentTraitCollection;
- (void)setEnvironmentTraitCollection:(ASEnvironmentTraitCollection)traitCollection;
@end
#define ASPrimitiveTraitCollectionDefaults \
@@ -111,16 +100,6 @@ ASDISPLAYNODE_EXTERN_C_END
_primitiveTraitCollection = traitCollection;\
}\
#define ASPrimitiveTraitCollectionDeprecatedImplementation \
- (ASEnvironmentTraitCollection)environmentTraitCollection\
{\
return self.primitiveTraitCollection;\
}\
- (void)setEnvironmentTraitCollection:(ASEnvironmentTraitCollection)traitCollection\
{\
[self setPrimitiveTraitCollection:traitCollection];\
}\
#define ASLayoutElementCollectionTableSetTraitCollection(lock) \
- (void)setPrimitiveTraitCollection:(ASPrimitiveTraitCollection)traitCollection\
{\
+86 -3
View File
@@ -23,9 +23,21 @@
#import <AsyncDisplayKit/ASDisplayNode+Beta.h>
#import <AsyncDisplayKit/ASDisplayNodeInternal.h>
#import <queue>
NS_INLINE UIAccessibilityTraits InteractiveAccessibilityTraitsMask() {
return UIAccessibilityTraitLink | UIAccessibilityTraitKeyboardKey | UIAccessibilityTraitButton;
}
#pragma mark - UIAccessibilityElement
typedef NSComparisonResult (^SortAccessibilityElementsComparator)(UIAccessibilityElement *, UIAccessibilityElement *);
@protocol ASAccessibilityElementPositioning
@property (nonatomic, readonly) CGRect accessibilityFrame;
@end
typedef NSComparisonResult (^SortAccessibilityElementsComparator)(id<ASAccessibilityElementPositioning>, id<ASAccessibilityElementPositioning>);
/// Sort accessiblity elements first by y and than by x origin.
static void SortAccessibilityElements(NSMutableArray *elements)
@@ -35,7 +47,7 @@ static void SortAccessibilityElements(NSMutableArray *elements)
static SortAccessibilityElementsComparator comparator = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
comparator = ^NSComparisonResult(UIAccessibilityElement *a, UIAccessibilityElement *b) {
comparator = ^NSComparisonResult(id<ASAccessibilityElementPositioning> a, id<ASAccessibilityElementPositioning> b) {
CGPoint originA = a.accessibilityFrame.origin;
CGPoint originB = b.accessibilityFrame.origin;
if (originA.y == originB.y) {
@@ -50,7 +62,7 @@ static void SortAccessibilityElements(NSMutableArray *elements)
[elements sortUsingComparator:comparator];
}
@interface ASAccessibilityElement : UIAccessibilityElement
@interface ASAccessibilityElement : UIAccessibilityElement<ASAccessibilityElementPositioning>
@property (nonatomic, strong) ASDisplayNode *node;
@property (nonatomic, strong) ASDisplayNode *containerNode;
@@ -85,6 +97,25 @@ static void SortAccessibilityElements(NSMutableArray *elements)
#pragma mark - _ASDisplayView / UIAccessibilityContainer
@interface ASAccessibilityCustomAction : UIAccessibilityCustomAction<ASAccessibilityElementPositioning>
@property (nonatomic, strong) UIView *container;
@property (nonatomic, strong) ASDisplayNode *node;
@property (nonatomic, strong) ASDisplayNode *containerNode;
@end
@implementation ASAccessibilityCustomAction
- (CGRect)accessibilityFrame
{
CGRect accessibilityFrame = [self.containerNode convertRect:self.node.bounds fromNode:self.node];
accessibilityFrame = UIAccessibilityConvertFrameToScreenCoordinates(accessibilityFrame, self.container);
return accessibilityFrame;
}
@end
/// Collect all subnodes for the given node by walking down the subnode tree and calculates the screen coordinates based on the containerNode and container
static void CollectUIAccessibilityElementsForNode(ASDisplayNode *node, ASDisplayNode *containerNode, id container, NSMutableArray *elements)
{
@@ -100,12 +131,64 @@ static void CollectUIAccessibilityElementsForNode(ASDisplayNode *node, ASDisplay
});
}
static void CollectAccessibilityElementsForContainer(ASDisplayNode *container, _ASDisplayView *view, NSMutableArray *elements) {
UIAccessibilityElement *accessiblityElement = [ASAccessibilityElement accessibilityElementWithContainer:view node:container containerNode:container];
NSMutableArray<ASAccessibilityElement *> *labeledNodes = [NSMutableArray array];
NSMutableArray<ASAccessibilityCustomAction *> *actions = [NSMutableArray array];
std::queue<ASDisplayNode *> queue;
queue.push(container);
ASDisplayNode *node;
while (!queue.empty()) {
node = queue.front();
queue.pop();
if (node != container && node.isAccessibilityContainer) {
CollectAccessibilityElementsForContainer(node, view, elements);
continue;
}
if (node.accessibilityLabel.length > 0) {
if (node.accessibilityTraits & InteractiveAccessibilityTraitsMask()) {
ASAccessibilityCustomAction *action = [[ASAccessibilityCustomAction alloc] initWithName:node.accessibilityLabel target:node selector:@selector(performAccessibilityCustomAction:)];
action.node = node;
action.containerNode = node.supernode;
action.container = node.supernode.view;
[actions addObject:action];
} else {
// Even though not surfaced to UIKit, create a non-interactive element for purposes of building sorted aggregated label.
ASAccessibilityElement *nonInteractiveElement = [ASAccessibilityElement accessibilityElementWithContainer:view node:node containerNode:container];
[labeledNodes addObject:nonInteractiveElement];
}
}
for (ASDisplayNode *subnode in node.subnodes) {
queue.push(subnode);
}
}
SortAccessibilityElements(labeledNodes);
NSArray *labels = [labeledNodes valueForKey:@"accessibilityLabel"];
accessiblityElement.accessibilityLabel = [labels componentsJoinedByString:@", "];
SortAccessibilityElements(actions);
accessiblityElement.accessibilityCustomActions = actions;
[elements addObject:accessiblityElement];
}
/// Collect all accessibliity elements for a given view and view node
static void CollectAccessibilityElementsForView(_ASDisplayView *view, NSMutableArray *elements)
{
ASDisplayNodeCAssertNotNil(elements, @"Should pass in a NSMutableArray");
ASDisplayNode *node = view.asyncdisplaykit_node;
if (node.isAccessibilityContainer) {
CollectAccessibilityElementsForContainer(node, view, elements);
return;
}
// Handle rasterize case
if (node.rasterizesSubtree) {
-6
View File
@@ -16,7 +16,6 @@
//
#import <AsyncDisplayKit/ASBaseDefines.h>
#import <AsyncDisplayKit/ASDimensionDeprecated.h>
NS_ASSUME_NONNULL_BEGIN
@@ -30,11 +29,6 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property (nonatomic, assign) CGPoint layoutPosition;
#pragma mark Deprecated
@property (nonatomic, assign) ASRelativeSizeRange sizeRange ASDISPLAYNODE_DEPRECATED;
@end
NS_ASSUME_NONNULL_END
-9
View File
@@ -50,13 +50,4 @@ NS_ASSUME_NONNULL_BEGIN
@end
#pragma mark - Deprecated
@interface ASStaticLayoutSpec : ASAbsoluteLayoutSpec
+ (instancetype)staticLayoutSpecWithChildren:(NSArray<id<ASLayoutElement>> *)children AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED;
@end
NS_ASSUME_NONNULL_END
-15
View File
@@ -107,18 +107,3 @@
@end
#pragma mark - ASStaticLayoutSpec
@implementation ASStaticLayoutSpec : ASAbsoluteLayoutSpec
+ (instancetype)staticLayoutSpecWithChildren:(NSArray<id<ASLayoutElement>> *)children
{
return [self absoluteLayoutSpecWithSizing:ASAbsoluteLayoutSpecSizingSizeToFit children:children];
}
- (instancetype)initWithChildren:(NSArray *)children
{
return [super initWithSizing:ASAbsoluteLayoutSpecSizingSizeToFit children:children];
}
@end
-102
View File
@@ -1,102 +0,0 @@
//
// ASDimensionDeprecated.h
// Texture
//
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional
// grant of patent rights can be found in the PATENTS file in the same directory.
//
// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present,
// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
#pragma once
#import <AsyncDisplayKit/ASBaseDefines.h>
#import <AsyncDisplayKit/ASDimension.h>
ASDISPLAYNODE_EXTERN_C_BEGIN
NS_ASSUME_NONNULL_BEGIN
/**
* A dimension relative to constraints to be provided in the future.
* A ASDimension can be one of three types:
*
* "Auto" - This indicated "I have no opinion" and may be resolved in whatever way makes most sense given the circumstances.
*
* "Points" - Just a number. It will always resolve to exactly this amount.
*
* "Percent" - Multiplied to a provided parent amount to resolve a final amount.
*/
typedef NS_ENUM(NSInteger, ASRelativeDimensionType) {
/** This indicates "I have no opinion" and may be resolved in whatever way makes most sense given the circumstances. */
ASRelativeDimensionTypeAuto,
/** Just a number. It will always resolve to exactly this amount. This is the default type. */
ASRelativeDimensionTypePoints,
/** Multiplied to a provided parent amount to resolve a final amount. */
ASRelativeDimensionTypeFraction,
};
#define ASRelativeDimension ASDimension
#define ASRelativeSize ASLayoutSize
#define ASRelativeDimensionMakeWithPoints ASDimensionMakeWithPoints
#define ASRelativeDimensionMakeWithFraction ASDimensionMakeWithFraction
/**
* Function is deprecated. Use ASSizeRangeMake instead.
*/
extern AS_WARN_UNUSED_RESULT ASSizeRange ASSizeRangeMakeExactSize(CGSize size) ASDISPLAYNODE_DEPRECATED_MSG("Use ASSizeRangeMake instead.");
/**
Expresses an inclusive range of relative sizes. Used to provide additional constraint to layout.
Used by ASStaticLayoutSpec.
*/
typedef struct {
ASLayoutSize min;
ASLayoutSize max;
} ASRelativeSizeRange;
extern ASRelativeSizeRange const ASRelativeSizeRangeUnconstrained;
#pragma mark - ASRelativeDimension
extern ASDimension ASRelativeDimensionMake(ASRelativeDimensionType type, CGFloat value) ASDISPLAYNODE_DEPRECATED;
#pragma mark - ASRelativeSize
extern ASLayoutSize ASRelativeSizeMake(ASRelativeDimension width, ASRelativeDimension height) ASDISPLAYNODE_DEPRECATED;
/** Convenience constructor to provide size in points. */
extern ASLayoutSize ASRelativeSizeMakeWithCGSize(CGSize size) ASDISPLAYNODE_DEPRECATED;
/** Convenience constructor to provide size as a fraction. */
extern ASLayoutSize ASRelativeSizeMakeWithFraction(CGFloat fraction) ASDISPLAYNODE_DEPRECATED;
extern BOOL ASRelativeSizeEqualToRelativeSize(ASLayoutSize lhs, ASLayoutSize rhs) ASDISPLAYNODE_DEPRECATED;
extern NSString *NSStringFromASRelativeSize(ASLayoutSize size) ASDISPLAYNODE_DEPRECATED;
#pragma mark - ASRelativeSizeRange
extern ASRelativeSizeRange ASRelativeSizeRangeMake(ASLayoutSize min, ASLayoutSize max) ASDISPLAYNODE_DEPRECATED;
#pragma mark Convenience constructors to provide an exact size (min == max).
extern ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactRelativeSize(ASLayoutSize exact) ASDISPLAYNODE_DEPRECATED;
extern ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactCGSize(CGSize exact) ASDISPLAYNODE_DEPRECATED;
extern ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactFraction(CGFloat fraction) ASDISPLAYNODE_DEPRECATED;
extern ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactRelativeDimensions(ASRelativeDimension exactWidth, ASRelativeDimension exactHeight) ASDISPLAYNODE_DEPRECATED;
extern BOOL ASRelativeSizeRangeEqualToRelativeSizeRange(ASRelativeSizeRange lhs, ASRelativeSizeRange rhs) ASDISPLAYNODE_DEPRECATED;
/** Provided a parent size, compute final dimensions for this RelativeSizeRange to arrive at a SizeRange. */
extern ASSizeRange ASRelativeSizeRangeResolve(ASRelativeSizeRange relativeSizeRange, CGSize parentSize) ASDISPLAYNODE_DEPRECATED;
NS_ASSUME_NONNULL_END
ASDISPLAYNODE_EXTERN_C_END
-102
View File
@@ -1,102 +0,0 @@
//
// ASDimensionDeprecated.mm
// Texture
//
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional
// grant of patent rights can be found in the PATENTS file in the same directory.
//
// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present,
// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
#import <AsyncDisplayKit/ASDimensionDeprecated.h>
ASDimension ASRelativeDimensionMake(ASRelativeDimensionType type, CGFloat value)
{
if (type == ASRelativeDimensionTypePoints) {
return ASDimensionMakeWithPoints(value);
} else if (type == ASRelativeDimensionTypeFraction) {
return ASDimensionMakeWithFraction(value);
}
ASDisplayNodeCAssert(NO, @"ASRelativeDimensionMake does not support the given ASRelativeDimensionType");
return ASDimensionMakeWithPoints(0);
}
ASSizeRange ASSizeRangeMakeExactSize(CGSize size)
{
return ASSizeRangeMake(size);
}
ASRelativeSizeRange const ASRelativeSizeRangeUnconstrained = {};
#pragma mark - ASRelativeSize
ASLayoutSize ASRelativeSizeMake(ASRelativeDimension width, ASRelativeDimension height)
{
return ASLayoutSizeMake(width, height);
}
ASLayoutSize ASRelativeSizeMakeWithCGSize(CGSize size)
{
return ASRelativeSizeMake(ASRelativeDimensionMakeWithPoints(size.width),
ASRelativeDimensionMakeWithPoints(size.height));
}
ASLayoutSize ASRelativeSizeMakeWithFraction(CGFloat fraction)
{
return ASRelativeSizeMake(ASRelativeDimensionMakeWithFraction(fraction),
ASRelativeDimensionMakeWithFraction(fraction));
}
BOOL ASRelativeSizeEqualToRelativeSize(ASLayoutSize lhs, ASLayoutSize rhs)
{
return ASDimensionEqualToDimension(lhs.width, rhs.width)
&& ASDimensionEqualToDimension(lhs.height, rhs.height);
}
#pragma mark - ASRelativeSizeRange
ASRelativeSizeRange ASRelativeSizeRangeMake(ASLayoutSize min, ASLayoutSize max)
{
ASRelativeSizeRange sizeRange; sizeRange.min = min; sizeRange.max = max; return sizeRange;
}
ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactRelativeSize(ASLayoutSize exact)
{
return ASRelativeSizeRangeMake(exact, exact);
}
ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactCGSize(CGSize exact)
{
return ASRelativeSizeRangeMakeWithExactRelativeSize(ASRelativeSizeMakeWithCGSize(exact));
}
ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactFraction(CGFloat fraction)
{
return ASRelativeSizeRangeMakeWithExactRelativeSize(ASRelativeSizeMakeWithFraction(fraction));
}
ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactRelativeDimensions(ASRelativeDimension exactWidth, ASRelativeDimension exactHeight)
{
return ASRelativeSizeRangeMakeWithExactRelativeSize(ASRelativeSizeMake(exactWidth, exactHeight));
}
BOOL ASRelativeSizeRangeEqualToRelativeSizeRange(ASRelativeSizeRange lhs, ASRelativeSizeRange rhs)
{
return ASRelativeSizeEqualToRelativeSize(lhs.min, rhs.min) && ASRelativeSizeEqualToRelativeSize(lhs.max, rhs.max);
}
ASSizeRange ASRelativeSizeRangeResolve(ASRelativeSizeRange relativeSizeRange,
CGSize parentSize)
{
return ASSizeRangeMake(ASLayoutSizeResolveSize(relativeSizeRange.min, parentSize, parentSize),
ASLayoutSizeResolveSize(relativeSizeRange.max, parentSize, parentSize));
}
-17
View File
@@ -147,23 +147,6 @@ ASDISPLAYNODE_EXTERN_C_END
@end
#pragma mark - Deprecated
@interface ASLayout (Deprecated)
- (id <ASLayoutElement>)layoutableObject ASDISPLAYNODE_DEPRECATED;
+ (instancetype)layoutWithLayoutableObject:(id<ASLayoutElement>)layoutElement
constrainedSizeRange:(ASSizeRange)constrainedSizeRange
size:(CGSize)size ASDISPLAYNODE_DEPRECATED;
+ (instancetype)layoutWithLayoutableObject:(id<ASLayoutElement>)layoutElement
constrainedSizeRange:(ASSizeRange)constrainedSizeRange
size:(CGSize)size
sublayouts:(nullable NSArray<ASLayout *> *)sublayouts AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED;
@end
#pragma mark - Debugging
@interface ASLayout (Debugging)
+21 -24
View File
@@ -271,6 +271,27 @@ static std::atomic_bool static_retainsSublayoutLayoutElements = ATOMIC_VAR_INIT(
return layout;
}
#pragma mark - Equality Checking
- (BOOL)isEqual:(id)object
{
ASLayout *layout = ASDynamicCast(object, ASLayout);
if (layout == nil) {
return NO;
}
if (!CGSizeEqualToSize(_size, layout.size)) return NO;
if (!CGPointEqualToPoint(_position, layout.position)) return NO;
if (_layoutElement != layout.layoutElement) return NO;
NSArray *sublayouts = layout.sublayouts;
if (sublayouts != _sublayouts && (sublayouts == nil || _sublayouts == nil || ![_sublayouts isEqual:sublayouts])) {
return NO;
}
return YES;
}
#pragma mark - Accessors
- (ASLayoutElementType)type
@@ -353,30 +374,6 @@ static std::atomic_bool static_retainsSublayoutLayoutElements = ATOMIC_VAR_INIT(
@end
@implementation ASLayout (Deprecation)
- (id <ASLayoutElement>)layoutableObject
{
return self.layoutElement;
}
+ (instancetype)layoutWithLayoutableObject:(id<ASLayoutElement>)layoutElement
constrainedSizeRange:(ASSizeRange)constrainedSizeRange
size:(CGSize)size
{
return [self layoutWithLayoutElement:layoutElement size:size];
}
+ (instancetype)layoutWithLayoutableObject:(id<ASLayoutElement>)layoutElement
constrainedSizeRange:(ASSizeRange)constrainedSizeRange
size:(CGSize)size
sublayouts:(nullable NSArray<ASLayout *> *)sublayouts
{
return [self layoutWithLayoutElement:layoutElement size:size sublayouts:sublayouts];
}
@end
ASLayout *ASCalculateLayout(id<ASLayoutElement> layoutElement, const ASSizeRange sizeRange, const CGSize parentSize)
{
ASDisplayNodeCAssertNotNil(layoutElement, @"Not valid layoutElement passed in.");
-16
View File
@@ -147,22 +147,6 @@ typedef NS_ENUM(NSUInteger, ASLayoutElementType) {
- (BOOL)implementsLayoutMethod;
#pragma mark - Deprecated
#define ASLayoutable ASLayoutElement
/**
* @abstract Calculate a layout based on given size range.
*
* @param constrainedSize The minimum and maximum sizes the receiver should fit in.
*
* @return An ASLayout instance defining the layout of the receiver and its children.
*
* @deprecated Deprecated in version 2.0: Use layoutThatFits: or layoutThatFits:parentSize: if used in
* ASLayoutSpec subclasses
*/
- (nonnull ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize ASDISPLAYNODE_DEPRECATED_MSG("Use layoutThatFits: instead.");
@end
#pragma mark - ASLayoutElementStyle
-18
View File
@@ -804,22 +804,4 @@ do {\
#endif /* YOGA */
#pragma mark Deprecated
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- (ASRelativeSizeRange)sizeRange
{
return ASRelativeSizeRangeMake(self.minLayoutSize, self.maxLayoutSize);
}
- (void)setSizeRange:(ASRelativeSizeRange)sizeRange
{
self.minLayoutSize = sizeRange.min;
self.maxLayoutSize = sizeRange.max;
}
#pragma clang diagnostic pop
@end
-136
View File
@@ -51,11 +51,6 @@ NS_ASSUME_NONNULL_END
return [self layoutThatFits:constrainedSize parentSize:constrainedSize.max];\
}\
\
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize\
{\
return [self layoutThatFits:constrainedSize parentSize:constrainedSize.max];\
}\
\
- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize parentSize:(CGSize)parentSize\
{\
return [self calculateLayoutThatFits:constrainedSize restrictedToSize:self.style.size relativeToParentSize:parentSize];\
@@ -117,134 +112,3 @@ typedef struct ASLayoutElementStyleExtensions {
return [self.style layoutOptionExtensionEdgeInsetsAtIndex:idx];\
}\
#pragma mark ASLayoutElementStyleForwardingDeclaration (Deprecated)
#define ASLayoutElementStyleForwardingDeclaration \
@property (nonatomic, readwrite) CGFloat spacingBefore ASDISPLAYNODE_DEPRECATED_MSG("Use style.spacingBefore"); \
@property (nonatomic, readwrite) CGFloat spacingAfter ASDISPLAYNODE_DEPRECATED_MSG("Use style.spacingAfter"); \
@property (nonatomic, readwrite) CGFloat flexGrow ASDISPLAYNODE_DEPRECATED_MSG("Use style.flexGrow"); \
@property (nonatomic, readwrite) CGFloat flexShrink ASDISPLAYNODE_DEPRECATED_MSG("Use style.flexShrink"); \
@property (nonatomic, readwrite) ASDimension flexBasis ASDISPLAYNODE_DEPRECATED_MSG("Use style.flexBasis"); \
@property (nonatomic, readwrite) ASStackLayoutAlignSelf alignSelf ASDISPLAYNODE_DEPRECATED_MSG("Use style.alignSelf"); \
@property (nonatomic, readwrite) CGFloat ascender ASDISPLAYNODE_DEPRECATED_MSG("Use style.ascender"); \
@property (nonatomic, readwrite) CGFloat descender ASDISPLAYNODE_DEPRECATED_MSG("Use style.descender"); \
@property (nonatomic, assign) ASRelativeSizeRange sizeRange ASDISPLAYNODE_DEPRECATED_MSG("Don't use sizeRange anymore instead set style.width or style.height"); \
@property (nonatomic, assign) CGPoint layoutPosition ASDISPLAYNODE_DEPRECATED_MSG("Use style.layoutPosition"); \
#pragma mark - ASLayoutElementStyleForwarding (Deprecated)
// For the time beeing we are forwading all style related properties on ASDisplayNode and ASLayoutSpec. This define
// help us to not have duplicate code while moving from 1.x to 2.0s
#define ASLayoutElementStyleForwarding \
\
@dynamic spacingBefore, spacingAfter, flexGrow, flexShrink, flexBasis, alignSelf, ascender, descender, sizeRange, layoutPosition;\
\
_Pragma("mark - ASStackLayoutElement")\
\
- (void)setSpacingBefore:(CGFloat)spacingBefore\
{\
self.style.spacingBefore = spacingBefore;\
}\
\
- (CGFloat)spacingBefore\
{\
return self.style.spacingBefore;\
}\
\
- (void)setSpacingAfter:(CGFloat)spacingAfter\
{\
self.style.spacingAfter = spacingAfter;\
}\
\
- (CGFloat)spacingAfter\
{\
return self.style.spacingAfter;\
}\
\
- (void)setFlexGrow:(CGFloat)flexGrow\
{\
self.style.flexGrow = flexGrow;\
}\
\
- (CGFloat)flexGrow\
{\
return self.style.flexGrow;\
}\
\
- (void)setFlexShrink:(CGFloat)flexShrink\
{\
self.style.flexShrink = flexShrink;\
}\
\
- (CGFloat)flexShrink\
{\
return self.style.flexShrink;\
}\
\
- (void)setFlexBasis:(ASDimension)flexBasis\
{\
self.style.flexBasis = flexBasis;\
}\
\
- (ASDimension)flexBasis\
{\
return self.style.flexBasis;\
}\
\
- (void)setAlignSelf:(ASStackLayoutAlignSelf)alignSelf\
{\
self.style.alignSelf = alignSelf;\
}\
\
- (ASStackLayoutAlignSelf)alignSelf\
{\
return self.style.alignSelf;\
}\
\
- (void)setAscender:(CGFloat)ascender\
{\
self.style.ascender = ascender;\
}\
\
- (CGFloat)ascender\
{\
return self.style.ascender;\
}\
\
- (void)setDescender:(CGFloat)descender\
{\
self.style.descender = descender;\
}\
\
- (CGFloat)descender\
{\
return self.style.descender;\
}\
\
_Pragma("mark - ASAbsoluteLayoutElement")\
\
- (void)setLayoutPosition:(CGPoint)layoutPosition\
{\
self.style.layoutPosition = layoutPosition;\
}\
\
- (CGPoint)layoutPosition\
{\
return self.style.layoutPosition;\
}\
\
_Pragma("clang diagnostic push")\
_Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"")\
\
- (void)setSizeRange:(ASRelativeSizeRange)sizeRange\
{\
self.style.sizeRange = sizeRange;\
}\
\
- (ASRelativeSizeRange)sizeRange\
{\
return self.style.sizeRange;\
}\
\
_Pragma("clang diagnostic pop")\
-6
View File
@@ -104,10 +104,4 @@ NS_ASSUME_NONNULL_BEGIN
@end
@interface ASLayoutSpec (Deprecated)
ASLayoutElementStyleForwardingDeclaration
@end
NS_ASSUME_NONNULL_END
-20
View File
@@ -35,17 +35,6 @@
@dynamic layoutElementType;
@synthesize debugName = _debugName;
#pragma mark - Class
+ (void)initialize
{
[super initialize];
if (self != [ASLayoutSpec class]) {
ASDisplayNodeAssert(!ASSubclassOverridesSelector([ASLayoutSpec class], self, @selector(measureWithSizeRange:)), @"Subclass %@ must not override measureWithSizeRange: method. Instead override calculateLayoutThatFits:", NSStringFromClass(self));
}
}
#pragma mark - Lifecycle
- (instancetype)init
@@ -167,7 +156,6 @@ ASLayoutElementLayoutCalculationDefaults
}
ASPrimitiveTraitCollectionDefaults
ASPrimitiveTraitCollectionDeprecatedImplementation
#pragma mark - ASLayoutElementStyleExtensibility
@@ -353,11 +341,3 @@ ASLayoutElementStyleExtensibilityForwarding
}
@end
#pragma mark - ASLayoutSpec (Deprecated)
@implementation ASLayoutSpec (Deprecated)
ASLayoutElementStyleForwarding
@end
+3 -1
View File
@@ -15,9 +15,11 @@
#if YOGA /* YOGA */
#import <AsyncDisplayKit/ASLayout.h>
#import <AsyncDisplayKit/ASLog.h>
#import <AsyncDisplayKit/ASDisplayNode+Beta.h>
#define ASYogaLog(...) //NSLog(__VA_ARGS__)
// Should pass a string literal, not an NSString as the first argument to ASYogaLog
#define ASYogaLog(x, ...) as_log_verbose(ASLayoutLog(), x, ##__VA_ARGS__);
@interface ASDisplayNode (YogaHelpers)
+49 -12
View File
@@ -69,18 +69,35 @@ static const ASScrollDirection kASStaticScrollDirection = (ASScrollDirectionRigh
- (ASCollectionLayoutContext *)layoutContextWithElements:(ASElementMap *)elements
{
ASDisplayNodeAssertMainThread();
CGSize viewportSize = [self _viewportSize];
CGPoint contentOffset = _collectionNode.contentOffset;
Class<ASCollectionLayoutDelegate> layoutDelegateClass = [_layoutDelegate class];
ASCollectionLayoutCache *layoutCache = _layoutCache;
ASCollectionNode *collectionNode = _collectionNode;
if (collectionNode == nil) {
return [[ASCollectionLayoutContext alloc] initWithViewportSize:CGSizeZero
initialContentOffset:CGPointZero
scrollableDirections:ASScrollDirectionNone
elements:[[ASElementMap alloc] init]
layoutDelegateClass:layoutDelegateClass
layoutCache:layoutCache
additionalInfo:nil];
}
ASScrollDirection scrollableDirections = [_layoutDelegate scrollableDirections];
CGSize viewportSize = [ASCollectionLayout _viewportSizeForCollectionNode:collectionNode scrollableDirections:scrollableDirections];
CGPoint contentOffset = collectionNode.contentOffset;
id additionalInfo = nil;
if (_layoutDelegateFlags.implementsAdditionalInfoForLayoutWithElements) {
additionalInfo = [_layoutDelegate additionalInfoForLayoutWithElements:elements];
}
return [[ASCollectionLayoutContext alloc] initWithViewportSize:viewportSize
initialContentOffset:contentOffset
scrollableDirections:[_layoutDelegate scrollableDirections]
scrollableDirections:scrollableDirections
elements:elements
layoutDelegateClass:[_layoutDelegate class]
layoutCache:_layoutCache
layoutDelegateClass:layoutDelegateClass
layoutCache:layoutCache
additionalInfo:additionalInfo];
}
@@ -208,21 +225,41 @@ static const ASScrollDirection kASStaticScrollDirection = (ASScrollDirectionRigh
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
return (! CGSizeEqualToSize([self _viewportSize], newBounds.size));
return (! CGSizeEqualToSize([ASCollectionLayout _boundsForCollectionNode:_collectionNode], newBounds.size));
}
#pragma mark - Private methods
- (CGSize)_viewportSize
+ (CGSize)_boundsForCollectionNode:(nonnull ASCollectionNode *)collectionNode
{
ASCollectionNode *collectionNode = _collectionNode;
if (collectionNode != nil && !collectionNode.isNodeLoaded) {
if (collectionNode == nil) {
return CGSizeZero;
}
if (!collectionNode.isNodeLoaded) {
// TODO consider calculatedSize as well
return collectionNode.threadSafeBounds.size;
} else {
ASDisplayNodeAssertMainThread();
return self.collectionView.bounds.size;
}
ASDisplayNodeAssertMainThread();
return collectionNode.view.bounds.size;
}
+ (CGSize)_viewportSizeForCollectionNode:(nonnull ASCollectionNode *)collectionNode scrollableDirections:(ASScrollDirection)scrollableDirections
{
if (collectionNode == nil) {
return CGSizeZero;
}
CGSize result = [ASCollectionLayout _boundsForCollectionNode:collectionNode];
// TODO: Consider using adjustedContentInset on iOS 11 and later, to include the safe area of the scroll view
UIEdgeInsets contentInset = collectionNode.contentInset;
if (ASScrollDirectionContainsHorizontalDirection(scrollableDirections)) {
result.height -= (contentInset.top + contentInset.bottom);
} else {
result.width -= (contentInset.left + contentInset.right);
}
return result;
}
/**
@@ -75,6 +75,8 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, weak) id<ASCollectionViewLayoutInspecting> layoutInspector;
@property (nonatomic, assign) UIEdgeInsets contentInset;
@property (nonatomic, assign) CGPoint contentOffset;
/**
+16 -1
View File
@@ -23,6 +23,8 @@
#import <AsyncDisplayKit/ASDisplayNode+FrameworkSubclasses.h>
#import <AsyncDisplayKit/ASInternalHelpers.h>
#import <AsyncDisplayKit/ASSignpost.h>
#import <AsyncDisplayKit/ASDisplayNodeExtras.h>
@interface ASDisplayNode () <_ASDisplayLayerDelegate>
@end
@@ -378,6 +380,7 @@
}
CALayer *layer = _layer;
BOOL rasterizesSubtree = _flags.rasterizesSubtree;
__instanceLock__.unlock();
@@ -408,7 +411,7 @@
return;
}
ASDisplayNodeAssert(_layer, @"Expect _layer to be not nil");
ASDisplayNodeAssert(layer, @"Expect _layer to be not nil");
// This block is called back on the main thread after rendering at the completion of the current async transaction, or immediately if !asynchronously
asyncdisplaykit_async_transaction_operation_completion_block_t completionBlock = ^(id<NSObject> value, BOOL canceled){
@@ -423,11 +426,23 @@
layer.contents = (id)image.CGImage;
}
[self didDisplayAsyncLayer:self.asyncLayer];
if (rasterizesSubtree) {
ASDisplayNodePerformBlockOnEverySubnode(self, NO, ^(ASDisplayNode * _Nonnull node) {
[node didDisplayAsyncLayer:node.asyncLayer];
});
}
}
};
// Call willDisplay immediately in either case
[self willDisplayAsyncLayer:self.asyncLayer asynchronously:asynchronously];
if (rasterizesSubtree) {
ASDisplayNodePerformBlockOnEverySubnode(self, NO, ^(ASDisplayNode * _Nonnull node) {
[node willDisplayAsyncLayer:node.asyncLayer asynchronously:asynchronously];
});
}
if (asynchronously) {
// Async rendering operations are contained by a transaction, which allows them to proceed and concurrently
+1 -2
View File
@@ -52,8 +52,6 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides)
ASDisplayNodeMethodOverrideLayoutSpecThatFits = 1 << 4,
ASDisplayNodeMethodOverrideCalcLayoutThatFits = 1 << 5,
ASDisplayNodeMethodOverrideCalcSizeThatFits = 1 << 6,
ASDisplayNodeMethodOverrideFetchData = 1 << 7,
ASDisplayNodeMethodOverrideClearFetchedData = 1 << 8
};
typedef NS_OPTIONS(uint_least32_t, ASDisplayNodeAtomicFlags)
@@ -199,6 +197,7 @@ FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBefo
NSArray *_accessibilityHeaderElements;
CGPoint _accessibilityActivationPoint;
UIBezierPath *_accessibilityPath;
BOOL _isAccessibilityContainer;
// performance measurement
ASDisplayNodePerformanceMeasurementOptions _measurementOptions;
+1 -8
View File
@@ -33,6 +33,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, weak) id<ASTableDelegate> asyncDelegate;
@property (nonatomic, weak) id<ASTableDataSource> asyncDataSource;
@property (nonatomic, assign) UIEdgeInsets contentInset;
@property (nonatomic, assign) CGPoint contentOffset;
@property (nonatomic, assign) BOOL automaticallyAdjustsContentOffset;
@property (nonatomic, assign) BOOL inverted;
@@ -154,14 +155,6 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (void)reloadData;
/**
* Reload everything from scratch entirely on the main thread, destroying the working range and all cached nodes.
*
* @warning This method is substantially more expensive than UITableView's version and will block the main thread while
* all the cells load.
*/
- (void)reloadDataImmediately;
/**
* Triggers a relayout of all nodes.
*
+2 -2
View File
@@ -28,7 +28,7 @@ NS_ASSUME_NONNULL_BEGIN
AS_SUBCLASSING_RESTRICTED
@interface ASWeakMapEntry<Value> : NSObject
@property (nonatomic, retain, readonly) Value value;
@property (atomic, strong, readonly) Value value;
@end
@@ -49,7 +49,7 @@ AS_SUBCLASSING_RESTRICTED
* The underlying storage is a hash table and the Key type should implement `hash` and `isEqual:`.
*/
AS_SUBCLASSING_RESTRICTED
@interface ASWeakMap<__covariant Key : NSObject *, Value> : NSObject
@interface ASWeakMap<__covariant Key, Value> : NSObject
/**
* Read from the cache. The Value object is accessible from the returned ASWeakMapEntry.
+6 -10
View File
@@ -18,12 +18,13 @@
#import <AsyncDisplayKit/ASWeakMap.h>
@interface ASWeakMapEntry ()
@property (nonatomic, strong) NSObject *key;
@property (nonatomic, strong, readonly) id key;
@property (atomic, strong) id value;
@end
@implementation ASWeakMapEntry
- (instancetype)initWithKey:(NSObject *)key value:(NSObject *)value
- (instancetype)initWithKey:(id)key value:(id)value
{
self = [super init];
if (self) {
@@ -33,16 +34,11 @@
return self;
}
- (void)setValue:(NSObject *)value
{
_value = value;
}
@end
@interface ASWeakMap ()
@property (nonatomic, strong) NSMapTable<NSObject *, ASWeakMapEntry *> *hashTable;
@property (nonatomic, strong, readonly) NSMapTable<id, ASWeakMapEntry *> *hashTable;
@end
/**
@@ -69,12 +65,12 @@
return self;
}
- (ASWeakMapEntry *)entryForKey:(NSObject *)key
- (ASWeakMapEntry *)entryForKey:(id)key
{
return [self.hashTable objectForKey:key];
}
- (ASWeakMapEntry *)setObject:(NSObject *)value forKey:(NSObject *)key
- (ASWeakMapEntry *)setObject:(id)value forKey:(id)key
{
ASWeakMapEntry *entry = [self.hashTable objectForKey:key];
if (entry != nil) {
@@ -71,7 +71,7 @@ static ASLayout *crossChildLayout(const ASStackLayoutSpecChild &child,
crossMax);
const ASSizeRange childSizeRange = directionSizeRange(style.direction, stackMin, stackMax, childCrossMin, childCrossMax);
ASLayout *layout = [child.element layoutThatFits:childSizeRange parentSize:parentSize];
ASDisplayNodeCAssertNotNil(layout, @"ASLayout returned from measureWithSizeRange: must not be nil: %@", child.element);
ASDisplayNodeCAssertNotNil(layout, @"ASLayout returned from -layoutThatFits:parentSize: must not be nil: %@", child.element);
return layout ? : [ASLayout layoutWithLayoutElement:child.element size:{0, 0}];
}
@@ -38,7 +38,6 @@
ASLayoutElementStyleExtensibilityForwarding
ASPrimitiveTraitCollectionDefaults
ASPrimitiveTraitCollectionDeprecatedImplementation
- (ASTraitCollection *)asyncTraitCollection
{
+3 -11
View File
@@ -25,19 +25,11 @@
#import <AsyncDisplayKit/ASDataController.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
// If assertions are enabled and they haven't forced us to suppress the exception,
// then throw, otherwise log.
// If assertions are enabled, throw. Otherwise log.
#if ASDISPLAYNODE_ASSERTIONS_ENABLED
#define ASFailUpdateValidation(...)\
_Pragma("clang diagnostic push")\
_Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"")\
if ([ASDisplayNode suppressesInvalidCollectionUpdateExceptions]) {\
NSLog(__VA_ARGS__);\
} else {\
NSLog(__VA_ARGS__);\
[NSException raise:ASCollectionInvalidUpdateException format:__VA_ARGS__];\
}\
_Pragma("clang diagnostic pop")
NSLog(__VA_ARGS__);\
[NSException raise:ASCollectionInvalidUpdateException format:__VA_ARGS__];
#else
#define ASFailUpdateValidation(...) NSLog(__VA_ARGS__);
#endif
+1 -1
View File
@@ -71,7 +71,7 @@
- (void)tearDown
{
[collectionNode waitUntilAllUpdatesAreCommitted];
[collectionNode waitUntilAllUpdatesAreProcessed];
[super tearDown];
}
+20 -11
View File
@@ -260,7 +260,7 @@
[window makeKeyAndVisible];
[testController.collectionNode reloadData];
[testController.collectionNode waitUntilAllUpdatesAreCommitted];
[testController.collectionNode waitUntilAllUpdatesAreProcessed];
[testController.collectionView layoutIfNeeded];
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:0];
@@ -397,7 +397,7 @@
window.rootViewController = testController;\
\
[cn reloadData];\
[cn waitUntilAllUpdatesAreCommitted]; \
[cn waitUntilAllUpdatesAreProcessed]; \
[testController.collectionView layoutIfNeeded];
- (void)testThatSubmittingAValidInsertDoesNotThrowAnException
@@ -620,7 +620,7 @@
[window makeKeyAndVisible];
for (NSInteger i = 0; i < 2; i++) {
// NOTE: waitUntilAllUpdatesAreCommitted or reloadDataImmediately is not sufficient here!!
// NOTE: reloadData and waitUntilAllUpdatesAreProcessed are not sufficient here!!
XCTestExpectation *done = [self expectationWithDescription:[NSString stringWithFormat:@"Reload #%td complete", i]];
[cn reloadDataWithCompletion:^{
[done fulfill];
@@ -755,7 +755,7 @@
del.sectionGeneration++;
[cn reloadData];
[cn waitUntilAllUpdatesAreCommitted];
[cn waitUntilAllUpdatesAreProcessed];
NSInteger sectionCount = del->_itemCounts.size();
for (NSInteger section = 0; section < sectionCount; section++) {
@@ -857,7 +857,7 @@
[window layoutIfNeeded];
ASCollectionNode *cn = testController.collectionNode;
[cn waitUntilAllUpdatesAreCommitted];
[cn waitUntilAllUpdatesAreProcessed];
[cn.view layoutIfNeeded];
ASCellNode *node = [cn nodeForItemAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]];
XCTAssertTrue(node.visible);
@@ -880,7 +880,7 @@
[window layoutIfNeeded];
ASCollectionNode *cn = testController.collectionNode;
[cn waitUntilAllUpdatesAreCommitted];
[cn waitUntilAllUpdatesAreProcessed];
XCTAssertGreaterThan(cn.bounds.size.height, cn.view.contentSize.height, @"Expected initial data not to fill collection view area.");
__block NSUInteger batchFetchCount = 0;
@@ -926,7 +926,7 @@
[window layoutIfNeeded];
ASCollectionNode *cn = testController.collectionNode;
[cn waitUntilAllUpdatesAreCommitted];
[cn waitUntilAllUpdatesAreProcessed];
__block NSUInteger batchFetchCount = 0;
XCTestExpectation *e = [self expectationWithDescription:@"Batch fetching completed"];
@@ -1020,7 +1020,7 @@
[view layoutIfNeeded];
// Wait for ASDK reload to finish
[cn waitUntilAllUpdatesAreCommitted];
[cn waitUntilAllUpdatesAreProcessed];
// Force UIKit to read updated data & range controller to update and account for it
[cn.view layoutIfNeeded];
[self waitForExpectationsWithTimeout:60 handler:nil];
@@ -1050,8 +1050,17 @@
// Trigger the initial reload to start
[window layoutIfNeeded];
// Test the APIs that monitor ASCollectionNode update handling
XCTAssertTrue(cn.isProcessingUpdates, @"ASCollectionNode should still be processing updates after initial layoutIfNeeded call (reloadData)");
[cn onDidFinishProcessingUpdates:^{
XCTAssertTrue(!cn.isProcessingUpdates, @"ASCollectionNode should no longer be processing updates inside -onDidFinishProcessingUpdates: block");
}];
// Wait for ASDK reload to finish
[cn waitUntilAllUpdatesAreCommitted];
[cn waitUntilAllUpdatesAreProcessed];
XCTAssertTrue(!cn.isProcessingUpdates, @"ASCollectionNode should no longer be processing updates after -wait call");
// Force UIKit to read updated data & range controller to update and account for it
[cn.view layoutIfNeeded];
@@ -1093,7 +1102,7 @@
traitCollection.containerSize = screenBounds.size;
cn.primitiveTraitCollection = traitCollection;
[cn waitUntilAllUpdatesAreCommitted];
[cn waitUntilAllUpdatesAreProcessed];
[cn.view layoutIfNeeded];
// Assert that the new trait collection is picked up by all cell nodes, including ones that were not allocated but are forced to allocate now
@@ -1124,7 +1133,7 @@
[window makeKeyAndVisible];
[window layoutIfNeeded];
[cn waitUntilAllUpdatesAreCommitted];
[cn waitUntilAllUpdatesAreProcessed];
for (NSInteger i = 0; i < itemCount; i++) {
ASTextCellNodeWithSetSelectedCounter *node = [cn nodeForItemAtIndexPath:[NSIndexPath indexPathForItem:i inSection:0]];
XCTAssert(node.automaticallyManagesSubnodes, @"Expected test cell node to use automatic subnode management. Can modify the test with a different class if needed.");
+1 -1
View File
@@ -44,7 +44,7 @@
ASXCTAssertEqualSizes(displayNode.calculatedSize, CGSizeZero, @"Calculated size before measurement and layout should be 0");
ASXCTAssertEqualSizes(buttonNode.calculatedSize, CGSizeZero, @"Calculated size before measurement and layout should be 0");
// Trigger view creation and layout pass without a manual measure: call before so the automatic measurement
// Trigger view creation and layout pass without a manual -layoutThatFits: call before so the automatic measurement
// pass will trigger in the layout pass
[displayNode.view layoutIfNeeded];
+49 -22
View File
@@ -24,7 +24,6 @@
#import <AsyncDisplayKit/_ASDisplayView.h>
#import <AsyncDisplayKit/ASDisplayNode+Subclasses.h>
#import <AsyncDisplayKit/ASDisplayNode+FrameworkPrivate.h>
#import <AsyncDisplayKit/ASDisplayNode+Deprecated.h>
#import <AsyncDisplayKit/ASDisplayNodeInternal.h>
#import "ASDisplayNodeTestsHelper.h"
#import <AsyncDisplayKit/UIView+ASConvenience.h>
@@ -111,6 +110,10 @@ for (ASDisplayNode *n in @[ nodes ]) {\
@property (nonatomic) BOOL hasPreloaded;
@property (nonatomic) BOOL preloadStateChangedToYES;
@property (nonatomic) BOOL preloadStateChangedToNO;
@property (nonatomic, assign) NSUInteger displayWillStartCount;
@property (nonatomic, assign) NSUInteger didDisplayCount;
@end
@interface ASTestResponderNode : ASTestDisplayNode
@@ -155,6 +158,18 @@ for (ASDisplayNode *n in @[ nodes ]) {\
}
}
- (void)displayDidFinish
{
[super displayDidFinish];
_didDisplayCount++;
}
- (void)displayWillStartAsynchronously:(BOOL)asynchronously
{
[super displayWillStartAsynchronously:asynchronously];
_displayWillStartCount++;
}
@end
@interface UIDisplayNodeTestView : UIView
@@ -2019,6 +2034,39 @@ static bool stringContainsPointer(NSString *description, id p) {
XCTAssertThrows([rasterizedSupernode addSubnode:subnode]);
}
- (void)testThatSubnodesGetDisplayUpdatesIfRasterized
{
ASTestDisplayNode *supernode = [[ASTestDisplayNode alloc] init];
supernode.frame = CGRectMake(0.0, 0.0, 100.0, 100.0);
[supernode enableSubtreeRasterization];
ASTestDisplayNode *subnode = [[ASTestDisplayNode alloc] init];
ASTestDisplayNode *subSubnode = [[ASTestDisplayNode alloc] init];
ASSetDebugNames(supernode, subnode);
UIWindow *window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
[subnode addSubnode:subSubnode];
[supernode addSubnode:subnode];
[window addSubnode:supernode];
[window makeKeyAndVisible];
XCTAssertTrue(ASDisplayNodeRunRunLoopUntilBlockIsTrue(^BOOL{
return (subnode.didDisplayCount == 1);
}));
XCTAssertTrue(ASDisplayNodeRunRunLoopUntilBlockIsTrue(^BOOL{
return (subSubnode.didDisplayCount == 1);
}));
XCTAssertTrue(ASDisplayNodeRunRunLoopUntilBlockIsTrue(^BOOL{
return (subnode.displayWillStartCount == 1);
}));
XCTAssertTrue(ASDisplayNodeRunRunLoopUntilBlockIsTrue(^BOOL{
return (subSubnode.displayWillStartCount == 1);
}));
}
// Underlying issue for: https://github.com/facebook/AsyncDisplayKit/issues/2011
- (void)testThatLayerBackedSubnodesAreMarkedInvisibleBeforeDeallocWhenSupernodesViewIsRemovedFromHierarchyWhileBeingRetained
{
@@ -2128,27 +2176,6 @@ static bool stringContainsPointer(NSString *description, id p) {
XCTAssertEqualObjects(calls, expected);
}
- (void)testPreferredFrameSizeDeprecated
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
ASDisplayNode *node = [ASDisplayNode new];
// Default auto preferred frame size will be CGSizeZero
XCTAssert(CGSizeEqualToSize(node.preferredFrameSize, CGSizeZero));
// Set a specific preferredFrameSize
node.preferredFrameSize = CGSizeMake(100, 100);
ASXCTAssertEqualSizes(node.preferredFrameSize, CGSizeMake(100, 100));
// CGSizeZero should be returned if width or height is not of unit type points
node.style.width = ASDimensionMakeWithFraction(0.5);
ASXCTAssertEqualSizes(node.preferredFrameSize, CGSizeZero);
#pragma clang diagnostic pop
}
- (void)testSettingPropertiesViaStyllableProtocol
{
ASDisplayNode *node = [[ASDisplayNode alloc] init];
+12 -3
View File
@@ -2,8 +2,17 @@
// ASLayoutSpecTests.m
// Texture
//
// Created by Michael Schneider on 1/27/17.
// Copyright © 2017 Facebook. All rights reserved.
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional
// grant of patent rights can be found in the PATENTS file in the same directory.
//
// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present,
// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
#import <XCTest/XCTest.h>
@@ -39,7 +48,7 @@ ASDK_STYLE_PROP_OBJ(NSString *, extendedName, setExtendedName);
@end
/*
* As the ASLayoutableStyle conforms to the ASDKExtendedLayoutable protocol now, ASDKExtendedLayoutable properties
* As the ASLayoutElementStyle conforms to the ASDKExtendedLayoutElement protocol now, ASDKExtendedLayoutElement properties
* can be accessed in ASDKExtendedLayoutSpec
*/
@interface ASDKExtendedLayoutSpec : ASLayoutSpec
+2 -2
View File
@@ -138,7 +138,7 @@
XCTAssertEqualObjects(NSStringFromCGRect(window.bounds), NSStringFromCGRect(node.frame));
XCTAssertEqualObjects(NSStringFromCGRect(window.bounds), NSStringFromCGRect(cell.frame));
XCTAssertEqual(pagerNode.contentOffset.y, 0);
XCTAssertEqual(pagerNode.view.contentInset.top, 0);
XCTAssertEqual(pagerNode.contentInset.top, 0);
e = [self expectationWithDescription:@"Transition completed"];
// Push another view controller
@@ -168,7 +168,7 @@
XCTAssertEqualObjects(NSStringFromCGRect(window.bounds), NSStringFromCGRect(node.frame));
XCTAssertEqualObjects(NSStringFromCGRect(window.bounds), NSStringFromCGRect(cell.frame));
XCTAssertEqual(pagerNode.contentOffset.y, 0);
XCTAssertEqual(pagerNode.view.contentInset.top, 0);
XCTAssertEqual(pagerNode.contentInset.top, 0);
}
@end
+160
View File
@@ -0,0 +1,160 @@
//
// ASRunLoopQueueTests.m
// Texture
//
// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
#import <XCTest/XCTest.h>
#import <AsyncDisplayKit/ASRunLoopQueue.h>
static NSTimeInterval const kRunLoopRunTime = 0.001; // Allow the RunLoop to run for one millisecond each time.
@interface ASRunLoopQueueTests : XCTestCase
@end
@implementation ASRunLoopQueueTests
#pragma mark enqueue tests
- (void)testEnqueueNilObjectsToQueue
{
ASRunLoopQueue *queue = [[ASRunLoopQueue alloc] initWithRunLoop:CFRunLoopGetMain() retainObjects:YES handler:nil];
id object = nil;
[queue enqueue:object];
XCTAssertTrue(queue.isEmpty);
}
- (void)testEnqueueSameObjectTwiceToDefaultQueue
{
id object = [[NSObject alloc] init];
__unsafe_unretained id weakObject = object;
__block NSUInteger dequeuedCount = 0;
ASRunLoopQueue *queue = [[ASRunLoopQueue alloc] initWithRunLoop:CFRunLoopGetMain() retainObjects:YES handler:^(id _Nonnull dequeuedItem, BOOL isQueueDrained) {
if (dequeuedItem == weakObject) {
dequeuedCount++;
}
}];
[queue enqueue:object];
[queue enqueue:object];
[[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:kRunLoopRunTime]];
XCTAssert(dequeuedCount == 1);
}
- (void)testEnqueueSameObjectTwiceToNonExclusiveMembershipQueue
{
id object = [[NSObject alloc] init];
__unsafe_unretained id weakObject = object;
__block NSUInteger dequeuedCount = 0;
ASRunLoopQueue *queue = [[ASRunLoopQueue alloc] initWithRunLoop:CFRunLoopGetMain() retainObjects:YES handler:^(id _Nonnull dequeuedItem, BOOL isQueueDrained) {
if (dequeuedItem == weakObject) {
dequeuedCount++;
}
}];
queue.ensureExclusiveMembership = NO;
[queue enqueue:object];
[queue enqueue:object];
[[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:kRunLoopRunTime]];
XCTAssert(dequeuedCount == 2);
}
#pragma mark processQueue tests
- (void)testDefaultQueueProcessObjectsOneAtATime
{
ASRunLoopQueue *queue = [[ASRunLoopQueue alloc] initWithRunLoop:CFRunLoopGetMain() retainObjects:YES handler:^(id _Nonnull dequeuedItem, BOOL isQueueDrained) {
[NSThread sleepForTimeInterval:kRunLoopRunTime * 2]; // So each element takes more time than the available
}];
[queue enqueue:[[NSObject alloc] init]];
[queue enqueue:[[NSObject alloc] init]];
[[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:kRunLoopRunTime]];
XCTAssertFalse(queue.isEmpty);
}
- (void)testQueueProcessObjectsInBatchesOfSpecifiedSize
{
ASRunLoopQueue *queue = [[ASRunLoopQueue alloc] initWithRunLoop:CFRunLoopGetMain() retainObjects:YES handler:^(id _Nonnull dequeuedItem, BOOL isQueueDrained) {
[NSThread sleepForTimeInterval:kRunLoopRunTime * 2]; // So each element takes more time than the available
}];
queue.batchSize = 2;
[queue enqueue:[[NSObject alloc] init]];
[queue enqueue:[[NSObject alloc] init]];
[[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:kRunLoopRunTime]];
XCTAssertTrue(queue.isEmpty);
}
- (void)testQueueOnlySendsIsDrainedForLastObjectInBatch
{
id objectA = [[NSObject alloc] init];
id objectB = [[NSObject alloc] init];
__unsafe_unretained id weakObjectA = objectA;
__unsafe_unretained id weakObjectB = objectB;
__block BOOL isQueueDrainedWhenProcessingA = NO;
__block BOOL isQueueDrainedWhenProcessingB = NO;
ASRunLoopQueue *queue = [[ASRunLoopQueue alloc] initWithRunLoop:CFRunLoopGetMain() retainObjects:YES handler:^(id _Nonnull dequeuedItem, BOOL isQueueDrained) {
if (dequeuedItem == weakObjectA) {
isQueueDrainedWhenProcessingA = isQueueDrained;
} else if (dequeuedItem == weakObjectB) {
isQueueDrainedWhenProcessingB = isQueueDrained;
}
}];
queue.batchSize = 2;
[queue enqueue:objectA];
[queue enqueue:objectB];
[[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:kRunLoopRunTime]];
XCTAssertFalse(isQueueDrainedWhenProcessingA);
XCTAssertTrue(isQueueDrainedWhenProcessingB);
}
#pragma mark strong/weak tests
- (void)testStrongQueueRetainsObjects
{
id object = [[NSObject alloc] init];
__unsafe_unretained id weakObject = object;
__block BOOL didProcessObject = NO;
ASRunLoopQueue *queue = [[ASRunLoopQueue alloc] initWithRunLoop:CFRunLoopGetMain() retainObjects:YES handler:^(id _Nonnull dequeuedItem, BOOL isQueueDrained) {
if (dequeuedItem == weakObject) {
didProcessObject = YES;
}
}];
[queue enqueue:object];
object = nil;
[[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:kRunLoopRunTime]];
XCTAssertTrue(didProcessObject);
}
- (void)testWeakQueueDoesNotRetainsObjects
{
id object = [[NSObject alloc] init];
__unsafe_unretained id weakObject = object;
__block BOOL didProcessObject = NO;
ASRunLoopQueue *queue = [[ASRunLoopQueue alloc] initWithRunLoop:CFRunLoopGetMain() retainObjects:NO handler:^(id _Nonnull dequeuedItem, BOOL isQueueDrained) {
if (dequeuedItem == weakObject) {
didProcessObject = YES;
}
}];
[queue enqueue:object];
object = nil;
[[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:kRunLoopRunTime]];
XCTAssertFalse(didProcessObject);
}
- (void)testWeakQueueWithAllDeallocatedObjectsIsDrained
{
ASRunLoopQueue *queue = [[ASRunLoopQueue alloc] initWithRunLoop:CFRunLoopGetMain() retainObjects:NO handler:nil];
id object = [[NSObject alloc] init];
[queue enqueue:object];
object = nil;
XCTAssertFalse(queue.isEmpty);
[[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:kRunLoopRunTime]];
XCTAssertTrue(queue.isEmpty);
}
@end
+10 -9
View File
@@ -253,7 +253,8 @@
tableView.asyncDelegate = delegate;
tableView.asyncDataSource = dataSource;
[tableView reloadDataImmediately];
[tableView reloadData];
[tableView waitUntilAllUpdatesAreCommitted];
[tableView setNeedsLayout];
[tableView layoutIfNeeded];
@@ -610,7 +611,7 @@
[UITableView as_recordEditingCallsIntoArray:selectors];
XCTAssertGreaterThan(node.numberOfSections, 0);
[node waitUntilAllUpdatesAreCommitted];
[node waitUntilAllUpdatesAreProcessed];
XCTAssertGreaterThan(node.view.numberOfSections, 0);
// The first reloadData call helps prevent UITableView from calling it multiple times while ASDataController is working.
@@ -635,13 +636,13 @@
// Load initial data.
XCTAssertGreaterThan(node.numberOfSections, 0);
[node waitUntilAllUpdatesAreCommitted];
[node waitUntilAllUpdatesAreProcessed];
XCTAssertGreaterThan(node.view.numberOfSections, 0);
// Reload data.
[UITableView as_recordEditingCallsIntoArray:selectors];
[node reloadData];
[node waitUntilAllUpdatesAreCommitted];
[node waitUntilAllUpdatesAreProcessed];
// Assert that the beginning of the call pattern is correct.
// There is currently noise that comes after that we will allow for this test.
@@ -668,7 +669,7 @@
// Trigger data load BEFORE first layout pass, to ensure constrained size is correct.
XCTAssertGreaterThan(node.numberOfSections, 0);
[node waitUntilAllUpdatesAreCommitted];
[node waitUntilAllUpdatesAreProcessed];
ASSizeRange expectedSizeRange = ASSizeRangeMake(CGSizeMake(cellWidth, 0));
expectedSizeRange.max.height = CGFLOAT_MAX;
@@ -703,7 +704,7 @@
// So we need to force a new layout pass so that the table will pick up a new constrained size and apply to its node.
[node setNeedsLayout];
[node.view layoutIfNeeded];
[node waitUntilAllUpdatesAreCommitted];
[node waitUntilAllUpdatesAreProcessed];
UITableViewCell *cell = [node.view cellForRowAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]];
XCTAssertNotNil(cell);
@@ -758,7 +759,7 @@
[window makeKeyAndVisible];
[window layoutIfNeeded];
[node waitUntilAllUpdatesAreCommitted];
[node waitUntilAllUpdatesAreProcessed];
XCTAssertEqual(node.view.numberOfSections, NumberOfSections);
ASXCTAssertEqualRects(CGRectMake(0, 32, 375, 44), [node rectForRowAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]], @"This text requires very specific geometry. The rect for the first row should match up.");
@@ -812,7 +813,7 @@
node.dataSource = ds;
[node.view layoutIfNeeded];
[node waitUntilAllUpdatesAreCommitted];
[node waitUntilAllUpdatesAreProcessed];
CGFloat rowHeight = [node.view rectForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]].size.height;
// Scroll to row (0,1) + 10pt
node.contentOffset = CGPointMake(0, rowHeight + 10);
@@ -825,7 +826,7 @@
[node deleteRowsAtIndexPaths:@[ [NSIndexPath indexPathForItem:0 inSection:i]] withRowAnimation:UITableViewRowAnimationAutomatic];
}
} completion:nil];
[node waitUntilAllUpdatesAreCommitted];
[node waitUntilAllUpdatesAreProcessed];
// Now that row (0,0) is deleted, we should have slid up to be at just 10
// i.e. we should have subtracted the deleted row height from our content offset.
+2 -1
View File
@@ -226,7 +226,8 @@ static atomic_uint ASThrashTestSectionNextID = 1;
#else
_tableView.asyncDelegate = self;
_tableView.asyncDataSource = self;
[_tableView reloadDataImmediately];
[_tableView reloadData];
[_tableView waitUntilAllUpdatesAreCommitted];
#endif
[_tableView layoutIfNeeded];
}
+2 -2
View File
@@ -45,7 +45,7 @@ Pod::Spec.new do |spec|
end
spec.subspec 'PINRemoteImage' do |pin|
pin.dependency 'PINRemoteImage/iOS', '= 3.0.0-beta.11'
pin.dependency 'PINRemoteImage/iOS', '= 3.0.0-beta.12'
pin.dependency 'PINRemoteImage/PINCache'
pin.dependency 'Texture/Core'
end
@@ -57,7 +57,7 @@ Pod::Spec.new do |spec|
spec.subspec 'Yoga' do |yoga|
yoga.xcconfig = { 'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) YOGA=1' }
yoga.dependency 'Yoga', '1.5.0'
yoga.dependency 'Yoga', '1.6.0'
yoga.dependency 'Texture/Core'
end
+3 -3
View File
@@ -17,7 +17,7 @@ When it comes to corner rounding, many developers stick with CALayer's `.cornerR
## CALayer's .cornerRadius is Expensive
Why is `.cornerRadius` so expensive? Use of CALayer's `.cornerRadius` property triggers off-screen rendering to perform the clipping operation on every frame - 60 FPS during scrolling - even if the content in that area isn't changing! This means that the GPU has to switch contexts on every frame, between compositing the overall frame + additional passes for each use of `.cornerRadius`.
Why is `.cornerRadius` so expensive? Use of CALayer's `.cornerRadius` property triggers offscreen rendering to perform the clipping operation on every frame - 60 FPS during scrolling - even if the content in that area isn't changing! This means that the GPU has to switch contexts on every frame, between compositing the overall frame + additional passes for each use of `.cornerRadius`.
Importantly, these costs don't show up in the Time Profiler, because they affect work done by the CoreAnimation Render Server on your app's behalf. This intensive thrash annihilates performance for a lot of devices. On the iPhone 4, 4S, and 5 / 5C (along with comparable iPads / iPods), expect to see notably degraded performance. On the iPhone 5S and newer, even if you can't see the impact directly, it will reduce headroom so that it takes less to cause a frame drop.
@@ -53,11 +53,11 @@ The final consideration is to determine if all four corners cover the same node
### Precomposited Corners
Precomposited corners refer to corners drawn using bezier paths to clip the content in a CGContext / UIGraphicsContext. In this scenario, the corners become part of the image itself — and are "baked in" to the single CALayer. There are two types of precomposited corners.
Precomposited corners refer to corners drawn using bezier paths to clip the content in a CGContext / UIGraphicsContext (`[path clip]`). In this scenario, the corners become part of the image itself — and are "baked in" to the single CALayer. There are two types of precomposited corners.
The absolute best method is to use **precomposited opaque corners**. This is the most efficient method available, resulting in zero alpha blending (although this is much less critical than avoiding offscreen rendering). Unfortunately, this method is also the least flexible; the background behind the corners will need to be a solid color if the rounded image needs to move around on top of it. It's possible, but tricky to make precomposited corners with a textured or photo background - usually it's best to use precomposited alpha corners instead'.'
The second method involves using bezier paths with **precomposited alpha corners** (`[path clip]`). This method is pretty flexible and should be one of the most frequently used. It does incur the cost of alpha blending across the full size of the content, and including an alpha channel increases memory impact by 25% over opaque precompositing - but these costs are tiny on modern devices, and a different order of magnitude than `.cornerRadius` offscreen rendering.
The second method involves using bezier paths with **precomposited alpha corners**. This method is pretty flexible and should be one of the most frequently used. It does incur the cost of alpha blending across the full size of the content, and including an alpha channel increases memory impact by 25% over opaque precompositing - but these costs are tiny on modern devices, and a different order of magnitude than `.cornerRadius` offscreen rendering.
A key limitation of precomposited corners is that the corners must only touch one node and not intersect with any subnodes. If either of these conditions exist, clip corners must be used.
@@ -112,7 +112,7 @@
#pragma mark - ASCollectionGalleryLayoutPropertiesProviding
- (CGSize)sizeForElements:(ASElementMap *)elements
- (CGSize)galleryLayoutDelegate:(ASCollectionGalleryLayoutDelegate *)delegate sizeForElements:(ASElementMap *)elements
{
ASDisplayNodeAssertMainThread();
return CGSizeMake(180, 90);
@@ -59,7 +59,7 @@
[_activityIndicatorView stopAnimating];
[self insertNewRows:newPhotos];
[self.tableView reloadData];
[self requestCommentsForPhotos:newPhotos];
// immediately start second larger fetch
@@ -74,7 +74,8 @@
NSMutableArray *indexPaths = [NSMutableArray array];
NSInteger newTotalNumberOfPhotos = [_photoFeed numberOfItemsInFeed];
for (NSInteger row = newTotalNumberOfPhotos - newPhotos.count; row < newTotalNumberOfPhotos; row++) {
NSInteger existingNumberOfPhotos = newTotalNumberOfPhotos - newPhotos.count;
for (NSInteger row = existingNumberOfPhotos; row < newTotalNumberOfPhotos; row++) {
NSIndexPath *path = [NSIndexPath indexPathForRow:row inSection:section];
[indexPaths addObject:path];
}
+12 -14
View File
@@ -1,20 +1,18 @@
//
// PageNode.m
// Sample
//
// Created by McCallum, Levi on 12/7/15.
// Texture
//
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional
// grant of patent rights can be found in the PATENTS file in the same directory.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present,
// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
#import "PageNode.h"
@@ -26,10 +24,10 @@
return constrainedSize;
}
- (void)fetchData
- (void)didEnterPreloadState
{
[super fetchData];
NSLog(@"Fetching data for node: %@", self);
[super didEnterPreloadState];
NSLog(@"didEnterPreloadState for node: %@", self);
}
@end