Merge commit '771c068ad67a2ecde7f0da2bf727e0326d156c54'

# Conflicts:
#	Source/ASVideoPlayerNode.h
#	Source/Details/ASElementMap.mm
#	Source/Details/_ASCollectionReusableView.mm
#	Source/Details/_ASCollectionViewCell.mm
#	Source/Private/ASMutableElementMap.mm
This commit is contained in:
Peter Iakovlev
2018-11-16 14:41:49 +04:00
223 changed files with 3785 additions and 3054 deletions

View File

@@ -0,0 +1,3 @@
issues=false
since-tag=2.3
future-release=2.8

File diff suppressed because it is too large Load Diff

View File

@@ -1,218 +1,625 @@
## master
* Add your own contributions to the next release on the line below this with your name.
- [ASPhotosFrameworkImageRequestTests] Guard photo library with macro for tests. [Max Wang](https://github.com/wsdwsd0829). [#1147](https://github.com/TextureGroup/Texture/pull/1147)
- [ASDisplayNode] Do not cancel display when in exit hierarchy but let interface state changing to handle it. [Max Wang](https://github.com/wsdwsd0829). [#1110](https://github.com/TextureGroup/Texture/pull/1110)
- [Breaking][ASDisplayNode] Make interface state delegate protocol required. [Max Wang](https://github.com/wsdwsd0829). [#1112](https://github.com/TextureGroup/Texture/pull/1112)
- [ASCollectionView] Fix reording of cells manually for iOS 9 & 10. [Max Wang](https://github.com/wsdwsd0829). [#1081](https://github.com/TextureGroup/Texture/pull/1081)
- [ASDisplayNode] Allow add/remove interface state delegates on background thread. [Max Wang](https://github.com/wsdwsd0829). [#1090](https://github.com/TextureGroup/Texture/pull/1090)
- [License] Simplify the Texture license to be pure Apache 2 (removing ASDK-Licenses).[Scott Goodson](https://github.com/appleguy) [#1077](https://github.com/TextureGroup/Texture/pull/1077)
- [ASNetworkImageNode] Allow delegate methods to be called on background thread. [Max Wang](https://github.com/wsdwsd0829). [#1007](https://github.com/TextureGroup/Texture/pull/1007)
- [ASLayoutTransition] Add support for preserving order after node moves during transitions. (This order defines the z-order as well.) [Kevin Smith](https://github.com/wiseoldduck) [#1006]
- [ASDisplayNode] Adds support for multiple interface state delegates. [Garrett Moon](https://github.com/garrettmoon) [#979](https://github.com/TextureGroup/Texture/pull/979)
- [ASDataController] Add capability to renew supplementary views (update map) when size change from zero to non-zero.[Max Wang](https://github.com/wsdwsd0829) [#842](https://github.com/TextureGroup/Texture/pull/842)
- Make `ASPerformMainThreadDeallocation` visible in C. [Adlai Holler](https://github.com/Adlai-Holler)
- Add snapshot test for astextnode2. [Max Wang](https://github.com/wsdwsd0829) [#935](https://github.com/TextureGroup/Texture/pull/935)
- Internal housekeeping on the async transaction (rendering) system. [Adlai Holler](https://github.com/Adlai-Holler)
- Add new protocol `ASLocking` that extends `NSLocking` with `tryLock`, and allows taking multiple locks safely. [Adlai Holler](https://github.com/Adlai-Holler)
- Make the main thread ivar deallocation system available to other classes. Plus a little optimization. See `ASMainThreadDeallocation.h`. [Adlai Holler](https://github.com/Adlai-Holler) [#959](https://github.com/TextureGroup/Texture/pull/959)
- Reduce usage of autorelease pools. [Adlai Holler](https://github.com/Adlai-Holler) [#968](https://github.com/TextureGroup/Texture/pull/968)
- Clean up unused/unneeded header macros. [Adlai Holler](https://github.com/Adlai-Holler)
- Clean up C-function `extern` decorators. [Adlai Holler](https://github.com/Adlai-Holler)
- Add an experiment to reduce work involved in collection teardown. [Adlai Holler](https://github.com/Adlai-Holler)
- Optimize layout flattening, particularly reducing retain/release operations. [Adlai Holler](https://github.com/Adlai-Holler)
- Create a method to transfer strong C-arrays into immutable NSArrays, reducing retain/release traffic. [Adlai Holler](https://github.com/Adlai-Holler)
- Remove yoga layout spec, which has been superseded by tighter Yoga integration (`ASDisplayNode+Yoga.h`)
- Properly consider node for responder methods [Michael Schneider](https://github.com/maicki)
- Introduce let / var macros and some further cleanup. [Michael Schneider](https://github.com/maicki) [#1012](https://github.com/TextureGroup/Texture/pull/1012)
- [IGListKit] Adds missing UIScrollViewDelegate method to DataSource proxy [Sergey Pronin](https://github.com/wannabehero)
- Fix misleading/scary stack trace shown when an assertion occurs during node measurement [Huy Nguyen](https://github.com/nguyenhuy) [#1022](https://github.com/TextureGroup/Texture/pull/1022)
- Fix build on 32-bit simulator in Xcode 9.3 and later, caused by `Thread-local storage is not supported on this architecture.` [Adlai Holler](https://github.com/Adlai-Holler)
- Enable locking assertions (and add some more) to improve and enforce locking safety within the framework [Huy Nguyen](https://github.com/nguyenhuy) [#1024](https://github.com/TextureGroup/Texture/pull/1024)
- Split MapKit, Photos, and AssetsLibrary dependent code into separate subspecs to improve binary size and start time when they're not needed. The default subspec includes all three for backwards compatibility, but this **will change in 3.0**. When using non-Cocoapods build environments, define `AS_USE_PHOTOS, AS_USE_MAPKIT, AS_USE_ASSETS_LIBRARY` to 1 respectively to signal their use. [Adlai Holler](https://github.com/Adlai-Holler)
- Optimization: Removed an NSMutableArray in flattened layouts. [Adlai Holler](https://github.com/Adlai-Holler)
- Reduced binary size by disabling exception support (which we don't use.) [Adlai Holler](https://github.com/Adlai-Holler)
- Create and set delegate for clip corner layers within ASDisplayNode [Michael Schneider](https://github.com/maicki) [#1029](https://github.com/TextureGroup/Texture/pull/1029)
- Improve locking situation in ASVideoPlayerNode [Michael Schneider](https://github.com/maicki) [#1042](https://github.com/TextureGroup/Texture/pull/1042)
- Optimize ASDisplayNode -> ASNodeController reference by removing weak proxy and objc associated objects. [Adlai Holler](https://github.com/Adlai-Holler)
- Remove CA transaction signpost injection because it causes more transactions and is too chatty. [Adlai Holler](https://github.com/Adlai-Holler)
- Optimize display node accessibility by not creating attributed & non-attributed copies of hint, label, and value. [Adlai Holler](https://github.com/Adlai-Holler)
- Add an experimental feature that reuses CTFramesetter objects in ASTextNode2 to improve performance. [Adlai Holler](https://github.com/Adlai-Holler)
- Add NS_DESIGNATED_INITIALIZER to ASViewController initWithNode: [Michael Schneider](https://github.com/maicki) [#1054](https://github.com/TextureGroup/Texture/pull/1054)
- Optimize text stack by removing unneeded copying. [Adlai Holler](https://github.com/Adlai-Holler)
- Renamed `accessibleElements` to `accessibilityElements` and removed the re-definition of the property in ASDisplayView. [Jia Wern Lim](https://github.com/jiawernlim)
- Remove double scaling of lineHeightMultiple & paragraphSpacing attributes in ASTextKitFontSizeAdjuster. [Eric Jensen](https://github.com/ejensen)
- Add a delegate callback for when the framework has initialized. [Adlai Holler](https://github.com/Adlai-Holler)
- Improve TextNode2 by skipping an unneeded copy during measurement. [Adlai Holler](https://github.com/Adlai-Holler)
- Improve locking around clearContents [Michael Schneider](https://github.com/maicki)
- Unlock before cleanup and calling out to subclass hooks for animated images. [Michael Schneider](https://github.com/maicki) [#1087](https://github.com/TextureGroup/Texture/pull/1087)
- [ASDisplayNode] Fix interface state update for layer backed nodes when layer thrashes (interface coaleascing case).[Max Wang](https://github.com/wsdwsd0829). [#1111](https://github.com/TextureGroup/Texture/pull/1111)
- [ASPINRemoteImageManager] Add a new API for setting a preconfigured PINRemoteImageManager. [Ernest Ma](https://github.com/ernestmama) [#1124](https://github.com/TextureGroup/Texture/pull/1124)
- Small optimization to the layout spec & yoga layout systems by eliminating array copies. [Adlai Holler](https://github.com/Adlai-Holler)
- Optimize layout process by removing `ASRectMap`. [Adlai Holler](https://github.com/Adlai-Holler)
- Remove necessity to use view to access rangeController in ASTableNode, ASCollectionNode. [Michael Schneider](https://github.com/maicki)
- Remove display node's reliance on shared_ptr. [Adlai Holler](https://github.com/Adlai-Holler)
- [ASCollectionView] Fix a crash that is caused by clearing a collection view's data while it's still being used. [Huy Nguyen](https://github.com/nguyenhuy) [#1154](https://github.com/TextureGroup/Texture/pull/1154)
- Clean up timing of layout tree flattening/ copying of unflattened tree for Weaver. [Michael Zuccarino](https://github.com/mikezucc) [#1157](https://github.com/TextureGroup/Texture/pull/1157)
# Change Log
## 2.7
- Fix pager node for interface coalescing. [Max Wang](https://github.com/wsdwsd0829) [#877](https://github.com/TextureGroup/Texture/pull/877)
- [ASTextNode2] Upgrade lock safety by protecting all ivars (including rarely-changed ones).
- User FLT_EPSILON in ASCeilPixelValue and ASFloorPixelValue to help with floating point precision errors when computing layouts for 3x devices. [Ricky Cancro](https://github.com/rcancro) [#838](https://github.com/TextureGroup/Texture/pull/864)
- Disable interface colescing and match to pre-colescing interface update behavior [Max Wang](https://github.com/wsdwsd0829) [#862](https://github.com/TextureGroup/Texture/pull/862)
- [ASDisplayNode] Add safeAreaInsets, layoutMargins and related properties to ASDisplayNode, with full support for older OS versions [Yevgen Pogribnyi](https://github.com/ypogribnyi) [#685](https://github.com/TextureGroup/Texture/pull/685)
- [ASPINRemoteImageDownloader] Allow cache to provide animated image. [Max Wang](https://github.com/wsdwsd0829) [#850](https://github.com/TextureGroup/Texture/pull/850)
- [tvOS] Fixes errors when building against tvOS SDK [Alex Hill](https://github.com/alexhillc) [#728](https://github.com/TextureGroup/Texture/pull/728)
- [ASDisplayNode] Add unit tests for layout z-order changes (with an open issue to fix).
- [ASWrapperCellNode] Introduce a new class allowing more control of UIKit passthrough cells.
- [ASDisplayNode] Consolidate main thread initialization and allow apps to invoke it manually instead of +load.
- [ASRunloopQueue] Introduce new runloop queue(ASCATransactionQueue) to coalesce Interface state update calls for view controller transitions.
- [ASRangeController] Fix stability of "minimum" rangeMode if the app has more than one layout before scrolling.
- **Important** ASDisplayNode's cornerRadius is a new thread-safe bridged property that should be preferred over CALayer's. Use the latter at your own risk! [Huy Nguyen](https://github.com/nguyenhuy) [#749](https://github.com/TextureGroup/Texture/pull/749).
- [ASCellNode] Adds mapping for UITableViewCell focusStyle [Alex Hill](https://github.com/alexhillc) [#727](https://github.com/TextureGroup/Texture/pull/727)
- [ASNetworkImageNode] Fix capturing self in the block while loading image in ASNetworkImageNode. [Denis Mororozov](https://github.com/morozkin) [#777](https://github.com/TextureGroup/Texture/pull/777)
- [ASTraitCollection] Add new properties of UITraitCollection to ASTraitCollection. [Yevgen Pogribnyi](https://github.com/ypogribnyi)
- [ASRectMap] Replace implementation of ASRectTable with a simpler one based on unordered_map.[Scott Goodson](https://github.com/appleguy) [#719](https://github.com/TextureGroup/Texture/pull/719)
- [ASCollectionView] Add missing flags for ASCollectionDelegate [Ilya Zheleznikov](https://github.com/ilyailya) [#718](https://github.com/TextureGroup/Texture/pull/718)
- [ASNetworkImageNode] Deprecates .URLs in favor of .URL [Garrett Moon](https://github.com/garrettmoon) [#699](https://github.com/TextureGroup/Texture/pull/699)
- [iOS11] Update project settings and fix errors [Eke](https://github.com/Eke) [#676](https://github.com/TextureGroup/Texture/pull/676)
- [ASCornerLayoutSpec] New layout spec class for declarative corner element layout. [#657](https://github.com/TextureGroup/Texture/pull/657) [huangkun](https://github.com/huang-kun)
- [ASDisplayNode layout] Fix an issue that causes a pending layout to be applied multiple times. [Huy Nguyen](https://github.com/nguyenhuy) [#695](https://github.com/TextureGroup/Texture/pull/695)
- [ASDisplayNode layout] Fix an issue that sometimes causes a node's pending layout to not be applied. [Huy Nguyen](https://github.com/nguyenhuy) [#792](https://github.com/TextureGroup/Texture/pull/792)
- [ASScrollNode] Ensure the node respects the given size range while calculating its layout. [#637](https://github.com/TextureGroup/Texture/pull/637) [Huy Nguyen](https://github.com/nguyenhuy)
- [ASScrollNode] Invalidate the node's calculated layout if its scrollable directions changed. Also add unit tests for the class. [#637](https://github.com/TextureGroup/Texture/pull/637) [Huy Nguyen](https://github.com/nguyenhuy)
- Add new unit testing to the layout engine. [Adlai Holler](https://github.com/Adlai-Holler) [#424](https://github.com/TextureGroup/Texture/pull/424)
- [Automatic Subnode Management] Nodes with ASM enabled now insert/delete their subnodes as soon as they enter preload state, so subnodes can start preloading right away. [Huy Nguyen](https://github.com/nguyenhuy) [#706](https://github.com/TextureGroup/Texture/pull/706)
- [ASCollectionNode] Added support for interactive item movement. [Adlai Holler](https://github.com/Adlai-Holler)
- Added an experimental "no-copy" rendering API. See ASGraphicsContext.h for info. [Adlai Holler](https://github.com/Adlai-Holler)
- Dropped support for iOS 8. [Adlai Holler](https://github.com/Adlai-Holler)
- Added a configuration API a unified place to turn on/off experimental Texture features. See `ASConfiguration.h` for info. [Adlai Holler](https://github.com/Adlai-Holler)
- **Breaking** Changes to ASNetworkImageNode: [Adlai Holler](https://github.com/Adlai-Holler)
- Modified `ASImageDownloaderCompletion` to add an optional `id userInfo` field. Your custom downloader can pass `nil`.
- Modified the last argument to `-[ASNetworkImageNodeDelegate imageNode:didLoadImage:info:]` method from a struct to an object of new class `ASNetworkImageLoadInfo` which includes other metadata about the load operation.
- Removed +load static initializer from ASDisplayNode. [Adlai Holler](https://github.com/Adlai-Holler)
- Optimized ASNetworkImageNode loading and resolved edge cases where the image provided to the delegate was not the image that was loaded. [Adlai Holler](https://github.com/Adlai-Holler) [#778](https://github.com/TextureGroup/Texture/pull/778/)
- Make `ASCellNode` tint color apply to table view cell accessories. [Vladyslav Chapaev](https://github.com/ShogunPhyched) [#764](https://github.com/TextureGroup/Texture/pull/764)
- Fix ASTextNode2 is accessing backgroundColor off main while sizing / layout is happening. [Michael Schneider](https://github.com/maicki) [#794](https://github.com/TextureGroup/Texture/pull/778/)
- Pass scrollViewWillEndDragging delegation through in ASIGListAdapterDataSource for IGListKit integration. [#796](https://github.com/TextureGro
- up/Texture/pull/796)
- Fix UIResponder handling with view backing ASDisplayNode. [Michael Schneider](https://github.com/maicki) [#789](https://github.com/TextureGroup/Texture/pull/789/)
- Optimized thread-local storage by replacing pthread_specific with C11 thread-local variables. [Adlai Holler](https://github.com/Adlai-Holler) [#811](https://github.com/TextureGroup/Texture/pull/811/)
- Fixed a thread-sanitizer warning in ASTextNode. [Adlai Holler](https://github.com/Adlai-Holler) [#830](https://github.com/TextureGroup/Texture/pull/830/)
- Fix ASTextNode2 handling background color incorrectly. [Adlai Holler](https://github.com/Adlai-Holler) [#831](https://github.com/TextureGroup/Texture/pull/831/)
- [NoCopyRendering] Improved performance & fixed image memory not being tagged in Instruments. [Adlai Holler](https://github.com/Adlai-Holler) [#833](https://github.com/TextureGroup/Texture/pull/833/)
- Use `NS_RETURNS_RETAINED` macro to make our methods a tiny bit faster. [Adlai Holler](https://github.com/Adlai-Holler) [#843](https://github.com/TextureGroup/Texture/pull/843/)
- `ASDisplayNode, ASLayoutSpec, and ASLayoutElementStyle` now conform to `NSLocking`. They act as recursive locks. Useful locking macros have been added as `ASThread.h`. Subclasses / client code can lock these objects but should be careful as usual when dealing with locks. [Adlai Holler](https://github.com/Adlai-Holler)
- Introduces `ASRecursiveUnfairLock` as an experiment to improve locking performance. [Adlai Holler](https://github.com/Adlai-Holler)
- Adds an experiment to shorten init time. [Adlai Holler](https://github.com/Adlai-Holler)
- Adds a check that Texture is compiled with stdc++11 as specified by the podfile. gnu++11 can cause subtle issues that are currently being investigated. [Adlai Holler](https://github.com/Adlai-Holler)
- Adds an experiment to call ASNetworkImageNode callbacks off main. [Garrett Moon](https://github.com/garrettmoon)
- Prevent UITextView from updating contentOffset while deallocating [Michael Schneider](https://github.com/maicki)
- [ASCollectionNode/ASTableNode] Fix a crash occurs while remeasuring cell nodes. [Huy Nguyen](https://github.com/nguyenhuy) [#917](https://github.com/TextureGroup/Texture/pull/917)
- Fix an issue where ASConfigurationDelegate would not call out for "control" users. If set, it now receives events whenever an experimental feature decision point occurs, whether it's enabled or not. [Adlai Holler](https://github.com/Adlai-Holler)
- [ASDisplayNode] Fix an issue that causes a node to sometimes return an outdated calculated size or size range. [Huy Nguyen](https://github.com/nguyenhuy) [#808](https://github.com/TextureGroup/Texture/pull/808)
- Add an experimental deallocation queue implementation that's more efficient. [Adlai Holler](https://github.com/Adlai-Holler)
- Standardize property declaration style. [Adlai Holler](https://github.com/Adlai-Holler)
- [ASTableView] Fix an issue that causes table view to use one of a cell's invalid layouts instead of generating a new one. [Huy Nguyen](https://github.com/nguyenhuy) [#942](https://github.com/TextureGroup/Texture/pull/942)
## [2.8](https://github.com/TextureGroup/Texture/tree/2.8) (2018-11-04)
[Full Changelog](https://github.com/TextureGroup/Texture/compare/2.7...2.8)
## 2.6
- [Xcode 9] Updated to require Xcode 9 (to fix warnings) [Garrett Moon](https://github.com/garrettmoon)
- [ASCollectionView] Improve performance and behavior of rotation / bounds changes. [Scott Goodson](https://github.com/appleguy) [#431](https://github.com/TextureGroup/Texture/pull/431)
- [ASCollectionView] Improve index space translation of Flow Layout Delegate methods. [Scott Goodson](https://github.com/appleguy)
- [Animated Image] Adds support for animated WebP as well as improves GIF handling. [#605](https://github.com/TextureGroup/Texture/pull/605) [Garrett Moon](https://github.com/garrettmoon)
- [ASCollectionView] Check if batch fetching is needed if batch fetching parameter has been changed. [#624](https://github.com/TextureGroup/Texture/pull/624) [Garrett Moon](https://github.com/garrettmoon)
- [ASNetworkImageNode] New delegate callback to tell the consumer whether the image was loaded from cache or download. [Adlai Holler](https://github.com/Adlai-Holler)
- [Layout] Fixes a deadlock in layout. [#638](https://github.com/TextureGroup/Texture/pull/638) [Garrett Moon](https://github.com/garrettmoon)
- Updated to be backwards compatible with Xcode 8. [Adlai Holler](https://github.com/Adlai-Holler)
- [API CHANGES] `ASPerformMainThreadDeallocation` and `ASPerformBackgroundDeallocation` functions take `id *` instead of `id` and they're now more reliable. Also, in Swift, `ASDeallocQueue.sharedDeallocationQueue() -> ASDeallocQueue.sharedDeallocationQueue`. [Adlai Holler](https://github.com/Adlai-Holler) [#651](https://github.com/TextureGroup/Texture/pull/651)
- [Collection/Table] Added direct support for mapping section indexes between data spaces. [Adlai Holler](https://github.com/Adlai-Holler) [#651](https://github.com/TextureGroup/Texture/pull/660)
**Merged pull requests:**
## 2.5.1
- [ASVideoNode] Fix unreleased time observer. [Flo Vouin](https://github.com/flovouin)
- [PINCache] Set a default .byteLimit to reduce disk usage and startup time. [#595](https://github.com/TextureGroup/Texture/pull/595) [Scott Goodson](https://github.com/appleguy)
- [ASNetworkImageNode] Fix deadlock in GIF handling. [#582](https://github.com/TextureGroup/Texture/pull/582) [Garrett Moon](https://github.com/garrettmoon)
- [ASDisplayNode] Add attributed versions of a11y label, hint and value. [#554](https://github.com/TextureGroup/Texture/pull/554) [Alexander Hüllmandel](https://github.com/fruitcoder)
- [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)
- Add -Wno-implicit-retain-self to podspec + smaller cleanups \#trivial [\#1209](https://github.com/TextureGroup/Texture/pull/1209) ([maicki](https://github.com/maicki))
- Address compiler warnings \#trivial [\#1207](https://github.com/TextureGroup/Texture/pull/1207) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Convert the codebase to Objective-C++ [\#1206](https://github.com/TextureGroup/Texture/pull/1206) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Add tests for accessibility [\#1205](https://github.com/TextureGroup/Texture/pull/1205) ([wiseoldduck](https://github.com/wiseoldduck))
- Revert \#1023 \#trivial [\#1204](https://github.com/TextureGroup/Texture/pull/1204) ([maicki](https://github.com/maicki))
- Follow up cleanup \#trivial [\#1203](https://github.com/TextureGroup/Texture/pull/1203) ([maicki](https://github.com/maicki))
- Add experiment flag to skip layoutIfNeeded in enterPreloadState for ASM nodes \#trivial [\#1201](https://github.com/TextureGroup/Texture/pull/1201) ([maicki](https://github.com/maicki))
- Fix logic cleaning data if delegate / dataSource changes and bring over logic to ASTableView [\#1200](https://github.com/TextureGroup/Texture/pull/1200) ([maicki](https://github.com/maicki))
- Tweak a11y label aggregation behavior to enable container label overrides [\#1199](https://github.com/TextureGroup/Texture/pull/1199) ([maicki](https://github.com/maicki))
- Fix shadowed var warning \(and add clarity\) \#trivial [\#1198](https://github.com/TextureGroup/Texture/pull/1198) ([wiseoldduck](https://github.com/wiseoldduck))
- Allow configuring imageCache when initializing ASPINRemoteImageDownloader. [\#1197](https://github.com/TextureGroup/Texture/pull/1197) ([wiseoldduck](https://github.com/wiseoldduck))
- ASTextNode2 to consider both width and height when determining if it is calculating an intrinsic size [\#1196](https://github.com/TextureGroup/Texture/pull/1196) ([ernestmama](https://github.com/ernestmama))
- Remove extraneous ";" \#trivial [\#1194](https://github.com/TextureGroup/Texture/pull/1194) ([wiseoldduck](https://github.com/wiseoldduck))
- Newline character support and truncated line sizing improvement. [\#1193](https://github.com/TextureGroup/Texture/pull/1193) ([wiseoldduck](https://github.com/wiseoldduck))
- Correct linePositionModifier behavior [\#1192](https://github.com/TextureGroup/Texture/pull/1192) ([maicki](https://github.com/maicki))
- Move AS\_TEXT\_ALERT\_UNIMPLEMENTED\_FEATURE into ASTextNodeCommon \#trivial [\#1191](https://github.com/TextureGroup/Texture/pull/1191) ([maicki](https://github.com/maicki))
- A11y for scrollnode [\#1188](https://github.com/TextureGroup/Texture/pull/1188) ([wsdwsd0829](https://github.com/wsdwsd0829))
- Yoga integration improvements [\#1187](https://github.com/TextureGroup/Texture/pull/1187) ([maicki](https://github.com/maicki))
- Remove unnecessary ASWeakProxy import \#trivial [\#1186](https://github.com/TextureGroup/Texture/pull/1186) ([maicki](https://github.com/maicki))
- Directly use \_\_instanceLock\_\_ to lock / unlock without having to create and destroy a MutextUnlocker \#trivial [\#1185](https://github.com/TextureGroup/Texture/pull/1185) ([maicki](https://github.com/maicki))
- Dont handle touches on additional attributed message if passthrough is enabled [\#1184](https://github.com/TextureGroup/Texture/pull/1184) ([maicki](https://github.com/maicki))
- Set the default values for showsVerticalScrollIndicator and showsHorizontalScrollIndicator \#trivial [\#1181](https://github.com/TextureGroup/Texture/pull/1181) ([maicki](https://github.com/maicki))
- Move import of stdatomic to ASRecursiveUnfairLock implementation file \#trivial [\#1180](https://github.com/TextureGroup/Texture/pull/1180) ([maicki](https://github.com/maicki))
- Add NSLocking conformance to ASNodeController [\#1179](https://github.com/TextureGroup/Texture/pull/1179) ([maicki](https://github.com/maicki))
- Only initialize framework once, avoid multiple across tests \#trivial [\#1178](https://github.com/TextureGroup/Texture/pull/1178) ([maicki](https://github.com/maicki))
- Expose a way to determine if a text node will truncate for a given constrained size \#trivial [\#1177](https://github.com/TextureGroup/Texture/pull/1177) ([maicki](https://github.com/maicki))
- Fix define spaces \#trivial [\#1176](https://github.com/TextureGroup/Texture/pull/1176) ([maicki](https://github.com/maicki))
- Expose test\_resetWithConfiguration: for testing \#trivial [\#1175](https://github.com/TextureGroup/Texture/pull/1175) ([maicki](https://github.com/maicki))
- Add way to suppress invalid CollectionUpdateExceptions \#trivial [\#1173](https://github.com/TextureGroup/Texture/pull/1173) ([maicki](https://github.com/maicki))
- Use interface state to manage image loading \#trivial [\#1172](https://github.com/TextureGroup/Texture/pull/1172) ([maicki](https://github.com/maicki))
- ASTableNode init method match checks from ASCollectionNode [\#1171](https://github.com/TextureGroup/Texture/pull/1171) ([maicki](https://github.com/maicki))
- \[ASDisplayNode\] Expose default Texture-set accessibility values as properties [\#1170](https://github.com/TextureGroup/Texture/pull/1170) ([jiawernlim](https://github.com/jiawernlim))
- Fix mismatch in UIAccessibilityAction selector method [\#1169](https://github.com/TextureGroup/Texture/pull/1169) ([maicki](https://github.com/maicki))
- Small fix in ASTextKitRenderer \#trivial [\#1167](https://github.com/TextureGroup/Texture/pull/1167) ([nguyenhuy](https://github.com/nguyenhuy))
- ASTextNode2 to ignore certain text alignments while calculating intrinsic size [\#1166](https://github.com/TextureGroup/Texture/pull/1166) ([nguyenhuy](https://github.com/nguyenhuy))
- Update Jekyll to 3.6.3 [\#1165](https://github.com/TextureGroup/Texture/pull/1165) ([nguyenhuy](https://github.com/nguyenhuy))
- Migrate placeholder example project from 1.0 to 2.x [\#1164](https://github.com/TextureGroup/Texture/pull/1164) ([ay8s](https://github.com/ay8s))
- Update documentation of ASNetworkImageNodeDelegate \#trivial [\#1163](https://github.com/TextureGroup/Texture/pull/1163) ([nguyenhuy](https://github.com/nguyenhuy))
- Mismatch name experimental features [\#1159](https://github.com/TextureGroup/Texture/pull/1159) ([wsdwsd0829](https://github.com/wsdwsd0829))
- Set default tuning params [\#1158](https://github.com/TextureGroup/Texture/pull/1158) ([wsdwsd0829](https://github.com/wsdwsd0829))
- Clean up timing of layout tree flattening/ copying of unflattened tree for Weaver [\#1157](https://github.com/TextureGroup/Texture/pull/1157) ([mikezucc](https://github.com/mikezucc))
- Only clear ASCollectionView's data during deallocation [\#1154](https://github.com/TextureGroup/Texture/pull/1154) ([nguyenhuy](https://github.com/nguyenhuy))
- \[ASTextNode2\] Add improved support for all line-break modes in experimental text node. [\#1150](https://github.com/TextureGroup/Texture/pull/1150) ([wiseoldduck](https://github.com/wiseoldduck))
- Guard photo library with macro for tests [\#1147](https://github.com/TextureGroup/Texture/pull/1147) ([wsdwsd0829](https://github.com/wsdwsd0829))
- Rollout ASDeallocQueueV2 \#trivial [\#1143](https://github.com/TextureGroup/Texture/pull/1143) ([ernestmama](https://github.com/ernestmama))
- Fix crash setting attributed text on multiple threads [\#1141](https://github.com/TextureGroup/Texture/pull/1141) ([maicki](https://github.com/maicki))
- Add missing NS\_NOESCAPE attributes in overwritten methods \#trivial [\#1139](https://github.com/TextureGroup/Texture/pull/1139) ([ejensen](https://github.com/ejensen))
- Add missing comma in ASExperimentalFeatures \#trivial [\#1137](https://github.com/TextureGroup/Texture/pull/1137) ([nguyenhuy](https://github.com/nguyenhuy))
- Add ASExperimentalSkipClearData \#trivial [\#1136](https://github.com/TextureGroup/Texture/pull/1136) ([maicki](https://github.com/maicki))
- Fix RemoteImageDownloader name mismatch \#trivial [\#1134](https://github.com/TextureGroup/Texture/pull/1134) ([ernestmama](https://github.com/ernestmama))
- Fix compilation warnings \#trivial [\#1132](https://github.com/TextureGroup/Texture/pull/1132) ([ejensen](https://github.com/ejensen))
- Remove reliance on shared\_ptr for ASDisplayNodeLayouts [\#1131](https://github.com/TextureGroup/Texture/pull/1131) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Make yoga & layout specs faster by eliminating some copies [\#1128](https://github.com/TextureGroup/Texture/pull/1128) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Remove ASRectMap, which is not worth its own weight [\#1127](https://github.com/TextureGroup/Texture/pull/1127) ([Adlai-Holler](https://github.com/Adlai-Holler))
- \[ASPINRemoteImageDownloader\] Fix +setSharedPreconfiguredRemoteImageManager:'s doc \#trivial [\#1126](https://github.com/TextureGroup/Texture/pull/1126) ([nguyenhuy](https://github.com/nguyenhuy))
- Add a method for setting preconfigured PINRemoteImageManager [\#1124](https://github.com/TextureGroup/Texture/pull/1124) ([ernestmama](https://github.com/ernestmama))
- Don't copy onDidLoadBlocks \#trivial [\#1123](https://github.com/TextureGroup/Texture/pull/1123) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Remove use of NSHashTable for interface state delegates \#trivial [\#1122](https://github.com/TextureGroup/Texture/pull/1122) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Fix typos and minor code cleanups \#trivial [\#1120](https://github.com/TextureGroup/Texture/pull/1120) ([nguyenhuy](https://github.com/nguyenhuy))
- Don't setNeedsDisplay on text node 2 measure \#trivial [\#1116](https://github.com/TextureGroup/Texture/pull/1116) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Don't copy container during ASTextNode2 measure [\#1115](https://github.com/TextureGroup/Texture/pull/1115) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Make interface state delegate non optional [\#1112](https://github.com/TextureGroup/Texture/pull/1112) ([wsdwsd0829](https://github.com/wsdwsd0829))
- Interface state not update correctly during layer thrash. [\#1111](https://github.com/TextureGroup/Texture/pull/1111) ([wsdwsd0829](https://github.com/wsdwsd0829))
- Fix layer backed nodes not update properly [\#1110](https://github.com/TextureGroup/Texture/pull/1110) ([wsdwsd0829](https://github.com/wsdwsd0829))
- changelog fix: let / var macros did not make it to 2.7 [\#1109](https://github.com/TextureGroup/Texture/pull/1109) ([jozsefmihalicza](https://github.com/jozsefmihalicza))
- Improve locking around clearContents [\#1107](https://github.com/TextureGroup/Texture/pull/1107) ([maicki](https://github.com/maicki))
- Add missing argument for calling image download completion block \#trivial [\#1106](https://github.com/TextureGroup/Texture/pull/1106) ([maicki](https://github.com/maicki))
- Fix URL for blog about Pinterest [\#1105](https://github.com/TextureGroup/Texture/pull/1105) ([muukii](https://github.com/muukii))
- Remove necessity to use view to access rangeController in ASTableNode, ASCollectionNode [\#1103](https://github.com/TextureGroup/Texture/pull/1103) ([maicki](https://github.com/maicki))
- Add a -textureDidInitialize delegate callback [\#1100](https://github.com/TextureGroup/Texture/pull/1100) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Reuse interface state delegates when calling out \#trivial [\#1099](https://github.com/TextureGroup/Texture/pull/1099) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Add an explicit cast to satisfy strict compilers \#trivial [\#1098](https://github.com/TextureGroup/Texture/pull/1098) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Fix a couple typos. [\#1092](https://github.com/TextureGroup/Texture/pull/1092) ([jtbthethird](https://github.com/jtbthethird))
- \#trivial Shouldn't hold the lock while adding subnodes [\#1091](https://github.com/TextureGroup/Texture/pull/1091) ([garrettmoon](https://github.com/garrettmoon))
- Allow to add interface state delegate in background. [\#1090](https://github.com/TextureGroup/Texture/pull/1090) ([wsdwsd0829](https://github.com/wsdwsd0829))
- Fix Typo [\#1089](https://github.com/TextureGroup/Texture/pull/1089) ([jtbthethird](https://github.com/jtbthethird))
- Add subnode should not be called with the lock held. \#trivial [\#1088](https://github.com/TextureGroup/Texture/pull/1088) ([garrettmoon](https://github.com/garrettmoon))
- Unlock before cleanup and calling out to subclass hooks for animated images. [\#1087](https://github.com/TextureGroup/Texture/pull/1087) ([maicki](https://github.com/maicki))
- Fix collection editing [\#1081](https://github.com/TextureGroup/Texture/pull/1081) ([wsdwsd0829](https://github.com/wsdwsd0829))
- Fix compiler error in ASLocking \#trivial [\#1079](https://github.com/TextureGroup/Texture/pull/1079) ([nguyenhuy](https://github.com/nguyenhuy))
- Update showcase to add Wishpoke [\#1078](https://github.com/TextureGroup/Texture/pull/1078) ([dhatuna](https://github.com/dhatuna))
- \[License\] Simplify the Texture license to be pure Apache 2 \(removing ASDK-Licenses\). [\#1077](https://github.com/TextureGroup/Texture/pull/1077) ([appleguy](https://github.com/appleguy))
- Fix multiple documentation issues \#trivial [\#1073](https://github.com/TextureGroup/Texture/pull/1073) ([maicki](https://github.com/maicki))
- Refactored `accessibleElements` to `accessibilityElements` [\#1069](https://github.com/TextureGroup/Texture/pull/1069) ([jiawernlim](https://github.com/jiawernlim))
- Readability improvements in ASDataController \#trivial [\#1067](https://github.com/TextureGroup/Texture/pull/1067) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Remove direct ivar access on non-self object to fix mocking case \#trivial [\#1066](https://github.com/TextureGroup/Texture/pull/1066) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Reduce copying in ASTextNode2 stack [\#1065](https://github.com/TextureGroup/Texture/pull/1065) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Add an experimental framesetter cache in ASTextNode2 [\#1063](https://github.com/TextureGroup/Texture/pull/1063) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Remove extra string/attributed string creation in accessibility props [\#1062](https://github.com/TextureGroup/Texture/pull/1062) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Remove objc association & weak proxy from node -\> controller pointer [\#1061](https://github.com/TextureGroup/Texture/pull/1061) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Remove CATransaction signposts [\#1060](https://github.com/TextureGroup/Texture/pull/1060) ([Adlai-Holler](https://github.com/Adlai-Holler))
- \[ASTextNode2\] Simplify allocWithZone: + initialize implementation \#trivial [\#1059](https://github.com/TextureGroup/Texture/pull/1059) ([Adlai-Holler](https://github.com/Adlai-Holler))
- \[ASTextNode\] Fixes in ASTextKitFontSizeAdjuster [\#1056](https://github.com/TextureGroup/Texture/pull/1056) ([ejensen](https://github.com/ejensen))
- Revert "Optimize drawing code + add examples how to round corners \(\#996\) [\#1055](https://github.com/TextureGroup/Texture/pull/1055) ([maicki](https://github.com/maicki))
- Add NS\_DESIGNATED\_INITIALIZER to ASViewController initWithNode: [\#1054](https://github.com/TextureGroup/Texture/pull/1054) ([maicki](https://github.com/maicki))
- Fix headers in markdown [\#1053](https://github.com/TextureGroup/Texture/pull/1053) ([Un3qual](https://github.com/Un3qual))
- Avoid setting frame on a node's backing store while holding its lock [\#1048](https://github.com/TextureGroup/Texture/pull/1048) ([nguyenhuy](https://github.com/nguyenhuy))
- \#trivial Add a comment about tiling mode and issue \#1046 [\#1047](https://github.com/TextureGroup/Texture/pull/1047) ([wiseoldduck](https://github.com/wiseoldduck))
- Add documentation for rounding corners within Texture \#trivial [\#1044](https://github.com/TextureGroup/Texture/pull/1044) ([maicki](https://github.com/maicki))
- Improve locking situation in ASVideoPlayerNode [\#1042](https://github.com/TextureGroup/Texture/pull/1042) ([maicki](https://github.com/maicki))
- Revert unreleased layout debug method name change from \#1030 \#trivial [\#1039](https://github.com/TextureGroup/Texture/pull/1039) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Pin OCMock version to 3.4.1 because 3.4.2 has issues [\#1038](https://github.com/TextureGroup/Texture/pull/1038) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Fix & update ASCollectionNode constrained size doc. \#trivial [\#1037](https://github.com/TextureGroup/Texture/pull/1037) ([ay8s](https://github.com/ay8s))
- Fix warning for ASLayout method override for the designated initializer of the superclass '-init' not found \#trivial [\#1036](https://github.com/TextureGroup/Texture/pull/1036) ([maicki](https://github.com/maicki))
- Fix the bug I introduced in \#1030 \#trivial [\#1035](https://github.com/TextureGroup/Texture/pull/1035) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Turn off exceptions to reduce binary size \(-600KB for arm64\) [\#1033](https://github.com/TextureGroup/Texture/pull/1033) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Turn lock-checking on only when assertions are enabled \#trivial [\#1032](https://github.com/TextureGroup/Texture/pull/1032) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Remove NSMutableArray for retaining sublayout elements [\#1030](https://github.com/TextureGroup/Texture/pull/1030) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Create and set delegate for clip corner layers within ASDisplayNode [\#1029](https://github.com/TextureGroup/Texture/pull/1029) ([maicki](https://github.com/maicki))
- Split framework dependencies into separate subspecs [\#1028](https://github.com/TextureGroup/Texture/pull/1028) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Remove misleading comment and add assertion \#trivial [\#1027](https://github.com/TextureGroup/Texture/pull/1027) ([wiseoldduck](https://github.com/wiseoldduck))
- Address warnings in Xcode \>= 9.3 about using %zd for NSInteger \#trivial [\#1026](https://github.com/TextureGroup/Texture/pull/1026) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Fix 32-bit simulator build on Xcode \>= 9.3 [\#1025](https://github.com/TextureGroup/Texture/pull/1025) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Stricter locking assertions [\#1024](https://github.com/TextureGroup/Texture/pull/1024) ([nguyenhuy](https://github.com/nguyenhuy))
- Make sure -\_completePendingLayoutTransition is called without the node's instance lock \#trivial [\#1023](https://github.com/TextureGroup/Texture/pull/1023) ([nguyenhuy](https://github.com/nguyenhuy))
- Fix misleading/scary stack trace shown when an assertion occurs during node measurement [\#1022](https://github.com/TextureGroup/Texture/pull/1022) ([nguyenhuy](https://github.com/nguyenhuy))
- Add an introduction for ASCornerLayoutSpec in layout2-layoutspec-types.md \#trivial [\#1021](https://github.com/TextureGroup/Texture/pull/1021) ([huang-kun](https://github.com/huang-kun))
- Add showsHorizontal\(Vertical\)ScrollIndicator property applying from pending state \#trivial [\#1016](https://github.com/TextureGroup/Texture/pull/1016) ([maicki](https://github.com/maicki))
- \[IGListKit\] Adds missing UIScrollViewDelegate method to DataSource proxy [\#1015](https://github.com/TextureGroup/Texture/pull/1015) ([wannabehero](https://github.com/wannabehero))
- Introduce let / var macros and some further cleanup [\#1012](https://github.com/TextureGroup/Texture/pull/1012) ([maicki](https://github.com/maicki))
- Properly consider node for responder methods [\#1008](https://github.com/TextureGroup/Texture/pull/1008) ([maicki](https://github.com/maicki))
- Background image load api [\#1007](https://github.com/TextureGroup/Texture/pull/1007) ([wsdwsd0829](https://github.com/wsdwsd0829))
- Add move detection and support to ASLayoutTransition [\#1006](https://github.com/TextureGroup/Texture/pull/1006) ([wiseoldduck](https://github.com/wiseoldduck))
- Fix warnings and a memory leak \#trivial [\#1003](https://github.com/TextureGroup/Texture/pull/1003) ([maicki](https://github.com/maicki))
- Rewrite Swift Example [\#1002](https://github.com/TextureGroup/Texture/pull/1002) ([maicki](https://github.com/maicki))
- Remove yoga layout spec, which has been superseded [\#999](https://github.com/TextureGroup/Texture/pull/999) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Optimize drawing code + add examples how to round corners [\#996](https://github.com/TextureGroup/Texture/pull/996) ([maicki](https://github.com/maicki))
- Fix typo in containers-asviewcontroller.md [\#989](https://github.com/TextureGroup/Texture/pull/989) ([muukii](https://github.com/muukii))
- Create transfer-array method and use it [\#987](https://github.com/TextureGroup/Texture/pull/987) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Add missing instance variables in ASTextNode and warnings cleanup \#trivial [\#984](https://github.com/TextureGroup/Texture/pull/984) ([maicki](https://github.com/maicki))
- Optimize layout flattening [\#982](https://github.com/TextureGroup/Texture/pull/982) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Changed lost images to existing one. \#trivial [\#981](https://github.com/TextureGroup/Texture/pull/981) ([tataevr](https://github.com/tataevr))
- \[texturegroup.org\] Use valid link for Upgrade to 2.0 beta 1 page \#trivial [\#980](https://github.com/TextureGroup/Texture/pull/980) ([mikezucc](https://github.com/mikezucc))
- Adds support for having multiple interface state delegates. [\#979](https://github.com/TextureGroup/Texture/pull/979) ([garrettmoon](https://github.com/garrettmoon))
- Create an experiment to remove extra collection teardown step [\#975](https://github.com/TextureGroup/Texture/pull/975) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Remove unused/unneeded header macros [\#973](https://github.com/TextureGroup/Texture/pull/973) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Standardize "extern" decls on AS\_EXTERN [\#972](https://github.com/TextureGroup/Texture/pull/972) ([Adlai-Holler](https://github.com/Adlai-Holler))
- ASConfiguration version check only when have json dict [\#971](https://github.com/TextureGroup/Texture/pull/971) ([wsdwsd0829](https://github.com/wsdwsd0829))
- Pointer check [\#970](https://github.com/TextureGroup/Texture/pull/970) ([wsdwsd0829](https://github.com/wsdwsd0829))
- Reduce usage of autorelease pools [\#968](https://github.com/TextureGroup/Texture/pull/968) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Update showcase to include Apollo for Reddit [\#967](https://github.com/TextureGroup/Texture/pull/967) ([christianselig](https://github.com/christianselig))
- Fix crash when call needsMainThreadDeallocation on NSProxy instances \#trivial [\#965](https://github.com/TextureGroup/Texture/pull/965) ([nguyenhuy](https://github.com/nguyenhuy))
- Fix name typo \#trivial [\#963](https://github.com/TextureGroup/Texture/pull/963) ([wsdwsd0829](https://github.com/wsdwsd0829))
- Generalize the main thread ivar deallocation system [\#959](https://github.com/TextureGroup/Texture/pull/959) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Add support for acquiring multiple locks at once [\#958](https://github.com/TextureGroup/Texture/pull/958) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Clean up async transaction system a bit [\#955](https://github.com/TextureGroup/Texture/pull/955) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Added 'Waplog' to showcase. [\#953](https://github.com/TextureGroup/Texture/pull/953) ([malikkuru](https://github.com/malikkuru))
- Make ASPerformMainThreadDeallocation visible in C [\#952](https://github.com/TextureGroup/Texture/pull/952) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Cut 2.7 release [\#949](https://github.com/TextureGroup/Texture/pull/949) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Fixed removing node from supernode after layout transition [\#937](https://github.com/TextureGroup/Texture/pull/937) ([atitovdev](https://github.com/atitovdev))
- add ASTextNode2 snapshot test [\#935](https://github.com/TextureGroup/Texture/pull/935) ([wsdwsd0829](https://github.com/wsdwsd0829))
- \[ASTextNode\] One more check variables before calling delegate method \#trivial [\#922](https://github.com/TextureGroup/Texture/pull/922) ([Flatout73](https://github.com/Flatout73))
- Assert node did load before did enter visible way 1 [\#886](https://github.com/TextureGroup/Texture/pull/886) ([wsdwsd0829](https://github.com/wsdwsd0829))
- Renew supplementary node on relayout [\#842](https://github.com/TextureGroup/Texture/pull/842) ([wsdwsd0829](https://github.com/wsdwsd0829))
## 2.5
## [2.7](https://github.com/TextureGroup/Texture/tree/2.7) (2018-05-29)
[Full Changelog](https://github.com/TextureGroup/Texture/compare/2.6...2.7)
- [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)
- 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) [#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)
- Fix crashes caused by failing to unlock or destroy a static mutex while the app is being terminated [Huy Nguyen](https://github.com/nguyenhuy)
**Merged pull requests:**
## 2.4
- Fix an issue where inserting/deleting sections could lead to inconsistent supplementary element behavior. [Adlai Holler](https://github.com/Adlai-Holler)
- Overhaul logging and add activity tracing support. [Adlai Holler](https://github.com/Adlai-Holler)
- Fix a crash where scrolling a table view after entering editing mode could lead to bad internal states in the table. [Huy Nguyen](https://github.com/nguyenhuy) [#416](https://github.com/TextureGroup/Texture/pull/416/)
- Fix a crash in collection view that occurs if batch updates are performed while scrolling [Huy Nguyen](https://github.com/nguyenhuy) [#378](https://github.com/TextureGroup/Texture/issues/378)
- Some improvements in ASCollectionView [Huy Nguyen](https://github.com/nguyenhuy) [#407](https://github.com/TextureGroup/Texture/pull/407)
- Small refactors in ASDataController [Huy Nguyen](https://github.com/TextureGroup/Texture/pull/443) [#443](https://github.com/TextureGroup/Texture/pull/443)
- [ASCollectionView] Add delegate bridging and index space translation for missing UICollectionViewLayout properties. [Scott Goodson](https://github.com/appleguy)
- [ASTextNode2] Add initial implementation for link handling. [Scott Goodson](https://github.com/appleguy) [#396](https://github.com/TextureGroup/Texture/pull/396)
- [ASTextNode2] Provide compile flag to globally enable new implementation of ASTextNode: ASTEXTNODE_EXPERIMENT_GLOBAL_ENABLE. [Scott Goodson](https://github.com/appleguy) [#396](https://github.com/TextureGroup/Texture/pull/410)
- Add ASCollectionGalleryLayoutDelegate - an async collection layout that makes same-size collections (e.g photo galleries, pagers, etc) fast and lightweight! [Huy Nguyen](https://github.com/nguyenhuy/) [#76](https://github.com/TextureGroup/Texture/pull/76) [#451](https://github.com/TextureGroup/Texture/pull/451)
- Fix an issue that causes infinite layout loop in ASDisplayNode after [#428](https://github.com/TextureGroup/Texture/pull/428) [Huy Nguyen](https://github.com/nguyenhuy) [#455](https://github.com/TextureGroup/Texture/pull/455)
- Rename ASCellNode.viewModel to ASCellNode.nodeModel to reduce collisions with subclass properties implemented by clients. [Adlai Holler](https://github.com/Adlai-Holler) [#504](https://github.com/TextureGroup/Texture/pull/504)
- Update AppIcon in showcase [\#946](https://github.com/TextureGroup/Texture/pull/946) ([muukii](https://github.com/muukii))
- Update tip-1-nodeBlocks.md [\#943](https://github.com/TextureGroup/Texture/pull/943) ([sagarbhosale](https://github.com/sagarbhosale))
- \[ASTableView\] Generate a new cell layout if existing ones are invalid [\#942](https://github.com/TextureGroup/Texture/pull/942) ([nguyenhuy](https://github.com/nguyenhuy))
- Update to unsplash [\#938](https://github.com/TextureGroup/Texture/pull/938) ([garrettmoon](https://github.com/garrettmoon))
- \[ASTextNode2\] Simplify compare-assign check & lock \_pointScaleFactors accessor \#trivial [\#934](https://github.com/TextureGroup/Texture/pull/934) ([appleguy](https://github.com/appleguy))
- Create a new dealloc queue that is more efficient [\#931](https://github.com/TextureGroup/Texture/pull/931) ([Adlai-Holler](https://github.com/Adlai-Holler))
- \[ASImageNode+AnimatedImage\] Fix early return when animatedImage is nil in setAnimatedImage \#trivial [\#925](https://github.com/TextureGroup/Texture/pull/925) ([flovouin](https://github.com/flovouin))
- Remove assert. fix \#878 \#914 [\#924](https://github.com/TextureGroup/Texture/pull/924) ([wsdwsd0829](https://github.com/wsdwsd0829))
- Always call out to delegate for experiments, whether enabled or not [\#923](https://github.com/TextureGroup/Texture/pull/923) ([Adlai-Holler](https://github.com/Adlai-Holler))
- \[ASTextNode2\] Upgrade lock safety by protecting all ivars \(including rarely-changed ones\). [\#918](https://github.com/TextureGroup/Texture/pull/918) ([appleguy](https://github.com/appleguy))
- \[ASCollectionNode/ASTableNode\] Fix a crash occurs while remeasuring cell nodes [\#917](https://github.com/TextureGroup/Texture/pull/917) ([nguyenhuy](https://github.com/nguyenhuy))
- \[ASDisplayNode\] Improve thread-safety of didExitHierarchy \#trivial [\#916](https://github.com/TextureGroup/Texture/pull/916) ([nguyenhuy](https://github.com/nguyenhuy))
- Prevent UITextView from updating contentOffset while deallocating [\#915](https://github.com/TextureGroup/Texture/pull/915) ([maicki](https://github.com/maicki))
- Fix ASDKgram-Swift to avoid 'error parsing JSON within PhotoModel Init' [\#913](https://github.com/TextureGroup/Texture/pull/913) ([kenstir](https://github.com/kenstir))
- \#trivial Add forgotten experiment into Schemas/configuration.json [\#912](https://github.com/TextureGroup/Texture/pull/912) ([garrettmoon](https://github.com/garrettmoon))
- \#trivial Fix the C++ assertion [\#911](https://github.com/TextureGroup/Texture/pull/911) ([garrettmoon](https://github.com/garrettmoon))
- Add 'iDiva - Beauty & Wedding tips' to Showcase [\#909](https://github.com/TextureGroup/Texture/pull/909) ([sudhanshutil](https://github.com/sudhanshutil))
- Issue ASNetworkImageNode callbacks off main thread [\#908](https://github.com/TextureGroup/Texture/pull/908) ([garrettmoon](https://github.com/garrettmoon))
- \[ASTextNode\] Fix a deadlock that could occur when enabling experimental ASTextNode2 via ASConfiguration [\#903](https://github.com/TextureGroup/Texture/pull/903) ([appleguy](https://github.com/appleguy))
- \[Docs\] Add new lightning talk from Buffer \#trivial [\#902](https://github.com/TextureGroup/Texture/pull/902) ([ay8s](https://github.com/ay8s))
- Request std=c++11 dialect again, and add warning [\#900](https://github.com/TextureGroup/Texture/pull/900) ([Adlai-Holler](https://github.com/Adlai-Holler))
- \[ASTextNode\] Check variables before calling delegate method \#trivial [\#898](https://github.com/TextureGroup/Texture/pull/898) ([Jauzee](https://github.com/Jauzee))
- ASDKFastImageNamed UIImage initializer nullability \#trivial [\#897](https://github.com/TextureGroup/Texture/pull/897) ([alexhillc](https://github.com/alexhillc))
- \#trivial Fixes an issue where playback may not start [\#896](https://github.com/TextureGroup/Texture/pull/896) ([garrettmoon](https://github.com/garrettmoon))
- Update configuration schema \#trivial [\#893](https://github.com/TextureGroup/Texture/pull/893) ([Adlai-Holler](https://github.com/Adlai-Holler))
- replace ` with code in containers-overview.md [\#884](https://github.com/TextureGroup/Texture/pull/884) ([everettjf](https://github.com/everettjf))
- \[Docs\] Fix typos in layout specs section \#trivial [\#883](https://github.com/TextureGroup/Texture/pull/883) ([morozkin](https://github.com/morozkin))
- Match interfacestate update sequence to uikit [\#882](https://github.com/TextureGroup/Texture/pull/882) ([wsdwsd0829](https://github.com/wsdwsd0829))
- Add experiment to skip creating UIViews altogether for constants [\#881](https://github.com/TextureGroup/Texture/pull/881) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Fix ASDISPLAYNODE\_ASSERTIONS\_ENABLED and ASDefaultPlaybackButton warnings \#trivial [\#880](https://github.com/TextureGroup/Texture/pull/880) ([maicki](https://github.com/maicki))
- Fix macro definition for AS\_KDEBUG\_ENABLE producing warning \#trivial [\#879](https://github.com/TextureGroup/Texture/pull/879) ([andrewrohn](https://github.com/andrewrohn))
- Fix pager node for interface coalescing [\#877](https://github.com/TextureGroup/Texture/pull/877) ([wsdwsd0829](https://github.com/wsdwsd0829))
- Standardize Property Declaration Style in Core Classes [\#870](https://github.com/TextureGroup/Texture/pull/870) ([Adlai-Holler](https://github.com/Adlai-Holler))
- \[NoCopyRendering\] In non-VM case, use calloc to get a zerod buffer \#trivial [\#869](https://github.com/TextureGroup/Texture/pull/869) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Check in Xcode 9.3 "workspace checks" file [\#868](https://github.com/TextureGroup/Texture/pull/868) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Remove Redundant Atomic Store from Recursive Unfair Lock in Recursive Case \#trivial [\#867](https://github.com/TextureGroup/Texture/pull/867) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Update Podspec [\#866](https://github.com/TextureGroup/Texture/pull/866) ([Adlai-Holler](https://github.com/Adlai-Holler))
- \[Issue 838\] Update ASCeilPixelValue and ASRoundPixelValue [\#864](https://github.com/TextureGroup/Texture/pull/864) ([rcancro](https://github.com/rcancro))
- Disable interface coalescing [\#862](https://github.com/TextureGroup/Texture/pull/862) ([wsdwsd0829](https://github.com/wsdwsd0829))
- Introduce ASRecursiveUnfairLock and tests [\#858](https://github.com/TextureGroup/Texture/pull/858) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Make NSIndexSet+ASHelpers.h reference local \#trivial [\#857](https://github.com/TextureGroup/Texture/pull/857) ([dmaclach](https://github.com/dmaclach))
- Make ASBatchContext lock-free \#trivial [\#854](https://github.com/TextureGroup/Texture/pull/854) ([Adlai-Holler](https://github.com/Adlai-Holler))
- \[ASNetworkImageNode\] Replace NSUUID sentinel with integer \#trivial [\#852](https://github.com/TextureGroup/Texture/pull/852) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Make objects conform to NSLocking [\#851](https://github.com/TextureGroup/Texture/pull/851) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Make cache support animated image [\#850](https://github.com/TextureGroup/Texture/pull/850) ([wsdwsd0829](https://github.com/wsdwsd0829))
- \[bugfix\] Align timing of interface coalescing and range update. \#trivial [\#847](https://github.com/TextureGroup/Texture/pull/847) ([wsdwsd0829](https://github.com/wsdwsd0829))
- Update layout2-layout-element-properties.md [\#844](https://github.com/TextureGroup/Texture/pull/844) ([arielelkin](https://github.com/arielelkin))
- Use NS\_RETURNS\_RETAINED macro to save time [\#843](https://github.com/TextureGroup/Texture/pull/843) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Handle nil backgroundColor in ASTextNode2 \#trivial [\#841](https://github.com/TextureGroup/Texture/pull/841) ([maicki](https://github.com/maicki))
- Put back VM flag in ASCGImageBuffer [\#839](https://github.com/TextureGroup/Texture/pull/839) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Order items in XCode project navigator by name [\#835](https://github.com/TextureGroup/Texture/pull/835) ([OleksiyA](https://github.com/OleksiyA))
- \[NoCopyRendering\] Use vm instead of malloc [\#833](https://github.com/TextureGroup/Texture/pull/833) ([Adlai-Holler](https://github.com/Adlai-Holler))
- \[ASTextNode2\] Fix background color drawing [\#831](https://github.com/TextureGroup/Texture/pull/831) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Fix Text Node Thread Sanitizer Warning [\#830](https://github.com/TextureGroup/Texture/pull/830) ([Adlai-Holler](https://github.com/Adlai-Holler))
- access view first before checking canBecome/Resign responder in becomeResponder methods [\#829](https://github.com/TextureGroup/Texture/pull/829) ([wsdwsd0829](https://github.com/wsdwsd0829))
- \[\#trivial\] fixes rendered image quality on networked image nodes whic… [\#826](https://github.com/TextureGroup/Texture/pull/826) ([garrettmoon](https://github.com/garrettmoon))
- \[ASTextNode\] Fix locking, add test for issue \#trivial [\#825](https://github.com/TextureGroup/Texture/pull/825) ([Adlai-Holler](https://github.com/Adlai-Holler))
- \[\#trivial\] I don't think we need this extra locked method. [\#824](https://github.com/TextureGroup/Texture/pull/824) ([garrettmoon](https://github.com/garrettmoon))
- \#trivial Hopefully made this a bit more readable. [\#823](https://github.com/TextureGroup/Texture/pull/823) ([garrettmoon](https://github.com/garrettmoon))
- \[ASTextNode\] Avoid acquiring instance lock multiple times \#trivial [\#820](https://github.com/TextureGroup/Texture/pull/820) ([nguyenhuy](https://github.com/nguyenhuy))
- \[Showcase\] Fix mensXP showcase and attach Vingle-Tech-Talk Medium [\#818](https://github.com/TextureGroup/Texture/pull/818) ([GeekTree0101](https://github.com/GeekTree0101))
- \[ASDisplayNode\] Add unit tests for layout z-order changes \(with an open issue to fix\). [\#816](https://github.com/TextureGroup/Texture/pull/816) ([appleguy](https://github.com/appleguy))
- \[ASDKGram Example\] image\_url has been changed from URL string to Array by 5… [\#813](https://github.com/TextureGroup/Texture/pull/813) ([kaar3k](https://github.com/kaar3k))
- Replace pthread specifics with C11 thread-local variables [\#811](https://github.com/TextureGroup/Texture/pull/811) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Upgrade dangerfile [\#810](https://github.com/TextureGroup/Texture/pull/810) ([garrettmoon](https://github.com/garrettmoon))
- \[ASDisplayNode\] Fix an issue that causes a node to sometimes return an outdated calculated size or size range [\#808](https://github.com/TextureGroup/Texture/pull/808) ([nguyenhuy](https://github.com/nguyenhuy))
- Avoid triggering main thread assertions in collection/table dealloc \#trivial [\#803](https://github.com/TextureGroup/Texture/pull/803) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Update IGListKit dependency to allow for updated versions [\#802](https://github.com/TextureGroup/Texture/pull/802) ([johntmcintosh](https://github.com/johntmcintosh))
- \[ASDisplayNode\] Consolidate main thread initialization and allow apps to invoke it manually instead of +load. [\#798](https://github.com/TextureGroup/Texture/pull/798) ([appleguy](https://github.com/appleguy))
- \[ASWrapperCellNode\] Introduce a new class allowing more control of UIKit passthrough cells. [\#797](https://github.com/TextureGroup/Texture/pull/797) ([appleguy](https://github.com/appleguy))
- Add missing scrollViewWillEndDragging passthrough delegate [\#796](https://github.com/TextureGroup/Texture/pull/796) ([xezero](https://github.com/xezero))
- Fix ASTextNode2 is accessing backgroundColor off main while sizing / layout is happening [\#794](https://github.com/TextureGroup/Texture/pull/794) ([maicki](https://github.com/maicki))
- \[ASTableNode & ASCollectionNode\] Keepalive reference for node if their view is necessarily alive \(has a superview\). [\#793](https://github.com/TextureGroup/Texture/pull/793) ([wsdwsd0829](https://github.com/wsdwsd0829))
- \[ASDisplayNode layout\] Fix an issue that sometimes causes a node's pending layout to not be applied [\#792](https://github.com/TextureGroup/Texture/pull/792) ([nguyenhuy](https://github.com/nguyenhuy))
- \[ASRangeController\] Fix stability of "minimum" rangeMode if the app has more than one layout before scrolling. [\#790](https://github.com/TextureGroup/Texture/pull/790) ([appleguy](https://github.com/appleguy))
- Fix UIResponder handling with view backing ASDisplayNode [\#789](https://github.com/TextureGroup/Texture/pull/789) ([maicki](https://github.com/maicki))
- New runloop queue to coalesce Interface state update calls. [\#788](https://github.com/TextureGroup/Texture/pull/788) ([wsdwsd0829](https://github.com/wsdwsd0829))
- \[Graphics contexts\] Retain the reference color space \#trivial [\#784](https://github.com/TextureGroup/Texture/pull/784) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Get CatDealsCollectionView example running again \#trivial [\#783](https://github.com/TextureGroup/Texture/pull/783) ([maicki](https://github.com/maicki))
- Improve nullable annotations for \_ASDisplayLayer and \_ASDisplayView \#trivial [\#780](https://github.com/TextureGroup/Texture/pull/780) ([maicki](https://github.com/maicki))
- \[ASDisplayNode\] Force a layout pass on a visible node as soon as it enters preload state [\#779](https://github.com/TextureGroup/Texture/pull/779) ([nguyenhuy](https://github.com/nguyenhuy))
- Improve ASNetworkImageNode delegate callout behavior [\#778](https://github.com/TextureGroup/Texture/pull/778) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Fix capturing self in the block while loading image in ASNetworkImageNode [\#777](https://github.com/TextureGroup/Texture/pull/777) ([morozkin](https://github.com/morozkin))
- Fix synchronous state of node if +viewClass or +layerClass is overwritten \#trivial [\#776](https://github.com/TextureGroup/Texture/pull/776) ([maicki](https://github.com/maicki))
- Add support for providing additional info to network image node delegate [\#775](https://github.com/TextureGroup/Texture/pull/775) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Expose asyncdisplaykit\_node in \_ASDisplayView same as in \_ASDisplayLayer \#trivial [\#773](https://github.com/TextureGroup/Texture/pull/773) ([maicki](https://github.com/maicki))
- Improve no-copy rendering experiment, remove +load method [\#771](https://github.com/TextureGroup/Texture/pull/771) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Fix typos in layout2-layoutspec-types.md \#trivial [\#770](https://github.com/TextureGroup/Texture/pull/770) ([morozkin](https://github.com/morozkin))
- Update PINCache [\#769](https://github.com/TextureGroup/Texture/pull/769) ([justinswart](https://github.com/justinswart))
- Fix misprint \#trivial [\#768](https://github.com/TextureGroup/Texture/pull/768) ([Flatout73](https://github.com/Flatout73))
- NoCopyRendering experiment: Fix possible memory leak if image node rendering is canceled \#trivial [\#765](https://github.com/TextureGroup/Texture/pull/765) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Node tint color [\#764](https://github.com/TextureGroup/Texture/pull/764) ([ShogunPhyched](https://github.com/ShogunPhyched))
- Revert "Faster collection operations" [\#759](https://github.com/TextureGroup/Texture/pull/759) ([Adlai-Holler](https://github.com/Adlai-Holler))
- \[ASPrimitiveTraitCollection\] Always treat preferredContentSize as a potential nil \#trivial [\#757](https://github.com/TextureGroup/Texture/pull/757) ([ypogribnyi](https://github.com/ypogribnyi))
- Update subclassing.md [\#753](https://github.com/TextureGroup/Texture/pull/753) ([janechoi6](https://github.com/janechoi6))
- \[ASDisplayNode\] Don't force a layout pass on a visible node that enters preload state [\#751](https://github.com/TextureGroup/Texture/pull/751) ([nguyenhuy](https://github.com/nguyenhuy))
- Fix the dangerfile [\#750](https://github.com/TextureGroup/Texture/pull/750) ([Adlai-Holler](https://github.com/Adlai-Holler))
- \[ASDisplayNode\] Always return the thread-safe cornerRadius property, even in slow CALayer rounding mode [\#749](https://github.com/TextureGroup/Texture/pull/749) ([nguyenhuy](https://github.com/nguyenhuy))
- Faster collection operations [\#748](https://github.com/TextureGroup/Texture/pull/748) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Create a centralized configuration API [\#747](https://github.com/TextureGroup/Texture/pull/747) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Update dangerfile for 2018 \#trivial [\#746](https://github.com/TextureGroup/Texture/pull/746) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Raise deployment target to iOS 9 [\#743](https://github.com/TextureGroup/Texture/pull/743) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Add an experimental "no-copy" renderer [\#741](https://github.com/TextureGroup/Texture/pull/741) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Fixed: completeBatchFetching is called on a background thread [\#731](https://github.com/TextureGroup/Texture/pull/731) ([aaronr93](https://github.com/aaronr93))
- \[tvOS\] Fixes errors when building against tvOS SDK [\#728](https://github.com/TextureGroup/Texture/pull/728) ([alexhillc](https://github.com/alexhillc))
- \[ASCellNode\] focusStyle mapping [\#727](https://github.com/TextureGroup/Texture/pull/727) ([alexhillc](https://github.com/alexhillc))
- \[ASDisplayNode\] Provide safeAreaInsets and layoutMargins bridge [\#685](https://github.com/TextureGroup/Texture/pull/685) ([ypogribnyi](https://github.com/ypogribnyi))
- \[ASTraitCollection\] Add missing properties to ASTraitCollection [\#625](https://github.com/TextureGroup/Texture/pull/625) ([ypogribnyi](https://github.com/ypogribnyi))
## 2.3.4
- [Yoga] Rewrite YOGA_TREE_CONTIGUOUS mode with improved behavior and cleaner integration [Scott Goodson](https://github.com/appleguy)
- [ASTraitCollection] Convert ASPrimitiveTraitCollection from lock to atomic. [Scott Goodson](https://github.com/appleguy)
- Add a synchronous mode to ASCollectionNode, for colletion view data source debugging. [Hannah Troisi](https://github.com/hannahmbanana)
- [ASDisplayNode+Layout] Add check for orphaned nodes after layout transition to clean up. #336. [Scott Goodson](https://github.com/appleguy)
- Fixed an issue where GIFs with placeholders never had their placeholders uncover the GIF. [Garrett Moon](https://github.com/garrettmoon)
- [Yoga] Implement ASYogaLayoutSpec, a simplified integration strategy for Yoga-powered layout calculation. [Scott Goodson](https://github.com/appleguy)
- Fixed an issue where calls to setNeedsDisplay and setNeedsLayout would stop working on loaded nodes. [Garrett Moon](https://github.com/garrettmoon)
- Migrated unit tests to OCMock 3.4 (from 2.2) and improved the multiplex image node tests. [Adlai Holler](https://github.com/Adlai-Holler)
- Fix CollectionNode double-load issue. This should significantly improve performance in cases where a collection node has content immediately available on first layout i.e. not fetched from the network. [Adlai Holler](https://github.com/Adlai-Holler)
- Overhaul layout flattening algorithm [Huy Nguyen](https://github.com/nguyenhuy) [#395](https://github.com/TextureGroup/Texture/pull/395).
## [2.6](https://github.com/TextureGroup/Texture/tree/2.6) (2018-01-12)
[Full Changelog](https://github.com/TextureGroup/Texture/compare/2.5.1...2.6)
## 2.3.3
- [ASTextKitFontSizeAdjuster] Replace use of NSAttributedString's boundingRectWithSize:options:context: with NSLayoutManager's boundingRectForGlyphRange:inTextContainer: [Ricky Cancro](https://github.com/rcancro)
- Add support for IGListKit post-removal-of-IGListSectionType, in preparation for IGListKit 3.0.0 release. [Adlai Holler](https://github.com/Adlai-Holler) [#49](https://github.com/TextureGroup/Texture/pull/49)
- Fix `__has_include` check in ASLog.h [Philipp Smorygo](Philipp.Smorygo@jetbrains.com)
- Fix potential deadlock in ASControlNode [Garrett Moon](https://github.com/garrettmoon)
- [Yoga Beta] Improvements to the experimental support for Yoga layout [Scott Goodson](appleguy)
- Make cell node `indexPath` and `supplementaryElementKind` atomic so you can read from any thread. [Adlai-Holler](https://github.com/Adlai-Holler) [#49](https://github.com/TextureGroup/Texture/pull/74)
- Update the rasterization API and un-deprecate it. [Adlai Holler](https://github.com/Adlai-Holler)[#82](https://github.com/TextureGroup/Texture/pull/49)
- Simplified & optimized hashing code. [Adlai Holler](https://github.com/Adlai-Holler) [#86](https://github.com/TextureGroup/Texture/pull/86)
- Improve the performance & safety of ASDisplayNode subnodes. [Adlai Holler](https://github.com/Adlai-Holler) [#223](https://github.com/TextureGroup/Texture/pull/223)
- Move more properties from ASTableView, ASCollectionView to their respective node classes. [Adlai Holler](https://github.com/Adlai-Holler)
- Remove finalLayoutElement [Michael Schneider](https://github.com/maicki)[#96](https://github.com/TextureGroup/Texture/pull/96)
- Add ASPageTable - A map table for fast retrieval of objects within a certain page [Huy Nguyen](https://github.com/nguyenhuy)
- Add new public `-supernodes`, `-supernodesIncludingSelf`, and `-supernodeOfClass:includingSelf:` methods. [Adlai Holler](https://github.com/Adlai-Holler)[#246](https://github.com/TextureGroup/Texture/pull/246)
- Improve our handling supernode traversal to avoid loading layers and fix assertion failures you might hit in debug. [Adlai Holler](https://github.com/Adlai-Holler)[#246](https://github.com/TextureGroup/Texture/pull/246)
- [ASDisplayNode] Pass drawParameter in rendering context callbacks [Michael Schneider](https://github.com/maicki)[#248](https://github.com/TextureGroup/Texture/pull/248)
- [ASTextNode] Move to class method of drawRect:withParameters:isCancelled:isRasterizing: for drawing [Michael Schneider](https://github.com/maicki)[#232](https://github.com/TextureGroup/Texture/pull/232)
- [ASDisplayNode] Remove instance:-drawRect:withParameters:isCancelled:isRasterizing: (https://github.com/maicki)[#232](https://github.com/TextureGroup/Texture/pull/232)
- [ASTextNode] Add an experimental new implementation. See `+[ASTextNode setExperimentOptions:]`. [Adlai Holler](https://github.com/Adlai-Holler)[#259](https://github.com/TextureGroup/Texture/pull/259)
- [ASVideoNode] Added error reporing to ASVideoNode and it's delegate [#260](https://github.com/TextureGroup/Texture/pull/260)
- [ASCollectionNode] Fixed conversion of item index paths between node & view. [Adlai Holler](https://github.com/Adlai-Holler) [#262](https://github.com/TextureGroup/Texture/pull/262)
- [Layout] Extract layout implementation code into it's own subcategories [Michael Schneider](https://github.com/maicki)[#272](https://github.com/TextureGroup/Texture/pull/272)
- [Fix] Fix a potential crash when cell nodes that need layout are deleted during the same runloop. [Adlai Holler](https://github.com/Adlai-Holler) [#279](https://github.com/TextureGroup/Texture/pull/279)
- [Batch fetching] Add ASBatchFetchingDelegate that takes scroll velocity and remaining time into account [Huy Nguyen](https://github.com/nguyenhuy) [#281](https://github.com/TextureGroup/Texture/pull/281)
- [Fix] Fix a major regression in our image node contents caching. [Adlai Holler](https://github.com/Adlai-Holler) [#287](https://github.com/TextureGroup/Texture/pull/287)
- [Fix] Fixed a bug where ASVideoNodeDelegate error reporting callback would crash an app because of not responding to selector. [Sergey Petrachkov](https://github.com/Petrachkov) [#291](https://github.com/TextureGroup/Texture/issues/291)
- [IGListKit] Add IGListKit headers to public section of Xcode project [Michael Schneider](https://github.com/maicki)[#286](https://github.com/TextureGroup/Texture/pull/286)
- [Layout] Ensure -layout and -layoutDidFinish are called only if a node is loaded. [Huy Nguyen](https://github.com/nguyenhuy) [#285](https://github.com/TextureGroup/Texture/pull/285)
- [Layout Debugger] Small changes needed for the coming layout debugger [Huy Nguyen](https://github.com/nguyenhuy) [#337](https://github.com/TextureGroup/Texture/pull/337)
**Merged pull requests:**
- Add MensXP to Showcase [\#739](https://github.com/TextureGroup/Texture/pull/739) ([sudhanshutil](https://github.com/sudhanshutil))
- Enable collection node interactive moves [\#735](https://github.com/TextureGroup/Texture/pull/735) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Add Blendle to our showcase page [\#721](https://github.com/TextureGroup/Texture/pull/721) ([nguyenhuy](https://github.com/nguyenhuy))
- \#trivial Fixes image nodes being stuck not being able to download image [\#720](https://github.com/TextureGroup/Texture/pull/720) ([garrettmoon](https://github.com/garrettmoon))
- Reimplement ASRectTable using unordered\_map to avoid obscure NSMapTable exception. [\#719](https://github.com/TextureGroup/Texture/pull/719) ([appleguy](https://github.com/appleguy))
- Add missing flags for ASCollectionDelegate [\#718](https://github.com/TextureGroup/Texture/pull/718) ([ilyailya](https://github.com/ilyailya))
- Add support for toggling logs off and back on at runtime [\#714](https://github.com/TextureGroup/Texture/pull/714) ([johntmcintosh](https://github.com/johntmcintosh))
- \[Update Showcase\] Update Showcase, add Vingle very community [\#711](https://github.com/TextureGroup/Texture/pull/711) ([GeekTree0101](https://github.com/GeekTree0101))
- \[ASCollectionElement\] Check for nil elements on ASTableView as well. [\#710](https://github.com/TextureGroup/Texture/pull/710) ([cesteban](https://github.com/cesteban))
- Ensure an ASM enabled node applies its pending layout when enters preload state [\#706](https://github.com/TextureGroup/Texture/pull/706) ([nguyenhuy](https://github.com/nguyenhuy))
- The ASDKgram example doesn't compile. [\#700](https://github.com/TextureGroup/Texture/pull/700) ([onato](https://github.com/onato))
- Revert Adds support for specifying a quality indexed array of URLs [\#699](https://github.com/TextureGroup/Texture/pull/699) ([garrettmoon](https://github.com/garrettmoon))
- Correct Synchronous Concurrency Talk Link [\#698](https://github.com/TextureGroup/Texture/pull/698) ([ay8s](https://github.com/ay8s))
- \[ASDisplayNode+Layout\] Ensure a pending layout is applied once [\#695](https://github.com/TextureGroup/Texture/pull/695) ([nguyenhuy](https://github.com/nguyenhuy))
- Add missing \</div\> tags in Layout API Sizing docs [\#691](https://github.com/TextureGroup/Texture/pull/691) ([richardhenry](https://github.com/richardhenry))
- Fix bug that breaks ASNodeController docs page [\#690](https://github.com/TextureGroup/Texture/pull/690) ([richardhenry](https://github.com/richardhenry))
- Add a recent talk by @smeis at CocoaHeadsNL [\#687](https://github.com/TextureGroup/Texture/pull/687) ([nguyenhuy](https://github.com/nguyenhuy))
- Update subtree-rasterization.md [\#679](https://github.com/TextureGroup/Texture/pull/679) ([WymzeeLabs](https://github.com/WymzeeLabs))
- Update layer-backing.md [\#678](https://github.com/TextureGroup/Texture/pull/678) ([WymzeeLabs](https://github.com/WymzeeLabs))
- \[iOS11\] Update project settings and fix errors [\#676](https://github.com/TextureGroup/Texture/pull/676) ([Eke](https://github.com/Eke))
- Fix swift sample. [\#669](https://github.com/TextureGroup/Texture/pull/669) ([rwinzhang](https://github.com/rwinzhang))
- Bugfix/fix yoga logging aligning api changes [\#668](https://github.com/TextureGroup/Texture/pull/668) ([wsdwsd0829](https://github.com/wsdwsd0829))
- Make it possible to map between sections even if they're empty [\#660](https://github.com/TextureGroup/Texture/pull/660) ([Adlai-Holler](https://github.com/Adlai-Holler))
- \[ASCornerLayoutSpec\] New layout spec class for declarative corner element layout. [\#657](https://github.com/TextureGroup/Texture/pull/657) ([huang-kun](https://github.com/huang-kun))
- Update layout2-layoutspec-types.md [\#655](https://github.com/TextureGroup/Texture/pull/655) ([TBXark](https://github.com/TBXark))
- \[Minor Breaking API\] Make deallocation queues more reliable [\#651](https://github.com/TextureGroup/Texture/pull/651) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Make the framework backwards compatible with Xcode 8 [\#650](https://github.com/TextureGroup/Texture/pull/650) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Disable this test for now, it's too flakey and no one has time to inv… [\#649](https://github.com/TextureGroup/Texture/pull/649) ([garrettmoon](https://github.com/garrettmoon))
- \[Documentation\] Update Inversion Docs [\#647](https://github.com/TextureGroup/Texture/pull/647) ([GeekTree0101](https://github.com/GeekTree0101))
- Have ASNetworkImageNode report whether images were cached or not [\#639](https://github.com/TextureGroup/Texture/pull/639) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Fix a layout deadlock caused by holding the lock and going up the tree. [\#638](https://github.com/TextureGroup/Texture/pull/638) ([garrettmoon](https://github.com/garrettmoon))
- \[ASScrollNode\] Fix small bugs and add unit tests [\#637](https://github.com/TextureGroup/Texture/pull/637) ([nguyenhuy](https://github.com/nguyenhuy))
- A couple performance tweaks for animated images \#trivial [\#634](https://github.com/TextureGroup/Texture/pull/634) ([garrettmoon](https://github.com/garrettmoon))
- \[Documentation\] Update "Getting Started" page [\#633](https://github.com/TextureGroup/Texture/pull/633) ([nguyenhuy](https://github.com/nguyenhuy))
- \[Tests\] Add test scrollToPageAtIndex ASPagerNode [\#629](https://github.com/TextureGroup/Texture/pull/629) ([remirobert](https://github.com/remirobert))
- \[Tests\] Introducing tests for the ASTabBarController [\#628](https://github.com/TextureGroup/Texture/pull/628) ([remirobert](https://github.com/remirobert))
- \[Tests\] Introducing tests for the ASNavigationController [\#627](https://github.com/TextureGroup/Texture/pull/627) ([remirobert](https://github.com/remirobert))
- \[ASCollectionView\] Call -invalidateFlowLayoutDelegateMetrics when rotating. \#trivial [\#616](https://github.com/TextureGroup/Texture/pull/616) ([appleguy](https://github.com/appleguy))
- Add unit tests for the layout engine [\#424](https://github.com/TextureGroup/Texture/pull/424) ([Adlai-Holler](https://github.com/Adlai-Holler))
## [2.5.1](https://github.com/TextureGroup/Texture/tree/2.5.1) (2017-10-24)
[Full Changelog](https://github.com/TextureGroup/Texture/compare/2.5...2.5.1)
**Merged pull requests:**
- Dispatch batch update to main \#trivial [\#626](https://github.com/TextureGroup/Texture/pull/626) ([garrettmoon](https://github.com/garrettmoon))
- Check if we need to do a batch update [\#624](https://github.com/TextureGroup/Texture/pull/624) ([garrettmoon](https://github.com/garrettmoon))
- Fix naming conflict with YYText \#trivial [\#623](https://github.com/TextureGroup/Texture/pull/623) ([maicki](https://github.com/maicki))
- Fix "This block and function declaration is not a prototype" warning. [\#619](https://github.com/TextureGroup/Texture/pull/619) ([mbesnili](https://github.com/mbesnili))
- update Pinterest CDN URL in example code [\#613](https://github.com/TextureGroup/Texture/pull/613) ([derekargueta](https://github.com/derekargueta))
- \[ASTextKitComponents\] Make sure Main Thread Checker isn't triggered during background calculations \#trivial [\#612](https://github.com/TextureGroup/Texture/pull/612) ([nguyenhuy](https://github.com/nguyenhuy))
- \[ASTextKitComponents\] Temporary components can be deallocated off main \#trivial [\#610](https://github.com/TextureGroup/Texture/pull/610) ([nguyenhuy](https://github.com/nguyenhuy))
- Update layout2-layoutspec-types.md [\#608](https://github.com/TextureGroup/Texture/pull/608) ([olcayertas](https://github.com/olcayertas))
- Don't set download results if no longer in preload range. [\#606](https://github.com/TextureGroup/Texture/pull/606) ([garrettmoon](https://github.com/garrettmoon))
- Animated WebP support [\#605](https://github.com/TextureGroup/Texture/pull/605) ([garrettmoon](https://github.com/garrettmoon))
- \[ASVideoNode\] Time observer fix [\#604](https://github.com/TextureGroup/Texture/pull/604) ([flovouin](https://github.com/flovouin))
- Add assertion in dealloc that it is on main in ASTextKitComponents \#trivial [\#603](https://github.com/TextureGroup/Texture/pull/603) ([maicki](https://github.com/maicki))
- ASTextKitComponents needs to be deallocated on main [\#598](https://github.com/TextureGroup/Texture/pull/598) ([maicki](https://github.com/maicki))
- update faq toc links to match the generated html id \#trivial [\#597](https://github.com/TextureGroup/Texture/pull/597) ([romankl](https://github.com/romankl))
- \[PINCache\] Set a default .byteLimit to reduce disk usage & startup time. [\#595](https://github.com/TextureGroup/Texture/pull/595) ([appleguy](https://github.com/appleguy))
- Move clearing out of ASTextKitComponents property delegates into ASTextKitComponents dealloc \#trivial [\#591](https://github.com/TextureGroup/Texture/pull/591) ([maicki](https://github.com/maicki))
- Clear ivar after scheduling for main thread deallocation \#trivial [\#590](https://github.com/TextureGroup/Texture/pull/590) ([maicki](https://github.com/maicki))
- Use Nil for "no class" instead of nil \#trivial [\#589](https://github.com/TextureGroup/Texture/pull/589) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Update showcase.md [\#587](https://github.com/TextureGroup/Texture/pull/587) ([hannahmbanana](https://github.com/hannahmbanana))
- Rolling back CI to known version for now [\#585](https://github.com/TextureGroup/Texture/pull/585) ([garrettmoon](https://github.com/garrettmoon))
- Use node lock instead of separate one to avoid deadlocks. [\#582](https://github.com/TextureGroup/Texture/pull/582) ([garrettmoon](https://github.com/garrettmoon))
- \[\_ASPendingState\] Make sure accessibility strings are not nil before allocating attributed strings for them \#trivial [\#581](https://github.com/TextureGroup/Texture/pull/581) ([nguyenhuy](https://github.com/nguyenhuy))
- \[ASTextNode\] Implement an example comparing ASTextNode 1 & 2 behavior. [\#570](https://github.com/TextureGroup/Texture/pull/570) ([wsdwsd0829](https://github.com/wsdwsd0829))
- \[ASCollectionView\] Fix index space translation of Flow Layout Delegate methods. [\#467](https://github.com/TextureGroup/Texture/pull/467) ([appleguy](https://github.com/appleguy))
- \[ASCollectionView\] Improve performance and behavior of rotation / bounds changes. [\#431](https://github.com/TextureGroup/Texture/pull/431) ([appleguy](https://github.com/appleguy))
## [2.5](https://github.com/TextureGroup/Texture/tree/2.5) (2017-09-26)
[Full Changelog](https://github.com/TextureGroup/Texture/compare/v2.5...2.5)
**Merged pull requests:**
- Fix crashes caused by failing to unlock or destroy a static mutex while the app is being terminated [\#577](https://github.com/TextureGroup/Texture/pull/577) ([nguyenhuy](https://github.com/nguyenhuy))
- Update yoga version [\#569](https://github.com/TextureGroup/Texture/pull/569) ([wsdwsd0829](https://github.com/wsdwsd0829))
- \[ASDKgram Example\] fix crash on startup [\#566](https://github.com/TextureGroup/Texture/pull/566) ([hannahmbanana](https://github.com/hannahmbanana))
- Added attributed versions of accessibilityLabel, accessibilityHint, accessibilityValue [\#554](https://github.com/TextureGroup/Texture/pull/554) ([fruitcoder](https://github.com/fruitcoder))
- \[Yoga\] Add insertYogaNode:atIndex: method. Improve handling of relayouts. [\#469](https://github.com/TextureGroup/Texture/pull/469) ([appleguy](https://github.com/appleguy))
- \[ASCornerRounding\] Introduce .cornerRoundingType: CALayer, Precomposited, or Clip Corners. [\#465](https://github.com/TextureGroup/Texture/pull/465) ([appleguy](https://github.com/appleguy))
- \[ASElementMap\] Fix indexPath's section or item is actually negative \#trivial [\#457](https://github.com/TextureGroup/Texture/pull/457) ([Anyewuya](https://github.com/Anyewuya))
## [v2.5](https://github.com/TextureGroup/Texture/tree/v2.5) (2017-09-14)
[Full Changelog](https://github.com/TextureGroup/Texture/compare/2.4...v2.5)
**Merged pull requests:**
- Fix -\[ASPagerNode view\] triggering pendingState + nodeLoaded assert \#trivial [\#564](https://github.com/TextureGroup/Texture/pull/564) ([samhsiung](https://github.com/samhsiung))
- \[ASCollectionLayout\] Exclude content inset on scrollable directions from viewport size [\#562](https://github.com/TextureGroup/Texture/pull/562) ([nguyenhuy](https://github.com/nguyenhuy))
- \[ASImageNode\] Always dealloc images in a background queue [\#561](https://github.com/TextureGroup/Texture/pull/561) ([nguyenhuy](https://github.com/nguyenhuy))
- \[ASCollectionNode\]\[ASTableNode\] Add content inset bridging property [\#560](https://github.com/TextureGroup/Texture/pull/560) ([nguyenhuy](https://github.com/nguyenhuy))
- Mark ASRunLoopQueue as drained if it contains only NULLs [\#558](https://github.com/TextureGroup/Texture/pull/558) ([cesteban](https://github.com/cesteban))
- Adds support for specifying a quality indexed array of URLs [\#557](https://github.com/TextureGroup/Texture/pull/557) ([garrettmoon](https://github.com/garrettmoon))
- Make ASWeakMapEntry Value Atomic [\#555](https://github.com/TextureGroup/Texture/pull/555) ([Adlai-Holler](https://github.com/Adlai-Holler))
- \[ASDisplayNode\] Deprecate -displayWillStart in favor of -displayWillStartAsynchronously: [\#536](https://github.com/TextureGroup/Texture/pull/536) ([nguyenhuy](https://github.com/nguyenhuy))
- SEP-491 prerequisite: add textViewShouldBeginEditing: to ASEditableTextNodeDelegate [\#535](https://github.com/TextureGroup/Texture/pull/535) ([yans](https://github.com/yans))
- \[Gallery layout\] Include the caller in properties providing methods [\#533](https://github.com/TextureGroup/Texture/pull/533) ([nguyenhuy](https://github.com/nguyenhuy))
- \[ASDisplayNode\] Notify rasterized subnodes that render pass has completed [\#532](https://github.com/TextureGroup/Texture/pull/532) ([smeis](https://github.com/smeis))
- \[Cleanup\] Remove deprecated APIs [\#529](https://github.com/TextureGroup/Texture/pull/529) ([nguyenhuy](https://github.com/nguyenhuy))
- Add a function to disable all logging at runtime [\#528](https://github.com/TextureGroup/Texture/pull/528) ([Adlai-Holler](https://github.com/Adlai-Holler))
- \[Table and collection views\] Consider content inset when calculating \(default\) element size range [\#525](https://github.com/TextureGroup/Texture/pull/525) ([nguyenhuy](https://github.com/nguyenhuy))
- \[ASCollectionNode\] Add -isProcessingUpdates and -onDidFinishProcessingUpdates: APIs. [\#522](https://github.com/TextureGroup/Texture/pull/522) ([appleguy](https://github.com/appleguy))
- ASImageNode+AnimatedImage playbackReadyCallback retain cycle [\#520](https://github.com/TextureGroup/Texture/pull/520) ([plarson](https://github.com/plarson))
- \[CI\] BuildKite to ignore all markdown files [\#517](https://github.com/TextureGroup/Texture/pull/517) ([nguyenhuy](https://github.com/nguyenhuy))
- ASCollectionLayout improvements [\#513](https://github.com/TextureGroup/Texture/pull/513) ([nguyenhuy](https://github.com/nguyenhuy))
- Update changelog and podspec for 2.4 [\#512](https://github.com/TextureGroup/Texture/pull/512) ([Adlai-Holler](https://github.com/Adlai-Holler))
- ASCollectionLayout to return a zero content size if its state is unavailable [\#509](https://github.com/TextureGroup/Texture/pull/509) ([nguyenhuy](https://github.com/nguyenhuy))
- Update corner-rounding.md [\#482](https://github.com/TextureGroup/Texture/pull/482) ([oferRounds](https://github.com/oferRounds))
- \[Accessibility\] Add .isAccessibilityContainer property, allowing automatic aggregation of children's a11y labels. [\#468](https://github.com/TextureGroup/Texture/pull/468) ([appleguy](https://github.com/appleguy))
- \[ASImageNode\] Enable .clipsToBounds by default \(fix .cornerRadius, GIFs overflow\). [\#466](https://github.com/TextureGroup/Texture/pull/466) ([appleguy](https://github.com/appleguy))
## [2.4](https://github.com/TextureGroup/Texture/tree/2.4) (2017-08-15)
[Full Changelog](https://github.com/TextureGroup/Texture/compare/2.3.4...2.4)
**Merged pull requests:**
- Avoid re-entrant call to self.view when applying initial pending state [\#510](https://github.com/TextureGroup/Texture/pull/510) ([Adlai-Holler](https://github.com/Adlai-Holler))
- \[examples/ASCollectionView\] Register supplementary kinds \#trivial [\#508](https://github.com/TextureGroup/Texture/pull/508) ([nguyenhuy](https://github.com/nguyenhuy))
- Rename the field again to nodeModel [\#504](https://github.com/TextureGroup/Texture/pull/504) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Rename -\[ASCellNode viewModel\] to -\[ASCellNode nodeViewModel\] to avoid collisions [\#499](https://github.com/TextureGroup/Texture/pull/499) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Fixed typo `UIKIt` [\#497](https://github.com/TextureGroup/Texture/pull/497) ([nixzhu](https://github.com/nixzhu))
- Improvements in ASCollectionGalleryLayoutDelegate [\#496](https://github.com/TextureGroup/Texture/pull/496) ([nguyenhuy](https://github.com/nguyenhuy))
- \[Showcase\] Update showcase - add blog post link to ClassDojo icon \#trivial [\#493](https://github.com/TextureGroup/Texture/pull/493) ([Kaspik](https://github.com/Kaspik))
- \[ASCoreAnimationExtras\] Update documentation for resizbale images \#trivial [\#492](https://github.com/TextureGroup/Texture/pull/492) ([Kaspik](https://github.com/Kaspik))
- \[ASStackLayoutSpec\] Fix interitem spacing not being reset on new lines and add snapshot tests \#trivial [\#491](https://github.com/TextureGroup/Texture/pull/491) ([nguyenhuy](https://github.com/nguyenhuy))
- \[Layout Transition\] Avoid calling didComplete method if pending layout transition is nil [\#490](https://github.com/TextureGroup/Texture/pull/490) ([nguyenhuy](https://github.com/nguyenhuy))
- \[LayoutTransition\] Call \_locked\_constrainedSizeForLayoutPass with the lock actually held \#trivial [\#488](https://github.com/TextureGroup/Texture/pull/488) ([nguyenhuy](https://github.com/nguyenhuy))
- iOS 11 UITableView automatic height estimation fix [\#485](https://github.com/TextureGroup/Texture/pull/485) ([christianselig](https://github.com/christianselig))
- Update scroll-node.md [\#484](https://github.com/TextureGroup/Texture/pull/484) ([oferRounds](https://github.com/oferRounds))
- Update adoption-guide-2-0-beta1.md [\#483](https://github.com/TextureGroup/Texture/pull/483) ([oferRounds](https://github.com/oferRounds))
- Update subclassing.md [\#479](https://github.com/TextureGroup/Texture/pull/479) ([oferRounds](https://github.com/oferRounds))
- Invalidate layouts more aggressively when transitioning with animation [\#476](https://github.com/TextureGroup/Texture/pull/476) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Update image-modification-block.md [\#474](https://github.com/TextureGroup/Texture/pull/474) ([oferRounds](https://github.com/oferRounds))
- \[ASStackLayoutSpec\] Flex wrap fix and lineSpacing property [\#472](https://github.com/TextureGroup/Texture/pull/472) ([flovouin](https://github.com/flovouin))
- \[ASNodeController\] Add -nodeDidLayout callback. Allow switching retain behavior at runtime. [\#470](https://github.com/TextureGroup/Texture/pull/470) ([appleguy](https://github.com/appleguy))
- \[Layout transition\] Invalidate calculated layout if transitioning using the same size range [\#464](https://github.com/TextureGroup/Texture/pull/464) ([nguyenhuy](https://github.com/nguyenhuy))
- \[ASTableNode\]\[ASCollectionNode\] Add content offset bridging property [\#460](https://github.com/TextureGroup/Texture/pull/460) ([nguyenhuy](https://github.com/nguyenhuy))
- \[ASDisplayNode\] Fix infinite layout loop [\#455](https://github.com/TextureGroup/Texture/pull/455) ([nguyenhuy](https://github.com/nguyenhuy))
- Add ASPagerNode+Beta to umbrella header \#trivial [\#454](https://github.com/TextureGroup/Texture/pull/454) ([nguyenhuy](https://github.com/nguyenhuy))
- \[ASPagerNode\] Remove unused flow layout reference \#trivial [\#452](https://github.com/TextureGroup/Texture/pull/452) ([nguyenhuy](https://github.com/nguyenhuy))
- \[ASCollectionLayout\] Add ASCollectionGalleryLayoutSizeProviding [\#451](https://github.com/TextureGroup/Texture/pull/451) ([nguyenhuy](https://github.com/nguyenhuy))
- fix SIMULATE\_WEB\_RESPONSE not imported \#449 [\#450](https://github.com/TextureGroup/Texture/pull/450) ([wsdwsd0829](https://github.com/wsdwsd0829))
- \[ASDataController \] Merge willUpdateWithChangeSet and didUpdateWithChangeSet delegate methods \#trivial [\#445](https://github.com/TextureGroup/Texture/pull/445) ([nguyenhuy](https://github.com/nguyenhuy))
- \[ASDataController\] Clean up [\#443](https://github.com/TextureGroup/Texture/pull/443) ([nguyenhuy](https://github.com/nguyenhuy))
- \[ASDataController\] Avoid asking for size ranges of soon-to-be-delete elements during relayouts [\#442](https://github.com/TextureGroup/Texture/pull/442) ([nguyenhuy](https://github.com/nguyenhuy))
- \[ASCollectionView\] Add delegate bridging and index space translation for missing UICollectionViewLayout properties. [\#440](https://github.com/TextureGroup/Texture/pull/440) ([appleguy](https://github.com/appleguy))
- \[ASDisplayNode\] Fix some gaps in the bridging of new contents\* properties. [\#435](https://github.com/TextureGroup/Texture/pull/435) ([appleguy](https://github.com/appleguy))
- \[ASDisplayNode\] Allow setting stretchable contents on nodes; add bridged properties. \#trivial [\#429](https://github.com/TextureGroup/Texture/pull/429) ([appleguy](https://github.com/appleguy))
- Use a sentinel NSUInteger for node layout data \#trivial [\#428](https://github.com/TextureGroup/Texture/pull/428) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Workaround clang4.0 \<stdatomic.h\> initialization \#trivial [\#426](https://github.com/TextureGroup/Texture/pull/426) ([bkase](https://github.com/bkase))
- Add missing import in ASDisplayNode+AsyncDisplay \#trivial [\#423](https://github.com/TextureGroup/Texture/pull/423) ([nguyenhuy](https://github.com/nguyenhuy))
- \[ASCollectionElement\] Add checks for nil element, prior to other PRs landing. [\#421](https://github.com/TextureGroup/Texture/pull/421) ([appleguy](https://github.com/appleguy))
- \[ASDataController\] Apply new visible map inside batch updates block [\#420](https://github.com/TextureGroup/Texture/pull/420) ([nguyenhuy](https://github.com/nguyenhuy))
- \[ASVideoPlayerNode\] Check that the video player's delegate implements the didTapFullScreenButtonNode method before calling it \#trivial [\#418](https://github.com/TextureGroup/Texture/pull/418) ([tnev](https://github.com/tnev))
- \[ASDataController\] Fix a crash in table view caused by executing an empty change set during layoutSubviews [\#416](https://github.com/TextureGroup/Texture/pull/416) ([nguyenhuy](https://github.com/nguyenhuy))
- \[ASDisplayNode+Layout\] In layoutThatFits:, check and use \_pending layout if valid. [\#413](https://github.com/TextureGroup/Texture/pull/413) ([appleguy](https://github.com/appleguy))
- Integrate Weaver into ASDKGram [\#412](https://github.com/TextureGroup/Texture/pull/412) ([nguyenhuy](https://github.com/nguyenhuy))
- \[ASDisplayNode\] -didEnterPreloadState does not need to call -layoutIfNeeded \#trivial [\#411](https://github.com/TextureGroup/Texture/pull/411) ([appleguy](https://github.com/appleguy))
- \[ASTextNode2\] Provide compiler flag to enable ASTextNode2 for all usages. [\#410](https://github.com/TextureGroup/Texture/pull/410) ([appleguy](https://github.com/appleguy))
- \[Yoga\] Refine the handling of measurement functions when Yoga is used. [\#408](https://github.com/TextureGroup/Texture/pull/408) ([appleguy](https://github.com/appleguy))
- \[ASCollectionView\] Small improvements [\#407](https://github.com/TextureGroup/Texture/pull/407) ([nguyenhuy](https://github.com/nguyenhuy))
- \[Documentation\] Improve description of synchronous concurrency with screenshot and video link. [\#406](https://github.com/TextureGroup/Texture/pull/406) ([appleguy](https://github.com/appleguy))
- Introduce ASIntegerMap, improve our changeset handling \#trivial [\#405](https://github.com/TextureGroup/Texture/pull/405) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Fix issue where supplementary elements don't track section changes [\#404](https://github.com/TextureGroup/Texture/pull/404) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Overhaul our logging, add activity tracing support. [\#399](https://github.com/TextureGroup/Texture/pull/399) ([Adlai-Holler](https://github.com/Adlai-Holler))
- \[ASTextNode2\] Add initial implementation for link handling. [\#396](https://github.com/TextureGroup/Texture/pull/396) ([appleguy](https://github.com/appleguy))
- Introduce ASCollectionGalleryLayoutDelegate [\#76](https://github.com/TextureGroup/Texture/pull/76) ([nguyenhuy](https://github.com/nguyenhuy))
## [2.3.4](https://github.com/TextureGroup/Texture/tree/2.3.4) (2017-06-30)
[Full Changelog](https://github.com/TextureGroup/Texture/compare/2.3.3...2.3.4)
**Merged pull requests:**
- Update to the latest betas of PINRemoteImage and PINCache [\#403](https://github.com/TextureGroup/Texture/pull/403) ([garrettmoon](https://github.com/garrettmoon))
- A bit of minor cleanup \#trivial [\#402](https://github.com/TextureGroup/Texture/pull/402) ([Adlai-Holler](https://github.com/Adlai-Holler))
- \[ASLayout\] Revisit the flattening algorithm [\#395](https://github.com/TextureGroup/Texture/pull/395) ([nguyenhuy](https://github.com/nguyenhuy))
- \[ASLayout\] If a layout has no sublayouts, don't bother initializing its rect table [\#394](https://github.com/TextureGroup/Texture/pull/394) ([nguyenhuy](https://github.com/nguyenhuy))
- \[ASLayout\] Fix documentation of retainSublayoutLayoutElements \#trivial [\#393](https://github.com/TextureGroup/Texture/pull/393) ([nguyenhuy](https://github.com/nguyenhuy))
- Fix compiling ASDimension if Yoga enabled \#trivial [\#389](https://github.com/TextureGroup/Texture/pull/389) ([maicki](https://github.com/maicki))
- comments to reflect code \#trivial [\#388](https://github.com/TextureGroup/Texture/pull/388) ([benjamin-chang](https://github.com/benjamin-chang))
- \[ASCellNode\] Remove unnecessary frame setting \#trivial [\#387](https://github.com/TextureGroup/Texture/pull/387) ([nguyenhuy](https://github.com/nguyenhuy))
- Horrible spelling mistake \#trivial [\#384](https://github.com/TextureGroup/Texture/pull/384) ([nguyenhuy](https://github.com/nguyenhuy))
- Fix for Video Table Example Building [\#383](https://github.com/TextureGroup/Texture/pull/383) ([ay8s](https://github.com/ay8s))
- ASDimensionMake to be more lenient \#trivial [\#382](https://github.com/TextureGroup/Texture/pull/382) ([nguyenhuy](https://github.com/nguyenhuy))
- Gate orphaned node detector behind YOGA flag \#trivial [\#380](https://github.com/TextureGroup/Texture/pull/380) ([nguyenhuy](https://github.com/nguyenhuy))
- \[Event Log\] Log ASM flag when modify subnodes \#trivial [\#379](https://github.com/TextureGroup/Texture/pull/379) ([nguyenhuy](https://github.com/nguyenhuy))
- Add new workspaces for tests for different integrations \#trivial [\#377](https://github.com/TextureGroup/Texture/pull/377) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Fix imageModificationBlock doc \#trivial [\#376](https://github.com/TextureGroup/Texture/pull/376) ([maicki](https://github.com/maicki))
- Fix double-load issue with ASCollectionNode [\#372](https://github.com/TextureGroup/Texture/pull/372) ([Adlai-Holler](https://github.com/Adlai-Holler))
- FIXED Typo in Layout Transition Documentation [\#371](https://github.com/TextureGroup/Texture/pull/371) ([martinjkelly](https://github.com/martinjkelly))
- \[Yoga\] Delete YOGA\_TREE\_CONTIGOUS gating and permanently enable. \#trivial [\#370](https://github.com/TextureGroup/Texture/pull/370) ([appleguy](https://github.com/appleguy))
- \[Yoga\] Minimize number of nodes that have MeasureFunc set on them. [\#369](https://github.com/TextureGroup/Texture/pull/369) ([appleguy](https://github.com/appleguy))
- Improve System Trace Implementation \#trivial [\#368](https://github.com/TextureGroup/Texture/pull/368) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Updates ASDKGram to use IGListKit 3.0.0 [\#367](https://github.com/TextureGroup/Texture/pull/367) ([ay8s](https://github.com/ay8s))
- Update link to AsyncDisplayKit 2.0 Launch Talk [\#363](https://github.com/TextureGroup/Texture/pull/363) ([appleguy](https://github.com/appleguy))
- \[ASTableView\] Use ASTableView tableNode property instead of calling ASViewToDisplayNode \#trivial [\#361](https://github.com/TextureGroup/Texture/pull/361) ([maicki](https://github.com/maicki))
- Add section-object support to new tests, improve test confinement. \#trivial [\#360](https://github.com/TextureGroup/Texture/pull/360) ([Adlai-Holler](https://github.com/Adlai-Holler))
- \[Docs\] Update 'Corner Rounding' document for Texture 2 [\#359](https://github.com/TextureGroup/Texture/pull/359) ([ArchimboldiMao](https://github.com/ArchimboldiMao))
- Add support for keeping letting cell nodes update to new view models when reloaded. \#trivial [\#357](https://github.com/TextureGroup/Texture/pull/357) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Add first-pass view model support to collection node. \#trivial [\#356](https://github.com/TextureGroup/Texture/pull/356) ([Adlai-Holler](https://github.com/Adlai-Holler))
- \[ASTraitCollection\] Convert ASPrimitiveTraitCollection from lock to atomic. [\#355](https://github.com/TextureGroup/Texture/pull/355) ([appleguy](https://github.com/appleguy))
- Improve collection node testing, reveal double-load issue. \#trivial [\#352](https://github.com/TextureGroup/Texture/pull/352) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Fix title in changelog [\#350](https://github.com/TextureGroup/Texture/pull/350) ([levi](https://github.com/levi))
- Add a Flag to Disable Main Thread Assertions \#trivial [\#348](https://github.com/TextureGroup/Texture/pull/348) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Migrate to Latest OCMock, Demonstrate Improved Unit Testing [\#347](https://github.com/TextureGroup/Texture/pull/347) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Upgrade ASLayoutElementContext to an Object \#trivial [\#344](https://github.com/TextureGroup/Texture/pull/344) ([Adlai-Holler](https://github.com/Adlai-Holler))
- \[Yoga\] Rewrite YOGA\_TREE\_CONTIGUOUS mode with improved behavior and cleaner integration [\#343](https://github.com/TextureGroup/Texture/pull/343) ([appleguy](https://github.com/appleguy))
- Fix internal Linter warnings \#trivial [\#340](https://github.com/TextureGroup/Texture/pull/340) ([maicki](https://github.com/maicki))
- Small changes required by the coming layout debugger [\#337](https://github.com/TextureGroup/Texture/pull/337) ([nguyenhuy](https://github.com/nguyenhuy))
- \[ASDataController\] Add event logging for transaction queue flush duration \#trivial [\#334](https://github.com/TextureGroup/Texture/pull/334) ([hannahmbanana](https://github.com/hannahmbanana))
- \[ASCollectionView\] synchronous mode [\#332](https://github.com/TextureGroup/Texture/pull/332) ([hannahmbanana](https://github.com/hannahmbanana))
- \[Performance\] Convert ASLayoutElementSize to atomic \#trivial [\#331](https://github.com/TextureGroup/Texture/pull/331) ([hannahmbanana](https://github.com/hannahmbanana))
- \[Yoga\] Refer to proper path name and use module import [\#306](https://github.com/TextureGroup/Texture/pull/306) ([weibel](https://github.com/weibel))
- \[ASImageNode\] Add documentation for image effects \#trivial [\#263](https://github.com/TextureGroup/Texture/pull/263) ([maicki](https://github.com/maicki))
## [2.3.3](https://github.com/TextureGroup/Texture/tree/2.3.3) (2017-06-06)
[Full Changelog](https://github.com/TextureGroup/Texture/compare/2.3.2...2.3.3)
**Merged pull requests:**
- Updating to 2.3.3 \#trivial [\#338](https://github.com/TextureGroup/Texture/pull/338) ([garrettmoon](https://github.com/garrettmoon))
- \[ASDisplayNode+Layout\] Add check for orphaned nodes after layout transition to clean up. [\#336](https://github.com/TextureGroup/Texture/pull/336) ([appleguy](https://github.com/appleguy))
- Update PINRemoteImage [\#328](https://github.com/TextureGroup/Texture/pull/328) ([garrettmoon](https://github.com/garrettmoon))
- Fix typo \#trivial [\#327](https://github.com/TextureGroup/Texture/pull/327) ([vitalybaev](https://github.com/vitalybaev))
- Fixes an issue with GIFs that would always be covered by their placeh… [\#326](https://github.com/TextureGroup/Texture/pull/326) ([garrettmoon](https://github.com/garrettmoon))
- Replace NSMutableSet with NSHashTable when Appropriate \#trivial [\#321](https://github.com/TextureGroup/Texture/pull/321) ([Adlai-Holler](https://github.com/Adlai-Holler))
- \[Cleanup\] Small fixes to improve conformance for strict compiler settings \#trivial [\#320](https://github.com/TextureGroup/Texture/pull/320) ([appleguy](https://github.com/appleguy))
- Rejigger Cell Visibility Tracking [\#317](https://github.com/TextureGroup/Texture/pull/317) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Clean Up ASAsyncTransaction \#trivial [\#316](https://github.com/TextureGroup/Texture/pull/316) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Clean Up ASDisplayLayer \#trivial [\#315](https://github.com/TextureGroup/Texture/pull/315) ([Adlai-Holler](https://github.com/Adlai-Holler))
- \[ASDisplayNode\] Revise assertion to log until Issue \#145 is addressed. \#trivial [\#313](https://github.com/TextureGroup/Texture/pull/313) ([appleguy](https://github.com/appleguy))
- \[Docs\] Fixed typo in carthage project name \#trivial [\#310](https://github.com/TextureGroup/Texture/pull/310) ([george-gw](https://github.com/george-gw))
- Fix non layout [\#309](https://github.com/TextureGroup/Texture/pull/309) ([garrettmoon](https://github.com/garrettmoon))
- Catch Invalid Layer Bounds in a Nonfatal Assertion \#trivial [\#308](https://github.com/TextureGroup/Texture/pull/308) ([Adlai-Holler](https://github.com/Adlai-Holler))
- \[ASCollectionNode\] Fix missing properties and layoutInspector \#trivial [\#305](https://github.com/TextureGroup/Texture/pull/305) ([flovouin](https://github.com/flovouin))
- \[Examples\] Fixed crash on SocialAppLayout-Inverted + behaviour comments [\#304](https://github.com/TextureGroup/Texture/pull/304) ([dimazen](https://github.com/dimazen))
- IGListKit related headers need to be in the module all time now \#trivial [\#300](https://github.com/TextureGroup/Texture/pull/300) ([maicki](https://github.com/maicki))
- \[ASDisplayNode\] Remove assertion in calculateSizeThatFits: and log an event \#trivial [\#299](https://github.com/TextureGroup/Texture/pull/299) ([maicki](https://github.com/maicki))
- ASBatchFetching to not round scroll velocity \#trivial [\#294](https://github.com/TextureGroup/Texture/pull/294) ([nguyenhuy](https://github.com/nguyenhuy))
- \[ASVideoNodeDelegate\] fix for \#291 crash [\#292](https://github.com/TextureGroup/Texture/pull/292) ([SergeyPetrachkov](https://github.com/SergeyPetrachkov))
- Fix Alignment of Hashed Structs [\#287](https://github.com/TextureGroup/Texture/pull/287) ([Adlai-Holler](https://github.com/Adlai-Holler))
- \[IGListKit\] Add IGListKit headers to public section of Xcode project [\#286](https://github.com/TextureGroup/Texture/pull/286) ([maicki](https://github.com/maicki))
- Only call -layout and -layoutDidFinish if the node is already loaded [\#285](https://github.com/TextureGroup/Texture/pull/285) ([nguyenhuy](https://github.com/nguyenhuy))
- \[Batch Fetching\] Add ASBatchFetchingDelegate [\#281](https://github.com/TextureGroup/Texture/pull/281) ([nguyenhuy](https://github.com/nguyenhuy))
- Ignore Relayout Requests for Deleted Cell Nodes [\#279](https://github.com/TextureGroup/Texture/pull/279) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Remove Unused Node Code \#trivial [\#278](https://github.com/TextureGroup/Texture/pull/278) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Fix Documentation Warnings \#trivial [\#276](https://github.com/TextureGroup/Texture/pull/276) ([Adlai-Holler](https://github.com/Adlai-Holler))
- \[Examples\] Fix LayoutSpecExamples and LayoutSpecExamples-Swift: image URLs were still pointing to asyncdisplaykit.org [\#275](https://github.com/TextureGroup/Texture/pull/275) ([cesteban](https://github.com/cesteban))
- \[Layout\] Extract layout implementation code into it's own subcategories [\#272](https://github.com/TextureGroup/Texture/pull/272) ([maicki](https://github.com/maicki))
- Fix Release Builds \#trivial [\#271](https://github.com/TextureGroup/Texture/pull/271) ([Adlai-Holler](https://github.com/Adlai-Holler))
- \[Yoga\] Implement ASYogaLayoutSpec, a simplified integration strategy for Yoga. [\#270](https://github.com/TextureGroup/Texture/pull/270) ([appleguy](https://github.com/appleguy))
- Simplify Layout Transition State \#trivial [\#269](https://github.com/TextureGroup/Texture/pull/269) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Store ASLayoutElementContext in Thread-Local Storage \#trivial [\#268](https://github.com/TextureGroup/Texture/pull/268) ([Adlai-Holler](https://github.com/Adlai-Holler))
- \[Examples\] Fix a couple of examples due to API changes recently \#trivial [\#267](https://github.com/TextureGroup/Texture/pull/267) ([maicki](https://github.com/maicki))
- Fix Collection Item Index Path Conversion [\#262](https://github.com/TextureGroup/Texture/pull/262) ([Adlai-Holler](https://github.com/Adlai-Holler))
- added error reporting callback to ASVideoNode [\#260](https://github.com/TextureGroup/Texture/pull/260) ([SergeyPetrachkov](https://github.com/SergeyPetrachkov))
- Add Experimental Text Node Implementation [\#259](https://github.com/TextureGroup/Texture/pull/259) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Add missing import and define in ASLog \#trivial [\#257](https://github.com/TextureGroup/Texture/pull/257) ([nguyenhuy](https://github.com/nguyenhuy))
- Simplify Override Checking, Only Do It When Assertions Are Enabled \#trivial [\#253](https://github.com/TextureGroup/Texture/pull/253) ([Adlai-Holler](https://github.com/Adlai-Holler))
- \[ASTextKitFontSizeAdjuster\] Replace use of boundingRectWithSize:options:context: with boundingRectForGlyphRange: inTextContainer: [\#251](https://github.com/TextureGroup/Texture/pull/251) ([rcancro](https://github.com/rcancro))
- Improve Ancestry Handling, Avoid Assertion Failure [\#246](https://github.com/TextureGroup/Texture/pull/246) ([Adlai-Holler](https://github.com/Adlai-Holler))
- \[Yoga\] Increment Yoga version to current, 1.5.0. [\#91](https://github.com/TextureGroup/Texture/pull/91) ([appleguy](https://github.com/appleguy))
- \[example/CustomCollectionView\] Implement MosaicCollectionLayoutDelegate [\#28](https://github.com/TextureGroup/Texture/pull/28) ([nguyenhuy](https://github.com/nguyenhuy))
## [2.3.2](https://github.com/TextureGroup/Texture/tree/2.3.2) (2017-05-09)
[Full Changelog](https://github.com/TextureGroup/Texture/compare/2.3.1...2.3.2)
**Merged pull requests:**
- \[ASDisplayNode\] Pass drawParameter in rendering context callbacks [\#248](https://github.com/TextureGroup/Texture/pull/248) ([maicki](https://github.com/maicki))
- Assert only once we know URL has changed [\#247](https://github.com/TextureGroup/Texture/pull/247) ([garrettmoon](https://github.com/garrettmoon))
- \[ASImageNode\] Move to class method of displayWithParameters:isCancelled: for drawing [\#244](https://github.com/TextureGroup/Texture/pull/244) ([maicki](https://github.com/maicki))
- Don't Use Associated Objects for Drawing Priority \#trivial [\#239](https://github.com/TextureGroup/Texture/pull/239) ([Adlai-Holler](https://github.com/Adlai-Holler))
- \[ASDimension\] Remove warning about float precision using CGFloat and … \#trivial [\#237](https://github.com/TextureGroup/Texture/pull/237) ([amegias](https://github.com/amegias))
- \[ASImageNode\] Move debug label and will- / didDisplayNodeContentWithRenderingContext out of drawing method \#trivial [\#235](https://github.com/TextureGroup/Texture/pull/235) ([maicki](https://github.com/maicki))
- Fixes assertion on startup in social app layout example [\#233](https://github.com/TextureGroup/Texture/pull/233) ([garrettmoon](https://github.com/garrettmoon))
- \[ASTextNode\] Move to class method of drawRect:withParameters:isCancelled:isRasterizing: for drawing [\#232](https://github.com/TextureGroup/Texture/pull/232) ([maicki](https://github.com/maicki))
- \[ASImageNode\] Remove unneeded pointer star \#trivial [\#231](https://github.com/TextureGroup/Texture/pull/231) ([maicki](https://github.com/maicki))
- \[ASTwoDimensionalArrayUtils\] Fix extern C function definition to fix compiler issue. \#trivial [\#229](https://github.com/TextureGroup/Texture/pull/229) ([appleguy](https://github.com/appleguy))
- Move Last Few Properties from ASTableView,ASCollectionView to Node [\#225](https://github.com/TextureGroup/Texture/pull/225) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Fix Issues in the Project File \#trivial [\#224](https://github.com/TextureGroup/Texture/pull/224) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Improve Our Handling of Subnodes [\#223](https://github.com/TextureGroup/Texture/pull/223) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Extract ASLayoutElement and ASLayoutElementStylability into categories \#trivial [\#131](https://github.com/TextureGroup/Texture/pull/131) ([maicki](https://github.com/maicki))
- \[Layout\] Remove finalLayoutElement [\#96](https://github.com/TextureGroup/Texture/pull/96) ([maicki](https://github.com/maicki))
- \[Docs\] Add workaround for setting a custom lineSpacing and maxNumberOfLines to ASTextNode docs [\#92](https://github.com/TextureGroup/Texture/pull/92) ([maicki](https://github.com/maicki))
- \[ASDisplayNode\] Implement a std::atomic-based flag system for superb performance [\#89](https://github.com/TextureGroup/Texture/pull/89) ([appleguy](https://github.com/appleguy))
- \[Yoga\] Ensure that calculated layout is nil'd in invalidate\*Layout [\#87](https://github.com/TextureGroup/Texture/pull/87) ([appleguy](https://github.com/appleguy))
- Simplify Hashing Code [\#86](https://github.com/TextureGroup/Texture/pull/86) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Fix site header [\#84](https://github.com/TextureGroup/Texture/pull/84) ([levi](https://github.com/levi))
- Tighten Rasterization API, Undeprecate It [\#82](https://github.com/TextureGroup/Texture/pull/82) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Implement ASPageTable [\#81](https://github.com/TextureGroup/Texture/pull/81) ([nguyenhuy](https://github.com/nguyenhuy))
- Make Cell Node Properties Atomic [\#74](https://github.com/TextureGroup/Texture/pull/74) ([Adlai-Holler](https://github.com/Adlai-Holler))
- \[ASNodeController+Beta\] Provide an option to allow nodes to own their controllers. [\#61](https://github.com/TextureGroup/Texture/pull/61) ([appleguy](https://github.com/appleguy))
- \[Yoga Beta\] Improvements to the experimental support for Yoga layout. [\#59](https://github.com/TextureGroup/Texture/pull/59) ([appleguy](https://github.com/appleguy))
- Fix issue with swipe to delete cell gesture. \#trivial [\#46](https://github.com/TextureGroup/Texture/pull/46) ([rewcraig](https://github.com/rewcraig))
- Fix CustomCollectionView-Swift sample [\#22](https://github.com/TextureGroup/Texture/pull/22) ([george-gw](https://github.com/george-gw))
- Automatically resume ASVideoNode after returning from background [\#13](https://github.com/TextureGroup/Texture/pull/13) ([plarson](https://github.com/plarson))
## [2.3.1](https://github.com/TextureGroup/Texture/tree/2.3.1) (2017-04-27)
[Full Changelog](https://github.com/TextureGroup/Texture/compare/2.2.1...2.3.1)
**Merged pull requests:**
- Don't run tests for the docs directory. [\#79](https://github.com/TextureGroup/Texture/pull/79) ([garrettmoon](https://github.com/garrettmoon))
- Fix SCSS build [\#78](https://github.com/TextureGroup/Texture/pull/78) ([levi](https://github.com/levi))
- Fix documentation warning on ASCollectionLayoutState.h \#trivial [\#77](https://github.com/TextureGroup/Texture/pull/77) ([garrettmoon](https://github.com/garrettmoon))
- ASLayoutSpec to use more default implementations \#trivial [\#73](https://github.com/TextureGroup/Texture/pull/73) ([nguyenhuy](https://github.com/nguyenhuy))
- Update the CI to the new ruby version [\#71](https://github.com/TextureGroup/Texture/pull/71) ([garrettmoon](https://github.com/garrettmoon))
- Missing a word [\#68](https://github.com/TextureGroup/Texture/pull/68) ([djblake](https://github.com/djblake))
- Update license v2 [\#67](https://github.com/TextureGroup/Texture/pull/67) ([garrettmoon](https://github.com/garrettmoon))
- \[ASCollectionView\] Prevent prefetching from being enabled to eliminate overhead. [\#65](https://github.com/TextureGroup/Texture/pull/65) ([appleguy](https://github.com/appleguy))
- \[CGPointNull\] Rename globally exported C function to avoid collisions \#trivial [\#62](https://github.com/TextureGroup/Texture/pull/62) ([appleguy](https://github.com/appleguy))
- \[RTL\] Bridge the UISemanticContentAttribute property for more convenient RTL support. [\#60](https://github.com/TextureGroup/Texture/pull/60) ([appleguy](https://github.com/appleguy))
- Fixes a potential deadlock; it's not safe to message likely super nod… [\#56](https://github.com/TextureGroup/Texture/pull/56) ([garrettmoon](https://github.com/garrettmoon))
- Fix \_\_has\_include check in ASLog.h \#trivial [\#55](https://github.com/TextureGroup/Texture/pull/55) ([fsmorygo](https://github.com/fsmorygo))
- GLKit workaround \#trivial [\#54](https://github.com/TextureGroup/Texture/pull/54) ([stephenkopylov](https://github.com/stephenkopylov))
- Layout debugger proposal [\#52](https://github.com/TextureGroup/Texture/pull/52) ([nguyenhuy](https://github.com/nguyenhuy))
- Fixes header check to accept the 'new' header for modified files. [\#50](https://github.com/TextureGroup/Texture/pull/50) ([garrettmoon](https://github.com/garrettmoon))
- Remove References to IGListSectionType, Now that It's Gone [\#49](https://github.com/TextureGroup/Texture/pull/49) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Move doc stylesheets to sass [\#47](https://github.com/TextureGroup/Texture/pull/47) ([levi](https://github.com/levi))
- Fixes for Dangerfile header checks [\#45](https://github.com/TextureGroup/Texture/pull/45) ([garrettmoon](https://github.com/garrettmoon))
- Enforce header file changes [\#44](https://github.com/TextureGroup/Texture/pull/44) ([garrettmoon](https://github.com/garrettmoon))
- Use \_ASCollectionReusableView inside ASIGListSupplementaryViewSourceMethods [\#40](https://github.com/TextureGroup/Texture/pull/40) ([plarson](https://github.com/plarson))
- Bump Cartfile versions to match podspec [\#37](https://github.com/TextureGroup/Texture/pull/37) ([dymv](https://github.com/dymv))
- \[Site\] Remove hero drop shadow [\#35](https://github.com/TextureGroup/Texture/pull/35) ([levi](https://github.com/levi))
- Add announcement banner to documentation site [\#31](https://github.com/TextureGroup/Texture/pull/31) ([levi](https://github.com/levi))
- \[ASCollectionLayout\] Manually set size to measured cells [\#24](https://github.com/TextureGroup/Texture/pull/24) ([nguyenhuy](https://github.com/nguyenhuy))
- Create a Pluggable "Tips" System to Help in Development [\#19](https://github.com/TextureGroup/Texture/pull/19) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Add danger [\#18](https://github.com/TextureGroup/Texture/pull/18) ([garrettmoon](https://github.com/garrettmoon))
- Fix Case where Network Image Node Stays Locked [\#17](https://github.com/TextureGroup/Texture/pull/17) ([Adlai-Holler](https://github.com/Adlai-Holler))
- Update the homepage URL [\#10](https://github.com/TextureGroup/Texture/pull/10) ([garrettmoon](https://github.com/garrettmoon))
## [2.2.1](https://github.com/TextureGroup/Texture/tree/2.2.1) (2017-04-14)
[Full Changelog](https://github.com/TextureGroup/Texture/compare/2.3...2.2.1)
**Merged pull requests:**
- Add blog post [\#9](https://github.com/TextureGroup/Texture/pull/9) ([garrettmoon](https://github.com/garrettmoon))
\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*

View File

@@ -1,11 +1,6 @@
require 'open-uri'
source_pattern = /(\.m|\.mm|\.h)$/
# Sometimes it's a README fix, or something like that - which isn't relevant for
# including in a project's CHANGELOG for example
declared_trivial = github.pr_title.include? "#trivial"
has_changes_in_source_directory = !git.modified_files.grep(/Source/).empty?
modified_source_files = git.modified_files.grep(source_pattern)
has_modified_source_files = !modified_source_files.empty?
@@ -18,10 +13,9 @@ warn("PR is classed as Work in Progress") if github.pr_title.include? "[WIP]"
# Warn when there is a big PR
warn("This is a big PR, please consider splitting it up to ease code review.") if git.lines_of_code > 500
# Changelog entries are required for changes to source files.
no_changelog_entry = !git.modified_files.include?("CHANGELOG.md")
if has_changes_in_source_directory && no_changelog_entry && !declared_trivial
warn("Any source code changes should have an entry in CHANGELOG.md or have #trivial in their title.")
# Modifying the changelog will probably get overwritten.
if git.modified_files.include?("CHANGELOG.md") && !github.pr_title.include?("#changelog")
warn("PR modifies CHANGELOG.md, which is a generated file. Add #changelog to the title to suppress this warning.")
end
def full_license(partial_license, filename)

View File

@@ -38,6 +38,10 @@ As the framework has grown, many features have been added that can save develope
We use Slack for real-time debugging, community updates, and general talk about Texture. [Signup](http://asdk-slack-auto-invite.herokuapp.com) yourself or email textureframework@gmail.com to get an invite.
## Release process
For the release process see the [RELEASE] (https://github.com/texturegroup/texture/blob/master/RELEASE.md) file.
## Contributing
We welcome any contributions. See the [CONTRIBUTING](https://github.com/texturegroup/texture/blob/master/CONTRIBUTING.md) file for how to get involved.

16
RELEASE.md Normal file
View File

@@ -0,0 +1,16 @@
# Release Process
This document describes the process for a public Texture release.
### Preparation
- Install [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator): `sudo gem install github_changelog_generator`
- Generate a GitHub Personal Access Token to prevent running into public GitHub API rate limits: https://github.com/github-changelog-generator/github-changelog-generator#github-token
### Process
- Run `github_changelog_generator` in Texture project directory: `github_changelog_generator --token <generated personal token> TextureGroup/Texture`
- Update `spec.version` within `Texture.podspec`.
- Create a new PR with the updated `Texture.podspec` and the newly generated changelog, add `#changelog` to the PR message so the CI will not prevent merging it.
- After merging in the PR, [create a new GitHub release](https://github.com/TextureGroup/Texture/releases/new). Use the generated changelog for the new release.
- Update `future-release` within `.github_changelog_generator` to the next version.
### Problems
- Sometimes we will still run into GitHub rate limit issues although using a personal token to generate the changelog. For now there is no solution for this. The issue to track is: [Triggering Github Rate limits #656](https://github.com/github-changelog-generator/github-changelog-generator/issues/656)

View File

@@ -63,7 +63,7 @@
_contentVerticalAlignment = ASVerticalAlignmentCenter;
_contentEdgeInsets = UIEdgeInsetsZero;
_imageAlignment = ASButtonNodeImageAlignmentBeginning;
self.accessibilityTraits = UIAccessibilityTraitButton;
self.accessibilityTraits = self.defaultAccessibilityTraits;
}
return self;
}
@@ -114,11 +114,7 @@
{
if (self.enabled != enabled) {
[super setEnabled:enabled];
if (enabled) {
self.accessibilityTraits = UIAccessibilityTraitButton;
} else {
self.accessibilityTraits = UIAccessibilityTraitButton | UIAccessibilityTraitNotEnabled;
}
self.accessibilityTraits = self.defaultAccessibilityTraits;
[self updateButtonContent];
}
}
@@ -204,7 +200,7 @@
_titleNode.attributedText = newTitle;
[self unlock];
self.accessibilityLabel = _titleNode.accessibilityLabel;
self.accessibilityLabel = self.defaultAccessibilityLabel;
[self setNeedsLayout];
return;
}
@@ -544,6 +540,18 @@
return spec;
}
- (NSString *)defaultAccessibilityLabel
{
ASLockScopeSelf();
return _titleNode.accessibilityLabel;
}
- (UIAccessibilityTraits)defaultAccessibilityTraits
{
return self.enabled ? UIAccessibilityTraitButton
: (UIAccessibilityTraitButton | UIAccessibilityTraitNotEnabled);
}
- (void)layout
{
[super layout];

View File

@@ -1,5 +1,5 @@
//
// ASCGImageBuffer.m
// ASCGImageBuffer.mm
// Texture
//
// Copyright (c) Pinterest, Inc. All rights reserved.

View File

@@ -28,6 +28,7 @@
#import <AsyncDisplayKit/ASCollectionView+Undeprecated.h>
#import <AsyncDisplayKit/ASThread.h>
#import <AsyncDisplayKit/ASRangeController.h>
#import <AsyncDisplayKit/ASAbstractLayoutController+FrameworkPrivate.h>
#pragma mark - _ASCollectionPendingState
@@ -63,13 +64,15 @@
self = [super init];
if (self) {
_rangeMode = ASLayoutRangeModeUnspecified;
_tuningParameters = std::vector<std::vector<ASRangeTuningParameters>> (ASLayoutRangeModeCount, std::vector<ASRangeTuningParameters> (ASLayoutRangeTypeCount, ASRangeTuningParametersZero));
_tuningParameters = [ASAbstractLayoutController defaultTuningParameters];
_allowsSelection = YES;
_allowsMultipleSelection = NO;
_inverted = NO;
_contentInset = UIEdgeInsetsZero;
_contentOffset = CGPointZero;
_animatesContentOffset = NO;
_showsVerticalScrollIndicator = YES;
_showsHorizontalScrollIndicator = YES;
}
return self;
}
@@ -221,11 +224,9 @@
let tuningParametersVectorRangeModeSize = tuningparametersRangeModeVector.size();
for (NSInteger rangeType = 0; rangeType < tuningParametersVectorRangeModeSize; rangeType++) {
ASRangeTuningParameters tuningParameters = tuningparametersRangeModeVector[rangeType];
if (!ASRangeTuningParametersEqualToRangeTuningParameters(tuningParameters, ASRangeTuningParametersZero)) {
[_rangeController setTuningParameters:tuningParameters
forRangeMode:(ASLayoutRangeMode)rangeMode
rangeType:(ASLayoutRangeType)rangeType];
}
[_rangeController setTuningParameters:tuningParameters
forRangeMode:(ASLayoutRangeMode)rangeMode
rangeType:(ASLayoutRangeType)rangeType];
}
}

View File

@@ -576,10 +576,16 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
- (void)_asyncDelegateOrDataSourceDidChange
{
ASDisplayNodeAssertMainThread();
if (_asyncDataSource == nil && _asyncDelegate == nil && _isDeallocating && ASActivateExperimentalFeature(ASExperimentalClearDataDuringDeallocation)) {
[_dataController clearData];
}
if (_asyncDataSource == nil && _asyncDelegate == nil) {
if (ASActivateExperimentalFeature(ASExperimentalClearDataDuringDeallocation)) {
if (_isDeallocating) {
[_dataController clearData];
}
} else {
[_dataController clearData];
}
}
}
- (void)setCollectionViewLayout:(nonnull UICollectionViewLayout *)collectionViewLayout

View File

@@ -1,12 +1,12 @@
//
// ASCollections.m
// ASCollections.mm
// Texture
//
// Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import "ASCollections.h"
#import <AsyncDisplayKit/ASCollections.h>
/**
* A private allocator that signals to our retain callback to skip the retain.

View File

@@ -1,5 +1,5 @@
//
// ASConfiguration.m
// ASConfiguration.mm
// Texture
//
// Copyright (c) Pinterest, Inc. All rights reserved.

View File

@@ -35,6 +35,9 @@ AS_SUBCLASSING_RESTRICTED
* Just use ASActivateExperimentalFeature to access this efficiently.
*/
/* Exposed for testing purposes only */
+ (void)test_resetWithConfiguration:(nullable ASConfiguration *)configuration;
@end
NS_ASSUME_NONNULL_END

View File

@@ -1,5 +1,5 @@
//
// ASConfigurationInternal.m
// ASConfigurationInternal.mm
// Texture
//
// Copyright (c) Pinterest, Inc. All rights reserved.
@@ -74,7 +74,7 @@
return NO;
}
NSAssert(__builtin_popcount(requested) == 1, @"Cannot activate multiple features at once with this method.");
NSAssert(__builtin_popcountl(requested) == 1, @"Cannot activate multiple features at once with this method.");
// We need to call out, whether it's enabled or not.
// A/B testing requires even "control" users to be activated.

View File

@@ -51,6 +51,19 @@ 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;
/**
* @abstract Recursively ensures node and all subnodes are displayed.
* @see Full documentation in ASDisplayNode+FrameworkPrivate.h
@@ -96,11 +109,25 @@ typedef struct {
*/
@property BOOL isAccessibilityContainer;
/**
* @abstract Returns the default accessibility property values set by Texture on this node. For
* example, the default accessibility label for a text node may be its text content, while most
* other nodes would have nil default labels.
*/
@property (nullable, readonly, copy) NSString *defaultAccessibilityLabel;
@property (nullable, readonly, copy) NSString *defaultAccessibilityHint;
@property (nullable, readonly, copy) NSString *defaultAccessibilityValue;
@property (nullable, readonly, copy) NSString *defaultAccessibilityIdentifier;
@property (readonly) UIAccessibilityTraits defaultAccessibilityTraits;
/**
* @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.
*
* @return Return a boolean value that determine whether to propagate through the responder chain.
* To halt propagation, return YES; otherwise, return NO.
*/
- (void)performAccessibilityCustomAction:(UIAccessibilityCustomAction *)action;
- (BOOL)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.
@@ -173,6 +200,9 @@ AS_EXTERN void ASDisplayNodePerformBlockOnEveryYogaChild(ASDisplayNode * _Nullab
@property BOOL yogaLayoutInProgress;
@property (nullable, nonatomic) ASLayout *yogaCalculatedLayout;
// Will walk up the Yoga tree and returns the root node
- (ASDisplayNode *)yogaRoot;
// These methods are intended to be used internally to Texture, and should not be called directly.
- (BOOL)shouldHaveYogaMeasureFunc;
- (void)invalidateCalculatedYogaLayout;

View File

@@ -1,5 +1,5 @@
//
// ASDisplayNode+Convenience.m
// ASDisplayNode+Convenience.mm
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.

View File

@@ -116,4 +116,15 @@ typedef NS_OPTIONS(NSUInteger, ASInterfaceState)
*/
- (void)hierarchyDisplayDidFinish;
@optional
/**
* @abstract Called when the node is about to calculate layout. This is only called before
* Yoga-driven layouts.
* @discussion Can be used for operations that are performed after the node's view is available.
* @note This method is guaranteed to be called on main, but implementations should be careful not
* to attempt to ascend the node tree when handling this, as the root node is locked when this is
* called.
*/
- (void)nodeWillCalculateLayout:(ASSizeRange)constrainedSize;
@end

View File

@@ -34,6 +34,11 @@
- (ASLayoutElementStyle *)style
{
ASDN::MutexLocker l(__instanceLock__);
return [self _locked_style];
}
- (ASLayoutElementStyle *)_locked_style
{
if (_style == nil) {
_style = [[ASLayoutElementStyle alloc] init];
}
@@ -295,124 +300,121 @@ ASLayoutElementStyleExtensibilityForwarding
{
ASAssertUnlocked(__instanceLock__);
BOOL isInLayoutPendingState = NO;
{
ASDN::MutexLocker l(__instanceLock__);
// Check if we are a subnode in a layout transition.
// In this case no measurement is needed as it's part of the layout transition
if ([self _locked_isLayoutTransitionInvalid]) {
return;
}
CGSize boundsSizeForLayout = ASCeilSizeValues(bounds.size);
// Prefer a newer and not yet applied _pendingDisplayNodeLayout over _calculatedDisplayNodeLayout
// If there is no such _pending, check if _calculated is valid to reuse (avoiding recalculation below).
BOOL pendingLayoutIsPreferred = NO;
if (_pendingDisplayNodeLayout.isValid(_layoutVersion)) {
NSUInteger calculatedVersion = _calculatedDisplayNodeLayout.version;
NSUInteger pendingVersion = _pendingDisplayNodeLayout.version;
if (pendingVersion > calculatedVersion) {
pendingLayoutIsPreferred = YES; // Newer _pending
} else if (pendingVersion == calculatedVersion
&& !ASSizeRangeEqualToSizeRange(_pendingDisplayNodeLayout.constrainedSize,
_calculatedDisplayNodeLayout.constrainedSize)) {
pendingLayoutIsPreferred = YES; // _pending with a different constrained size
}
}
BOOL calculatedLayoutIsReusable = (_calculatedDisplayNodeLayout.isValid(_layoutVersion)
&& (_calculatedDisplayNodeLayout.requestedLayoutFromAbove
|| CGSizeEqualToSize(_calculatedDisplayNodeLayout.layout.size, boundsSizeForLayout)));
if (!pendingLayoutIsPreferred && calculatedLayoutIsReusable) {
return;
}
as_activity_create_for_scope("Update node layout for current bounds");
as_log_verbose(ASLayoutLog(), "Node %@, bounds size %@, calculatedSize %@, calculatedIsDirty %d",
self,
NSStringFromCGSize(boundsSizeForLayout),
NSStringFromCGSize(_calculatedDisplayNodeLayout->layout.size),
_calculatedDisplayNodeLayout->version < _layoutVersion);
// _calculatedDisplayNodeLayout is not reusable we need to transition to a new one
[self cancelLayoutTransition];
BOOL didCreateNewContext = NO;
ASLayoutElementContext *context = ASLayoutElementGetCurrentContext();
if (context == nil) {
context = [[ASLayoutElementContext alloc] init];
ASLayoutElementPushContext(context);
didCreateNewContext = YES;
}
// Figure out previous and pending layouts for layout transition
ASDisplayNodeLayout nextLayout = _pendingDisplayNodeLayout;
#define layoutSizeDifferentFromBounds !CGSizeEqualToSize(nextLayout.layout.size, boundsSizeForLayout)
// nextLayout was likely created by a call to layoutThatFits:, check if it is valid and can be applied.
// If our bounds size is different than it, or invalid, recalculate. Use #define to avoid nullptr->
BOOL pendingLayoutApplicable = NO;
if (nextLayout.layout == nil) {
as_log_verbose(ASLayoutLog(), "No pending layout.");
} else if (!nextLayout.isValid(_layoutVersion)) {
as_log_verbose(ASLayoutLog(), "Pending layout is stale.");
} else if (layoutSizeDifferentFromBounds) {
as_log_verbose(ASLayoutLog(), "Pending layout size %@ doesn't match bounds size.", NSStringFromCGSize(nextLayout->layout.size));
} else {
as_log_verbose(ASLayoutLog(), "Using pending layout %@.", nextLayout->layout);
pendingLayoutApplicable = YES;
}
if (!pendingLayoutApplicable) {
as_log_verbose(ASLayoutLog(), "Measuring with previous constrained size.");
// Use the last known constrainedSize passed from a parent during layout (if never, use bounds).
NSUInteger version = _layoutVersion;
ASSizeRange constrainedSize = [self _locked_constrainedSizeForLayoutPass];
ASLayout *layout = [self calculateLayoutThatFits:constrainedSize
restrictedToSize:self.style.size
relativeToParentSize:boundsSizeForLayout];
nextLayout = ASDisplayNodeLayout(layout, constrainedSize, boundsSizeForLayout, version);
// Now that the constrained size of pending layout might have been reused, the layout is useless
// Release it and any orphaned subnodes it retains
_pendingDisplayNodeLayout.layout = nil;
}
if (didCreateNewContext) {
ASLayoutElementPopContext();
}
// If our new layout's desired size for self doesn't match current size, ask our parent to update it.
// This can occur for either pre-calculated or newly-calculated layouts.
if (nextLayout.requestedLayoutFromAbove == NO
&& CGSizeEqualToSize(boundsSizeForLayout, nextLayout.layout.size) == NO) {
as_log_verbose(ASLayoutLog(), "Layout size doesn't match bounds size. Requesting layout from above.");
// The layout that we have specifies that this node (self) would like to be a different size
// than it currently is. Because that size has been computed within the constrainedSize, we
// expect that calling setNeedsLayoutFromAbove will result in our parent resizing us to this.
// However, in some cases apps may manually interfere with this (setting a different bounds).
// In this case, we need to detect that we've already asked to be resized to match this
// particular ASLayout object, and shouldn't loop asking again unless we have a different ASLayout.
nextLayout.requestedLayoutFromAbove = YES;
{
ASDN::MutexUnlocker u(__instanceLock__);
[self _u_setNeedsLayoutFromAbove];
}
// Update the layout's version here because _u_setNeedsLayoutFromAbove calls __setNeedsLayout which in turn increases _layoutVersion
// Failing to do this will cause the layout to be invalid immediately
nextLayout.version = _layoutVersion;
}
// Prepare to transition to nextLayout
ASDisplayNodeAssertNotNil(nextLayout.layout, @"nextLayout->layout should not be nil! %@", self);
_pendingLayoutTransition = [[ASLayoutTransition alloc] initWithNode:self
pendingLayout:nextLayout
previousLayout:_calculatedDisplayNodeLayout];
isInLayoutPendingState = ASHierarchyStateIncludesLayoutPending(_hierarchyState);
ASDN::MutexLocker l(__instanceLock__);
// Check if we are a subnode in a layout transition.
// In this case no measurement is needed as it's part of the layout transition
if ([self _locked_isLayoutTransitionInvalid]) {
return;
}
CGSize boundsSizeForLayout = ASCeilSizeValues(bounds.size);
// Prefer a newer and not yet applied _pendingDisplayNodeLayout over _calculatedDisplayNodeLayout
// If there is no such _pending, check if _calculated is valid to reuse (avoiding recalculation below).
BOOL pendingLayoutIsPreferred = NO;
if (_pendingDisplayNodeLayout.isValid(_layoutVersion)) {
NSUInteger calculatedVersion = _calculatedDisplayNodeLayout.version;
NSUInteger pendingVersion = _pendingDisplayNodeLayout.version;
if (pendingVersion > calculatedVersion) {
pendingLayoutIsPreferred = YES; // Newer _pending
} else if (pendingVersion == calculatedVersion
&& !ASSizeRangeEqualToSizeRange(_pendingDisplayNodeLayout.constrainedSize,
_calculatedDisplayNodeLayout.constrainedSize)) {
pendingLayoutIsPreferred = YES; // _pending with a different constrained size
}
}
BOOL calculatedLayoutIsReusable = (_calculatedDisplayNodeLayout.isValid(_layoutVersion)
&& (_calculatedDisplayNodeLayout.requestedLayoutFromAbove
|| CGSizeEqualToSize(_calculatedDisplayNodeLayout.layout.size, boundsSizeForLayout)));
if (!pendingLayoutIsPreferred && calculatedLayoutIsReusable) {
return;
}
as_activity_create_for_scope("Update node layout for current bounds");
as_log_verbose(ASLayoutLog(), "Node %@, bounds size %@, calculatedSize %@, calculatedIsDirty %d",
self,
NSStringFromCGSize(boundsSizeForLayout),
NSStringFromCGSize(_calculatedDisplayNodeLayout->layout.size),
_calculatedDisplayNodeLayout->version < _layoutVersion);
// _calculatedDisplayNodeLayout is not reusable we need to transition to a new one
[self cancelLayoutTransition];
BOOL didCreateNewContext = NO;
ASLayoutElementContext *context = ASLayoutElementGetCurrentContext();
if (context == nil) {
context = [[ASLayoutElementContext alloc] init];
ASLayoutElementPushContext(context);
didCreateNewContext = YES;
}
// Figure out previous and pending layouts for layout transition
ASDisplayNodeLayout nextLayout = _pendingDisplayNodeLayout;
#define layoutSizeDifferentFromBounds !CGSizeEqualToSize(nextLayout.layout.size, boundsSizeForLayout)
// nextLayout was likely created by a call to layoutThatFits:, check if it is valid and can be applied.
// If our bounds size is different than it, or invalid, recalculate. Use #define to avoid nullptr->
BOOL pendingLayoutApplicable = NO;
if (nextLayout.layout == nil) {
as_log_verbose(ASLayoutLog(), "No pending layout.");
} else if (!nextLayout.isValid(_layoutVersion)) {
as_log_verbose(ASLayoutLog(), "Pending layout is stale.");
} else if (layoutSizeDifferentFromBounds) {
as_log_verbose(ASLayoutLog(), "Pending layout size %@ doesn't match bounds size.", NSStringFromCGSize(nextLayout->layout.size));
} else {
as_log_verbose(ASLayoutLog(), "Using pending layout %@.", nextLayout->layout);
pendingLayoutApplicable = YES;
}
if (!pendingLayoutApplicable) {
as_log_verbose(ASLayoutLog(), "Measuring with previous constrained size.");
// Use the last known constrainedSize passed from a parent during layout (if never, use bounds).
NSUInteger version = _layoutVersion;
ASSizeRange constrainedSize = [self _locked_constrainedSizeForLayoutPass];
ASLayout *layout = [self calculateLayoutThatFits:constrainedSize
restrictedToSize:self.style.size
relativeToParentSize:boundsSizeForLayout];
nextLayout = ASDisplayNodeLayout(layout, constrainedSize, boundsSizeForLayout, version);
// Now that the constrained size of pending layout might have been reused, the layout is useless
// Release it and any orphaned subnodes it retains
_pendingDisplayNodeLayout.layout = nil;
}
if (didCreateNewContext) {
ASLayoutElementPopContext();
}
// If our new layout's desired size for self doesn't match current size, ask our parent to update it.
// This can occur for either pre-calculated or newly-calculated layouts.
if (nextLayout.requestedLayoutFromAbove == NO
&& CGSizeEqualToSize(boundsSizeForLayout, nextLayout.layout.size) == NO) {
as_log_verbose(ASLayoutLog(), "Layout size doesn't match bounds size. Requesting layout from above.");
// The layout that we have specifies that this node (self) would like to be a different size
// than it currently is. Because that size has been computed within the constrainedSize, we
// expect that calling setNeedsLayoutFromAbove will result in our parent resizing us to this.
// However, in some cases apps may manually interfere with this (setting a different bounds).
// In this case, we need to detect that we've already asked to be resized to match this
// particular ASLayout object, and shouldn't loop asking again unless we have a different ASLayout.
nextLayout.requestedLayoutFromAbove = YES;
{
__instanceLock__.unlock();
[self _u_setNeedsLayoutFromAbove];
__instanceLock__.lock();
}
// Update the layout's version here because _u_setNeedsLayoutFromAbove calls __setNeedsLayout which in turn increases _layoutVersion
// Failing to do this will cause the layout to be invalid immediately
nextLayout.version = _layoutVersion;
}
// Prepare to transition to nextLayout
ASDisplayNodeAssertNotNil(nextLayout.layout, @"nextLayout->layout should not be nil! %@", self);
_pendingLayoutTransition = [[ASLayoutTransition alloc] initWithNode:self
pendingLayout:nextLayout
previousLayout:_calculatedDisplayNodeLayout];
// If a parent is currently executing a layout transition, perform our layout application after it.
if (isInLayoutPendingState == NO) {
if (ASHierarchyStateIncludesLayoutPending(_hierarchyState) == NO) {
// If no transition, apply our new layout immediately (common case).
[self _completePendingLayoutTransition];
}
@@ -870,18 +872,12 @@ ASLayoutElementStyleExtensibilityForwarding
*/
- (void)_completePendingLayoutTransition
{
ASAssertUnlocked(__instanceLock__);
ASLayoutTransition *pendingLayoutTransition;
{
ASDN::MutexLocker l(__instanceLock__);
pendingLayoutTransition = _pendingLayoutTransition;
if (pendingLayoutTransition != nil) {
[self _locked_setCalculatedDisplayNodeLayout:pendingLayoutTransition.pendingLayout];
}
}
__instanceLock__.lock();
ASLayoutTransition *pendingLayoutTransition = _pendingLayoutTransition;
__instanceLock__.unlock();
if (pendingLayoutTransition != nil) {
[self _setCalculatedDisplayNodeLayout:pendingLayoutTransition.pendingLayout];
[self _completeLayoutTransition:pendingLayoutTransition];
[self _pendingLayoutTransitionDidComplete];
}
@@ -901,7 +897,8 @@ ASLayoutElementStyleExtensibilityForwarding
// Trampoline to the main thread if necessary
if (ASDisplayNodeThreadIsMain() || layoutTransition.isSynchronous == NO) {
// Committing the layout transition will result in subnode insertions and removals, both of which must be called without the lock held
ASAssertUnlocked(__instanceLock__);
// TODO: Disabled due to PR: https://github.com/TextureGroup/Texture/pull/1204
// ASAssertUnlocked(__instanceLock__);
[layoutTransition commitTransition];
} else {
// Subnode insertions and removals need to happen always on the main thread if at least one subnode is already loaded
@@ -961,7 +958,8 @@ ASLayoutElementStyleExtensibilityForwarding
#endif
// Subclass hook
ASAssertUnlocked(__instanceLock__);
// TODO: Disabled due to PR: https://github.com/TextureGroup/Texture/pull/1204
// ASAssertUnlocked(__instanceLock__);
[self calculatedLayoutDidChange];
// Grab lock after calling out to subclass
@@ -997,6 +995,12 @@ ASLayoutElementStyleExtensibilityForwarding
_pendingLayoutTransition = nil;
}
- (void)_setCalculatedDisplayNodeLayout:(const ASDisplayNodeLayout &)displayNodeLayout
{
ASDN::MutexLocker l(__instanceLock__);
[self _locked_setCalculatedDisplayNodeLayout:displayNodeLayout];
}
- (void)_locked_setCalculatedDisplayNodeLayout:(const ASDisplayNodeLayout &)displayNodeLayout
{
ASAssertLocked(__instanceLock__);

View File

@@ -19,6 +19,7 @@
#import <AsyncDisplayKit/ASDisplayNode+Subclasses.h>
#import <AsyncDisplayKit/ASDisplayNodeInternal.h>
#import <AsyncDisplayKit/ASLayout.h>
#import <AsyncDisplayKit/ASLayoutElementStylePrivate.h>
#define YOGA_LAYOUT_LOGGING 0
@@ -31,8 +32,19 @@
@implementation ASDisplayNode (Yoga)
- (ASDisplayNode *)yogaRoot
{
ASDisplayNode *yogaRoot = self;
ASDisplayNode *yogaParent = nil;
while ((yogaParent = yogaRoot.yogaParent)) {
yogaRoot = yogaParent;
}
return yogaRoot;
}
- (void)setYogaChildren:(NSArray *)yogaChildren
{
ASLockScope(self.yogaRoot);
for (ASDisplayNode *child in [_yogaChildren copy]) {
// Make sure to un-associate the YGNodeRef tree before replacing _yogaChildren
// If this becomes a performance bottleneck, it can be optimized by not doing the NSArray removals here.
@@ -51,11 +63,13 @@
- (void)addYogaChild:(ASDisplayNode *)child
{
ASLockScope(self.yogaRoot);
[self insertYogaChild:child atIndex:_yogaChildren.count];
}
- (void)removeYogaChild:(ASDisplayNode *)child
{
ASLockScope(self.yogaRoot);
if (child == nil) {
return;
}
@@ -68,6 +82,7 @@
- (void)insertYogaChild:(ASDisplayNode *)child atIndex:(NSUInteger)index
{
ASLockScope(self.yogaRoot);
if (child == nil) {
return;
}
@@ -216,6 +231,7 @@
- (BOOL)shouldHaveYogaMeasureFunc
{
// Size calculation via calculateSizeThatFits: or layoutSpecThatFits:
// For these nodes, we assume they may need custom Baseline calculation too.
// 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];
@@ -261,11 +277,23 @@
return;
}
[self enumerateInterfaceStateDelegates:^(id<ASInterfaceStateDelegate> _Nonnull delegate) {
if ([delegate respondsToSelector:@selector(nodeWillCalculateLayout:)]) {
[delegate nodeWillCalculateLayout:rootConstrainedSize];
}
}];
ASLockScopeSelf();
// Prepare all children for the layout pass with the current Yoga tree configuration.
ASDisplayNodePerformBlockOnEveryYogaChild(self, ^(ASDisplayNode * _Nonnull node) {
ASDisplayNodePerformBlockOnEveryYogaChild(self, ^(ASDisplayNode *_Nonnull node) {
node.yogaLayoutInProgress = YES;
ASDisplayNode *yogaParent = node.yogaParent;
if (yogaParent) {
node.style.parentAlignStyle = yogaParent.style.alignItems;
} else {
node.style.parentAlignStyle = ASStackLayoutAlignItemsNotSet;
};
});
if (ASSizeRangeEqualToSizeRange(rootConstrainedSize, ASSizeRangeUnconstrained)) {
@@ -293,7 +321,9 @@
// Reset accessible elements, since layout may have changed.
ASPerformBlockOnMainThread(^{
[(_ASDisplayView *)self.view setAccessibilityElements:nil];
if (self.nodeLoaded && !self.isSynchronous) {
[(_ASDisplayView *)self.view setAccessibilityElements:nil];
}
});
ASDisplayNodePerformBlockOnEveryYogaChild(self, ^(ASDisplayNode * _Nonnull node) {

View File

@@ -78,6 +78,7 @@ NSInteger const ASDefaultDrawingPriority = ASDefaultTransactionPriority;
@synthesize threadSafeBounds = _threadSafeBounds;
static std::atomic_bool suppressesInvalidCollectionUpdateExceptions = ATOMIC_VAR_INIT(NO);
static std::atomic_bool storesUnflattenedLayouts = ATOMIC_VAR_INIT(NO);
BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector)
@@ -415,12 +416,13 @@ ASSynthesizeLockingMethodsWithMutex(__instanceLock__);
- (void)onDidLoad:(ASDisplayNodeDidLoadBlock)body
{
ASDN::MutexLocker l(__instanceLock__);
ASDN::UniqueLock l(__instanceLock__);
if ([self _locked_isNodeLoaded]) {
ASDisplayNodeAssertThreadAffinity(self);
ASDN::MutexUnlocker l(__instanceLock__);
l.unlock();
body(self);
return;
} else if (_onDidLoadBlocks == nil) {
_onDidLoadBlocks = [NSMutableArray arrayWithObject:body];
} else {
@@ -600,7 +602,7 @@ ASSynthesizeLockingMethodsWithMutex(__instanceLock__);
- (UIView *)view
{
ASDN::MutexLocker l(__instanceLock__);
ASDN::UniqueLock l(__instanceLock__);
ASDisplayNodeAssert(!_flags.layerBacked, @"Call to -view undefined on layer-backed nodes");
BOOL isLayerBacked = _flags.layerBacked;
@@ -625,30 +627,28 @@ ASSynthesizeLockingMethodsWithMutex(__instanceLock__);
// in the background on a loaded node, which isn't currently supported.
if (_pendingViewState.hasSetNeedsLayout) {
// Need to unlock before calling setNeedsLayout to avoid deadlocks.
// MutexUnlocker will re-lock at the end of scope.
ASDN::MutexUnlocker u(__instanceLock__);
l.unlock();
[self __setNeedsLayout];
l.lock();
}
[self _locked_applyPendingStateToViewOrLayer];
{
// The following methods should not be called with a lock
ASDN::MutexUnlocker u(__instanceLock__);
// The following methods should not be called with a lock
l.unlock();
// No need for the lock as accessing the subviews or layers are always happening on main
[self _addSubnodeViewsAndLayers];
// A subclass hook should never be called with a lock
[self _didLoad];
}
// No need for the lock as accessing the subviews or layers are always happening on main
[self _addSubnodeViewsAndLayers];
// A subclass hook should never be called with a lock
[self _didLoad];
return _view;
}
- (CALayer *)layer
{
ASDN::MutexLocker l(__instanceLock__);
ASDN::UniqueLock l(__instanceLock__);
if (_layer != nil) {
return _layer;
}
@@ -660,31 +660,30 @@ ASSynthesizeLockingMethodsWithMutex(__instanceLock__);
// Loading a layer needs to happen on the main thread
ASDisplayNodeAssertMainThread();
[self _locked_loadViewOrLayer];
CALayer *layer = _layer;
// FIXME: Ideally we'd call this as soon as the node receives -setNeedsLayout
// but automatic subnode management would require us to modify the node tree
// in the background on a loaded node, which isn't currently supported.
if (_pendingViewState.hasSetNeedsLayout) {
// Need to unlock before calling setNeedsLayout to avoid deadlocks.
// MutexUnlocker will re-lock at the end of scope.
ASDN::MutexUnlocker u(__instanceLock__);
l.unlock();
[self __setNeedsLayout];
l.lock();
}
[self _locked_applyPendingStateToViewOrLayer];
{
// The following methods should not be called with a lock
ASDN::MutexUnlocker u(__instanceLock__);
// The following methods should not be called with a lock
l.unlock();
// No need for the lock as accessing the subviews or layers are always happening on main
[self _addSubnodeViewsAndLayers];
// A subclass hook should never be called with a lock
[self _didLoad];
}
// No need for the lock as accessing the subviews or layers are always happening on main
[self _addSubnodeViewsAndLayers];
// A subclass hook should never be called with a lock
[self _didLoad];
return _layer;
return layer;
}
// Returns nil if the layer is not an _ASDisplayLayer; will not create the layer if nil.
@@ -1032,7 +1031,7 @@ ASSynthesizeLockingMethodsWithMutex(__instanceLock__);
BOOL loaded = NO;
{
ASDN::MutexLocker l(__instanceLock__);
ASDN::UniqueLock l(__instanceLock__);
loaded = [self _locked_isNodeLoaded];
CGRect bounds = _threadSafeBounds;
@@ -1054,10 +1053,9 @@ ASSynthesizeLockingMethodsWithMutex(__instanceLock__);
// This method will confirm that the layout is up to date (and update if needed).
// Importantly, it will also APPLY the layout to all of our subnodes if (unless parent is transitioning).
{
ASDN::MutexUnlocker u(__instanceLock__);
[self _u_measureNodeWithBoundsIfNecessary:bounds];
}
l.unlock();
[self _u_measureNodeWithBoundsIfNecessary:bounds];
l.lock();
[self _locked_layoutPlaceholderIfNecessary];
}
@@ -1120,7 +1118,7 @@ ASSynthesizeLockingMethodsWithMutex(__instanceLock__);
{
__ASDisplayNodeCheckForLayoutMethodOverrides;
ASDN::MutexLocker l(__instanceLock__);
ASDN::UniqueLock l(__instanceLock__);
#if YOGA
// There are several cases where Yoga could arrive here:
@@ -1137,11 +1135,13 @@ ASSynthesizeLockingMethodsWithMutex(__instanceLock__);
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));
l.unlock();
[self calculateLayoutFromYogaRoot:constrainedSize];
l.lock();
} else {
ASYogaLog("Reusing existing yoga layout %@", _yogaCalculatedLayout);
}
@@ -2108,12 +2108,6 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) {
// NOTE: This method must be dealloc-safe (should not retain self).
- (ASDisplayNode *)supernode
{
#if CHECK_LOCKING_SAFETY
if (__instanceLock__.locked()) {
NSLog(@"WARNING: Accessing supernode while holding recursive instance lock of this node is worrisome. It's likely that you will soon try to acquire the supernode's lock, and this can easily cause deadlocks.");
}
#endif
ASDN::MutexLocker l(__instanceLock__);
return _supernode;
}
@@ -2214,7 +2208,8 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) {
- (void)_insertSubnode:(ASDisplayNode *)subnode atSubnodeIndex:(NSInteger)subnodeIndex sublayerIndex:(NSInteger)sublayerIndex andRemoveSubnode:(ASDisplayNode *)oldSubnode
{
ASDisplayNodeAssertThreadAffinity(self);
ASAssertUnlocked(__instanceLock__);
// TODO: Disabled due to PR: https://github.com/TextureGroup/Texture/pull/1204
// ASAssertUnlocked(__instanceLock__);
as_log_verbose(ASNodeLog(), "Insert subnode %@ at index %zd of %@ and remove subnode %@", subnode, subnodeIndex, self, oldSubnode);
@@ -2264,7 +2259,14 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) {
[_subnodes insertObject:subnode atIndex:subnodeIndex];
_cachedSubnodes = nil;
__instanceLock__.unlock();
if (!isRasterized && self.nodeLoaded) {
// Trigger the subnode to load its layer, which will create its view if it needs one.
// By doing this prior to downward propagation of .interfaceState in _setSupernode:,
// we can guarantee that -didEnterVisibleState is only called with .isNodeLoaded = YES.
[subnode layer];
}
// This call will apply our .hierarchyState to the new subnode.
// If we are a managed hierarchy, as in ASCellNode trees, it will also apply our .interfaceState.
[subnode _setSupernode:self];
@@ -2423,7 +2425,8 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) {
- (void)_insertSubnode:(ASDisplayNode *)subnode belowSubnode:(ASDisplayNode *)below
{
ASDisplayNodeAssertThreadAffinity(self);
ASAssertUnlocked(__instanceLock__);
// TODO: Disabled due to PR: https://github.com/TextureGroup/Texture/pull/1204
// ASAssertUnlocked(__instanceLock__);
if (subnode == nil) {
ASDisplayNodeFailAssert(@"Cannot insert a nil subnode");
@@ -2487,7 +2490,8 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) {
- (void)_insertSubnode:(ASDisplayNode *)subnode aboveSubnode:(ASDisplayNode *)above
{
ASDisplayNodeAssertThreadAffinity(self);
ASAssertUnlocked(__instanceLock__);
// TODO: Disabled due to PR: https://github.com/TextureGroup/Texture/pull/1204
// ASAssertUnlocked(__instanceLock__);
if (subnode == nil) {
ASDisplayNodeFailAssert(@"Cannot insert a nil subnode");
@@ -2549,7 +2553,8 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) {
- (void)_insertSubnode:(ASDisplayNode *)subnode atIndex:(NSInteger)idx
{
ASDisplayNodeAssertThreadAffinity(self);
ASAssertUnlocked(__instanceLock__);
// TODO: Disabled due to PR: https://github.com/TextureGroup/Texture/pull/1204
// ASAssertUnlocked(__instanceLock__);
if (subnode == nil) {
ASDisplayNodeFailAssert(@"Cannot insert a nil subnode");
@@ -2586,7 +2591,8 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) {
- (void)_removeSubnode:(ASDisplayNode *)subnode
{
ASDisplayNodeAssertThreadAffinity(self);
ASAssertUnlocked(__instanceLock__);
// TODO: Disabled due to PR: https://github.com/TextureGroup/Texture/pull/1204
// ASAssertUnlocked(__instanceLock__);
// Don't call self.supernode here because that will retain/autorelease the supernode. This method -_removeSupernode: is often called while tearing down a node hierarchy, and the supernode in question might be in the middle of its -dealloc. The supernode is never messaged, only compared by value, so this is safe.
// The particular issue that triggers this edge case is when a node calls -removeFromSupernode on a subnode from within its own -dealloc method.
@@ -2612,7 +2618,8 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) {
- (void)_removeFromSupernode
{
ASDisplayNodeAssertThreadAffinity(self);
ASAssertUnlocked(__instanceLock__);
// TODO: Disabled due to PR: https://github.com/TextureGroup/Texture/pull/1204
// ASAssertUnlocked(__instanceLock__);
__instanceLock__.lock();
__weak ASDisplayNode *supernode = _supernode;
@@ -2626,7 +2633,8 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) {
- (void)_removeFromSupernodeIfEqualTo:(ASDisplayNode *)supernode
{
ASDisplayNodeAssertThreadAffinity(self);
ASAssertUnlocked(__instanceLock__);
// TODO: Disabled due to PR: https://github.com/TextureGroup/Texture/pull/1204
// ASAssertUnlocked(__instanceLock__);
__instanceLock__.lock();
@@ -3168,7 +3176,7 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) {
if (nowDisplay) {
[ASDisplayNode scheduleNodeForRecursiveDisplay:self];
} else {
/*[[self asyncLayer] cancelAsyncDisplay];
[[self asyncLayer] cancelAsyncDisplay];
//schedule clear contents on next runloop
dispatch_async(dispatch_get_main_queue(), ^{
__instanceLock__.lock();
@@ -3177,7 +3185,7 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) {
if (ASInterfaceStateIncludesDisplay(interfaceState) == NO) {
[self clearContents];
}
});*/
});
}
}
}
@@ -3271,10 +3279,19 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) {
{
// subclass override
ASDisplayNodeAssertMainThread();
#if ASDISPLAYNODE_ASSERTIONS_ENABLED
// Rasterized node's loading state is merged with root node of rasterized tree.
if (!(self.hierarchyState & ASHierarchyStateRasterized)) {
ASDisplayNodeAssert(self.isNodeLoaded, @"Node should be loaded before entering visible state.");
}
#endif
ASAssertUnlocked(__instanceLock__);
[self enumerateInterfaceStateDelegates:^(id<ASInterfaceStateDelegate> del) {
[del didEnterVisibleState];
}];
#if AS_ENABLE_TIPS
[ASTipsController.shared nodeDidAppear:self];
#endif
@@ -3360,7 +3377,8 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) {
// - If it doesn't have a calculated or pending layout that fits its current bounds, a measurement pass will occur
// (see -__layout and -_u_measureNodeWithBoundsIfNecessary:). This scenario is uncommon,
// and running a measurement pass here is a fine trade-off because preloading any time after this point would be late.
if (self.automaticallyManagesSubnodes) {
if (self.automaticallyManagesSubnodes && !ASActivateExperimentalFeature(ASExperimentalDidEnterPreloadSkipASMLayout)) {
[self layoutIfNeeded];
}
[self enumerateInterfaceStateDelegates:^(id<ASInterfaceStateDelegate> del) {
@@ -3521,15 +3539,15 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) {
ASDisplayNodeAssertMainThread();
ASAssertUnlocked(__instanceLock__);
ASDN::MutexLocker l(__instanceLock__);
ASDN::UniqueLock l(__instanceLock__);
// FIXME: Ideally we'd call this as soon as the node receives -setNeedsLayout
// but automatic subnode management would require us to modify the node tree
// in the background on a loaded node, which isn't currently supported.
if (_pendingViewState.hasSetNeedsLayout) {
// Need to unlock before calling setNeedsLayout to avoid deadlocks.
// MutexUnlocker will re-lock at the end of scope.
ASDN::MutexUnlocker u(__instanceLock__);
l.unlock();
[self __setNeedsLayout];
l.lock();
}
[self _locked_applyPendingViewState];
@@ -3631,6 +3649,31 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) {
return _isAccessibilityContainer;
}
- (NSString *)defaultAccessibilityLabel
{
return nil;
}
- (NSString *)defaultAccessibilityHint
{
return nil;
}
- (NSString *)defaultAccessibilityValue
{
return nil;
}
- (NSString *)defaultAccessibilityIdentifier
{
return nil;
}
- (UIAccessibilityTraits)defaultAccessibilityTraits
{
return UIAccessibilityTraitNone;
}
#pragma mark - Debugging (Private)
#if ASEVENTLOG_ENABLE
@@ -3793,6 +3836,16 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) {
return _unflattenedLayout;
}
+ (void)setSuppressesInvalidCollectionUpdateExceptions:(BOOL)suppresses
{
suppressesInvalidCollectionUpdateExceptions.store(suppresses);
}
+ (BOOL)suppressesInvalidCollectionUpdateExceptions
{
return suppressesInvalidCollectionUpdateExceptions.load();
}
- (NSString *)displayNodeRecursiveDescription
{
return [self _recursiveDescriptionHelperWithIndent:@""];

View File

@@ -1021,4 +1021,38 @@
[_delegate editableTextNodeDidFinishEditing:self];
}
#pragma mark - UIAccessibilityContainer
- (NSInteger)accessibilityElementCount
{
if (!self.isNodeLoaded) {
ASDisplayNodeFailAssert(@"Cannot access accessibilityElementCount since ASEditableTextNode is not loaded");
return 0;
}
return 1;
}
- (NSArray *)accessibilityElements
{
if (!self.isNodeLoaded) {
ASDisplayNodeFailAssert(@"Cannot access accessibilityElements since ASEditableTextNode is not loaded");
return @[];
}
return @[self.textView];
}
- (id)accessibilityElementAtIndex:(NSInteger)index
{
if (!self.isNodeLoaded) {
ASDisplayNodeFailAssert(@"Cannot access accessibilityElementAtIndex: since ASEditableTextNode is not loaded");
return nil;
}
return self.textView;
}
- (NSInteger)indexOfAccessibilityElement:(id)element
{
return 0;
}
@end

View File

@@ -24,6 +24,7 @@ typedef NS_OPTIONS(NSUInteger, ASExperimentalFeatures) {
ASExperimentalCollectionTeardown = 1 << 6, // exp_collection_teardown
ASExperimentalFramesetterCache = 1 << 7, // exp_framesetter_cache
ASExperimentalClearDataDuringDeallocation = 1 << 8, // exp_clear_data_during_deallocation
ASExperimentalDidEnterPreloadSkipASMLayout = 1 << 9, // exp_did_enter_preload_skip_asm_layout
ASExperimentalFeatureAll = 0xFFFFFFFF
};

View File

@@ -1,5 +1,5 @@
//
// ASExperimentalFeatures.m
// ASExperimentalFeatures.mm
// Texture
//
// Copyright (c) Pinterest, Inc. All rights reserved.
@@ -18,10 +18,10 @@ NSArray<NSString *> *ASExperimentalFeaturesGetNames(ASExperimentalFeatures flags
@"exp_unfair_lock",
@"exp_infer_layer_defaults",
@"exp_network_image_queue",
@"exp_dealloc_queue_v2",
@"exp_collection_teardown",
@"exp_framesetter_cache",
@"exp_clear_data_during_deallocation"]));
@"exp_clear_data_during_deallocation",
@"exp_did_enter_preload_skip_asm_layout"]));
if (flags == ASExperimentalFeatureAll) {
return allNames;

View File

@@ -442,12 +442,12 @@ typedef void (^ASImageNodeDrawParametersBlock)(ASWeakMapEntry *entry);
static ASWeakMap<ASImageNodeContentsKey *, UIImage *> *cache = nil;
// Allocate cacheLock on the heap to prevent destruction at app exit (https://github.com/TextureGroup/Texture/issues/136)
static ASDN::StaticMutex& cacheLock = *new ASDN::StaticMutex;
static auto *cacheLock = new ASDN::Mutex;
+ (ASWeakMapEntry *)contentsForkey:(ASImageNodeContentsKey *)key drawParameters:(id)drawParameters isCancelled:(asdisplaynode_iscancelled_block_t)isCancelled
{
{
ASDN::StaticMutexLocker l(cacheLock);
ASDN::MutexLocker l(*cacheLock);
if (!cache) {
cache = [[ASWeakMap alloc] init];
}
@@ -464,7 +464,7 @@ static ASDN::StaticMutex& cacheLock = *new ASDN::StaticMutex;
}
{
ASDN::StaticMutexLocker l(cacheLock);
ASDN::MutexLocker l(*cacheLock);
return [cache setObject:contents forKey:key];
}
}
@@ -541,8 +541,15 @@ static ASDN::StaticMutex& cacheLock = *new ASDN::StaticMutex;
[super displayDidFinish];
__instanceLock__.lock();
void (^displayCompletionBlock)(BOOL canceled) = _displayCompletionBlock;
UIImage *image = _image;
void (^displayCompletionBlock)(BOOL canceled) = _displayCompletionBlock;
BOOL shouldPerformDisplayCompletionBlock = (image && displayCompletionBlock);
// Clear the ivar now. The block is retained and will be executed shortly.
if (shouldPerformDisplayCompletionBlock) {
_displayCompletionBlock = nil;
}
BOOL hasDebugLabel = (_debugLabelNode != nil);
__instanceLock__.unlock();
@@ -564,13 +571,8 @@ static ASDN::StaticMutex& cacheLock = *new ASDN::StaticMutex;
}
// If we've got a block to perform after displaying, do it.
if (image && displayCompletionBlock) {
if (shouldPerformDisplayCompletionBlock) {
displayCompletionBlock(NO);
__instanceLock__.lock();
_displayCompletionBlock = nil;
__instanceLock__.unlock();
}
}

View File

@@ -1,5 +1,5 @@
//
// ASNavigationController.m
// ASNavigationController.mm
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.

View File

@@ -1,5 +1,5 @@
//
// ASNetworkImageLoadInfo.m
// ASNetworkImageLoadInfo.mm
// Texture
//
// Copyright (c) Pinterest, Inc. All rights reserved.

View File

@@ -147,6 +147,15 @@ NS_ASSUME_NONNULL_BEGIN
@protocol ASNetworkImageNodeDelegate <NSObject>
@optional
/**
* Notification that the image node started to load
*
* @param imageNode The sender.
*
* @discussion Called on the main thread.
*/
- (void)imageNodeDidStartFetchingData:(ASNetworkImageNode *)imageNode;
/**
* Notification that the image node finished downloading an image, with additional info.
* If implemented, this method will be called instead of `imageNode:didLoadImage:`.
@@ -169,15 +178,6 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (void)imageNode:(ASNetworkImageNode *)imageNode didLoadImage:(UIImage *)image;
/**
* Notification that the image node started to load
*
* @param imageNode The sender.
*
* @discussion Called on a background queue.
*/
- (void)imageNodeDidStartFetchingData:(ASNetworkImageNode *)imageNode;
/**
* Notification that the image node failed to download the image.
*
@@ -192,6 +192,8 @@ NS_ASSUME_NONNULL_BEGIN
* Notification that the image node finished decoding an image.
*
* @param imageNode The sender.
*
* @discussion Called on the main thread.
*/
- (void)imageNodeDidFinishDecoding:(ASNetworkImageNode *)imageNode;

View File

@@ -360,9 +360,6 @@ static std::atomic_bool _useMainThreadDelegateCallbacks(true);
}
}
// TODO: Consider removing this; it predates ASInterfaceState, which now ensures that even non-range-managed nodes get a -preload call.
[self didEnterPreloadState];
if (self.image == nil && _downloaderFlags.downloaderImplementsSetPriority) {
id downloadIdentifier = ASLockedSelf(_downloadIdentifier);
if (downloadIdentifier != nil) {
@@ -574,11 +571,11 @@ static std::atomic_bool _useMainThreadDelegateCallbacks(true);
// it and try again.
{
ASLockScopeSelf();
url = _URL;
url = self->_URL;
}
downloadIdentifier = [_downloader downloadImageWithURL:url
downloadIdentifier = [self->_downloader downloadImageWithURL:url
callbackQueue:[self callbackQueue]
downloadProgress:NULL
completion:^(id <ASImageContainerProtocol> _Nullable imageContainer, NSError * _Nullable error, id _Nullable downloadIdentifier, id _Nullable userInfo) {
@@ -590,9 +587,9 @@ static std::atomic_bool _useMainThreadDelegateCallbacks(true);
{
ASLockScopeSelf();
if (ASObjectIsEqual(_URL, url)) {
if (ASObjectIsEqual(self->_URL, url)) {
// The download we kicked off is correct, no need to do any more work.
_downloadIdentifier = downloadIdentifier;
self->_downloadIdentifier = downloadIdentifier;
} else {
// The URL changed since we kicked off our download task. This shouldn't happen often so we'll pay the cost and
// cancel that request and kick off a new one.
@@ -603,7 +600,7 @@ static std::atomic_bool _useMainThreadDelegateCallbacks(true);
if (cancelAndReattempt) {
if (downloadIdentifier != nil) {
as_log_verbose(ASImageLoadingLog(), "Canceling image download no resume for %@ id: %@", self, downloadIdentifier);
[_downloader cancelImageDownloadForIdentifier:downloadIdentifier];
[self->_downloader cancelImageDownloadForIdentifier:downloadIdentifier];
}
[self _downloadImageWithCompletion:finished];
return;
@@ -615,6 +612,8 @@ static std::atomic_bool _useMainThreadDelegateCallbacks(true);
- (void)_lazilyLoadImageIfNecessary
{
ASDisplayNodeAssertMainThread();
[self lock];
__weak id<ASNetworkImageNodeDelegate> delegate = _delegate;
BOOL delegateDidStartFetchingData = _delegateFlags.delegateDidStartFetchingData;
@@ -633,11 +632,11 @@ static std::atomic_bool _useMainThreadDelegateCallbacks(true);
ASLockScopeSelf();
// Bail out if not the same URL anymore
if (!ASObjectIsEqual(URL, _URL)) {
if (!ASObjectIsEqual(URL, self->_URL)) {
return;
}
if (_shouldCacheImage) {
if (self->_shouldCacheImage) {
[self _locked__setImage:[UIImage imageNamed:URL.path.lastPathComponent]];
} else {
// First try to load the path directly, for efficiency assuming a developer who
@@ -654,10 +653,10 @@ static std::atomic_bool _useMainThreadDelegateCallbacks(true);
// If the file may be an animated gif and then created an animated image.
id<ASAnimatedImageProtocol> animatedImage = nil;
if (_downloaderFlags.downloaderImplementsAnimatedImage) {
if (self->_downloaderFlags.downloaderImplementsAnimatedImage) {
let data = [[NSData alloc] initWithContentsOfURL:URL];
if (data != nil) {
animatedImage = [_downloader animatedImageWithData:data];
animatedImage = [self->_downloader animatedImageWithData:data];
if ([animatedImage respondsToSelector:@selector(isDataSupported:)] && [animatedImage isDataSupported:data] == NO) {
animatedImage = nil;
@@ -672,15 +671,15 @@ static std::atomic_bool _useMainThreadDelegateCallbacks(true);
}
}
_imageLoaded = YES;
self->_imageLoaded = YES;
[self _setCurrentImageQuality:1.0];
if (_delegateFlags.delegateDidLoadImageWithInfo) {
if (self->_delegateFlags.delegateDidLoadImageWithInfo) {
ASUnlockScope(self);
let info = [[ASNetworkImageLoadInfo alloc] initWithURL:URL sourceType:ASNetworkImageSourceFileURL downloadIdentifier:nil userInfo:nil];
[delegate imageNode:self didLoadImage:self.image info:info];
} else if (_delegateFlags.delegateDidLoadImage) {
} else if (self->_delegateFlags.delegateDidLoadImage) {
ASUnlockScope(self);
[delegate imageNode:self didLoadImage:self.image];
}
@@ -731,17 +730,17 @@ static std::atomic_bool _useMainThreadDelegateCallbacks(true);
void (^calloutBlock)(ASNetworkImageNode *inst);
if (newImage) {
if (_delegateFlags.delegateDidLoadImageWithInfo) {
if (strongSelf->_delegateFlags.delegateDidLoadImageWithInfo) {
calloutBlock = ^(ASNetworkImageNode *strongSelf) {
let info = [[ASNetworkImageLoadInfo alloc] initWithURL:URL sourceType:imageSource downloadIdentifier:downloadIdentifier userInfo:userInfo];
[delegate imageNode:strongSelf didLoadImage:newImage info:info];
};
} else if (_delegateFlags.delegateDidLoadImage) {
} else if (strongSelf->_delegateFlags.delegateDidLoadImage) {
calloutBlock = ^(ASNetworkImageNode *strongSelf) {
[delegate imageNode:strongSelf didLoadImage:newImage];
};
}
} else if (error && _delegateFlags.delegateDidFailWithError) {
} else if (error && strongSelf->_delegateFlags.delegateDidFailWithError) {
calloutBlock = ^(ASNetworkImageNode *strongSelf) {
[delegate imageNode:strongSelf didFailWithError:error];
};
@@ -755,7 +754,7 @@ static std::atomic_bool _useMainThreadDelegateCallbacks(true);
}
});
} else {
calloutBlock(self);
calloutBlock(strongSelf);
}
}
});
@@ -770,11 +769,11 @@ static std::atomic_bool _useMainThreadDelegateCallbacks(true);
ASImageCacherCompletion completion = ^(id <ASImageContainerProtocol> imageContainer) {
// If the cache sentinel changed, that means this request was cancelled.
if (ASLockedSelf(_cacheSentinel != cacheSentinel)) {
if (ASLockedSelf(self->_cacheSentinel != cacheSentinel)) {
return;
}
if ([imageContainer asdk_image] == nil && _downloader != nil) {
if ([imageContainer asdk_image] == nil && self->_downloader != nil) {
[self _downloadImageWithCompletion:^(id<ASImageContainerProtocol> imageContainer, NSError *error, id downloadIdentifier, id userInfo) {
finished(imageContainer, error, downloadIdentifier, ASNetworkImageSourceDownload, userInfo);
}];

View File

@@ -11,7 +11,8 @@
#import <AsyncDisplayKit/ASDisplayNode+Subclasses.h> // for ASInterfaceState protocol
/* ASNodeController is currently beta and open to change in the future */
@interface ASNodeController<__covariant DisplayNodeType : ASDisplayNode *> : NSObject <ASInterfaceStateDelegate>
@interface ASNodeController<__covariant DisplayNodeType : ASDisplayNode *>
: NSObject <ASInterfaceStateDelegate, NSLocking>
@property (nonatomic, strong /* may be weak! */) DisplayNodeType node;
@@ -26,6 +27,9 @@
- (void)nodeDidLoad ASDISPLAYNODE_REQUIRES_SUPER;
- (void)nodeDidLayout ASDISPLAYNODE_REQUIRES_SUPER;
// This is only called during Yoga-driven layouts.
- (void)nodeWillCalculateLayout:(ASSizeRange)constrainedSize ASDISPLAYNODE_REQUIRES_SUPER;
- (void)didEnterVisibleState ASDISPLAYNODE_REQUIRES_SUPER;
- (void)didExitVisibleState ASDISPLAYNODE_REQUIRES_SUPER;

View File

@@ -7,10 +7,10 @@
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <AsyncDisplayKit/ASInternalHelpers.h>
#import <AsyncDisplayKit/ASDisplayNodeInternal.h>
#import <AsyncDisplayKit/ASDisplayNode+FrameworkPrivate.h>
#import <AsyncDisplayKit/ASNodeController+Beta.h>
#import <AsyncDisplayKit/ASThread.h>
#define _node (_shouldInvertStrongReference ? _weakNode : _strongNode)
@@ -18,15 +18,18 @@
{
ASDisplayNode *_strongNode;
__weak ASDisplayNode *_weakNode;
ASDN::RecursiveMutex __instanceLock__;
}
- (void)loadNode
{
ASLockScopeSelf();
self.node = [[ASDisplayNode alloc] init];
}
- (ASDisplayNode *)node
{
ASLockScopeSelf();
if (_node == nil) {
[self loadNode];
}
@@ -35,6 +38,7 @@
- (void)setupReferencesWithNode:(ASDisplayNode *)node
{
ASLockScopeSelf();
if (_shouldInvertStrongReference) {
// The node should own the controller; weak reference from controller to node.
_weakNode = node;
@@ -51,11 +55,13 @@
- (void)setNode:(ASDisplayNode *)node
{
ASLockScopeSelf();
[self setupReferencesWithNode:node];
}
- (void)setShouldInvertStrongReference:(BOOL)shouldInvertStrongReference
{
ASLockScopeSelf();
if (_shouldInvertStrongReference != shouldInvertStrongReference) {
// Because the BOOL controls which ivar we access, get the node before toggling.
ASDisplayNode *node = _node;
@@ -67,6 +73,7 @@
// subclass overrides
- (void)nodeDidLoad {}
- (void)nodeDidLayout {}
- (void)nodeWillCalculateLayout:(ASSizeRange)constrainedSize {}
- (void)didEnterVisibleState {}
- (void)didExitVisibleState {}
@@ -82,11 +89,24 @@
- (void)hierarchyDisplayDidFinish {}
#pragma mark NSLocking
- (void)lock
{
__instanceLock__.lock();
}
- (void)unlock
{
__instanceLock__.unlock();
}
@end
@implementation ASDisplayNode (ASNodeController)
- (ASNodeController *)nodeController {
- (ASNodeController *)nodeController
{
return _weakNodeController ?: _strongNodeController;
}

View File

@@ -1,5 +1,5 @@
//
// ASPagerFlowLayout.m
// ASPagerFlowLayout.mm
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.

View File

@@ -1,5 +1,5 @@
//
// ASPagerNode.m
// ASPagerNode.mm
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.

View File

@@ -191,7 +191,7 @@ static void runLoopSourceCallback(void *info) {
#if ASRunLoopQueueLoggingEnabled
- (void)checkRunLoop
{
NSLog(@"<%@> - Jobs: %ld", self, _internalQueue.size());
NSLog(@"<%@> - Jobs: %ld", self, _internalQueue.count);
}
#endif
@@ -323,10 +323,8 @@ ASSynthesizeLockingMethodsWithMutex(_internalQueueLock)
CFRunLoopRef _runLoop;
CFRunLoopSourceRef _runLoopSource;
CFRunLoopObserverRef _preTransactionObserver;
CFRunLoopObserverRef _postTransactionObserver;
NSPointerArray *_internalQueue;
ASDN::RecursiveMutex _internalQueueLock;
BOOL _CATransactionCommitInProgress;
// In order to not pollute the top-level activities, each queue has 1 root activity.
os_activity_t _rootActivity;
@@ -343,9 +341,6 @@ ASSynthesizeLockingMethodsWithMutex(_internalQueueLock)
// CoreAnimation commit order is 2000000, the goal of this is to process shortly beforehand
// but after most other scheduled work on the runloop has processed.
static int const kASASCATransactionQueueOrder = 1000000;
// This will mark the end of current loop and any node enqueued between kASASCATransactionQueueOrder
// and kASASCATransactionQueuePostOrder will apply interface change immediately.
static int const kASASCATransactionQueuePostOrder = 3000000;
+ (ASCATransactionQueue *)sharedQueue NS_RETURNS_RETAINED
{
@@ -377,17 +372,13 @@ static int const kASASCATransactionQueuePostOrder = 3000000;
// __unsafe_unretained allows us to avoid flagging the memory cycle detector.
__unsafe_unretained __typeof__(self) weakSelf = self;
void (^handlerBlock) (CFRunLoopObserverRef observer, CFRunLoopActivity activity) = ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
while (weakSelf->_internalQueue.count > 0) {
[weakSelf processQueue];
};
void (^postHandlerBlock) (CFRunLoopObserverRef observer, CFRunLoopActivity activity) = ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
ASDN::MutexLocker l(_internalQueueLock);
_CATransactionCommitInProgress = NO;
}
};
_preTransactionObserver = CFRunLoopObserverCreateWithHandler(NULL, kCFRunLoopBeforeWaiting, true, kASASCATransactionQueueOrder, handlerBlock);
_postTransactionObserver = CFRunLoopObserverCreateWithHandler(NULL, kCFRunLoopBeforeWaiting, true, kASASCATransactionQueuePostOrder, postHandlerBlock);
CFRunLoopAddObserver(_runLoop, _preTransactionObserver, kCFRunLoopCommonModes);
CFRunLoopAddObserver(_runLoop, _postTransactionObserver, kCFRunLoopCommonModes);
// It is not guaranteed that the runloop will turn if it has no scheduled work, and this causes processing of
// the queue to stop. Attaching a custom loop source to the run loop and signal it if new work needs to be done
@@ -416,19 +407,14 @@ static int const kASASCATransactionQueuePostOrder = 3000000;
if (CFRunLoopObserverIsValid(_preTransactionObserver)) {
CFRunLoopObserverInvalidate(_preTransactionObserver);
}
if (CFRunLoopObserverIsValid(_postTransactionObserver)) {
CFRunLoopObserverInvalidate(_postTransactionObserver);
}
CFRelease(_preTransactionObserver);
CFRelease(_postTransactionObserver);
_preTransactionObserver = nil;
_postTransactionObserver = nil;
}
#if ASRunLoopQueueLoggingEnabled
- (void)checkRunLoop
{
NSLog(@"<%@> - Jobs: %ld", self, _internalQueue.size());
NSLog(@"<%@> - Jobs: %ld", self, _internalQueue.count);
}
#endif
@@ -441,11 +427,6 @@ static int const kASASCATransactionQueuePostOrder = 3000000;
{
ASDN::MutexLocker l(_internalQueueLock);
// Mark the queue will end coalescing shortly until after CATransactionCommit.
// This will give the queue a chance to apply any further interfaceState changes/enqueue
// immediately within current runloop instead of pushing the work to next runloop cycle.
_CATransactionCommitInProgress = YES;
NSInteger internalQueueCount = _internalQueue.count;
// Early-exit if the queue is empty.
if (internalQueueCount == 0) {
@@ -502,7 +483,7 @@ static int const kASASCATransactionQueuePostOrder = 3000000;
return;
}
if (!self.enabled || _CATransactionCommitInProgress) {
if (!self.enabled) {
[object prepareForCATransactionCommit];
return;
}

View File

@@ -52,6 +52,11 @@
}
}
- (NSArray *)accessibilityElements
{
return [self.asyncdisplaykit_node accessibilityElements];
}
@end
@implementation ASScrollNode

View File

@@ -1,5 +1,5 @@
//
// ASTabBarController.m
// ASTabBarController.mm
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.

View File

@@ -23,6 +23,7 @@
#import <AsyncDisplayKit/ASThread.h>
#import <AsyncDisplayKit/ASDisplayNode+Beta.h>
#import <AsyncDisplayKit/ASRangeController.h>
#import <AsyncDisplayKit/ASAbstractLayoutController+FrameworkPrivate.h>
#pragma mark - _ASTablePendingState
@@ -55,7 +56,7 @@
self = [super init];
if (self) {
_rangeMode = ASLayoutRangeModeUnspecified;
_tuningParameters = std::vector<std::vector<ASRangeTuningParameters>> (ASLayoutRangeModeCount, std::vector<ASRangeTuningParameters> (ASLayoutRangeTypeCount, ASRangeTuningParametersZero));
_tuningParameters = [ASAbstractLayoutController defaultTuningParameters];
_allowsSelection = YES;
_allowsSelectionDuringEditing = NO;
_allowsMultipleSelection = NO;
@@ -155,6 +156,7 @@
if (_pendingState) {
_ASTablePendingState *pendingState = _pendingState;
self.pendingState = nil;
view.asyncDelegate = pendingState.delegate;
view.asyncDataSource = pendingState.dataSource;
view.inverted = pendingState.inverted;
@@ -162,9 +164,17 @@
view.allowsSelectionDuringEditing = pendingState.allowsSelectionDuringEditing;
view.allowsMultipleSelection = pendingState.allowsMultipleSelection;
view.allowsMultipleSelectionDuringEditing = pendingState.allowsMultipleSelectionDuringEditing;
view.contentInset = pendingState.contentInset;
self.pendingState = nil;
UIEdgeInsets contentInset = pendingState.contentInset;
if (!UIEdgeInsetsEqualToEdgeInsets(contentInset, UIEdgeInsetsZero)) {
view.contentInset = contentInset;
}
CGPoint contentOffset = pendingState.contentOffset;
if (!CGPointEqualToPoint(contentOffset, CGPointZero)) {
[view setContentOffset:contentOffset animated:pendingState.animatesContentOffset];
}
let tuningParametersVector = pendingState->_tuningParameters;
let tuningParametersVectorSize = tuningParametersVector.size();
for (NSInteger rangeMode = 0; rangeMode < tuningParametersVectorSize; rangeMode++) {
@@ -172,19 +182,15 @@
let tuningParametersVectorRangeModeSize = tuningparametersRangeModeVector.size();
for (NSInteger rangeType = 0; rangeType < tuningParametersVectorRangeModeSize; rangeType++) {
ASRangeTuningParameters tuningParameters = tuningparametersRangeModeVector[rangeType];
if (!ASRangeTuningParametersEqualToRangeTuningParameters(tuningParameters, ASRangeTuningParametersZero)) {
[_rangeController setTuningParameters:tuningParameters
forRangeMode:(ASLayoutRangeMode)rangeMode
rangeType:(ASLayoutRangeType)rangeType];
}
[_rangeController setTuningParameters:tuningParameters
forRangeMode:(ASLayoutRangeMode)rangeMode
rangeType:(ASLayoutRangeType)rangeType];
}
}
if (pendingState.rangeMode != ASLayoutRangeModeUnspecified) {
[_rangeController updateCurrentRangeWithMode:pendingState.rangeMode];
}
[view setContentOffset:pendingState.contentOffset animated:pendingState.animatesContentOffset];
}
}

View File

@@ -437,6 +437,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
_dataController.validationErrorSource = asyncDataSource;
super.dataSource = (id<UITableViewDataSource>)_proxyDataSource;
[self _asyncDelegateOrDataSourceDidChange];
}
- (id<ASTableDelegate>)asyncDelegate
@@ -507,6 +508,22 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
}
super.delegate = (id<UITableViewDelegate>)_proxyDelegate;
[self _asyncDelegateOrDataSourceDidChange];
}
- (void)_asyncDelegateOrDataSourceDidChange
{
ASDisplayNodeAssertMainThread();
if (_asyncDataSource == nil && _asyncDelegate == nil) {
if (ASActivateExperimentalFeature(ASExperimentalClearDataDuringDeallocation)) {
if (_isDeallocating) {
[_dataController clearData];
}
} else {
[_dataController clearData];
}
}
}
- (void)proxyTargetHasDeallocated:(ASDelegateProxy *)proxy

View File

@@ -35,6 +35,11 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property (readonly) BOOL usingExperiment;
/**
* Returns a Boolean indicating if the text node will truncate for the given constrained size
*/
- (BOOL)shouldTruncateForConstrainedSize:(ASSizeRange)constrainedSize;
@end
NS_ASSUME_NONNULL_END

View File

@@ -217,6 +217,15 @@ NS_ASSUME_NONNULL_BEGIN
@end
/**
* @abstract Text node unsupported properties
*/
@interface ASTextNode (Unsupported)
@property (nullable, nonatomic) id textContainerLinePositionModifier;
@end
/**
* @abstract Text node deprecated properties
*/

View File

@@ -226,7 +226,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
// Accessibility
self.isAccessibilityElement = YES;
self.accessibilityTraits = UIAccessibilityTraitStaticText;
self.accessibilityTraits = self.defaultAccessibilityTraits;
// Placeholders
// Disabled by default in ASDisplayNode, but add a few options for those who toggle
@@ -365,6 +365,17 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
};
}
- (NSString *)defaultAccessibilityLabel
{
ASLockScopeSelf();
return _attributedText.string;
}
- (UIAccessibilityTraits)defaultAccessibilityTraits
{
return UIAccessibilityTraitStaticText;
}
#pragma mark - Layout and Sizing
- (void)setTextContainerInset:(UIEdgeInsets)textContainerInset
@@ -440,39 +451,46 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
if (attributedText == nil) {
attributedText = [[NSAttributedString alloc] initWithString:@"" attributes:nil];
}
// Don't hold textLock for too long.
{
ASLockScopeSelf();
if (ASObjectIsEqual(attributedText, _attributedText)) {
return;
}
_attributedText = ASCleanseAttributedStringOfCoreTextAttributes(attributedText);
#if AS_TEXTNODE_RECORD_ATTRIBUTED_STRINGS
[ASTextNode _registerAttributedText:_attributedText];
#endif
}
// Since truncation text matches style of attributedText, invalidate it now.
[self _invalidateTruncationText];
NSUInteger length = _attributedText.length;
if (length > 0) {
self.style.ascender = [[self class] ascenderWithAttributedString:_attributedText];
self.style.descender = [[_attributedText attribute:NSFontAttributeName atIndex:length - 1 effectiveRange:NULL] descender];
}
NSAttributedString *cleanedAttributedString = ASCleanseAttributedStringOfCoreTextAttributes(attributedText);
// Invalidating the truncation text must be done while we still hold the lock. Because after we release it,
// another thread may set a new truncation text that will then be cleared by this thread, other may draw
// this soon-to-be-invalidated text.
[self _locked_invalidateTruncationText];
NSUInteger length = cleanedAttributedString.length;
if (length > 0) {
// Updating ascender and descender in one transaction while holding the lock.
ASLayoutElementStyle *style = [self _locked_style];
style.ascender = [[self class] ascenderWithAttributedString:cleanedAttributedString];
style.descender = [[attributedText attribute:NSFontAttributeName atIndex:cleanedAttributedString.length - 1 effectiveRange:NULL] descender];
}
// Update attributed text with cleaned attributed string
_attributedText = cleanedAttributedString;
}
// Tell the display node superclasses that the cached layout is incorrect now
[self setNeedsLayout];
// Force display to create renderer with new size and redisplay with new string
[self setNeedsDisplay];
// Accessiblity
self.accessibilityLabel = _attributedText.string;
self.isAccessibilityElement = (length != 0); // We're an accessibility element by default if there is a string.
let currentAttributedText = self.attributedText; // Grab attributed string again in case it changed in the meantime
self.accessibilityLabel = self.defaultAccessibilityLabel;
self.isAccessibilityElement = (currentAttributedText.length != 0); // We're an accessibility element by default if there is a string.
#if AS_TEXTNODE_RECORD_ATTRIBUTED_STRINGS
[ASTextNode _registerAttributedText:_attributedText];
#endif
}
#pragma mark - Text Layout
@@ -589,7 +607,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
return;
}
for (NSString *attributeName in _linkAttributeNames) {
for (NSString *attributeName in self->_linkAttributeNames) {
NSRange range;
id value = [attributedString attribute:attributeName atIndex:characterIndex longestEffectiveRange:&range inRange:clampedRange];
NSString *name = attributeName;
@@ -601,8 +619,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
// If highlighting, check with delegate first. If not implemented, assume YES.
if (highlighting
&& [_delegate respondsToSelector:@selector(textNode:shouldHighlightLinkAttribute:value:atPoint:)]
&& ![_delegate textNode:self shouldHighlightLinkAttribute:name value:value atPoint:point]) {
&& [self->_delegate respondsToSelector:@selector(textNode:shouldHighlightLinkAttribute:value:atPoint:)]
&& ![self->_delegate textNode:self shouldHighlightLinkAttribute:name value:value atPoint:point]) {
value = nil;
name = nil;
}
@@ -955,9 +973,7 @@ static CGRect ASTextNodeAdjustRenderRectForShadowPadding(CGRect rendererRect, UI
NSUInteger lastCharIndex = NSIntegerMax;
BOOL linkCrossesVisibleRange = (lastCharIndex > range.location) && (lastCharIndex < NSMaxRange(range) - 1);
if (inAdditionalTruncationMessage) {
return YES;
} else if (range.length && !linkCrossesVisibleRange && linkAttributeValue != nil && linkAttributeName != nil) {
if (range.length > 0 && !linkCrossesVisibleRange && linkAttributeValue != nil && linkAttributeName != nil) {
return YES;
} else {
return NO;
@@ -993,7 +1009,7 @@ static CGRect ASTextNodeAdjustRenderRectForShadowPadding(CGRect rendererRect, UI
}
NSRange truncationMessageRange = [self _additionalTruncationMessageRangeWithVisibleRange:visibleRange];
[self _setHighlightRange:truncationMessageRange forAttributeName:ASTextNodeTruncationTokenAttributeName value:nil animated:YES];
} else if (range.length && !linkCrossesVisibleRange && linkAttributeValue != nil && linkAttributeName != nil) {
} else if (range.length > 0 && !linkCrossesVisibleRange && linkAttributeValue != nil && linkAttributeName != nil) {
[self _setHighlightRange:range forAttributeName:linkAttributeName value:linkAttributeValue animated:YES];
}
}
@@ -1166,6 +1182,7 @@ static NSAttributedString *DefaultTruncationAttributedString()
{
if (ASLockedSelfCompareAssignCopy(_truncationAttributedText, truncationAttributedText)) {
[self _invalidateTruncationText];
[self setNeedsDisplay];
}
}
@@ -1173,6 +1190,7 @@ static NSAttributedString *DefaultTruncationAttributedString()
{
if (ASLockedSelfCompareAssignCopy(_additionalTruncationMessage, additionalTruncationMessage)) {
[self _invalidateTruncationText];
[self setNeedsDisplay];
}
}
@@ -1198,6 +1216,11 @@ static NSAttributedString *DefaultTruncationAttributedString()
return ASLockedSelf([[self _locked_renderer] isTruncated]);
}
- (BOOL)shouldTruncateForConstrainedSize:(ASSizeRange)constrainedSize
{
return ASLockedSelf([[self _locked_rendererWithBounds:{.size = constrainedSize.max}] isTruncated]);
}
- (void)setPointSizeScaleFactors:(NSArray<NSNumber *> *)pointSizeScaleFactors
{
if (ASLockedSelfCompareAssignCopy(_pointSizeScaleFactors, pointSizeScaleFactors)) {
@@ -1231,12 +1254,13 @@ static NSAttributedString *DefaultTruncationAttributedString()
- (void)_invalidateTruncationText
{
{
ASLockScopeSelf();
_composedTruncationText = nil;
}
ASLockScopeSelf();
[self _locked_invalidateTruncationText];
}
[self setNeedsDisplay];
- (void)_locked_invalidateTruncationText
{
_composedTruncationText = nil;
}
/**
@@ -1365,6 +1389,21 @@ static NSAttributedString *DefaultTruncationAttributedString()
@end
@implementation ASTextNode (Unsupported)
- (void)setTextContainerLinePositionModifier:(id)textContainerLinePositionModifier
{
AS_TEXT_ALERT_UNIMPLEMENTED_FEATURE();
}
- (id)textContainerLinePositionModifier
{
AS_TEXT_ALERT_UNIMPLEMENTED_FEATURE();
return nil;
}
@end
@implementation ASTextNode (Deprecated)
- (void)setAttributedString:(NSAttributedString *)attributedString

View File

@@ -9,6 +9,8 @@
#import <AsyncDisplayKit/ASControlNode.h>
#import <AsyncDisplayKit/ASTextNodeCommon.h>
@protocol ASTextLinePositionModifier;
NS_ASSUME_NONNULL_BEGIN
/**
@@ -207,6 +209,10 @@ NS_ASSUME_NONNULL_BEGIN
+ (void)enableDebugging;
#pragma mark - Layout and Sizing
@property (nullable, nonatomic) id<ASTextLinePositionModifier> textContainerLinePositionModifier;
@end
@interface ASTextNode2 (Unavailable)

View File

@@ -48,11 +48,93 @@
*/
#define AS_TEXTNODE2_RECORD_ATTRIBUTED_STRINGS 0
#define AS_TEXT_ALERT_UNIMPLEMENTED_FEATURE() { \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
NSLog(@"[Texture] Warning: Feature %@ is unimplemented in the experimental text node.", NSStringFromSelector(_cmd)); \
});\
/**
* If it can't find a compatible layout, this method creates one.
*
* NOTE: Be careful to copy `text` if needed.
*/
static NS_RETURNS_RETAINED ASTextLayout *ASTextNodeCompatibleLayoutWithContainerAndText(ASTextContainer *container, NSAttributedString *text) {
// Allocate layoutCacheLock on the heap to prevent destruction at app exit (https://github.com/TextureGroup/Texture/issues/136)
static auto *layoutCacheLock = new ASDN::Mutex;
static NSCache<NSAttributedString *, ASTextCacheValue *> *textLayoutCache;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
textLayoutCache = [[NSCache alloc] init];
});
layoutCacheLock->lock();
ASTextCacheValue *cacheValue = [textLayoutCache objectForKey:text];
if (cacheValue == nil) {
cacheValue = [[ASTextCacheValue alloc] init];
[textLayoutCache setObject:cacheValue forKey:[text copy]];
}
// Lock the cache item for the rest of the method. Only after acquiring can we release the NSCache.
ASDN::MutexLocker lock(cacheValue->_m);
layoutCacheLock->unlock();
CGRect containerBounds = (CGRect){ .size = container.size };
{
for (let &t : cacheValue->_layouts) {
CGSize constrainedSize = std::get<0>(t);
ASTextLayout *layout = std::get<1>(t);
CGSize layoutSize = layout.textBoundingSize;
// 1. CoreText can return frames that are narrower than the constrained width, for obvious reasons.
// 2. CoreText can return frames that are slightly wider than the constrained width, for some reason.
// We have to trust that somehow it's OK to try and draw within our size constraint, despite the return value.
// 3. Thus, those two values (constrained width & returned width) form a range, where
// intermediate values in that range will be snapped. Thus, we can use a given layout as long as our
// width is in that range, between the min and max of those two values.
CGRect minRect = CGRectMake(0, 0, MIN(layoutSize.width, constrainedSize.width), MIN(layoutSize.height, constrainedSize.height));
if (!CGRectContainsRect(containerBounds, minRect)) {
continue;
}
CGRect maxRect = CGRectMake(0, 0, MAX(layoutSize.width, constrainedSize.width), MAX(layoutSize.height, constrainedSize.height));
if (!CGRectContainsRect(maxRect, containerBounds)) {
continue;
}
if (!CGSizeEqualToSize(container.size, constrainedSize)) {
continue;
}
// Now check container params.
ASTextContainer *otherContainer = layout.container;
if (!UIEdgeInsetsEqualToEdgeInsets(container.insets, otherContainer.insets)) {
continue;
}
if (!ASObjectIsEqual(container.exclusionPaths, otherContainer.exclusionPaths)) {
continue;
}
if (container.maximumNumberOfRows != otherContainer.maximumNumberOfRows) {
continue;
}
if (container.truncationType != otherContainer.truncationType) {
continue;
}
if (!ASObjectIsEqual(container.truncationToken, otherContainer.truncationToken)) {
continue;
}
// TODO: When we get a cache hit, move this entry to the front (LRU).
return layout;
}
}
// Cache Miss. Compute the text layout.
ASTextLayout *layout = [ASTextLayout layoutWithContainer:container text:text];
// Store the result in the cache.
{
// This is a critical section. However we also must hold the lock until this point, in case
// another thread requests this cache item while a layout is being calculated, so they don't race.
cacheValue->_layouts.push_front(std::make_tuple(container.size, layout));
if (cacheValue->_layouts.size() > 3) {
cacheValue->_layouts.pop_back();
}
}
return layout;
}
static const CGFloat ASTextNodeHighlightLightOpacity = 0.11;
@@ -115,7 +197,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
// Accessibility
self.isAccessibilityElement = YES;
self.accessibilityTraits = UIAccessibilityTraitStaticText;
self.accessibilityTraits = self.defaultAccessibilityTraits;
// Placeholders
// Disabled by default in ASDisplayNode, but add a few options for those who toggle
@@ -209,6 +291,17 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
return YES;
}
- (NSString *)defaultAccessibilityLabel
{
ASLockScopeSelf();
return _attributedText.string;
}
- (UIAccessibilityTraits)defaultAccessibilityTraits
{
return UIAccessibilityTraitStaticText;
}
#pragma mark - Layout and Sizing
- (void)setTextContainerInset:(UIEdgeInsets)textContainerInset
@@ -225,6 +318,17 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
return _textContainer.insets;
}
- (void)setTextContainerLinePositionModifier:(id<ASTextLinePositionModifier>)modifier
{
ASLockedSelfCompareAssignObjects(_textContainer.linePositionModifier, modifier);
}
- (id<ASTextLinePositionModifier>)textContainerLinePositionModifier
{
ASLockScopeSelf();
return _textContainer.linePositionModifier;
}
- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize
{
ASDisplayNodeAssert(constrainedSize.width >= 0, @"Constrained width for text (%f) is too narrow", constrainedSize.width);
@@ -234,11 +338,19 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
_textContainer.size = constrainedSize;
[self _ensureTruncationText];
// If the constrained size has a max/inf value on the text's forward direction, the text node is calculating its intrinsic size.
// Need to consider both width and height when determining if it is calculating instrinsic size. Even the constrained width is provided, the height can be inf
// it may provide a text that is longer than the width and require a wordWrapping line break mode and looking for the height to be calculated.
BOOL isCalculatingIntrinsicSize = (_textContainer.size.width >= ASTextContainerMaxSize.width) || (_textContainer.size.height >= ASTextContainerMaxSize.height);
NSMutableAttributedString *mutableText = [_attributedText mutableCopy];
[self prepareAttributedString:mutableText];
ASTextLayout *layout = [ASTextNode2 compatibleLayoutWithContainer:_textContainer text:mutableText];
[self prepareAttributedString:mutableText isForIntrinsicSize:isCalculatingIntrinsicSize];
ASTextLayout *layout = ASTextNodeCompatibleLayoutWithContainerAndText(_textContainer, mutableText);
if (layout.truncatedLine != nil && layout.truncatedLine.size.width > layout.textBoundingSize.width) {
return (CGSize) {MIN(constrainedSize.width, layout.truncatedLine.size.width), layout.textBoundingSize.height};
}
return layout.textBoundingSize;
}
@@ -280,12 +392,13 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
}
// Since truncation text matches style of attributedText, invalidate it now.
[self _invalidateTruncationText];
[self _locked_invalidateTruncationText];
NSUInteger length = attributedText.length;
if (length > 0) {
self.style.ascender = [[self class] ascenderWithAttributedString:attributedText];
self.style.descender = [[attributedText attribute:NSFontAttributeName atIndex:attributedText.length - 1 effectiveRange:NULL] descender];
ASLayoutElementStyle *style = [self _locked_style];
style.ascender = [[self class] ascenderWithAttributedString:attributedText];
style.descender = [[attributedText attribute:NSFontAttributeName atIndex:attributedText.length - 1 effectiveRange:NULL] descender];
}
// Tell the display node superclasses that the cached layout is incorrect now
@@ -295,7 +408,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
[self setNeedsDisplay];
// Accessiblity
self.accessibilityLabel = attributedText.string;
self.accessibilityLabel = self.defaultAccessibilityLabel;
self.isAccessibilityElement = (length != 0); // We're an accessibility element by default if there is a string.
#if AS_TEXTNODE2_RECORD_ATTRIBUTED_STRINGS
@@ -320,21 +433,56 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
return _textContainer.exclusionPaths;
}
- (void)prepareAttributedString:(NSMutableAttributedString *)attributedString
- (void)prepareAttributedString:(NSMutableAttributedString *)attributedString isForIntrinsicSize:(BOOL)isForIntrinsicSize
{
ASLockScopeSelf();
// Apply paragraph style if needed
NSLineBreakMode innerMode;
switch (_truncationMode) {
case NSLineBreakByWordWrapping:
case NSLineBreakByCharWrapping:
case NSLineBreakByClipping:
innerMode = _truncationMode;
break;
default:
innerMode = NSLineBreakByWordWrapping;
}
// Apply/Fix paragraph style if needed
[attributedString enumerateAttribute:NSParagraphStyleAttributeName inRange:NSMakeRange(0, attributedString.length) options:kNilOptions usingBlock:^(NSParagraphStyle *style, NSRange range, BOOL * _Nonnull stop) {
if (style == nil || style.lineBreakMode == _truncationMode) {
BOOL applyTruncationMode = YES;
NSMutableParagraphStyle *paragraphStyle = nil;
// Only "left" and "justified" alignments are supported while calculating intrinsic size.
// Other alignments like "right", "center" and "natural" cause the size to be bigger than needed and thus should be ignored/overridden.
const BOOL forceLeftAlignment = (style != nil
&& isForIntrinsicSize
&& style.alignment != NSTextAlignmentLeft
&& style.alignment != NSTextAlignmentJustified);
if (style != nil) {
if (innerMode == style.lineBreakMode) {
applyTruncationMode = NO;
}
paragraphStyle = [style mutableCopy];
} else {
if (innerMode == NSLineBreakByWordWrapping) {
applyTruncationMode = NO;
}
paragraphStyle = [NSMutableParagraphStyle new];
}
if (!applyTruncationMode && !forceLeftAlignment) {
return;
}
NSMutableParagraphStyle *paragraphStyle = [style mutableCopy] ?: [[NSMutableParagraphStyle alloc] init];
paragraphStyle.lineBreakMode = _truncationMode;
paragraphStyle.lineBreakMode = innerMode;
if (applyTruncationMode) {
paragraphStyle.lineBreakMode = _truncationMode;
}
if (forceLeftAlignment) {
paragraphStyle.alignment = NSTextAlignmentLeft;
}
[attributedString addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:range];
}];
// Apply shadow if needed
if (_shadowOpacity > 0 && (_shadowRadius != 0 || !CGSizeEqualToSize(_shadowOffset, CGSizeZero)) && CGColorGetAlpha(_shadowColor) > 0) {
NSShadow *shadow = [[NSShadow alloc] init];
@@ -363,8 +511,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
copiedContainer.size = self.bounds.size;
[copiedContainer makeImmutable];
NSMutableAttributedString *mutableText = [_attributedText mutableCopy] ?: [[NSMutableAttributedString alloc] init];
[self prepareAttributedString:mutableText];
[self prepareAttributedString:mutableText isForIntrinsicSize:NO];
return @{
@"container": copiedContainer,
@@ -373,104 +521,12 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
};
}
/**
* If it can't find a compatible layout, this method creates one.
*
* NOTE: Be careful to copy `text` if needed.
*/
+ (ASTextLayout *)compatibleLayoutWithContainer:(ASTextContainer *)container
text:(NSAttributedString *)text NS_RETURNS_RETAINED
{
// Allocate layoutCacheLock on the heap to prevent destruction at app exit (https://github.com/TextureGroup/Texture/issues/136)
static ASDN::StaticMutex& layoutCacheLock = *new ASDN::StaticMutex;
static NSCache<NSAttributedString *, ASTextCacheValue *> *textLayoutCache;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
textLayoutCache = [[NSCache alloc] init];
});
layoutCacheLock.lock();
ASTextCacheValue *cacheValue = [textLayoutCache objectForKey:text];
if (cacheValue == nil) {
cacheValue = [[ASTextCacheValue alloc] init];
[textLayoutCache setObject:cacheValue forKey:[text copy]];
}
// Lock the cache item for the rest of the method. Only after acquiring can we release the NSCache.
ASDN::MutexLocker lock(cacheValue->_m);
layoutCacheLock.unlock();
CGRect containerBounds = (CGRect){ .size = container.size };
{
for (let &t : cacheValue->_layouts) {
CGSize constrainedSize = std::get<0>(t);
ASTextLayout *layout = std::get<1>(t);
CGSize layoutSize = layout.textBoundingSize;
// 1. CoreText can return frames that are narrower than the constrained width, for obvious reasons.
// 2. CoreText can return frames that are slightly wider than the constrained width, for some reason.
// We have to trust that somehow it's OK to try and draw within our size constraint, despite the return value.
// 3. Thus, those two values (constrained width & returned width) form a range, where
// intermediate values in that range will be snapped. Thus, we can use a given layout as long as our
// width is in that range, between the min and max of those two values.
CGRect minRect = CGRectMake(0, 0, MIN(layoutSize.width, constrainedSize.width), MIN(layoutSize.height, constrainedSize.height));
if (!CGRectContainsRect(containerBounds, minRect)) {
continue;
}
CGRect maxRect = CGRectMake(0, 0, MAX(layoutSize.width, constrainedSize.width), MAX(layoutSize.height, constrainedSize.height));
if (!CGRectContainsRect(maxRect, containerBounds)) {
continue;
}
if (!CGSizeEqualToSize(container.size, constrainedSize)) {
continue;
}
// Now check container params.
ASTextContainer *otherContainer = layout.container;
if (!UIEdgeInsetsEqualToEdgeInsets(container.insets, otherContainer.insets)) {
continue;
}
if (!ASObjectIsEqual(container.exclusionPaths, otherContainer.exclusionPaths)) {
continue;
}
if (container.maximumNumberOfRows != otherContainer.maximumNumberOfRows) {
continue;
}
if (container.truncationType != otherContainer.truncationType) {
continue;
}
if (!ASObjectIsEqual(container.truncationToken, otherContainer.truncationToken)) {
continue;
}
// TODO: When we get a cache hit, move this entry to the front (LRU).
return layout;
}
}
// Cache Miss. Compute the text layout.
ASTextLayout *layout = [ASTextLayout layoutWithContainer:container text:text];
// Store the result in the cache.
{
// This is a critical section. However we also must hold the lock until this point, in case
// another thread requests this cache item while a layout is being calculated, so they don't race.
cacheValue->_layouts.push_front(std::make_tuple(container.size, layout));
if (cacheValue->_layouts.size() > 3) {
cacheValue->_layouts.pop_back();
}
}
return layout;
}
+ (void)drawRect:(CGRect)bounds withParameters:(NSDictionary *)layoutDict isCancelled:(NS_NOESCAPE asdisplaynode_iscancelled_block_t)isCancelledBlock isRasterizing:(BOOL)isRasterizing
{
ASTextContainer *container = layoutDict[@"container"];
NSAttributedString *text = layoutDict[@"text"];
UIColor *bgColor = layoutDict[@"bgColor"];
ASTextLayout *layout = [self compatibleLayoutWithContainer:container text:text];
ASTextLayout *layout = ASTextNodeCompatibleLayoutWithContainerAndText(container, text);
if (isCancelledBlock()) {
return;
@@ -519,21 +575,18 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
// See discussion in https://github.com/TextureGroup/Texture/pull/396
ASTextContainer *containerCopy = [_textContainer copy];
containerCopy.size = self.calculatedSize;
ASTextLayout *layout = [ASTextNode2 compatibleLayoutWithContainer:containerCopy text:_attributedText];
NSRange visibleRange = layout.visibleRange;
NSRange clampedRange = NSIntersectionRange(visibleRange, NSMakeRange(0, _attributedText.length));
ASTextLayout *layout = ASTextNodeCompatibleLayoutWithContainerAndText(containerCopy, _attributedText);
ASTextRange *range = [layout closestTextRangeAtPoint:point];
// For now, assume that a tap inside this text, but outside the text range is a tap on the
// truncation token.
if (![layout textRangeAtPoint:point]) {
if ([self _locked_pointInsideAdditionalTruncationMessage:point withLayout:layout]) {
if (inAdditionalTruncationMessageOut != NULL) {
*inAdditionalTruncationMessageOut = YES;
}
return nil;
}
NSRange visibleRange = layout.visibleRange;
NSRange clampedRange = NSIntersectionRange(visibleRange, NSMakeRange(0, _attributedText.length));
ASTextRange *range = [layout closestTextRangeAtPoint:point];
NSRange effectiveRange = NSMakeRange(0, 0);
for (__strong NSString *attributeName in self.linkAttributeNames) {
id value = [self.attributedText attribute:attributeName atIndex:range.start.offset longestEffectiveRange:&effectiveRange inRange:clampedRange];
@@ -565,6 +618,61 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
return nil;
}
- (BOOL)_locked_pointInsideAdditionalTruncationMessage:(CGPoint)point withLayout:(ASTextLayout *)layout
{
// Check if the range is within the additional truncation range
BOOL inAdditionalTruncationMessage = NO;
CTLineRef truncatedCTLine = layout.truncatedLine.CTLine;
if (truncatedCTLine != NULL && _additionalTruncationMessage != nil) {
CFIndex stringIndexForPosition = CTLineGetStringIndexForPosition(truncatedCTLine, point);
if (stringIndexForPosition != kCFNotFound) {
CFIndex truncatedCTLineGlyphCount = CTLineGetGlyphCount(truncatedCTLine);
CTLineRef truncationTokenLine = CTLineCreateWithAttributedString((CFAttributedStringRef)_truncationAttributedText);
CFIndex truncationTokenLineGlyphCount = truncationTokenLine ? CTLineGetGlyphCount(truncationTokenLine) : 0;
CTLineRef additionalTruncationTokenLine = CTLineCreateWithAttributedString((CFAttributedStringRef)_additionalTruncationMessage);
CFIndex additionalTruncationTokenLineGlyphCount = additionalTruncationTokenLine ? CTLineGetGlyphCount(additionalTruncationTokenLine) : 0;
switch (_textContainer.truncationType) {
case ASTextTruncationTypeStart: {
CFIndex composedTruncationTextLineGlyphCount = truncationTokenLineGlyphCount + additionalTruncationTokenLineGlyphCount;
if (stringIndexForPosition > truncationTokenLineGlyphCount &&
stringIndexForPosition < composedTruncationTextLineGlyphCount) {
inAdditionalTruncationMessage = YES;
}
break;
}
case ASTextTruncationTypeMiddle: {
CFIndex composedTruncationTextLineGlyphCount = truncationTokenLineGlyphCount + additionalTruncationTokenLineGlyphCount;
CFIndex firstTruncatedTokenIndex = (truncatedCTLineGlyphCount - composedTruncationTextLineGlyphCount) / 2.0;
if ((firstTruncatedTokenIndex + truncationTokenLineGlyphCount) < stringIndexForPosition &&
stringIndexForPosition < (firstTruncatedTokenIndex + composedTruncationTextLineGlyphCount)) {
inAdditionalTruncationMessage = YES;
}
break;
}
case ASTextTruncationTypeEnd: {
if (stringIndexForPosition > (truncatedCTLineGlyphCount - additionalTruncationTokenLineGlyphCount)) {
inAdditionalTruncationMessage = YES;
}
break;
}
default:
// For now, assume that a tap inside this text, but outside the text range is a tap on the
// truncation token.
if (![layout textRangeAtPoint:point]) {
inAdditionalTruncationMessage = YES;
}
break;
}
}
}
return inAdditionalTruncationMessage;
}
#pragma mark - UIGestureRecognizerDelegate
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
@@ -737,9 +845,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
NSUInteger lastCharIndex = NSIntegerMax;
BOOL linkCrossesVisibleRange = (lastCharIndex > range.location) && (lastCharIndex < NSMaxRange(range) - 1);
if (inAdditionalTruncationMessage) {
return YES;
} else if (range.length && !linkCrossesVisibleRange && linkAttributeValue != nil && linkAttributeName != nil) {
if (range.length > 0 && !linkCrossesVisibleRange && linkAttributeValue != nil && linkAttributeName != nil) {
return YES;
} else {
return NO;
@@ -775,12 +881,12 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
// See discussion in https://github.com/TextureGroup/Texture/pull/396
ASTextContainer *containerCopy = [_textContainer copy];
containerCopy.size = self.calculatedSize;
ASTextLayout *layout = [ASTextNode2 compatibleLayoutWithContainer:containerCopy text:_attributedText];
ASTextLayout *layout = ASTextNodeCompatibleLayoutWithContainerAndText(containerCopy, _attributedText);
visibleRange = layout.visibleRange;
}
NSRange truncationMessageRange = [self _additionalTruncationMessageRangeWithVisibleRange:visibleRange];
[self _setHighlightRange:truncationMessageRange forAttributeName:ASTextNodeTruncationTokenAttributeName value:nil animated:YES];
} else if (range.length && !linkCrossesVisibleRange && linkAttributeValue != nil && linkAttributeName != nil) {
} else if (range.length > 0 && !linkCrossesVisibleRange && linkAttributeValue != nil && linkAttributeName != nil) {
[self _setHighlightRange:range forAttributeName:linkAttributeName value:linkAttributeValue animated:YES];
}
@@ -1031,8 +1137,19 @@ static NSAttributedString *DefaultTruncationAttributedString()
- (BOOL)isTruncated
{
AS_TEXT_ALERT_UNIMPLEMENTED_FEATURE();
return NO;
return ASLockedSelf([self locked_textLayoutForSize:[self _locked_threadSafeBounds].size].truncatedLine == nil);
}
- (BOOL)shouldTruncateForConstrainedSize:(ASSizeRange)constrainedSize
{
return ASLockedSelf([self locked_textLayoutForSize:constrainedSize.max].truncatedLine != nil);
}
- (ASTextLayout *)locked_textLayoutForSize:(CGSize)size
{
ASTextContainer *container = [_textContainer copy];
container.size = size;
return ASTextNodeCompatibleLayoutWithContainerAndText(container, _attributedText);
}
- (NSUInteger)maximumNumberOfLines
@@ -1061,10 +1178,15 @@ static NSAttributedString *DefaultTruncationAttributedString()
- (void)_invalidateTruncationText
{
ASLockScopeSelf();
_textContainer.truncationToken = nil;
[self _locked_invalidateTruncationText];
[self setNeedsDisplay];
}
- (void)_locked_invalidateTruncationText
{
_textContainer.truncationToken = nil;
}
/**
* @return the additional truncation message range within the as-rendered text.
* Must be called from main thread

View File

@@ -10,6 +10,13 @@
@class ASTextNode;
#define AS_TEXT_ALERT_UNIMPLEMENTED_FEATURE() { \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
NSLog(@"[Texture] Warning: Feature %@ is unimplemented in %@.", NSStringFromSelector(_cmd), NSStringFromClass(self.class)); \
});\
}
/**
* Highlight styles.
*/

View File

@@ -10,6 +10,7 @@
#import <AVFoundation/AVFoundation.h>
#import <AsyncDisplayKit/ASDisplayNode+FrameworkPrivate.h>
#import <AsyncDisplayKit/ASDisplayNode+Subclasses.h>
#import <AsyncDisplayKit/ASDisplayNodeInternal.h>
#import <AsyncDisplayKit/ASVideoNode.h>
#import <AsyncDisplayKit/ASEqualityHelpers.h>
#import <AsyncDisplayKit/ASInternalHelpers.h>
@@ -322,7 +323,7 @@ static NSString * const kRate = @"rate";
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
ASLockScopeSelf();
ASDN::UniqueLock l(__instanceLock__);
if (object == _currentPlayerItem) {
if ([keyPath isEqualToString:kStatus]) {
@@ -330,8 +331,9 @@ static NSString * const kRate = @"rate";
if (self.playerState != ASVideoNodePlayerStatePlaying) {
self.playerState = ASVideoNodePlayerStateReadyToPlay;
if (_shouldBePlaying && ASInterfaceStateIncludesVisible(self.interfaceState)) {
ASUnlockScope(self);
l.unlock();
[self play];
l.lock();
}
}
// If we don't yet have a placeholder image update it now that we should have data available for it
@@ -354,8 +356,10 @@ static NSString * const kRate = @"rate";
[self.delegate videoNodeDidRecoverFromStall:self];
}
ASUnlockScope(self);
l.unlock();
[self play]; // autoresume after buffer catches up
// NOTE: Early return without re-locking.
return;
}
} else if ([keyPath isEqualToString:kplaybackBufferEmpty]) {
if (_shouldBePlaying && [change[NSKeyValueChangeNewKey] boolValue] == YES && ASInterfaceStateIncludesVisible(self.interfaceState)) {
@@ -373,6 +377,8 @@ static NSString * const kRate = @"rate";
}
}
}
// NOTE: Early return above.
}
- (void)tapped

View File

@@ -1,5 +1,5 @@
//
// ASVisibilityProtocols.m
// ASVisibilityProtocols.mm
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.

View File

@@ -63,6 +63,7 @@
#define ASDisplayNodeCAssertPositiveReal(description, num) ASDisplayNodeCAssert(num >= 0 && num <= CGFLOAT_MAX, @"%@ must be a real positive integer: %f.", description, (CGFloat)num)
#define ASDisplayNodeCAssertInfOrPositiveReal(description, num) ASDisplayNodeCAssert(isinf(num) || (num >= 0 && num <= CGFLOAT_MAX), @"%@ must be infinite or a real positive integer: %f.", description, (CGFloat)num)
#define ASDisplayNodeCAssertPermanent(object) ASDisplayNodeCAssert(CFGetRetainCount((__bridge CFTypeRef)(object)) == CFGetRetainCount(kCFNull), @"Expected %s to be a permanent object.", #object)
#define ASDisplayNodeErrorDomain @"ASDisplayNodeErrorDomain"
#define ASDisplayNodeNonFatalErrorCode 1

View File

@@ -1,5 +1,5 @@
//
// ASAssert.m
// ASAssert.mm
// Texture
//
// Copyright (c) Pinterest, Inc. All rights reserved.
@@ -39,19 +39,19 @@ static pthread_key_t ASMainThreadAssertionsDisabledKey() {
}
BOOL ASMainThreadAssertionsAreDisabled() {
return (pthread_getspecific(ASMainThreadAssertionsDisabledKey()) > 0);
return (nullptr != pthread_getspecific(ASMainThreadAssertionsDisabledKey()));
}
void ASPushMainThreadAssertionsDisabled() {
let key = ASMainThreadAssertionsDisabledKey();
let oldVal = pthread_getspecific(key);
pthread_setspecific(key, oldVal + 1);
let oldVal = (intptr_t)pthread_getspecific(key);
pthread_setspecific(key, (void *)(oldVal + 1));
}
void ASPopMainThreadAssertionsDisabled() {
let key = ASMainThreadAssertionsDisabledKey();
let oldVal = pthread_getspecific(key);
pthread_setspecific(key, oldVal - 1);
let oldVal = (intptr_t)pthread_getspecific(key);
pthread_setspecific(key, (void *)(oldVal - 1));
ASDisplayNodeCAssert(oldVal > 0, @"Attempt to pop thread assertion-disabling without corresponding push.");
}

View File

@@ -14,15 +14,15 @@
#define AS_TLS_AVAILABLE 0
#ifndef AS_USE_PHOTOS
# define AS_USE_PHOTOS 0
#define AS_USE_PHOTOS 0
#endif
#ifndef AS_USE_MAPKIT
# define AS_USE_MAPKIT 0
#define AS_USE_MAPKIT 0
#endif
#ifndef AS_USE_ASSETS_LIBRARY
# define AS_USE_ASSETS_LIBRARY 0
#define AS_USE_ASSETS_LIBRARY 0
#endif
#ifndef kCFCoreFoundationVersionNumber_iOS_10_0

View File

@@ -12,6 +12,7 @@
#define AS_EXTERN FOUNDATION_EXTERN
#define unowned __unsafe_unretained
// TODO: Remove these now that we're all-C++.
#if defined(__cplusplus)
# define var auto
# define let const auto
@@ -20,6 +21,18 @@
# define let const __auto_type
#endif
/**
* Hack to support building for iOS with Xcode 9. UIUserInterfaceStyle was previously tvOS-only,
* and it was added to iOS 12. Xcode 9 (iOS 11 SDK) will flat-out refuse to build anything that
* references this enum targeting iOS, even if it's guarded with the right availability macros,
* because it thinks the entire platform isn't compatible with the enum.
*/
#if TARGET_OS_TV || (defined(__IPHONE_12_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_12_0)
#define AS_BUILD_UIUSERINTERFACESTYLE 1
#else
#define AS_BUILD_UIUSERINTERFACESTYLE 0
#endif
#ifdef __GNUC__
# define ASDISPLAYNODE_GNUC(major, minor) \
(__GNUC__ > (major) || (__GNUC__ == (major) && __GNUC_MINOR__ >= (minor)))

View File

@@ -1,5 +1,5 @@
//
// ASDisplayNode+Ancestry.m
// ASDisplayNode+Ancestry.mm
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.

View File

@@ -1,5 +1,5 @@
//
// ASLog.m
// ASLog.mm
// Texture
//
// Copyright (c) Pinterest, Inc. All rights reserved.

View File

@@ -1,5 +1,5 @@
//
// AsyncDisplayKit+Debug.m
// AsyncDisplayKit+Debug.mm
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.

View File

@@ -1,5 +1,5 @@
//
// AsyncDisplayKit+Tips.m
// AsyncDisplayKit+Tips.mm
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
@@ -7,7 +7,7 @@
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import "AsyncDisplayKit+Tips.h"
#import <AsyncDisplayKit/AsyncDisplayKit+Tips.h>
#import <AsyncDisplayKit/ASDisplayNode+Ancestry.h>
@implementation ASDisplayNode (Tips)

View File

@@ -8,11 +8,9 @@
//
#import <AsyncDisplayKit/ASAbstractLayoutController.h>
#import <AsyncDisplayKit/ASAbstractLayoutController+FrameworkPrivate.h>
#import <AsyncDisplayKit/ASAssert.h>
#include <vector>
ASRangeTuningParameters const ASRangeTuningParametersZero = {};
BOOL ASRangeTuningParametersEqualToRangeTuningParameters(ASRangeTuningParameters lhs, ASRangeTuningParameters rhs)
@@ -89,6 +87,52 @@ CGRect CGRectExpandToRangeWithScrollableDirections(CGRect rect, ASRangeTuningPar
@implementation ASAbstractLayoutController
+ (std::vector<std::vector<ASRangeTuningParameters>>)defaultTuningParameters
{
var tuningParameters = std::vector<std::vector<ASRangeTuningParameters>> (ASLayoutRangeModeCount, std::vector<ASRangeTuningParameters> (ASLayoutRangeTypeCount));
tuningParameters[ASLayoutRangeModeFull][ASLayoutRangeTypeDisplay] = {
.leadingBufferScreenfuls = 1.0,
.trailingBufferScreenfuls = 0.5
};
tuningParameters[ASLayoutRangeModeFull][ASLayoutRangeTypePreload] = {
.leadingBufferScreenfuls = 2.5,
.trailingBufferScreenfuls = 1.5
};
tuningParameters[ASLayoutRangeModeMinimum][ASLayoutRangeTypeDisplay] = {
.leadingBufferScreenfuls = 0.25,
.trailingBufferScreenfuls = 0.25
};
tuningParameters[ASLayoutRangeModeMinimum][ASLayoutRangeTypePreload] = {
.leadingBufferScreenfuls = 0.5,
.trailingBufferScreenfuls = 0.25
};
tuningParameters[ASLayoutRangeModeVisibleOnly][ASLayoutRangeTypeDisplay] = {
.leadingBufferScreenfuls = 0,
.trailingBufferScreenfuls = 0
};
tuningParameters[ASLayoutRangeModeVisibleOnly][ASLayoutRangeTypePreload] = {
.leadingBufferScreenfuls = 0,
.trailingBufferScreenfuls = 0
};
// The Low Memory range mode has special handling. Because a zero range still includes the visible area / bounds,
// in order to implement the behavior of releasing all graphics memory (backing stores), ASRangeController must check
// for this range mode and use an empty set for displayIndexPaths rather than querying the ASLayoutController for the indexPaths.
tuningParameters[ASLayoutRangeModeLowMemory][ASLayoutRangeTypeDisplay] = {
.leadingBufferScreenfuls = 0,
.trailingBufferScreenfuls = 0
};
tuningParameters[ASLayoutRangeModeLowMemory][ASLayoutRangeTypePreload] = {
.leadingBufferScreenfuls = 0,
.trailingBufferScreenfuls = 0
};
return tuningParameters;
}
- (instancetype)init
{
if (!(self = [super init])) {
@@ -96,46 +140,7 @@ CGRect CGRectExpandToRangeWithScrollableDirections(CGRect rect, ASRangeTuningPar
}
ASDisplayNodeAssert(self.class != [ASAbstractLayoutController class], @"Should never create instances of abstract class ASAbstractLayoutController.");
_tuningParameters = std::vector<std::vector<ASRangeTuningParameters>> (ASLayoutRangeModeCount, std::vector<ASRangeTuningParameters> (ASLayoutRangeTypeCount));
_tuningParameters[ASLayoutRangeModeFull][ASLayoutRangeTypeDisplay] = {
.leadingBufferScreenfuls = 1.0,
.trailingBufferScreenfuls = 0.5
};
_tuningParameters[ASLayoutRangeModeFull][ASLayoutRangeTypePreload] = {
.leadingBufferScreenfuls = 2.5,
.trailingBufferScreenfuls = 1.5
};
_tuningParameters[ASLayoutRangeModeMinimum][ASLayoutRangeTypeDisplay] = {
.leadingBufferScreenfuls = 0.25,
.trailingBufferScreenfuls = 0.25
};
_tuningParameters[ASLayoutRangeModeMinimum][ASLayoutRangeTypePreload] = {
.leadingBufferScreenfuls = 0.5,
.trailingBufferScreenfuls = 0.25
};
_tuningParameters[ASLayoutRangeModeVisibleOnly][ASLayoutRangeTypeDisplay] = {
.leadingBufferScreenfuls = 0,
.trailingBufferScreenfuls = 0
};
_tuningParameters[ASLayoutRangeModeVisibleOnly][ASLayoutRangeTypePreload] = {
.leadingBufferScreenfuls = 0,
.trailingBufferScreenfuls = 0
};
// The Low Memory range mode has special handling. Because a zero range still includes the visible area / bounds,
// in order to implement the behavior of releasing all graphics memory (backing stores), ASRangeController must check
// for this range mode and use an empty set for displayIndexPaths rather than querying the ASLayoutController for the indexPaths.
_tuningParameters[ASLayoutRangeModeLowMemory][ASLayoutRangeTypeDisplay] = {
.leadingBufferScreenfuls = 0,
.trailingBufferScreenfuls = 0
};
_tuningParameters[ASLayoutRangeModeLowMemory][ASLayoutRangeTypePreload] = {
.leadingBufferScreenfuls = 0,
.trailingBufferScreenfuls = 0
};
_tuningParameters = [[self class] defaultTuningParameters];
return self;
}

View File

@@ -39,11 +39,11 @@ NSString * const kASBasicImageDownloaderContextCompletionBlock = @"kASBasicImage
static NSMutableDictionary *currentRequests = nil;
// Allocate currentRequestsLock on the heap to prevent destruction at app exit (https://github.com/TextureGroup/Texture/issues/136)
static ASDN::StaticMutex& currentRequestsLock = *new ASDN::StaticMutex;
static auto *currentRequestsLock = new ASDN::Mutex;
+ (ASBasicImageDownloaderContext *)contextForURL:(NSURL *)URL
{
ASDN::StaticMutexLocker l(currentRequestsLock);
ASDN::MutexLocker l(*currentRequestsLock);
if (!currentRequests) {
currentRequests = [[NSMutableDictionary alloc] init];
}
@@ -57,7 +57,7 @@ static ASDN::StaticMutex& currentRequestsLock = *new ASDN::StaticMutex;
+ (void)cancelContextWithURL:(NSURL *)URL
{
ASDN::StaticMutexLocker l(currentRequestsLock);
ASDN::MutexLocker l(*currentRequestsLock);
if (currentRequests) {
[currentRequests removeObjectForKey:URL];
}

View File

@@ -1,5 +1,5 @@
//
// ASBatchContext.m
// ASBatchContext.mm
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.

View File

@@ -1,5 +1,5 @@
//
// ASCollectionFlowLayoutDelegate.m
// ASCollectionFlowLayoutDelegate.mm
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.

View File

@@ -1,5 +1,5 @@
//
// ASCollectionLayoutContext.m
// ASCollectionLayoutContext.mm
// Texture
//
// Copyright (c) Pinterest, Inc. All rights reserved.

View File

@@ -1,131 +0,0 @@
//
// ASCollectionViewLayoutController.m
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#ifndef MINIMAL_ASDK
#import <AsyncDisplayKit/ASCollectionViewLayoutController.h>
#import <AsyncDisplayKit/ASAssert.h>
#import <AsyncDisplayKit/ASCollectionView+Undeprecated.h>
#import <AsyncDisplayKit/ASElementMap.h>
#import <AsyncDisplayKit/CoreGraphics+ASConvenience.h>
#import <AsyncDisplayKit/UICollectionViewLayout+ASConvenience.h>
struct ASRangeGeometry {
CGRect rangeBounds;
CGRect updateBounds;
};
typedef struct ASRangeGeometry ASRangeGeometry;
#pragma mark -
#pragma mark ASCollectionViewLayoutController
@interface ASCollectionViewLayoutController ()
{
@package
ASCollectionView * __weak _collectionView;
UICollectionViewLayout * __strong _collectionViewLayout;
}
@end
@implementation ASCollectionViewLayoutController
- (instancetype)initWithCollectionView:(ASCollectionView *)collectionView
{
if (!(self = [super init])) {
return nil;
}
_collectionView = collectionView;
_collectionViewLayout = [collectionView collectionViewLayout];
return self;
}
- (NSHashTable<ASCollectionElement *> *)elementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType map:(ASElementMap *)map
{
ASRangeTuningParameters tuningParameters = [self tuningParametersForRangeMode:rangeMode rangeType:rangeType];
CGRect rangeBounds = [self rangeBoundsWithScrollDirection:scrollDirection rangeTuningParameters:tuningParameters];
return [self elementsWithinRangeBounds:rangeBounds map:map];
}
- (void)allElementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode displaySet:(NSHashTable<ASCollectionElement *> *__autoreleasing _Nullable *)displaySet preloadSet:(NSHashTable<ASCollectionElement *> *__autoreleasing _Nullable *)preloadSet map:(ASElementMap *)map
{
if (displaySet == NULL || preloadSet == NULL) {
return;
}
ASRangeTuningParameters displayParams = [self tuningParametersForRangeMode:rangeMode rangeType:ASLayoutRangeTypeDisplay];
ASRangeTuningParameters preloadParams = [self tuningParametersForRangeMode:rangeMode rangeType:ASLayoutRangeTypePreload];
CGRect displayBounds = [self rangeBoundsWithScrollDirection:scrollDirection rangeTuningParameters:displayParams];
CGRect preloadBounds = [self rangeBoundsWithScrollDirection:scrollDirection rangeTuningParameters:preloadParams];
CGRect unionBounds = CGRectUnion(displayBounds, preloadBounds);
NSArray *layoutAttributes = [_collectionViewLayout layoutAttributesForElementsInRect:unionBounds];
NSInteger count = layoutAttributes.count;
__auto_type display = [[NSHashTable<ASCollectionElement *> alloc] initWithOptions:NSHashTableObjectPointerPersonality capacity:count];
__auto_type preload = [[NSHashTable<ASCollectionElement *> alloc] initWithOptions:NSHashTableObjectPointerPersonality capacity:count];
for (UICollectionViewLayoutAttributes *la in layoutAttributes) {
// Manually filter out elements that don't intersect the range bounds.
// See comment in elementsForItemsWithinRangeBounds:
// This is re-implemented here so that the iteration over layoutAttributes can be done once to check both ranges.
CGRect frame = la.frame;
BOOL intersectsDisplay = CGRectIntersectsRect(displayBounds, frame);
BOOL intersectsPreload = CGRectIntersectsRect(preloadBounds, frame);
if (intersectsDisplay == NO && intersectsPreload == NO && CATransform3DIsIdentity(la.transform3D) == YES) {
// Questionable why the element would be included here, but it doesn't belong.
continue;
}
// Avoid excessive retains and releases, as well as property calls. We know the element is kept alive by map.
__unsafe_unretained ASCollectionElement *e = [map elementForLayoutAttributes:la];
if (e != nil && intersectsDisplay) {
[display addObject:e];
}
if (e != nil && intersectsPreload) {
[preload addObject:e];
}
}
*displaySet = display;
*preloadSet = preload;
return;
}
- (NSHashTable<ASCollectionElement *> *)elementsWithinRangeBounds:(CGRect)rangeBounds map:(ASElementMap *)map
{
NSArray *layoutAttributes = [_collectionViewLayout layoutAttributesForElementsInRect:rangeBounds];
NSHashTable<ASCollectionElement *> *elementSet = [[NSHashTable alloc] initWithOptions:NSHashTableObjectPointerPersonality capacity:layoutAttributes.count];
for (UICollectionViewLayoutAttributes *la in layoutAttributes) {
// Manually filter out elements that don't intersect the range bounds.
// If a layout returns elements outside the requested rect this can be a huge problem.
// For instance in a paging flow, you may only want to preload 3 pages (one center, one on each side)
// but if flow layout includes the 4th page (which it does! as of iOS 9&10), you will preload a 4th
// page as well.
if (CATransform3DIsIdentity(la.transform3D) && CGRectIntersectsRect(la.frame, rangeBounds) == NO) {
continue;
}
[elementSet addObject:[map elementForLayoutAttributes:la]];
}
return elementSet;
}
- (CGRect)rangeBoundsWithScrollDirection:(ASScrollDirection)scrollDirection
rangeTuningParameters:(ASRangeTuningParameters)tuningParameters
{
CGRect rect = _collectionView.bounds;
return CGRectExpandToRangeWithScrollableDirections(rect, tuningParameters, [_collectionView scrollableDirections], scrollDirection);
}
@end
#endif

View File

@@ -1,17 +1,18 @@
//
// ASCollectionViewLayoutController.mm
// AsyncDisplayKit
// 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.
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#ifndef MINIMAL_ASDK
#import <AsyncDisplayKit/ASCollectionViewLayoutController.h>
#import <AsyncDisplayKit/ASAssert.h>
#import <AsyncDisplayKit/ASCollectionView.h>
#import <AsyncDisplayKit/ASCollectionView+Undeprecated.h>
#import <AsyncDisplayKit/ASElementMap.h>
#import <AsyncDisplayKit/CoreGraphics+ASConvenience.h>
#import <AsyncDisplayKit/UICollectionViewLayout+ASConvenience.h>
@@ -21,7 +22,6 @@ struct ASRangeGeometry {
};
typedef struct ASRangeGeometry ASRangeGeometry;
#pragma mark -
#pragma mark ASCollectionViewLayoutController
@@ -46,21 +46,64 @@ typedef struct ASRangeGeometry ASRangeGeometry;
return self;
}
- (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType
- (NSHashTable<ASCollectionElement *> *)elementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType map:(ASElementMap *)map
{
ASRangeTuningParameters tuningParameters = [self tuningParametersForRangeMode:rangeMode rangeType:rangeType];
CGRect rangeBounds = [self rangeBoundsWithScrollDirection:scrollDirection rangeTuningParameters:tuningParameters];
return [self indexPathsForItemsWithinRangeBounds:rangeBounds];
return [self elementsWithinRangeBounds:rangeBounds map:map];
}
- (NSSet *)indexPathsForItemsWithinRangeBounds:(CGRect)rangeBounds
- (void)allElementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode displaySet:(NSHashTable<ASCollectionElement *> *__autoreleasing _Nullable *)displaySet preloadSet:(NSHashTable<ASCollectionElement *> *__autoreleasing _Nullable *)preloadSet map:(ASElementMap *)map
{
if (displaySet == NULL || preloadSet == NULL) {
return;
}
ASRangeTuningParameters displayParams = [self tuningParametersForRangeMode:rangeMode rangeType:ASLayoutRangeTypeDisplay];
ASRangeTuningParameters preloadParams = [self tuningParametersForRangeMode:rangeMode rangeType:ASLayoutRangeTypePreload];
CGRect displayBounds = [self rangeBoundsWithScrollDirection:scrollDirection rangeTuningParameters:displayParams];
CGRect preloadBounds = [self rangeBoundsWithScrollDirection:scrollDirection rangeTuningParameters:preloadParams];
CGRect unionBounds = CGRectUnion(displayBounds, preloadBounds);
NSArray *layoutAttributes = [_collectionViewLayout layoutAttributesForElementsInRect:unionBounds];
NSInteger count = layoutAttributes.count;
__auto_type display = [[NSHashTable<ASCollectionElement *> alloc] initWithOptions:NSHashTableObjectPointerPersonality capacity:count];
__auto_type preload = [[NSHashTable<ASCollectionElement *> alloc] initWithOptions:NSHashTableObjectPointerPersonality capacity:count];
for (UICollectionViewLayoutAttributes *la in layoutAttributes) {
// Manually filter out elements that don't intersect the range bounds.
// See comment in elementsForItemsWithinRangeBounds:
// This is re-implemented here so that the iteration over layoutAttributes can be done once to check both ranges.
CGRect frame = la.frame;
BOOL intersectsDisplay = CGRectIntersectsRect(displayBounds, frame);
BOOL intersectsPreload = CGRectIntersectsRect(preloadBounds, frame);
if (intersectsDisplay == NO && intersectsPreload == NO && CATransform3DIsIdentity(la.transform3D) == YES) {
// Questionable why the element would be included here, but it doesn't belong.
continue;
}
// Avoid excessive retains and releases, as well as property calls. We know the element is kept alive by map.
__unsafe_unretained ASCollectionElement *e = [map elementForLayoutAttributes:la];
if (e != nil && intersectsDisplay) {
[display addObject:e];
}
if (e != nil && intersectsPreload) {
[preload addObject:e];
}
}
*displaySet = display;
*preloadSet = preload;
return;
}
- (NSHashTable<ASCollectionElement *> *)elementsWithinRangeBounds:(CGRect)rangeBounds map:(ASElementMap *)map
{
NSArray *layoutAttributes = [_collectionViewLayout layoutAttributesForElementsInRect:rangeBounds];
NSMutableSet *indexPathSet = [NSMutableSet setWithCapacity:layoutAttributes.count];
NSHashTable<ASCollectionElement *> *elementSet = [[NSHashTable alloc] initWithOptions:NSHashTableObjectPointerPersonality capacity:layoutAttributes.count];
for (UICollectionViewLayoutAttributes *la in layoutAttributes) {
//ASDisplayNodeAssert(![indexPathSet containsObject:la.indexPath], @"Shouldn't already contain indexPath");
// Manually filter out elements that don't intersect the range bounds.
// If a layout returns elements outside the requested rect this can be a huge problem.
// For instance in a paging flow, you may only want to preload 3 pages (one center, one on each side)
@@ -69,10 +112,10 @@ typedef struct ASRangeGeometry ASRangeGeometry;
if (CATransform3DIsIdentity(la.transform3D) && CGRectIntersectsRect(la.frame, rangeBounds) == NO) {
continue;
}
[indexPathSet addObject:la.indexPath];
[elementSet addObject:[map elementForLayoutAttributes:la]];
}
return indexPathSet;
return elementSet;
}
- (CGRect)rangeBoundsWithScrollDirection:(ASScrollDirection)scrollDirection
@@ -84,5 +127,4 @@ typedef struct ASRangeGeometry ASRangeGeometry;
}
@end
#endif

View File

@@ -1,5 +1,5 @@
//
// ASCollectionViewLayoutInspector.m
// ASCollectionViewLayoutInspector.mm
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.

View File

@@ -1,5 +1,5 @@
//
// ASDelegateProxy.m
// ASDelegateProxy.mm
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.

View File

@@ -1,5 +1,5 @@
//
// ASElementMap.m
// ASElementMap.mm
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
@@ -9,7 +9,7 @@
#ifndef MINIMAL_ASDK
#import "ASElementMap.h"
#import <AsyncDisplayKit/ASElementMap.h>
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASCollectionElement.h>
#import <AsyncDisplayKit/ASTwoDimensionalArrayUtils.h>

View File

@@ -1,12 +1,12 @@
//
// ASGraphicsContext.m
// ASGraphicsContext.mm
// Texture
//
// Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import "ASGraphicsContext.h"
#import <AsyncDisplayKit/ASGraphicsContext.h>
#import <AsyncDisplayKit/ASCGImageBuffer.h>
#import <AsyncDisplayKit/ASAssert.h>
#import <AsyncDisplayKit/ASConfigurationInternal.h>

View File

@@ -1,5 +1,5 @@
//
// ASHashing.m
// ASHashing.mm
// Texture
//
// Copyright (c) Pinterest, Inc. All rights reserved.

View File

@@ -1,5 +1,5 @@
//
// ASImageContainerProtocolCategories.m
// ASImageContainerProtocolCategories.mm
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.

View File

@@ -40,29 +40,32 @@
- (void)performBlockOnMainThread:(dispatch_block_t)block
{
ASDN::MutexLocker l(_serialQueueLock);
ASDN::UniqueLock l(_serialQueueLock);
[_blocks addObject:block];
{
ASDN::MutexUnlocker u(_serialQueueLock);
l.unlock();
[self runBlocks];
l.lock();
}
}
- (void)runBlocks
{
dispatch_block_t mainThread = ^{
ASDN::UniqueLock l(self->_serialQueueLock);
do {
ASDN::MutexLocker l(_serialQueueLock);
dispatch_block_t block;
if (_blocks.count > 0) {
if (self->_blocks.count > 0) {
block = _blocks[0];
[_blocks removeObjectAtIndex:0];
[self->_blocks removeObjectAtIndex:0];
} else {
break;
}
{
ASDN::MutexUnlocker u(_serialQueueLock);
l.unlock();
block();
l.lock();
}
} while (true);
};

View File

@@ -1,5 +1,5 @@
//
// ASMutableAttributedStringBuilder.m
// ASMutableAttributedStringBuilder.mm
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.

View File

@@ -1,5 +1,5 @@
//
// ASObjectDescriptionHelpers.m
// ASObjectDescriptionHelpers.mm
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.

View File

@@ -18,6 +18,7 @@
NS_ASSUME_NONNULL_BEGIN
@class PINRemoteImageManager;
@protocol PINRemoteImageCaching;
@interface ASPINRemoteImageDownloader : NSObject <ASImageCacheProtocol, ASImageDownloaderProtocol>
@@ -30,7 +31,6 @@ NS_ASSUME_NONNULL_BEGIN
*/
+ (ASPINRemoteImageDownloader *)sharedDownloader NS_RETURNS_RETAINED;
/**
* Sets the default NSURLSessionConfiguration that will be used by @c ASNetworkImageNodes and @c ASMultiplexImageNodes
* while loading images off the network. This must be specified early in the application lifecycle before
@@ -41,15 +41,27 @@ NS_ASSUME_NONNULL_BEGIN
*/
+ (void)setSharedImageManagerWithConfiguration:(nullable NSURLSessionConfiguration *)configuration;
/**
* Sets the default NSURLSessionConfiguration that will be used by @c ASNetworkImageNodes and @c ASMultiplexImageNodes
* while loading images off the network. This must be specified early in the application lifecycle before
* `sharedDownloader` is accessed.
*
* @param configuration The session configuration that will be used by `sharedDownloader`
* @param imageCache The cache to be used by PINRemoteImage - nil will set up a default cache: PINCache
* if it is available, PINRemoteImageBasicCache (NSCache) if not.
*
*/
+ (void)setSharedImageManagerWithConfiguration:(nullable NSURLSessionConfiguration *)configuration
imageCache:(nullable id<PINRemoteImageCaching>)imageCache;
/**
* Sets a custom preconfigured PINRemoteImageManager that will be used by @c ASNetworkImageNodes and @c ASMultiplexImageNodes
* while loading images off the network. This must be specified early in the application lifecycle before
* `sharedDownloader` is accessed. If nil is passed in as the PINRemoteImageManager, it will create
* a default image manager with a nil session configuration.
* `sharedDownloader` is accessed.
*
* @param preconfiguredPINRemoteImageManager The preconfigured remote image manager that will be used by `sharedDownloader`
*/
+ (void)setSharedPreconfiguredRemoteImageManager:(nullable PINRemoteImageManager *)preconfiguredPINRemoteImageManager;
+ (void)setSharedPreconfiguredRemoteImageManager:(PINRemoteImageManager *)preconfiguredPINRemoteImageManager;
/**
* The shared instance of a @c PINRemoteImageManager used by all @c ASPINRemoteImageDownloaders

View File

@@ -1,5 +1,5 @@
//
// ASPINRemoteImageDownloader.m
// ASPINRemoteImageDownloader.mm
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
@@ -77,7 +77,7 @@
@implementation ASPINRemoteImageManager
//Share image cache with sharedImageManager image cache.
- (id <PINRemoteImageCaching>)defaultImageCache
+ (id <PINRemoteImageCaching>)defaultImageCache
{
static dispatch_once_t onceToken;
static id <PINRemoteImageCaching> cache = nil;
@@ -108,7 +108,6 @@ static PINRemoteImageManager *sharedPINRemoteImageManager = nil;
+ (ASPINRemoteImageDownloader *)sharedDownloader NS_RETURNS_RETAINED
{
static dispatch_once_t onceToken = 0;
dispatch_once(&onceToken, ^{
sharedDownloader = [[ASPINRemoteImageDownloader alloc] init];
@@ -118,56 +117,68 @@ static PINRemoteImageManager *sharedPINRemoteImageManager = nil;
+ (void)setSharedImageManagerWithConfiguration:(nullable NSURLSessionConfiguration *)configuration
{
NSAssert(sharedDownloader == nil, @"Singleton has been created and session can no longer be configured.");
NSAssert1(sharedPINRemoteImageManager == nil, @"An instance of %@ has been set. Either configuration or preconfigured image manager can be set at a time and only once.", [[sharedPINRemoteImageManager class] description]);
__unused PINRemoteImageManager *sharedManager = [self sharedPINRemoteImageManagerWithConfiguration:configuration preconfiguredPINRemoteImageManager:nil];
PINRemoteImageManager *sharedManager = [self PINRemoteImageManagerWithConfiguration:configuration imageCache:nil];
[self setSharedPreconfiguredRemoteImageManager:sharedManager];
}
+ (void)setSharedPreconfiguredRemoteImageManager:(nullable PINRemoteImageManager *)preconfiguredPINRemoteImageManager
+ (void)setSharedImageManagerWithConfiguration:(nullable NSURLSessionConfiguration *)configuration
imageCache:(nullable id<PINRemoteImageCaching>)imageCache
{
NSAssert(sharedDownloader == nil, @"Singleton has been created and session can no longer be configured.");
NSAssert1(sharedPINRemoteImageManager == nil, @"An instance of %@ has been set. Either configuration or preconfigured image manager can be set at a time and only once.", [[sharedPINRemoteImageManager class] description]);
__unused PINRemoteImageManager *sharedManager = [self sharedPINRemoteImageManagerWithConfiguration:nil preconfiguredPINRemoteImageManager:preconfiguredPINRemoteImageManager];
PINRemoteImageManager *sharedManager = [self PINRemoteImageManagerWithConfiguration:configuration imageCache:imageCache];
[self setSharedPreconfiguredRemoteImageManager:sharedManager];
}
+ (PINRemoteImageManager *)sharedPINRemoteImageManagerWithConfiguration:(NSURLSessionConfiguration *)configuration preconfiguredPINRemoteImageManager:(PINRemoteImageManager *)preconfiguredPINRemoteImageManager
{
NSAssert(!(configuration != nil && preconfiguredPINRemoteImageManager != nil), @"Either configuration or preconfigured image manager can be set at a time.");
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
static dispatch_once_t shared_init_predicate;
if (preconfiguredPINRemoteImageManager) {
sharedPINRemoteImageManager = preconfiguredPINRemoteImageManager;
} else {
#if PIN_ANIMATED_AVAILABLE
// Check that Carthage users have linked both PINRemoteImage & PINCache by testing for one file each
if (!(NSClassFromString(@"PINRemoteImageManager"))) {
NSException *e = [NSException
exceptionWithName:@"FrameworkSetupException"
reason:@"Missing the path to the PINRemoteImage framework."
userInfo:nil];
@throw e;
}
if (!(NSClassFromString(@"PINCache"))) {
NSException *e = [NSException
exceptionWithName:@"FrameworkSetupException"
reason:@"Missing the path to the PINCache framework."
userInfo:nil];
@throw e;
}
sharedPINRemoteImageManager = [[ASPINRemoteImageManager alloc] initWithSessionConfiguration:configuration
alternativeRepresentationProvider:[self sharedDownloader]];
#else
sharedPINRemoteImageManager = [[ASPINRemoteImageManager alloc] initWithSessionConfiguration:configuration];
#endif
}
+ (void)setSharedPreconfiguredRemoteImageManager:(PINRemoteImageManager *)preconfiguredPINRemoteImageManager
{
NSAssert(preconfiguredPINRemoteImageManager != nil, @"setSharedPreconfiguredRemoteImageManager requires a non-nil parameter");
NSAssert(sharedDownloader == nil, @"Singleton has been created and session can no longer be configured.");
NSAssert1(sharedPINRemoteImageManager == nil, @"An instance of %@ has been set. Either configuration or preconfigured image manager can be set at a time and only once.", [[sharedPINRemoteImageManager class] description]);
dispatch_once(&shared_init_predicate, ^{
sharedPINRemoteImageManager = preconfiguredPINRemoteImageManager;
});
return sharedPINRemoteImageManager;
}
+ (PINRemoteImageManager *)PINRemoteImageManagerWithConfiguration:(nullable NSURLSessionConfiguration *)configuration imageCache:(nullable id<PINRemoteImageCaching>)imageCache
{
PINRemoteImageManager *manager = nil;
#if DEBUG
// Check that Carthage users have linked both PINRemoteImage & PINCache by testing for one file each
if (!(NSClassFromString(@"PINRemoteImageManager"))) {
NSException *e = [NSException
exceptionWithName:@"FrameworkSetupException"
reason:@"Missing the path to the PINRemoteImage framework."
userInfo:nil];
@throw e;
}
if (!(NSClassFromString(@"PINCache"))) {
NSException *e = [NSException
exceptionWithName:@"FrameworkSetupException"
reason:@"Missing the path to the PINCache framework."
userInfo:nil];
@throw e;
}
#endif
#if PIN_ANIMATED_AVAILABLE
manager = [[ASPINRemoteImageManager alloc] initWithSessionConfiguration:configuration
alternativeRepresentationProvider:[self sharedDownloader]
imageCache:imageCache];
#else
manager = [[ASPINRemoteImageManager alloc] initWithSessionConfiguration:configuration
alternativeRepresentationProvider:nil
imageCache:imageCache];
#endif
return manager;
}
- (PINRemoteImageManager *)sharedPINRemoteImageManager
{
return [ASPINRemoteImageDownloader sharedPINRemoteImageManagerWithConfiguration:nil preconfiguredPINRemoteImageManager:nil];
dispatch_once(&shared_init_predicate, ^{
sharedPINRemoteImageManager = [ASPINRemoteImageDownloader PINRemoteImageManagerWithConfiguration:nil imageCache:nil];
});
return sharedPINRemoteImageManager;
}
- (BOOL)sharedImageManagerSupportsMemoryRemoval

View File

@@ -1,5 +1,5 @@
//
// ASPageTable.m
// ASPageTable.mm
// Texture
//
// Copyright (c) Pinterest, Inc. All rights reserved.

View File

@@ -1,5 +1,5 @@
//
// ASPhotosFrameworkImageRequest.m
// ASPhotosFrameworkImageRequest.mm
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.

View File

@@ -11,11 +11,6 @@
#import <pthread/pthread.h>
#import <os/lock.h>
// Don't import C-only header if we're in a C++ file
#ifndef __cplusplus
#import <stdatomic.h>
#endif
// Note: We don't use ATOMIC_VAR_INIT here because C++ compilers don't like it,
// and it literally does absolutely nothing.
#define AS_RECURSIVE_UNFAIR_LOCK_INIT ((ASRecursiveUnfairLock){ OS_UNFAIR_LOCK_INIT, NULL, 0})
@@ -24,7 +19,7 @@ NS_ASSUME_NONNULL_BEGIN
OS_UNFAIR_LOCK_AVAILABILITY
typedef struct {
os_unfair_lock _lock;
os_unfair_lock _lock OS_UNFAIR_LOCK_AVAILABILITY;
_Atomic(pthread_t) _thread; // Write-protected by lock
int _count; // Protected by lock
} ASRecursiveUnfairLock;

View File

@@ -1,5 +1,5 @@
//
// ASRecursiveUnfairLock.m
// ASRecursiveUnfairLock.mm
// Texture
//
// Copyright (c) Pinterest, Inc. All rights reserved.
@@ -8,6 +8,8 @@
#import "ASRecursiveUnfairLock.h"
#import <stdatomic.h>
/**
* For our atomic _thread, we use acquire/release memory order so that we can have
* the minimum possible constraint on the hardware. The default, `memory_order_seq_cst`

View File

@@ -1,5 +1,5 @@
//
// ASScrollDirection.m
// ASScrollDirection.mm
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.

View File

@@ -1,5 +1,5 @@
//
// ASTableLayoutController.m
// ASTableLayoutController.mm
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.

View File

@@ -9,11 +9,8 @@
#import <Foundation/Foundation.h>
#import <assert.h>
#import <os/lock.h>
#import <pthread.h>
#import <stdbool.h>
#import <stdlib.h>
#import <AsyncDisplayKit/ASAssert.h>
#import <AsyncDisplayKit/ASAvailability.h>
@@ -82,7 +79,7 @@ ASDISPLAYNODE_INLINE void _ASLockScopeUnownedCleanup(id<NSLocking> __unsafe_unre
#define ASSynthesizeLockingMethodsWithMutex(mutex) \
- (void)lock { mutex.lock(); } \
- (void)unlock { mutex.unlock(); } \
- (BOOL)tryLock { return mutex.tryLock(); }
- (BOOL)tryLock { return (BOOL)mutex.try_lock(); }
#define ASSynthesizeLockingMethodsWithObject(object) \
- (void)lock { [object lock]; } \
@@ -95,259 +92,118 @@ ASDISPLAYNODE_INLINE void _ASUnlockScopeCleanup(id<NSLocking> __strong *lockPtr)
#ifdef __cplusplus
#define TIME_LOCKER 0
/**
* Enable this flag to collect information on the owning thread and ownership level of a mutex.
* These properties are useful to determine if a mutex has been acquired and in case of a recursive mutex, how many times that happened.
*
* This flag also enable locking assertions (e.g ASAssertUnlocked(node)).
* The assertions are useful when you want to indicate and enforce the locking policy/expectation of methods.
* To determine when and which methods acquired a (recursive) mutex (to debug deadlocks, for example),
* put breakpoints at some assertions. When the breakpoints hit, walk through stack trace frames
* and check ownership count of the mutex.
*/
#if ASDISPLAYNODE_ASSERTIONS_ENABLED
#define CHECK_LOCKING_SAFETY 1
#else
#define CHECK_LOCKING_SAFETY 0
#endif
#if TIME_LOCKER
#import <QuartzCore/QuartzCore.h>
#endif
#include <memory>
#include <mutex>
#include <new>
#include <thread>
// This MUST always execute, even when assertions are disabled. Otherwise all lock operations become no-ops!
// (To be explicit, do not turn this into an NSAssert, assert(), or any other kind of statement where the
// evaluation of x_ can be compiled out.)
#define AS_POSIX_ASSERT_NOERR(x_) ({ \
__unused int res = (x_); \
ASDisplayNodeCAssert(res == 0, @"Expected %s to return 0, got %d instead. Error: %s", #x_, res, strerror(res)); \
})
/**
* Assert if the current thread owns a mutex.
* This assertion is useful when you want to indicate and enforce the locking policy/expectation of methods.
* To determine when and which methods acquired a (recursive) mutex (to debug deadlocks, for example),
* put breakpoints at some of these assertions. When the breakpoints hit, walk through stack trace frames
* and check ownership count of the mutex.
*/
#if CHECK_LOCKING_SAFETY
#define ASAssertUnlocked(lock) ASDisplayNodeAssertFalse(lock.locked())
#define ASAssertLocked(lock) ASDisplayNodeAssert(lock.locked(), @"Lock must be held by current thread")
#else
#define ASAssertUnlocked(lock)
#define ASAssertLocked(lock)
#endif
// These macros are here for legacy reasons. We may get rid of them later.
#define ASAssertLocked(m) m.AssertHeld()
#define ASAssertUnlocked(m) m.AssertNotHeld()
namespace ASDN {
template<class T>
class Locker
{
T &_l;
#if TIME_LOCKER
CFTimeInterval _ti;
const char *_name;
#endif
public:
#if !TIME_LOCKER
Locker (T &l) noexcept : _l (l) {
_l.lock ();
}
~Locker () {
_l.unlock ();
}
// non-copyable.
Locker(const Locker<T>&) = delete;
Locker &operator=(const Locker<T>&) = delete;
#else
Locker (T &l, const char *name = NULL) noexcept : _l (l), _name(name) {
_ti = CACurrentMediaTime();
_l.lock ();
}
~Locker () {
_l.unlock ();
if (_name) {
printf(_name, NULL);
printf(" dt:%f\n", CACurrentMediaTime() - _ti);
}
}
#endif
};
template<class T>
class SharedLocker
{
std::shared_ptr<T> _l;
#if TIME_LOCKER
CFTimeInterval _ti;
const char *_name;
#endif
public:
#if !TIME_LOCKER
SharedLocker (std::shared_ptr<T> const& l) noexcept : _l (l) {
ASDisplayNodeCAssertTrue(_l != nullptr);
_l->lock ();
}
~SharedLocker () {
_l->unlock ();
}
// non-copyable.
SharedLocker(const SharedLocker<T>&) = delete;
SharedLocker &operator=(const SharedLocker<T>&) = delete;
#else
SharedLocker (std::shared_ptr<T> const& l, const char *name = NULL) noexcept : _l (l), _name(name) {
_ti = CACurrentMediaTime();
_l->lock ();
}
~SharedLocker () {
_l->unlock ();
if (_name) {
printf(_name, NULL);
printf(" dt:%f\n", CACurrentMediaTime() - _ti);
}
}
#endif
};
template<class T>
class Unlocker
{
T &_l;
public:
Unlocker (T &l) noexcept : _l (l) { _l.unlock (); }
~Unlocker () {_l.lock ();}
Unlocker(Unlocker<T>&) = delete;
Unlocker &operator=(Unlocker<T>&) = delete;
};
// Set once in Mutex constructor. Linker fails if this is a member variable. ??
static BOOL gMutex_unfair;
static bool gMutex_unfair;
// Silence unguarded availability warnings in here, because
// perf is critical and we will check availability once
// and not again.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunguarded-availability"
struct Mutex
class Mutex
{
/// Constructs a non-recursive mutex (the default).
public:
/// Constructs a plain mutex (the default).
Mutex () : Mutex (false) {}
~Mutex () {
if (gMutex_unfair) {
// nop
} else {
AS_POSIX_ASSERT_NOERR(pthread_mutex_destroy (&_m));
// Manually destroy since unions can't do it.
switch (_type) {
case Plain:
_plain.~mutex();
break;
case Recursive:
_recursive.~recursive_mutex();
break;
case Unfair:
// nop
break;
case RecursiveUnfair:
// nop
break;
}
#if CHECK_LOCKING_SAFETY
_owner = 0;
_count = 0;
#endif
}
Mutex (const Mutex&) = delete;
Mutex &operator=(const Mutex&) = delete;
bool tryLock() {
if (gMutex_unfair) {
if (_recursive) {
return ASRecursiveUnfairLockTryLock(&_runfair);
} else {
return os_unfair_lock_trylock(&_unfair);
}
} else {
let result = pthread_mutex_trylock(&_m);
if (result == 0) {
return true;
} else if (result == EBUSY) {
return false;
} else {
ASDisplayNodeCFailAssert(@"Locking error: %s", strerror(result));
return true; // if we return false we may enter an infinite loop.
}
bool try_lock() {
bool success = false;
switch (_type) {
case Plain:
success = _plain.try_lock();
break;
case Recursive:
success = _recursive.try_lock();
break;
case Unfair:
success = os_unfair_lock_trylock(&_unfair);
break;
case RecursiveUnfair:
success = ASRecursiveUnfairLockTryLock(&_runfair);
break;
}
if (success) {
DidLock();
}
return success;
}
void lock() {
switch (_type) {
case Plain:
_plain.lock();
break;
case Recursive:
_recursive.lock();
break;
case Unfair:
os_unfair_lock_lock(&_unfair);
break;
case RecursiveUnfair:
ASRecursiveUnfairLockLock(&_runfair);
break;
}
DidLock();
}
void unlock() {
WillUnlock();
switch (_type) {
case Plain:
_plain.unlock();
break;
case Recursive:
_recursive.unlock();
break;
case Unfair:
os_unfair_lock_unlock(&_unfair);
break;
case RecursiveUnfair:
ASRecursiveUnfairLockUnlock(&_runfair);
break;
}
}
void AssertHeld() {
ASDisplayNodeCAssert(_owner == std::this_thread::get_id(), @"Thread should hold lock");
}
void AssertNotHeld() {
ASDisplayNodeCAssert(_owner != std::this_thread::get_id(), @"Thread should not hold lock");
}
void lock() {
if (gMutex_unfair) {
if (_recursive) {
ASRecursiveUnfairLockLock(&_runfair);
} else {
os_unfair_lock_lock(&_unfair);
}
} else {
AS_POSIX_ASSERT_NOERR(pthread_mutex_lock(&_m));
}
#if CHECK_LOCKING_SAFETY
mach_port_t thread_id = pthread_mach_thread_np(pthread_self());
if (thread_id != _owner) {
// New owner. Since this mutex can't be acquired by another thread if there is an existing owner, _owner and _count must be 0.
ASDisplayNodeCAssertTrue(0 == _owner);
ASDisplayNodeCAssertTrue(0 == _count);
_owner = thread_id;
} else {
// Existing owner tries to reacquire this (recursive) mutex. _count must already be positive.
ASDisplayNodeCAssertTrue(_count > 0);
}
++_count;
#endif
}
void unlock () {
#if CHECK_LOCKING_SAFETY
mach_port_t thread_id = pthread_mach_thread_np(pthread_self());
// Unlocking a mutex on an unowning thread causes undefined behaviour. Assert and fail early.
ASDisplayNodeCAssertTrue(thread_id == _owner);
// Current thread owns this mutex. _count must be positive.
ASDisplayNodeCAssertTrue(_count > 0);
--_count;
if (0 == _count) {
// Current thread is no longer the owner.
_owner = 0;
}
#endif
if (gMutex_unfair) {
if (_recursive) {
ASRecursiveUnfairLockUnlock(&_runfair);
} else {
os_unfair_lock_unlock(&_unfair);
}
} else {
AS_POSIX_ASSERT_NOERR(pthread_mutex_unlock(&_m));
}
}
pthread_mutex_t *mutex () { return &_m; }
#if CHECK_LOCKING_SAFETY
bool locked() {
return _count > 0 && pthread_mach_thread_np(pthread_self()) == _owner;
}
#endif
protected:
explicit Mutex (bool recursive) {
// Check if we can use unfair lock and store in static var.
@@ -358,44 +214,60 @@ namespace ASDN {
}
});
_recursive = recursive;
if (gMutex_unfair) {
if (recursive) {
if (recursive) {
if (gMutex_unfair) {
_type = RecursiveUnfair;
_runfair = AS_RECURSIVE_UNFAIR_LOCK_INIT;
} else {
_unfair = OS_UNFAIR_LOCK_INIT;
_type = Recursive;
new (&_recursive) std::recursive_mutex();
}
} else {
if (!recursive) {
AS_POSIX_ASSERT_NOERR(pthread_mutex_init (&_m, NULL));
if (gMutex_unfair) {
_type = Unfair;
_unfair = OS_UNFAIR_LOCK_INIT;
} else {
// Fall back to recursive mutex.
static pthread_mutexattr_t attr;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
AS_POSIX_ASSERT_NOERR(pthread_mutexattr_init (&attr));
AS_POSIX_ASSERT_NOERR(pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE));
});
AS_POSIX_ASSERT_NOERR(pthread_mutex_init(&_m, &attr));
_type = Plain;
new (&_plain) std::mutex();
}
}
#if CHECK_LOCKING_SAFETY
_owner = 0;
_count = 0;
#endif
}
private:
BOOL _recursive;
enum Type {
Plain,
Recursive,
Unfair,
RecursiveUnfair
};
void WillUnlock() {
#if ASDISPLAYNODE_ASSERTIONS_ENABLED
if (--_count == 0) {
_owner = std::thread::id();
}
#endif
}
void DidLock() {
#if ASDISPLAYNODE_ASSERTIONS_ENABLED
if (++_count == 1) {
// New owner.
_owner = std::this_thread::get_id();
}
#endif
}
Type _type;
union {
os_unfair_lock _unfair;
ASRecursiveUnfairLock _runfair;
pthread_mutex_t _m;
std::mutex _plain;
std::recursive_mutex _recursive;
};
#if CHECK_LOCKING_SAFETY
mach_port_t _owner;
uint32_t _count;
#if ASDISPLAYNODE_ASSERTIONS_ENABLED
std::thread::id _owner = std::thread::id();
int _count = 0;
#endif
};
#pragma clang diagnostic pop // ignored "-Wunguarded-availability"
@@ -409,46 +281,14 @@ namespace ASDN {
http://www.zaval.org/resources/library/butenhof1.html
http://www.fieryrobot.com/blog/2008/10/14/recursive-locks-will-kill-you/
*/
struct RecursiveMutex : Mutex
class RecursiveMutex : public Mutex
{
public:
RecursiveMutex () : Mutex (true) {}
};
typedef Locker<Mutex> MutexLocker;
typedef SharedLocker<Mutex> MutexSharedLocker;
typedef Unlocker<Mutex> MutexUnlocker;
/**
If you are creating a static mutex, use StaticMutex. This avoids expensive constructor overhead at startup (or worse, ordering
issues between different static objects). It also avoids running a destructor on app exit time (needless expense).
Note that you can, but should not, use StaticMutex for non-static objects. It will leak its mutex on destruction,
so avoid that!
*/
struct StaticMutex
{
StaticMutex () : _m (PTHREAD_MUTEX_INITIALIZER) {}
// non-copyable.
StaticMutex(const StaticMutex&) = delete;
StaticMutex &operator=(const StaticMutex&) = delete;
void lock () {
AS_POSIX_ASSERT_NOERR(pthread_mutex_lock (this->mutex()));
}
void unlock () {
AS_POSIX_ASSERT_NOERR(pthread_mutex_unlock (this->mutex()));
}
pthread_mutex_t *mutex () { return &_m; }
private:
pthread_mutex_t _m;
};
typedef Locker<StaticMutex> StaticMutexLocker;
typedef Unlocker<StaticMutex> StaticMutexUnlocker;
typedef std::lock_guard<Mutex> MutexLocker;
typedef std::unique_lock<Mutex> UniqueLock;
} // namespace ASDN

View File

@@ -1,5 +1,5 @@
//
// ASTraceEvent.m
// ASTraceEvent.mm
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.

View File

@@ -18,34 +18,6 @@
NS_ASSUME_NONNULL_BEGIN
#pragma mark - ASPrimitiveContentSizeCategory
/**
* ASPrimitiveContentSizeCategory is a UIContentSizeCategory that can be used inside a struct.
*
* We need an unretained pointer because ARC can't manage struct memory.
*
* WARNING: DO NOT cast UIContentSizeCategory values to ASPrimitiveContentSizeCategory directly.
* Use ASPrimitiveContentSizeCategoryMake(UIContentSizeCategory) instead.
* This is because we make some assumptions about the lifetime of the object it points to.
* Also note that cast from ASPrimitiveContentSizeCategory to UIContentSizeCategory is always safe.
*/
typedef __unsafe_unretained UIContentSizeCategory ASPrimitiveContentSizeCategory;
/**
* Safely casts from UIContentSizeCategory to ASPrimitiveContentSizeCategory.
*
* The UIKit documentation doesn't specify if we can receive a copy of the UIContentSizeCategory constant. While getting
* copies is fine with ARC, usage of unretained pointers requires us to ensure the lifetime of the object it points to.
* Manual retain&release of the UIContentSizeCategory object is not an option because it would require us to do that
* everywhere ASPrimitiveTraitCollection is used. This is error-prone and can lead to crashes and memory leaks. So, we
* explicitly limit possible values of ASPrimitiveContentSizeCategory to the predetermined set of global constants with
* known lifetime.
*
* @return a pointer to one of the UIContentSizeCategory constants.
*/
AS_EXTERN ASPrimitiveContentSizeCategory ASPrimitiveContentSizeCategoryMake(UIContentSizeCategory sizeCategory);
#pragma mark - ASPrimitiveTraitCollection
/**
@@ -56,24 +28,28 @@ AS_EXTERN ASPrimitiveContentSizeCategory ASPrimitiveContentSizeCategoryMake(UICo
* If you use ASPrimitiveTraitCollection, please do make sure to initialize it with ASPrimitiveTraitCollectionMakeDefault()
* or ASPrimitiveTraitCollectionFromUITraitCollection(UITraitCollection*).
*/
typedef struct ASPrimitiveTraitCollection {
#pragma clang diagnostic push
#pragma clang diagnostic warning "-Wpadded"
typedef struct {
UIUserInterfaceSizeClass horizontalSizeClass;
UIUserInterfaceSizeClass verticalSizeClass;
CGFloat displayScale;
UIDisplayGamut displayGamut;
UIDisplayGamut displayGamut API_AVAILABLE(ios(10.0));
UIUserInterfaceIdiom userInterfaceIdiom;
UIForceTouchCapability forceTouchCapability;
UITraitEnvironmentLayoutDirection layoutDirection;
#if TARGET_OS_TV
UIUserInterfaceStyle userInterfaceStyle;
UITraitEnvironmentLayoutDirection layoutDirection API_AVAILABLE(ios(10.0));
#if AS_BUILD_UIUSERINTERFACESTYLE
UIUserInterfaceStyle userInterfaceStyle API_AVAILABLE(tvos(10.0), ios(12.0));
#endif
ASPrimitiveContentSizeCategory preferredContentSizeCategory;
// NOTE: This must be a constant. We will assert.
unowned UIContentSizeCategory preferredContentSizeCategory API_AVAILABLE(ios(10.0));
CGSize containerSize;
} ASPrimitiveTraitCollection;
#pragma clang diagnostic pop
/**
* Creates ASPrimitiveTraitCollection with default values.
@@ -164,52 +140,21 @@ AS_EXTERN void ASTraitCollectionPropagateDown(id<ASLayoutElement> element, ASPri
AS_SUBCLASSING_RESTRICTED
@interface ASTraitCollection : NSObject
@property (nonatomic, readonly) UIUserInterfaceSizeClass horizontalSizeClass;
@property (nonatomic, readonly) UIUserInterfaceSizeClass verticalSizeClass;
@property (readonly) UIUserInterfaceSizeClass horizontalSizeClass;
@property (readonly) UIUserInterfaceSizeClass verticalSizeClass;
@property (nonatomic, readonly) CGFloat displayScale;
@property (nonatomic, readonly) UIDisplayGamut displayGamut;
@property (readonly) CGFloat displayScale;
@property (readonly) UIDisplayGamut displayGamut API_AVAILABLE(ios(10.0));
@property (nonatomic, readonly) UIUserInterfaceIdiom userInterfaceIdiom;
@property (nonatomic, readonly) UIForceTouchCapability forceTouchCapability;
@property (nonatomic, readonly) UITraitEnvironmentLayoutDirection layoutDirection;
#if TARGET_OS_TV
@property (nonatomic, readonly) UIUserInterfaceStyle userInterfaceStyle;
@property (readonly) UIUserInterfaceIdiom userInterfaceIdiom;
@property (readonly) UIForceTouchCapability forceTouchCapability;
@property (readonly) UITraitEnvironmentLayoutDirection layoutDirection API_AVAILABLE(ios(10.0));
#if AS_BUILD_UIUSERINTERFACESTYLE
@property (readonly) UIUserInterfaceStyle userInterfaceStyle API_AVAILABLE(tvos(10.0), ios(12.0));
#endif
@property (readonly) UIContentSizeCategory preferredContentSizeCategory API_AVAILABLE(ios(10.0));
@property (nonatomic, readonly) UIContentSizeCategory preferredContentSizeCategory;
@property (nonatomic, readonly) CGSize containerSize;
+ (ASTraitCollection *)traitCollectionWithUITraitCollection:(UITraitCollection *)traitCollection
containerSize:(CGSize)windowSize NS_RETURNS_RETAINED;
+ (ASTraitCollection *)traitCollectionWithUITraitCollection:(UITraitCollection *)traitCollection
containerSize:(CGSize)windowSize
fallbackContentSizeCategory:(UIContentSizeCategory)fallbackContentSizeCategory NS_RETURNS_RETAINED;
#if TARGET_OS_TV
+ (ASTraitCollection *)traitCollectionWithHorizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass
verticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass
displayScale:(CGFloat)displayScale
displayGamut:(UIDisplayGamut)displayGamut
userInterfaceIdiom:(UIUserInterfaceIdiom)userInterfaceIdiom
forceTouchCapability:(UIForceTouchCapability)forceTouchCapability
layoutDirection:(UITraitEnvironmentLayoutDirection)layoutDirection
userInterfaceStyle:(UIUserInterfaceStyle)userInterfaceStyle
preferredContentSizeCategory:(UIContentSizeCategory)preferredContentSizeCategory
containerSize:(CGSize)windowSize NS_RETURNS_RETAINED;
#else
+ (ASTraitCollection *)traitCollectionWithHorizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass
verticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass
displayScale:(CGFloat)displayScale
displayGamut:(UIDisplayGamut)displayGamut
userInterfaceIdiom:(UIUserInterfaceIdiom)userInterfaceIdiom
forceTouchCapability:(UIForceTouchCapability)forceTouchCapability
layoutDirection:(UITraitEnvironmentLayoutDirection)layoutDirection
preferredContentSizeCategory:(UIContentSizeCategory)preferredContentSizeCategory
containerSize:(CGSize)windowSize NS_RETURNS_RETAINED;
#endif
@property (readonly) CGSize containerSize;
- (BOOL)isEqualToTraitCollection:(ASTraitCollection *)traitCollection;
@@ -226,18 +171,4 @@ AS_SUBCLASSING_RESTRICTED
@end
@interface ASTraitCollection (Deprecated)
- (instancetype)init ASDISPLAYNODE_DEPRECATED_MSG("The default constructor of this class is going to become unavailable. Use other constructors instead.");
+ (ASTraitCollection *)traitCollectionWithDisplayScale:(CGFloat)displayScale
userInterfaceIdiom:(UIUserInterfaceIdiom)userInterfaceIdiom
horizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass
verticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass
forceTouchCapability:(UIForceTouchCapability)forceTouchCapability
containerSize:(CGSize)windowSize
NS_RETURNS_RETAINED ASDISPLAYNODE_DEPRECATED_MSG("Use full version of this method instead.");
@end
NS_ASSUME_NONNULL_END

View File

@@ -1,535 +0,0 @@
//
// ASTraitCollection.m
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <AsyncDisplayKit/ASAvailability.h>
#import <AsyncDisplayKit/ASTraitCollection.h>
#import <AsyncDisplayKit/ASObjectDescriptionHelpers.h>
#import <AsyncDisplayKit/ASLayoutElement.h>
#pragma mark - ASPrimitiveContentSizeCategory
// UIContentSizeCategoryUnspecified is available only in iOS 10.0 and later.
// This is used for compatibility with older iOS versions.
ASDISPLAYNODE_INLINE UIContentSizeCategory AS_UIContentSizeCategoryUnspecified() {
if (AS_AVAILABLE_IOS(10)) {
return UIContentSizeCategoryUnspecified;
} else {
return @"_UICTContentSizeCategoryUnspecified";
}
}
ASDISPLAYNODE_INLINE UIContentSizeCategory _Nonnull AS_safeContentSizeCategory(UIContentSizeCategory _Nullable sizeCategory) {
return sizeCategory ? sizeCategory : AS_UIContentSizeCategoryUnspecified();
}
ASPrimitiveContentSizeCategory ASPrimitiveContentSizeCategoryMake(UIContentSizeCategory sizeCategory) {
if ([sizeCategory isEqualToString:UIContentSizeCategoryExtraSmall]) {
return UIContentSizeCategoryExtraSmall;
}
if ([sizeCategory isEqualToString:UIContentSizeCategorySmall]) {
return UIContentSizeCategorySmall;
}
if ([sizeCategory isEqualToString:UIContentSizeCategoryMedium]) {
return UIContentSizeCategoryMedium;
}
if ([sizeCategory isEqualToString:UIContentSizeCategoryLarge]) {
return UIContentSizeCategoryLarge;
}
if ([sizeCategory isEqualToString:UIContentSizeCategoryExtraLarge]) {
return UIContentSizeCategoryExtraLarge;
}
if ([sizeCategory isEqualToString:UIContentSizeCategoryExtraExtraLarge]) {
return UIContentSizeCategoryExtraExtraLarge;
}
if ([sizeCategory isEqualToString:UIContentSizeCategoryExtraExtraExtraLarge]) {
return UIContentSizeCategoryExtraExtraExtraLarge;
}
if ([sizeCategory isEqualToString:UIContentSizeCategoryAccessibilityMedium]) {
return UIContentSizeCategoryAccessibilityMedium;
}
if ([sizeCategory isEqualToString:UIContentSizeCategoryAccessibilityLarge]) {
return UIContentSizeCategoryAccessibilityLarge;
}
if ([sizeCategory isEqualToString:UIContentSizeCategoryAccessibilityExtraLarge]) {
return UIContentSizeCategoryAccessibilityExtraLarge;
}
if ([sizeCategory isEqualToString:UIContentSizeCategoryAccessibilityExtraExtraLarge]) {
return UIContentSizeCategoryAccessibilityExtraExtraLarge;
}
if ([sizeCategory isEqualToString:UIContentSizeCategoryAccessibilityExtraExtraExtraLarge]) {
return UIContentSizeCategoryAccessibilityExtraExtraExtraLarge;
}
return AS_UIContentSizeCategoryUnspecified();
}
#pragma mark - ASPrimitiveTraitCollection
void ASTraitCollectionPropagateDown(id<ASLayoutElement> element, ASPrimitiveTraitCollection traitCollection) {
if (element) {
element.primitiveTraitCollection = traitCollection;
}
for (id<ASLayoutElement> subelement in element.sublayoutElements) {
ASTraitCollectionPropagateDown(subelement, traitCollection);
}
}
ASPrimitiveTraitCollection ASPrimitiveTraitCollectionMakeDefault() {
return (ASPrimitiveTraitCollection) {
// Default values can be defined in here
.displayGamut = UIDisplayGamutUnspecified,
.userInterfaceIdiom = UIUserInterfaceIdiomUnspecified,
.layoutDirection = UITraitEnvironmentLayoutDirectionUnspecified,
.preferredContentSizeCategory = ASPrimitiveContentSizeCategoryMake(AS_UIContentSizeCategoryUnspecified()),
.containerSize = CGSizeZero,
};
}
ASPrimitiveTraitCollection ASPrimitiveTraitCollectionFromUITraitCollection(UITraitCollection *traitCollection) {
ASPrimitiveTraitCollection environmentTraitCollection = ASPrimitiveTraitCollectionMakeDefault();
environmentTraitCollection.horizontalSizeClass = traitCollection.horizontalSizeClass;
environmentTraitCollection.verticalSizeClass = traitCollection.verticalSizeClass;
environmentTraitCollection.displayScale = traitCollection.displayScale;
environmentTraitCollection.userInterfaceIdiom = traitCollection.userInterfaceIdiom;
environmentTraitCollection.forceTouchCapability = traitCollection.forceTouchCapability;
if (AS_AVAILABLE_IOS(10)) {
environmentTraitCollection.displayGamut = traitCollection.displayGamut;
environmentTraitCollection.layoutDirection = traitCollection.layoutDirection;
// preferredContentSizeCategory is also available on older iOS versions, but only via UIApplication class.
// It should be noted that [UIApplication sharedApplication] is unavailable because Texture is built with only extension-safe API.
environmentTraitCollection.preferredContentSizeCategory = ASPrimitiveContentSizeCategoryMake(traitCollection.preferredContentSizeCategory);
#if TARGET_OS_TV
environmentTraitCollection.userInterfaceStyle = traitCollection.userInterfaceStyle;
#endif
} else {
environmentTraitCollection.displayGamut = UIDisplayGamutSRGB; // We're on iOS 9 or lower, so this is not a P3 device.
}
return environmentTraitCollection;
}
BOOL ASPrimitiveTraitCollectionIsEqualToASPrimitiveTraitCollection(ASPrimitiveTraitCollection lhs, ASPrimitiveTraitCollection rhs) {
UIContentSizeCategory leftSizeCategory = AS_safeContentSizeCategory(lhs.preferredContentSizeCategory);
UIContentSizeCategory rightSizeCategory = AS_safeContentSizeCategory(rhs.preferredContentSizeCategory);
return
lhs.verticalSizeClass == rhs.verticalSizeClass &&
lhs.horizontalSizeClass == rhs.horizontalSizeClass &&
lhs.displayScale == rhs.displayScale &&
lhs.displayGamut == rhs.displayGamut &&
lhs.userInterfaceIdiom == rhs.userInterfaceIdiom &&
lhs.forceTouchCapability == rhs.forceTouchCapability &&
lhs.layoutDirection == rhs.layoutDirection &&
#if TARGET_OS_TV
lhs.userInterfaceStyle == rhs.userInterfaceStyle &&
#endif
[leftSizeCategory isEqualToString:rightSizeCategory] && // Simple pointer comparison should be sufficient here
CGSizeEqualToSize(lhs.containerSize, rhs.containerSize);
}
// Named so as not to conflict with a hidden Apple function, in case compiler decides not to inline
ASDISPLAYNODE_INLINE NSString *AS_NSStringFromUIUserInterfaceIdiom(UIUserInterfaceIdiom idiom) {
switch (idiom) {
case UIUserInterfaceIdiomTV:
return @"TV";
case UIUserInterfaceIdiomPad:
return @"Pad";
case UIUserInterfaceIdiomPhone:
return @"Phone";
case UIUserInterfaceIdiomCarPlay:
return @"CarPlay";
default:
return @"Unspecified";
}
}
// Named so as not to conflict with a hidden Apple function, in case compiler decides not to inline
ASDISPLAYNODE_INLINE NSString *AS_NSStringFromUIForceTouchCapability(UIForceTouchCapability capability) {
switch (capability) {
case UIForceTouchCapabilityAvailable:
return @"Available";
case UIForceTouchCapabilityUnavailable:
return @"Unavailable";
default:
return @"Unknown";
}
}
// Named so as not to conflict with a hidden Apple function, in case compiler decides not to inline
ASDISPLAYNODE_INLINE NSString *AS_NSStringFromUIUserInterfaceSizeClass(UIUserInterfaceSizeClass sizeClass) {
switch (sizeClass) {
case UIUserInterfaceSizeClassCompact:
return @"Compact";
case UIUserInterfaceSizeClassRegular:
return @"Regular";
default:
return @"Unspecified";
}
}
// Named so as not to conflict with a hidden Apple function, in case compiler decides not to inline
ASDISPLAYNODE_INLINE NSString *AS_NSStringFromUIDisplayGamut(UIDisplayGamut displayGamut) {
switch (displayGamut) {
case UIDisplayGamutSRGB:
return @"sRGB";
case UIDisplayGamutP3:
return @"P3";
default:
return @"Unspecified";
}
}
// Named so as not to conflict with a hidden Apple function, in case compiler decides not to inline
ASDISPLAYNODE_INLINE NSString *AS_NSStringFromUITraitEnvironmentLayoutDirection(UITraitEnvironmentLayoutDirection layoutDirection) {
switch (layoutDirection) {
case UITraitEnvironmentLayoutDirectionLeftToRight:
return @"LeftToRight";
case UITraitEnvironmentLayoutDirectionRightToLeft:
return @"RightToLeft";
default:
return @"Unspecified";
}
}
#if TARGET_OS_TV
// Named so as not to conflict with a hidden Apple function, in case compiler decides not to inline
ASDISPLAYNODE_INLINE NSString *AS_NSStringFromUIUserInterfaceStyle(UIUserInterfaceStyle userInterfaceStyle) {
switch (userInterfaceStyle) {
case UIUserInterfaceStyleLight:
return @"Light";
case UIUserInterfaceStyleDark:
return @"Dark";
default:
return @"Unspecified";
}
}
#endif
NSString *NSStringFromASPrimitiveTraitCollection(ASPrimitiveTraitCollection traits) {
NSMutableArray<NSDictionary *> *props = [NSMutableArray array];
[props addObject:@{ @"verticalSizeClass": AS_NSStringFromUIUserInterfaceSizeClass(traits.verticalSizeClass) }];
[props addObject:@{ @"horizontalSizeClass": AS_NSStringFromUIUserInterfaceSizeClass(traits.horizontalSizeClass) }];
[props addObject:@{ @"displayScale": [NSString stringWithFormat: @"%.0lf", (double)traits.displayScale] }];
[props addObject:@{ @"displayGamut": AS_NSStringFromUIDisplayGamut(traits.displayGamut) }];
[props addObject:@{ @"userInterfaceIdiom": AS_NSStringFromUIUserInterfaceIdiom(traits.userInterfaceIdiom) }];
[props addObject:@{ @"forceTouchCapability": AS_NSStringFromUIForceTouchCapability(traits.forceTouchCapability) }];
[props addObject:@{ @"layoutDirection": AS_NSStringFromUITraitEnvironmentLayoutDirection(traits.layoutDirection) }];
#if TARGET_OS_TV
[props addObject:@{ @"userInterfaceStyle": AS_NSStringFromUIUserInterfaceStyle(traits.userInterfaceStyle) }];
#endif
[props addObject:@{ @"preferredContentSizeCategory": AS_safeContentSizeCategory(traits.preferredContentSizeCategory) }];
[props addObject:@{ @"containerSize": NSStringFromCGSize(traits.containerSize) }];
return ASObjectDescriptionMakeWithoutObject(props);
}
#pragma mark - ASTraitCollection
@implementation ASTraitCollection
#if TARGET_OS_TV
- (instancetype)initWithHorizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass
verticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass
displayScale:(CGFloat)displayScale
displayGamut:(UIDisplayGamut)displayGamut
userInterfaceIdiom:(UIUserInterfaceIdiom)userInterfaceIdiom
forceTouchCapability:(UIForceTouchCapability)forceTouchCapability
layoutDirection:(UITraitEnvironmentLayoutDirection)layoutDirection
userInterfaceStyle:(UIUserInterfaceStyle)userInterfaceStyle
preferredContentSizeCategory:(UIContentSizeCategory _Nonnull)preferredContentSizeCategory
containerSize:(CGSize)windowSize
{
self = [super init];
if (self) {
_horizontalSizeClass = horizontalSizeClass;
_verticalSizeClass = verticalSizeClass;
_displayScale = displayScale;
_displayGamut = displayGamut;
_userInterfaceIdiom = userInterfaceIdiom;
_forceTouchCapability = forceTouchCapability;
_layoutDirection = layoutDirection;
_userInterfaceStyle = userInterfaceStyle;
_preferredContentSizeCategory = AS_safeContentSizeCategory(preferredContentSizeCategory); // guard against misuse
_containerSize = windowSize;
}
return self;
}
+ (instancetype)traitCollectionWithHorizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass
verticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass
displayScale:(CGFloat)displayScale
displayGamut:(UIDisplayGamut)displayGamut
userInterfaceIdiom:(UIUserInterfaceIdiom)userInterfaceIdiom
forceTouchCapability:(UIForceTouchCapability)forceTouchCapability
layoutDirection:(UITraitEnvironmentLayoutDirection)layoutDirection
userInterfaceStyle:(UIUserInterfaceStyle)userInterfaceStyle
preferredContentSizeCategory:(UIContentSizeCategory _Nonnull)preferredContentSizeCategory
containerSize:(CGSize)windowSize NS_RETURNS_RETAINED
{
return [[self alloc] initWithHorizontalSizeClass:horizontalSizeClass
verticalSizeClass:verticalSizeClass
displayScale:displayScale
displayGamut:displayGamut
userInterfaceIdiom:userInterfaceIdiom
forceTouchCapability:forceTouchCapability
layoutDirection:layoutDirection
userInterfaceStyle:userInterfaceStyle
preferredContentSizeCategory:preferredContentSizeCategory
containerSize:windowSize];
}
#else
- (instancetype)initWithHorizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass
verticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass
displayScale:(CGFloat)displayScale
displayGamut:(UIDisplayGamut)displayGamut
userInterfaceIdiom:(UIUserInterfaceIdiom)userInterfaceIdiom
forceTouchCapability:(UIForceTouchCapability)forceTouchCapability
layoutDirection:(UITraitEnvironmentLayoutDirection)layoutDirection
preferredContentSizeCategory:(UIContentSizeCategory _Nonnull)preferredContentSizeCategory
containerSize:(CGSize)windowSize
{
self = [super init];
if (self) {
_horizontalSizeClass = horizontalSizeClass;
_verticalSizeClass = verticalSizeClass;
_displayScale = displayScale;
_displayGamut = displayGamut;
_userInterfaceIdiom = userInterfaceIdiom;
_forceTouchCapability = forceTouchCapability;
_layoutDirection = layoutDirection;
_preferredContentSizeCategory = AS_safeContentSizeCategory(preferredContentSizeCategory); // guard against misuse
_containerSize = windowSize;
}
return self;
}
+ (instancetype)traitCollectionWithHorizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass
verticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass
displayScale:(CGFloat)displayScale
displayGamut:(UIDisplayGamut)displayGamut
userInterfaceIdiom:(UIUserInterfaceIdiom)userInterfaceIdiom
forceTouchCapability:(UIForceTouchCapability)forceTouchCapability
layoutDirection:(UITraitEnvironmentLayoutDirection)layoutDirection
preferredContentSizeCategory:(UIContentSizeCategory _Nonnull)preferredContentSizeCategory
containerSize:(CGSize)windowSize NS_RETURNS_RETAINED
{
return [[self alloc] initWithHorizontalSizeClass:horizontalSizeClass
verticalSizeClass:verticalSizeClass
displayScale:displayScale
displayGamut:displayGamut
userInterfaceIdiom:userInterfaceIdiom
forceTouchCapability:forceTouchCapability
layoutDirection:layoutDirection
preferredContentSizeCategory:preferredContentSizeCategory
containerSize:windowSize];
}
#endif
+ (instancetype)traitCollectionWithUITraitCollection:(UITraitCollection *)traitCollection
containerSize:(CGSize)windowSize NS_RETURNS_RETAINED
{
return [self traitCollectionWithUITraitCollection:traitCollection
containerSize:windowSize
fallbackContentSizeCategory:AS_UIContentSizeCategoryUnspecified()];
}
+ (instancetype)traitCollectionWithUITraitCollection:(UITraitCollection *)traitCollection
containerSize:(CGSize)windowSize
fallbackContentSizeCategory:(UIContentSizeCategory _Nonnull)fallbackContentSizeCategory NS_RETURNS_RETAINED
{
UIDisplayGamut displayGamut;
UITraitEnvironmentLayoutDirection layoutDirection;
UIContentSizeCategory sizeCategory;
#if TARGET_OS_TV
UIUserInterfaceStyle userInterfaceStyle;
#endif
if (AS_AVAILABLE_IOS(10)) {
displayGamut = traitCollection.displayGamut;
layoutDirection = traitCollection.layoutDirection;
sizeCategory = traitCollection.preferredContentSizeCategory;
#if TARGET_OS_TV
userInterfaceStyle = traitCollection.userInterfaceStyle;
#endif
} else {
displayGamut = UIDisplayGamutSRGB; // We're on iOS 9 or lower, so this is not a P3 device.
layoutDirection = UITraitEnvironmentLayoutDirectionUnspecified;
sizeCategory = fallbackContentSizeCategory;
#if TARGET_OS_TV
userInterfaceStyle = UIUserInterfaceStyleUnspecified;
#endif
}
#if TARGET_OS_TV
return [self traitCollectionWithHorizontalSizeClass:traitCollection.horizontalSizeClass
verticalSizeClass:traitCollection.verticalSizeClass
displayScale:traitCollection.displayScale
displayGamut:displayGamut
userInterfaceIdiom:traitCollection.userInterfaceIdiom
forceTouchCapability:traitCollection.forceTouchCapability
layoutDirection:layoutDirection
userInterfaceStyle:userInterfaceStyle
preferredContentSizeCategory:sizeCategory
containerSize:windowSize];
#else
return [self traitCollectionWithHorizontalSizeClass:traitCollection.horizontalSizeClass
verticalSizeClass:traitCollection.verticalSizeClass
displayScale:traitCollection.displayScale
displayGamut:displayGamut
userInterfaceIdiom:traitCollection.userInterfaceIdiom
forceTouchCapability:traitCollection.forceTouchCapability
layoutDirection:layoutDirection
preferredContentSizeCategory:sizeCategory
containerSize:windowSize];
#endif
}
- (BOOL)isEqualToTraitCollection:(ASTraitCollection *)traitCollection
{
if (traitCollection == nil) {
return NO;
}
if (self == traitCollection) {
return YES;
}
return
self.horizontalSizeClass == traitCollection.horizontalSizeClass &&
self.verticalSizeClass == traitCollection.verticalSizeClass &&
self.displayScale == traitCollection.displayScale &&
self.displayGamut == traitCollection.displayGamut &&
self.userInterfaceIdiom == traitCollection.userInterfaceIdiom &&
self.forceTouchCapability == traitCollection.forceTouchCapability &&
self.layoutDirection == traitCollection.layoutDirection &&
#if TARGET_OS_TV
self.userInterfaceStyle == traitCollection.userInterfaceStyle &&
#endif
[self.preferredContentSizeCategory isEqualToString:traitCollection.preferredContentSizeCategory] &&
CGSizeEqualToSize(self.containerSize, traitCollection.containerSize);
}
@end
@implementation ASTraitCollection (PrimitiveTraits)
+ (instancetype)traitCollectionWithASPrimitiveTraitCollection:(ASPrimitiveTraitCollection)traits NS_RETURNS_RETAINED
{
#if TARGET_OS_TV
return [self traitCollectionWithHorizontalSizeClass:traits.horizontalSizeClass
verticalSizeClass:traits.verticalSizeClass
displayScale:traits.displayScale
displayGamut:traits.displayGamut
userInterfaceIdiom:traits.userInterfaceIdiom
forceTouchCapability:traits.forceTouchCapability
layoutDirection:traits.layoutDirection
userInterfaceStyle:traits.userInterfaceStyle
preferredContentSizeCategory:AS_safeContentSizeCategory(traits.preferredContentSizeCategory)
containerSize:traits.containerSize];
#else
return [self traitCollectionWithHorizontalSizeClass:traits.horizontalSizeClass
verticalSizeClass:traits.verticalSizeClass
displayScale:traits.displayScale
displayGamut:traits.displayGamut
userInterfaceIdiom:traits.userInterfaceIdiom
forceTouchCapability:traits.forceTouchCapability
layoutDirection:traits.layoutDirection
preferredContentSizeCategory:AS_safeContentSizeCategory(traits.preferredContentSizeCategory)
containerSize:traits.containerSize];
#endif
}
- (ASPrimitiveTraitCollection)primitiveTraitCollection
{
return (ASPrimitiveTraitCollection) {
.horizontalSizeClass = self.horizontalSizeClass,
.verticalSizeClass = self.verticalSizeClass,
.displayScale = self.displayScale,
.displayGamut = self.displayGamut,
.userInterfaceIdiom = self.userInterfaceIdiom,
.forceTouchCapability = self.forceTouchCapability,
.layoutDirection = self.layoutDirection,
#if TARGET_OS_TV
.userInterfaceStyle = self.userInterfaceStyle,
#endif
.preferredContentSizeCategory = ASPrimitiveContentSizeCategoryMake(self.preferredContentSizeCategory),
.containerSize = self.containerSize,
};
}
@end
@implementation ASTraitCollection (Deprecated)
- (instancetype)init
{
#if TARGET_OS_TV
return [self initWithHorizontalSizeClass:UIUserInterfaceSizeClassUnspecified
verticalSizeClass:UIUserInterfaceSizeClassUnspecified
displayScale:0
displayGamut:UIDisplayGamutUnspecified
userInterfaceIdiom:UIUserInterfaceIdiomUnspecified
forceTouchCapability:UIForceTouchCapabilityUnknown
layoutDirection:UITraitEnvironmentLayoutDirectionUnspecified
userInterfaceStyle:UIUserInterfaceStyleUnspecified
preferredContentSizeCategory:AS_UIContentSizeCategoryUnspecified()
containerSize:CGSizeZero];
#else
return [self initWithHorizontalSizeClass:UIUserInterfaceSizeClassUnspecified
verticalSizeClass:UIUserInterfaceSizeClassUnspecified
displayScale:0
displayGamut:UIDisplayGamutUnspecified
userInterfaceIdiom:UIUserInterfaceIdiomUnspecified
forceTouchCapability:UIForceTouchCapabilityUnknown
layoutDirection:UITraitEnvironmentLayoutDirectionUnspecified
preferredContentSizeCategory:AS_UIContentSizeCategoryUnspecified()
containerSize:CGSizeZero];
#endif
}
+ (ASTraitCollection *)traitCollectionWithDisplayScale:(CGFloat)displayScale
userInterfaceIdiom:(UIUserInterfaceIdiom)userInterfaceIdiom
horizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass
verticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass
forceTouchCapability:(UIForceTouchCapability)forceTouchCapability
containerSize:(CGSize)windowSize NS_RETURNS_RETAINED
{
#if TARGET_OS_TV
return [self traitCollectionWithHorizontalSizeClass:horizontalSizeClass
verticalSizeClass:verticalSizeClass
displayScale:displayScale
displayGamut:UIDisplayGamutUnspecified
userInterfaceIdiom:userInterfaceIdiom
forceTouchCapability:forceTouchCapability
layoutDirection:UITraitEnvironmentLayoutDirectionUnspecified
userInterfaceStyle:UIUserInterfaceStyleUnspecified
preferredContentSizeCategory:AS_UIContentSizeCategoryUnspecified()
containerSize:windowSize];
#else
return [self traitCollectionWithHorizontalSizeClass:horizontalSizeClass
verticalSizeClass:verticalSizeClass
displayScale:displayScale
displayGamut:UIDisplayGamutUnspecified
userInterfaceIdiom:userInterfaceIdiom
forceTouchCapability:forceTouchCapability
layoutDirection:UITraitEnvironmentLayoutDirectionUnspecified
preferredContentSizeCategory:AS_UIContentSizeCategoryUnspecified()
containerSize:windowSize];
#endif
}
@end

View File

@@ -0,0 +1,249 @@
//
// ASTraitCollection.mm
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <AsyncDisplayKit/ASAvailability.h>
#import <AsyncDisplayKit/ASHashing.h>
#import <AsyncDisplayKit/ASTraitCollection.h>
#import <AsyncDisplayKit/ASObjectDescriptionHelpers.h>
#import <AsyncDisplayKit/ASLayoutElement.h>
#pragma mark - ASPrimitiveTraitCollection
void ASTraitCollectionPropagateDown(id<ASLayoutElement> element, ASPrimitiveTraitCollection traitCollection) {
if (element) {
element.primitiveTraitCollection = traitCollection;
}
for (id<ASLayoutElement> subelement in element.sublayoutElements) {
ASTraitCollectionPropagateDown(subelement, traitCollection);
}
}
ASPrimitiveTraitCollection ASPrimitiveTraitCollectionMakeDefault() {
ASPrimitiveTraitCollection tc;
tc.horizontalSizeClass = UIUserInterfaceSizeClassUnspecified;
tc.verticalSizeClass = UIUserInterfaceSizeClassUnspecified;
tc.containerSize = CGSizeZero;
if (AS_AVAILABLE_IOS(10)) {
tc.displayGamut = UIDisplayGamutUnspecified;
tc.preferredContentSizeCategory = UIContentSizeCategoryUnspecified;
tc.layoutDirection = UITraitEnvironmentLayoutDirectionUnspecified;
}
#if AS_BUILD_UIUSERINTERFACESTYLE
if (AS_AVAILABLE_IOS_TVOS(12, 10)) {
tc.userInterfaceStyle = UIUserInterfaceStyleUnspecified;
}
#endif
return tc;
}
ASPrimitiveTraitCollection ASPrimitiveTraitCollectionFromUITraitCollection(UITraitCollection *traitCollection) {
ASPrimitiveTraitCollection environmentTraitCollection = ASPrimitiveTraitCollectionMakeDefault();
environmentTraitCollection.horizontalSizeClass = traitCollection.horizontalSizeClass;
environmentTraitCollection.verticalSizeClass = traitCollection.verticalSizeClass;
environmentTraitCollection.displayScale = traitCollection.displayScale;
environmentTraitCollection.userInterfaceIdiom = traitCollection.userInterfaceIdiom;
environmentTraitCollection.forceTouchCapability = traitCollection.forceTouchCapability;
if (AS_AVAILABLE_IOS(10)) {
environmentTraitCollection.displayGamut = traitCollection.displayGamut;
environmentTraitCollection.layoutDirection = traitCollection.layoutDirection;
ASDisplayNodeCAssertPermanent(traitCollection.preferredContentSizeCategory);
environmentTraitCollection.preferredContentSizeCategory = traitCollection.preferredContentSizeCategory;
}
#if AS_BUILD_UIUSERINTERFACESTYLE
if (AS_AVAILABLE_IOS_TVOS(12, 10)) {
environmentTraitCollection.userInterfaceStyle = traitCollection.userInterfaceStyle;
}
#endif
return environmentTraitCollection;
}
BOOL ASPrimitiveTraitCollectionIsEqualToASPrimitiveTraitCollection(ASPrimitiveTraitCollection lhs, ASPrimitiveTraitCollection rhs) {
return !memcmp(&lhs, &rhs, sizeof(ASPrimitiveTraitCollection));
}
// Named so as not to conflict with a hidden Apple function, in case compiler decides not to inline
ASDISPLAYNODE_INLINE NSString *AS_NSStringFromUIUserInterfaceIdiom(UIUserInterfaceIdiom idiom) {
switch (idiom) {
case UIUserInterfaceIdiomTV:
return @"TV";
case UIUserInterfaceIdiomPad:
return @"Pad";
case UIUserInterfaceIdiomPhone:
return @"Phone";
case UIUserInterfaceIdiomCarPlay:
return @"CarPlay";
default:
return @"Unspecified";
}
}
// Named so as not to conflict with a hidden Apple function, in case compiler decides not to inline
ASDISPLAYNODE_INLINE NSString *AS_NSStringFromUIForceTouchCapability(UIForceTouchCapability capability) {
switch (capability) {
case UIForceTouchCapabilityAvailable:
return @"Available";
case UIForceTouchCapabilityUnavailable:
return @"Unavailable";
default:
return @"Unknown";
}
}
// Named so as not to conflict with a hidden Apple function, in case compiler decides not to inline
ASDISPLAYNODE_INLINE NSString *AS_NSStringFromUIUserInterfaceSizeClass(UIUserInterfaceSizeClass sizeClass) {
switch (sizeClass) {
case UIUserInterfaceSizeClassCompact:
return @"Compact";
case UIUserInterfaceSizeClassRegular:
return @"Regular";
default:
return @"Unspecified";
}
}
// Named so as not to conflict with a hidden Apple function, in case compiler decides not to inline
API_AVAILABLE(ios(10))
ASDISPLAYNODE_INLINE NSString *AS_NSStringFromUIDisplayGamut(UIDisplayGamut displayGamut) {
switch (displayGamut) {
case UIDisplayGamutSRGB:
return @"sRGB";
case UIDisplayGamutP3:
return @"P3";
default:
return @"Unspecified";
}
}
// Named so as not to conflict with a hidden Apple function, in case compiler decides not to inline
API_AVAILABLE(ios(10))
ASDISPLAYNODE_INLINE NSString *AS_NSStringFromUITraitEnvironmentLayoutDirection(UITraitEnvironmentLayoutDirection layoutDirection) {
switch (layoutDirection) {
case UITraitEnvironmentLayoutDirectionLeftToRight:
return @"LeftToRight";
case UITraitEnvironmentLayoutDirectionRightToLeft:
return @"RightToLeft";
default:
return @"Unspecified";
}
}
// Named so as not to conflict with a hidden Apple function, in case compiler decides not to inline
#if AS_BUILD_UIUSERINTERFACESTYLE
API_AVAILABLE(tvos(10.0), ios(12.0))
ASDISPLAYNODE_INLINE NSString *AS_NSStringFromUIUserInterfaceStyle(UIUserInterfaceStyle userInterfaceStyle) {
switch (userInterfaceStyle) {
case UIUserInterfaceStyleLight:
return @"Light";
case UIUserInterfaceStyleDark:
return @"Dark";
default:
return @"Unspecified";
}
}
#endif
NSString *NSStringFromASPrimitiveTraitCollection(ASPrimitiveTraitCollection traits) {
NSMutableArray<NSDictionary *> *props = [NSMutableArray array];
[props addObject:@{ @"verticalSizeClass": AS_NSStringFromUIUserInterfaceSizeClass(traits.verticalSizeClass) }];
[props addObject:@{ @"horizontalSizeClass": AS_NSStringFromUIUserInterfaceSizeClass(traits.horizontalSizeClass) }];
[props addObject:@{ @"displayScale": [NSString stringWithFormat: @"%.0lf", (double)traits.displayScale] }];
[props addObject:@{ @"userInterfaceIdiom": AS_NSStringFromUIUserInterfaceIdiom(traits.userInterfaceIdiom) }];
[props addObject:@{ @"forceTouchCapability": AS_NSStringFromUIForceTouchCapability(traits.forceTouchCapability) }];
#if AS_BUILD_UIUSERINTERFACESTYLE
if (AS_AVAILABLE_IOS_TVOS(12, 10)) {
[props addObject:@{ @"userInterfaceStyle": AS_NSStringFromUIUserInterfaceStyle(traits.userInterfaceStyle) }];
}
#endif
if (AS_AVAILABLE_IOS(10)) {
[props addObject:@{ @"layoutDirection": AS_NSStringFromUITraitEnvironmentLayoutDirection(traits.layoutDirection) }];
[props addObject:@{ @"preferredContentSizeCategory": traits.preferredContentSizeCategory }];
[props addObject:@{ @"displayGamut": AS_NSStringFromUIDisplayGamut(traits.displayGamut) }];
}
[props addObject:@{ @"containerSize": NSStringFromCGSize(traits.containerSize) }];
return ASObjectDescriptionMakeWithoutObject(props);
}
#pragma mark - ASTraitCollection
@implementation ASTraitCollection {
ASPrimitiveTraitCollection _prim;
}
+ (ASTraitCollection *)traitCollectionWithASPrimitiveTraitCollection:(ASPrimitiveTraitCollection)traits NS_RETURNS_RETAINED {
ASTraitCollection *tc = [[ASTraitCollection alloc] init];
if (AS_AVAILABLE_IOS(10)) {
ASDisplayNodeCAssertPermanent(traits.preferredContentSizeCategory);
}
tc->_prim = traits;
return tc;
}
- (ASPrimitiveTraitCollection)primitiveTraitCollection {
return _prim;
}
- (UIUserInterfaceSizeClass)horizontalSizeClass
{
return _prim.horizontalSizeClass;
}
-(UIUserInterfaceSizeClass)verticalSizeClass
{
return _prim.verticalSizeClass;
}
- (CGFloat)displayScale
{
return _prim.displayScale;
}
- (UIDisplayGamut)displayGamut
{
return _prim.displayGamut;
}
- (UIForceTouchCapability)forceTouchCapability
{
return _prim.forceTouchCapability;
}
- (UITraitEnvironmentLayoutDirection)layoutDirection
{
return _prim.layoutDirection;
}
#if AS_BUILD_UIUSERINTERFACESTYLE
- (UIUserInterfaceStyle)userInterfaceStyle
{
return _prim.userInterfaceStyle;
}
#endif
- (UIContentSizeCategory)preferredContentSizeCategory
{
return _prim.preferredContentSizeCategory;
}
- (NSUInteger)hash {
return ASHashBytes(&_prim, sizeof(ASPrimitiveTraitCollection));
}
- (BOOL)isEqual:(id)object {
if (!object || ![object isKindOfClass:ASTraitCollection.class]) {
return NO;
}
return [self isEqualToTraitCollection:object];
}
- (BOOL)isEqualToTraitCollection:(ASTraitCollection *)traitCollection
{
if (traitCollection == nil) {
return NO;
}
if (self == traitCollection) {
return YES;
}
return ASPrimitiveTraitCollectionIsEqualToASPrimitiveTraitCollection(_prim, traitCollection->_prim);
}
@end

View File

@@ -1,5 +1,5 @@
//
// ASWeakProxy.m
// ASWeakProxy.mm
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.

View File

@@ -1,5 +1,5 @@
//
// ASWeakSet.m
// ASWeakSet.mm
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.

View File

@@ -1,11 +0,0 @@
//
// CoreGraphics+ASConvenience.m
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <AsyncDisplayKit/CoreGraphics+ASConvenience.h>

View File

@@ -1,5 +1,5 @@
//
// NSIndexSet+ASHelpers.m
// NSIndexSet+ASHelpers.mm
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.

View File

@@ -1,5 +1,5 @@
//
// NSMutableAttributedString+TextKitAdditions.m
// NSMutableAttributedString+TextKitAdditions.mm
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.

View File

@@ -1,5 +1,5 @@
//
// _ASAsyncTransactionContainer.m
// _ASAsyncTransactionContainer.mm
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.

View File

@@ -1,5 +1,5 @@
//
// _ASAsyncTransactionGroup.m
// _ASAsyncTransactionGroup.mm
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.

View File

@@ -1,5 +1,5 @@
//
// UICollectionViewLayout+ASConvenience.m
// UICollectionViewLayout+ASConvenience.mm
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.

View File

@@ -1,5 +1,5 @@
//
// _ASCollectionReusableView.m
// _ASCollectionReusableView.mm
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
@@ -9,7 +9,8 @@
#ifndef MINIMAL_ASDK
#import "_ASCollectionReusableView.h"
#import <AsyncDisplayKit/_ASCollectionReusableView.h>
#import <AsyncDisplayKit/ASCellNode+Internal.h>
#import <AsyncDisplayKit/ASCollectionElement.h>

View File

@@ -1,5 +1,5 @@
//
// _ASCollectionViewCell.m
// _ASCollectionViewCell.mm
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
@@ -8,7 +8,8 @@
//
#ifndef MINIMAL_ASDK
#import "_ASCollectionViewCell.h"
#import <AsyncDisplayKit/_ASCollectionViewCell.h>
#import <AsyncDisplayKit/ASCellNode+Internal.h>
#import <AsyncDisplayKit/ASCollectionElement.h>
#import <AsyncDisplayKit/ASInternalHelpers.h>

View File

@@ -131,7 +131,7 @@ static void CollectUIAccessibilityElementsForNode(ASDisplayNode *node, ASDisplay
});
}
static void CollectAccessibilityElementsForContainer(ASDisplayNode *container, _ASDisplayView *view, NSMutableArray *elements) {
static void CollectAccessibilityElementsForContainer(ASDisplayNode *container, UIView *view, NSMutableArray *elements) {
UIAccessibilityElement *accessiblityElement = [ASAccessibilityElement accessibilityElementWithContainer:view node:container containerNode:container];
NSMutableArray<ASAccessibilityElement *> *labeledNodes = [[NSMutableArray alloc] init];
@@ -139,7 +139,14 @@ static void CollectAccessibilityElementsForContainer(ASDisplayNode *container, _
std::queue<ASDisplayNode *> queue;
queue.push(container);
ASDisplayNode *node;
// If the container does not have an accessibility label set, or if the label is meant for custom
// actions only, then aggregate its subnodes' labels. Otherwise, treat the label as an overriden
// value and do not perform the aggregation.
BOOL shouldAggregateSubnodeLabels =
(container.accessibilityLabel.length == 0) ||
(container.accessibilityTraits & InteractiveAccessibilityTraitsMask());
ASDisplayNode *node = nil;
while (!queue.empty()) {
node = queue.front();
queue.pop();
@@ -156,7 +163,7 @@ static void CollectAccessibilityElementsForContainer(ASDisplayNode *container, _
action.containerNode = node.supernode;
action.container = node.supernode.view;
[actions addObject:action];
} else {
} else if (node == container || shouldAggregateSubnodeLabels) {
// 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];
@@ -195,7 +202,7 @@ static void CollectAccessibilityElementsForContainer(ASDisplayNode *container, _
}
/// Collect all accessibliity elements for a given view and view node
static void CollectAccessibilityElementsForView(_ASDisplayView *view, NSMutableArray *elements)
static void CollectAccessibilityElementsForView(UIView *view, NSMutableArray *elements)
{
ASDisplayNodeCAssertNotNil(elements, @"Should pass in a NSMutableArray");
@@ -258,17 +265,28 @@ static void CollectAccessibilityElementsForView(_ASDisplayView *view, NSMutableA
if (viewNode == nil) {
return @[];
}
if (_accessibilityElements == nil) {
NSMutableArray *accessibilityElements = [[NSMutableArray alloc] init];
CollectAccessibilityElementsForView(self, accessibilityElements);
SortAccessibilityElements(accessibilityElements);
_accessibilityElements = accessibilityElements;
_accessibilityElements = [viewNode accessibilityElements];
}
return _accessibilityElements;
}
@end
@implementation ASDisplayNode (AccessibilityInternal)
- (NSArray *)accessibilityElements
{
if (!self.isNodeLoaded) {
ASDisplayNodeFailAssert(@"Cannot access accessibilityElements since node is not loaded");
return @[];
}
NSMutableArray *accessibilityElements = [[NSMutableArray alloc] init];
CollectAccessibilityElementsForView(self.view, accessibilityElements);
SortAccessibilityElements(accessibilityElements);
return accessibilityElements;
}
@end
#endif

View File

@@ -1,5 +1,5 @@
//
// IGListAdapter+AsyncDisplayKit.m
// IGListAdapter+AsyncDisplayKit.mm
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
@@ -11,9 +11,9 @@
#if AS_IG_LIST_KIT
#import "IGListAdapter+AsyncDisplayKit.h"
#import "ASIGListAdapterBasedDataSource.h"
#import "ASAssert.h"
#import <AsyncDisplayKit/IGListAdapter+AsyncDisplayKit.h>
#import <AsyncDisplayKit/ASIGListAdapterBasedDataSource.h>
#import <AsyncDisplayKit/ASAssert.h>
#import <objc/runtime.h>
@implementation IGListAdapter (AsyncDisplayKit)

View File

@@ -1,5 +1,5 @@
//
// ASAsciiArtBoxCreator.m
// ASAsciiArtBoxCreator.mm
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
@@ -10,7 +10,7 @@
#import <AsyncDisplayKit/ASAsciiArtBoxCreator.h>
#import <CoreGraphics/CoreGraphics.h>
#import <tgmath.h>
#import <cmath>
static const NSUInteger kDebugBoxPadding = 2;

View File

@@ -156,7 +156,7 @@ do {\
@implementation ASLayoutElementStyle {
ASDN::RecursiveMutex __instanceLock__;
ASLayoutElementStyleExtensions _extensions;
std::atomic<ASLayoutElementSize> _size;
std::atomic<CGFloat> _spacingBefore;
std::atomic<CGFloat> _spacingAfter;
@@ -181,6 +181,7 @@ do {\
std::atomic<ASEdgeInsets> _padding;
std::atomic<ASEdgeInsets> _border;
std::atomic<CGFloat> _aspectRatio;
ASStackLayoutAlignItems _parentAlignStyle;
#endif
}
@@ -203,6 +204,9 @@ do {\
self = [super init];
if (self) {
_size = ASLayoutElementSizeMake();
#if YOGA
_parentAlignStyle = ASStackLayoutAlignItemsNotSet;
#endif
}
return self;
}
@@ -778,6 +782,10 @@ ASSynthesizeLockingMethodsWithMutex(__instanceLock__)
- (ASEdgeInsets)padding { return _padding.load(); }
- (ASEdgeInsets)border { return _border.load(); }
- (CGFloat)aspectRatio { return _aspectRatio.load(); }
// private (ASLayoutElementStylePrivate.h)
- (ASStackLayoutAlignItems)parentAlignStyle {
return _parentAlignStyle;
}
- (void)setFlexWrap:(YGWrap)flexWrap {
_flexWrap.store(flexWrap);
@@ -823,6 +831,10 @@ ASSynthesizeLockingMethodsWithMutex(__instanceLock__)
_aspectRatio.store(aspectRatio);
ASLayoutElementStyleCallDelegate(ASYogaAspectRatioProperty);
}
// private (ASLayoutElementStylePrivate.h)
- (void)setParentAlignStyle:(ASStackLayoutAlignItems)style {
_parentAlignStyle = style;
}
#endif /* YOGA */

View File

@@ -26,6 +26,7 @@
@end
// pre-order, depth-first
AS_EXTERN void ASDisplayNodePerformBlockOnEveryYogaChild(ASDisplayNode *node, void(^block)(ASDisplayNode *node));
#pragma mark - Yoga Type Conversion Helpers
@@ -40,9 +41,10 @@ AS_EXTERN float yogaDimensionToPercent(ASDimension dimension);
AS_EXTERN ASDimension dimensionForEdgeWithEdgeInsets(YGEdge edge, ASEdgeInsets insets);
AS_EXTERN void ASLayoutElementYogaUpdateMeasureFunc(YGNodeRef yogaNode, id <ASLayoutElement> layoutElement);
AS_EXTERN float ASLayoutElementYogaBaselineFunc(YGNodeRef yogaNode, const float width, const float height);
AS_EXTERN YGSize ASLayoutElementYogaMeasureFunc(YGNodeRef yogaNode,
float width, YGMeasureMode widthMode,
float height, YGMeasureMode heightMode);
float width, YGMeasureMode widthMode,
float height, YGMeasureMode heightMode);
#pragma mark - Yoga Style Setter Helpers

View File

@@ -7,7 +7,7 @@
//
#import <AsyncDisplayKit/ASYogaUtilities.h>
#import <AsyncDisplayKit/ASLayoutElementStylePrivate.h>
#if YOGA /* YOGA */
@implementation ASDisplayNode (YogaHelpers)
@@ -143,23 +143,51 @@ void ASLayoutElementYogaUpdateMeasureFunc(YGNodeRef yogaNode, id <ASLayoutElemen
if (yogaNode == NULL) {
return;
}
BOOL hasMeasureFunc = (YGNodeGetMeasureFunc(yogaNode) != NULL);
if (layoutElement != nil && [layoutElement implementsLayoutMethod]) {
if (hasMeasureFunc == NO) {
BOOL shouldHaveMeasureFunc = [layoutElement implementsLayoutMethod];
// How expensive is it to set a baselineFunc on all (leaf) nodes?
BOOL shouldHaveBaselineFunc = YES;
if (layoutElement != nil) {
if (shouldHaveMeasureFunc || shouldHaveBaselineFunc) {
// Retain the Context object. This must be explicitly released with a
// __bridge_transfer - YGNodeFree() is not sufficient.
YGNodeSetContext(yogaNode, (__bridge_retained void *)layoutElement);
}
if (shouldHaveMeasureFunc) {
YGNodeSetMeasureFunc(yogaNode, &ASLayoutElementYogaMeasureFunc);
}
if (shouldHaveBaselineFunc) {
YGNodeSetBaselineFunc(yogaNode, &ASLayoutElementYogaBaselineFunc);
}
ASDisplayNodeCAssert(YGNodeGetContext(yogaNode) == (__bridge void *)layoutElement,
@"Yoga node context should contain layoutElement: %@", layoutElement);
} else if (hasMeasureFunc == YES) {
// If we lack any of the conditions above, and currently have a measure func, get rid of it.
} else {
// If we lack any of the conditions above, and currently have a measureFn/baselineFn/context,
// get rid of it.
// Release the __bridge_retained Context object.
__unused id <ASLayoutElement> element = (__bridge_transfer id)YGNodeGetContext(yogaNode);
__unused id<ASLayoutElement> element = (__bridge_transfer id)YGNodeGetContext(yogaNode);
YGNodeSetContext(yogaNode, NULL);
YGNodeSetMeasureFunc(yogaNode, NULL);
YGNodeSetBaselineFunc(yogaNode, NULL);
}
}
float ASLayoutElementYogaBaselineFunc(YGNodeRef yogaNode, const float width, const float height)
{
id<ASLayoutElement> layoutElement = (__bridge id<ASLayoutElement>)YGNodeGetContext(yogaNode);
ASDisplayNodeCAssert([layoutElement conformsToProtocol:@protocol(ASLayoutElement)],
@"Yoga context must be <ASLayoutElement>");
ASDisplayNode *displayNode = ASDynamicCast(layoutElement, ASDisplayNode);
switch (displayNode.style.parentAlignStyle) {
case ASStackLayoutAlignItemsBaselineFirst:
return layoutElement.style.ascender;
case ASStackLayoutAlignItemsBaselineLast:
return height + layoutElement.style.descender;
default:
return 0;
}
}

View File

@@ -0,0 +1,21 @@
//
// ASAbstractLayoutController+FrameworkPrivate.h
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
//
// The following methods are ONLY for use by _ASDisplayLayer, _ASDisplayView, and ASDisplayNode.
// These methods must never be called or overridden by other classes.
//
#include <vector>
@interface ASAbstractLayoutController (FrameworkPrivate)
+ (std::vector<std::vector<ASRangeTuningParameters>>)defaultTuningParameters;
@end

Some files were not shown because too many files have changed in this diff Show More