Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Dasmer Singh 2015-07-08 23:03:29 -04:00
commit 31eef22111
227 changed files with 7339 additions and 1014 deletions

View File

@ -0,0 +1,19 @@
/* 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.
*/
#import <UIKit/UIKit.h>
//! Project version number for AsyncDisplayKit-iOS.
FOUNDATION_EXPORT double AsyncDisplayKit_iOSVersionNumber;
//! Project version string for AsyncDisplayKit-iOS.
FOUNDATION_EXPORT const unsigned char AsyncDisplayKit_iOSVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <AsyncDisplayKit_iOS/PublicHeader.h>

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>com.facebook.$(PRODUCT_NAME:rfc1034identifier)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>

View File

@ -1,17 +1,18 @@
Pod::Spec.new do |spec|
spec.name = 'AsyncDisplayKit'
spec.version = '1.1.1'
spec.version = '1.2.2'
spec.license = { :type => 'BSD' }
spec.homepage = 'http://asyncdisplaykit.org'
spec.authors = { 'Ryan Nystrom' => 'rnystrom@fb.com', 'Scott Goodson' => 'scottg@fb.com' }
spec.authors = { 'Scott Goodson' => 'scottgoodson@gmail.com', 'Ryan Nystrom' => 'rnystrom@fb.com' }
spec.summary = 'Smooth asynchronous user interfaces for iOS apps.'
spec.source = { :git => 'https://github.com/facebook/AsyncDisplayKit.git', :tag => '1.1.1' }
spec.source = { :git => 'https://github.com/facebook/AsyncDisplayKit.git', :tag => '1.2.2' }
spec.documentation_url = 'http://asyncdisplaykit.org/appledoc/'
spec.public_header_files = [
'AsyncDisplayKit/*.h',
'AsyncDisplayKit/Details/**/*.h',
'AsyncDisplayKit/Layout/*.h',
'Base/*.h'
]
@ -25,7 +26,10 @@ Pod::Spec.new do |spec|
# ASDealloc2MainObject must be compiled with MRR
spec.requires_arc = true
spec.exclude_files = ['AsyncDisplayKit/Details/ASDealloc2MainObject.m']
spec.exclude_files = [
'AsyncDisplayKit/Details/ASDealloc2MainObject.h',
'AsyncDisplayKit/Details/ASDealloc2MainObject.m',
]
spec.subspec 'ASDealloc2MainObject' do |mrr|
mrr.requires_arc = false
mrr.source_files = [

View File

@ -26,7 +26,6 @@
055F1A3819ABD413004DAFF1 /* ASRangeController.h in Headers */ = {isa = PBXBuildFile; fileRef = 055F1A3619ABD413004DAFF1 /* ASRangeController.h */; settings = {ATTRIBUTES = (Public, ); }; };
055F1A3919ABD413004DAFF1 /* ASRangeController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 055F1A3719ABD413004DAFF1 /* ASRangeController.mm */; };
055F1A3C19ABD43F004DAFF1 /* ASCellNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 055F1A3A19ABD43F004DAFF1 /* ASCellNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
055F1A3D19ABD43F004DAFF1 /* ASCellNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 055F1A3B19ABD43F004DAFF1 /* ASCellNode.m */; };
056D21551ABCEF50001107EF /* ASImageNodeSnapshotTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 056D21541ABCEF50001107EF /* ASImageNodeSnapshotTests.m */; };
0574D5E219C110940097DC25 /* ASTableViewProtocols.h in Headers */ = {isa = PBXBuildFile; fileRef = 0574D5E119C110610097DC25 /* ASTableViewProtocols.h */; settings = {ATTRIBUTES = (Public, ); }; };
057D02C41AC0A66700C7AC3C /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 057D02C31AC0A66700C7AC3C /* main.m */; };
@ -141,6 +140,17 @@
05EA6FE71AC0966E00E35788 /* ASSnapshotTestCase.mm in Sources */ = {isa = PBXBuildFile; fileRef = 05EA6FE61AC0966E00E35788 /* ASSnapshotTestCase.mm */; };
05F20AA41A15733C00DCA68A /* ASImageProtocols.h in Headers */ = {isa = PBXBuildFile; fileRef = 05F20AA31A15733C00DCA68A /* ASImageProtocols.h */; settings = {ATTRIBUTES = (Public, ); }; };
1950C4491A3BB5C1005C8279 /* ASEqualityHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = 1950C4481A3BB5C1005C8279 /* ASEqualityHelpers.h */; };
204C979E1B362CB3002B1083 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 204C979D1B362CB3002B1083 /* Default-568h@2x.png */; };
205F0E0F1B371875007741D0 /* UICollectionViewLayout+ASConvenience.h in Headers */ = {isa = PBXBuildFile; fileRef = 205F0E0D1B371875007741D0 /* UICollectionViewLayout+ASConvenience.h */; settings = {ATTRIBUTES = (Public, ); }; };
205F0E101B371875007741D0 /* UICollectionViewLayout+ASConvenience.m in Sources */ = {isa = PBXBuildFile; fileRef = 205F0E0E1B371875007741D0 /* UICollectionViewLayout+ASConvenience.m */; };
205F0E121B371BD7007741D0 /* ASScrollDirection.m in Sources */ = {isa = PBXBuildFile; fileRef = 205F0E111B371BD7007741D0 /* ASScrollDirection.m */; };
205F0E191B37339C007741D0 /* ASAbstractLayoutController.h in Headers */ = {isa = PBXBuildFile; fileRef = 205F0E171B37339C007741D0 /* ASAbstractLayoutController.h */; settings = {ATTRIBUTES = (Public, ); }; };
205F0E1A1B37339C007741D0 /* ASAbstractLayoutController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 205F0E181B37339C007741D0 /* ASAbstractLayoutController.mm */; };
205F0E1D1B373A2C007741D0 /* ASCollectionViewLayoutController.h in Headers */ = {isa = PBXBuildFile; fileRef = 205F0E1B1B373A2C007741D0 /* ASCollectionViewLayoutController.h */; settings = {ATTRIBUTES = (Public, ); }; };
205F0E1E1B373A2C007741D0 /* ASCollectionViewLayoutController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 205F0E1C1B373A2C007741D0 /* ASCollectionViewLayoutController.mm */; };
205F0E211B376416007741D0 /* CGRect+ASConvenience.h in Headers */ = {isa = PBXBuildFile; fileRef = 205F0E1F1B376416007741D0 /* CGRect+ASConvenience.h */; };
205F0E221B376416007741D0 /* CGRect+ASConvenience.m in Sources */ = {isa = PBXBuildFile; fileRef = 205F0E201B376416007741D0 /* CGRect+ASConvenience.m */; };
242995D31B29743C00090100 /* ASBasicImageDownloaderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 242995D21B29743C00090100 /* ASBasicImageDownloaderTests.m */; };
2911485C1A77147A005D0878 /* ASControlNodeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 2911485B1A77147A005D0878 /* ASControlNodeTests.m */; };
291B63FB1AA53A7A000A71B3 /* ASScrollDirection.h in Headers */ = {isa = PBXBuildFile; fileRef = 296A0A311A951715005ACEAA /* ASScrollDirection.h */; settings = {ATTRIBUTES = (Public, ); }; };
292C599F1A956527007E5DD6 /* ASLayoutRangeType.h in Headers */ = {isa = PBXBuildFile; fileRef = 292C59991A956527007E5DD6 /* ASLayoutRangeType.h */; settings = {ATTRIBUTES = (Public, ); }; };
@ -157,6 +167,10 @@
299DA1AA1A828D2900162D41 /* ASBatchContext.mm in Sources */ = {isa = PBXBuildFile; fileRef = 299DA1A81A828D2900162D41 /* ASBatchContext.mm */; };
29CDC2E21AAE70D000833CA4 /* ASBasicImageDownloaderContextTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 29CDC2E11AAE70D000833CA4 /* ASBasicImageDownloaderContextTests.m */; };
3C9C128519E616EF00E942A0 /* ASTableViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3C9C128419E616EF00E942A0 /* ASTableViewTests.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
430E7C8F1B4C23F100697A4C /* ASIndexPath.h in Headers */ = {isa = PBXBuildFile; fileRef = 430E7C8D1B4C23F100697A4C /* ASIndexPath.h */; };
430E7C901B4C23F100697A4C /* ASIndexPath.h in Headers */ = {isa = PBXBuildFile; fileRef = 430E7C8D1B4C23F100697A4C /* ASIndexPath.h */; };
430E7C911B4C23F100697A4C /* ASIndexPath.m in Sources */ = {isa = PBXBuildFile; fileRef = 430E7C8E1B4C23F100697A4C /* ASIndexPath.m */; };
430E7C921B4C23F100697A4C /* ASIndexPath.m in Sources */ = {isa = PBXBuildFile; fileRef = 430E7C8E1B4C23F100697A4C /* ASIndexPath.m */; };
464052201A3F83C40061C0BA /* ASDataController.h in Headers */ = {isa = PBXBuildFile; fileRef = 464052191A3F83C40061C0BA /* ASDataController.h */; settings = {ATTRIBUTES = (Public, ); }; };
464052211A3F83C40061C0BA /* ASDataController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4640521A1A3F83C40061C0BA /* ASDataController.mm */; };
464052221A3F83C40061C0BA /* ASFlowLayoutController.h in Headers */ = {isa = PBXBuildFile; fileRef = 4640521B1A3F83C40061C0BA /* ASFlowLayoutController.h */; settings = {ATTRIBUTES = (Public, ); }; };
@ -164,10 +178,169 @@
464052241A3F83C40061C0BA /* ASLayoutController.h in Headers */ = {isa = PBXBuildFile; fileRef = 4640521D1A3F83C40061C0BA /* ASLayoutController.h */; settings = {ATTRIBUTES = (Public, ); }; };
464052251A3F83C40061C0BA /* ASMultidimensionalArrayUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 4640521E1A3F83C40061C0BA /* ASMultidimensionalArrayUtils.h */; };
464052261A3F83C40061C0BA /* ASMultidimensionalArrayUtils.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4640521F1A3F83C40061C0BA /* ASMultidimensionalArrayUtils.mm */; };
509E68601B3AED8E009B9150 /* ASScrollDirection.m in Sources */ = {isa = PBXBuildFile; fileRef = 205F0E111B371BD7007741D0 /* ASScrollDirection.m */; };
509E68611B3AEDA0009B9150 /* ASAbstractLayoutController.h in Headers */ = {isa = PBXBuildFile; fileRef = 205F0E171B37339C007741D0 /* ASAbstractLayoutController.h */; settings = {ATTRIBUTES = (Public, ); }; };
509E68621B3AEDA5009B9150 /* ASAbstractLayoutController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 205F0E181B37339C007741D0 /* ASAbstractLayoutController.mm */; };
509E68631B3AEDB4009B9150 /* ASCollectionViewLayoutController.h in Headers */ = {isa = PBXBuildFile; fileRef = 205F0E1B1B373A2C007741D0 /* ASCollectionViewLayoutController.h */; settings = {ATTRIBUTES = (Public, ); }; };
509E68641B3AEDB7009B9150 /* ASCollectionViewLayoutController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 205F0E1C1B373A2C007741D0 /* ASCollectionViewLayoutController.mm */; };
509E68651B3AEDC5009B9150 /* CGRect+ASConvenience.h in Headers */ = {isa = PBXBuildFile; fileRef = 205F0E1F1B376416007741D0 /* CGRect+ASConvenience.h */; };
509E68661B3AEDD7009B9150 /* CGRect+ASConvenience.m in Sources */ = {isa = PBXBuildFile; fileRef = 205F0E201B376416007741D0 /* CGRect+ASConvenience.m */; };
6BDC61F61979037800E50D21 /* AsyncDisplayKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 6BDC61F51978FEA400E50D21 /* AsyncDisplayKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
9F06E5CD1B4CAF4200F015D8 /* ASCollectionViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.m */; };
AC21EC101B3D0BF600C8B19A /* ASStackLayoutChild.h in Headers */ = {isa = PBXBuildFile; fileRef = AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutChild.h */; settings = {ATTRIBUTES = (Public, ); }; };
AC3C4A511A1139C100143C57 /* ASCollectionView.h in Headers */ = {isa = PBXBuildFile; fileRef = AC3C4A4F1A1139C100143C57 /* ASCollectionView.h */; settings = {ATTRIBUTES = (Public, ); }; };
AC3C4A521A1139C100143C57 /* ASCollectionView.mm in Sources */ = {isa = PBXBuildFile; fileRef = AC3C4A501A1139C100143C57 /* ASCollectionView.mm */; };
AC3C4A541A113EEC00143C57 /* ASCollectionViewProtocols.h in Headers */ = {isa = PBXBuildFile; fileRef = AC3C4A531A113EEC00143C57 /* ASCollectionViewProtocols.h */; settings = {ATTRIBUTES = (Public, ); }; };
AC47D9421B3B891B00AAEE9D /* ASCellNode.m in Sources */ = {isa = PBXBuildFile; fileRef = AC6456071B0A335000CF11B8 /* ASCellNode.m */; };
AC47D9451B3BB41900AAEE9D /* ASStaticLayoutSpecDimension.h in Headers */ = {isa = PBXBuildFile; fileRef = AC47D9431B3BB41900AAEE9D /* ASStaticLayoutSpecDimension.h */; settings = {ATTRIBUTES = (Public, ); }; };
AC47D9461B3BB41900AAEE9D /* ASStaticLayoutSpecDimension.mm in Sources */ = {isa = PBXBuildFile; fileRef = AC47D9441B3BB41900AAEE9D /* ASStaticLayoutSpecDimension.mm */; };
AC6456091B0A335000CF11B8 /* ASCellNode.m in Sources */ = {isa = PBXBuildFile; fileRef = AC6456071B0A335000CF11B8 /* ASCellNode.m */; };
ACF6ED1A1B17843500DA7C62 /* ASBackgroundLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = ACF6ED011B17843500DA7C62 /* ASBackgroundLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; };
ACF6ED1B1B17843500DA7C62 /* ASBackgroundLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED021B17843500DA7C62 /* ASBackgroundLayoutSpec.mm */; };
ACF6ED1C1B17843500DA7C62 /* ASCenterLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = ACF6ED031B17843500DA7C62 /* ASCenterLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; };
ACF6ED1D1B17843500DA7C62 /* ASCenterLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED041B17843500DA7C62 /* ASCenterLayoutSpec.mm */; };
ACF6ED201B17843500DA7C62 /* ASDimension.h in Headers */ = {isa = PBXBuildFile; fileRef = ACF6ED071B17843500DA7C62 /* ASDimension.h */; settings = {ATTRIBUTES = (Public, ); }; };
ACF6ED211B17843500DA7C62 /* ASDimension.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED081B17843500DA7C62 /* ASDimension.mm */; };
ACF6ED221B17843500DA7C62 /* ASInsetLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = ACF6ED091B17843500DA7C62 /* ASInsetLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; };
ACF6ED231B17843500DA7C62 /* ASInsetLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED0A1B17843500DA7C62 /* ASInsetLayoutSpec.mm */; };
ACF6ED241B17843500DA7C62 /* ASLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = ACF6ED0B1B17843500DA7C62 /* ASLayout.h */; settings = {ATTRIBUTES = (Public, ); }; };
ACF6ED251B17843500DA7C62 /* ASLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED0C1B17843500DA7C62 /* ASLayout.mm */; };
ACF6ED261B17843500DA7C62 /* ASLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = ACF6ED0D1B17843500DA7C62 /* ASLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; };
ACF6ED271B17843500DA7C62 /* ASLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED0E1B17843500DA7C62 /* ASLayoutSpec.mm */; };
ACF6ED2A1B17843500DA7C62 /* ASLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = ACF6ED111B17843500DA7C62 /* ASLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; };
ACF6ED2B1B17843500DA7C62 /* ASOverlayLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = ACF6ED121B17843500DA7C62 /* ASOverlayLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; };
ACF6ED2C1B17843500DA7C62 /* ASOverlayLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED131B17843500DA7C62 /* ASOverlayLayoutSpec.mm */; };
ACF6ED2D1B17843500DA7C62 /* ASRatioLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = ACF6ED141B17843500DA7C62 /* ASRatioLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; };
ACF6ED2E1B17843500DA7C62 /* ASRatioLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED151B17843500DA7C62 /* ASRatioLayoutSpec.mm */; };
ACF6ED2F1B17843500DA7C62 /* ASStackLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = ACF6ED161B17843500DA7C62 /* ASStackLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; };
ACF6ED301B17843500DA7C62 /* ASStackLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED171B17843500DA7C62 /* ASStackLayoutSpec.mm */; };
ACF6ED311B17843500DA7C62 /* ASStaticLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = ACF6ED181B17843500DA7C62 /* ASStaticLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; };
ACF6ED321B17843500DA7C62 /* ASStaticLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED191B17843500DA7C62 /* ASStaticLayoutSpec.mm */; };
ACF6ED4B1B17847A00DA7C62 /* ASInternalHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = ACF6ED431B17847A00DA7C62 /* ASInternalHelpers.h */; settings = {ATTRIBUTES = (Private, ); }; };
ACF6ED4C1B17847A00DA7C62 /* ASInternalHelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED441B17847A00DA7C62 /* ASInternalHelpers.mm */; };
ACF6ED4D1B17847A00DA7C62 /* ASLayoutSpecUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = ACF6ED451B17847A00DA7C62 /* ASLayoutSpecUtilities.h */; settings = {ATTRIBUTES = (Private, ); }; };
ACF6ED4E1B17847A00DA7C62 /* ASStackLayoutSpecUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = ACF6ED461B17847A00DA7C62 /* ASStackLayoutSpecUtilities.h */; settings = {ATTRIBUTES = (Private, ); }; };
ACF6ED4F1B17847A00DA7C62 /* ASStackPositionedLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = ACF6ED471B17847A00DA7C62 /* ASStackPositionedLayout.h */; settings = {ATTRIBUTES = (Private, ); }; };
ACF6ED501B17847A00DA7C62 /* ASStackPositionedLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED481B17847A00DA7C62 /* ASStackPositionedLayout.mm */; };
ACF6ED511B17847A00DA7C62 /* ASStackUnpositionedLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = ACF6ED491B17847A00DA7C62 /* ASStackUnpositionedLayout.h */; settings = {ATTRIBUTES = (Public, ); }; };
ACF6ED521B17847A00DA7C62 /* ASStackUnpositionedLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED4A1B17847A00DA7C62 /* ASStackUnpositionedLayout.mm */; };
ACF6ED5C1B178DC700DA7C62 /* ASCenterLayoutSpecSnapshotTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED531B178DC700DA7C62 /* ASCenterLayoutSpecSnapshotTests.mm */; };
ACF6ED5D1B178DC700DA7C62 /* ASDimensionTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED541B178DC700DA7C62 /* ASDimensionTests.mm */; };
ACF6ED5E1B178DC700DA7C62 /* ASInsetLayoutSpecSnapshotTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED551B178DC700DA7C62 /* ASInsetLayoutSpecSnapshotTests.mm */; };
ACF6ED601B178DC700DA7C62 /* ASLayoutSpecSnapshotTestsHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED581B178DC700DA7C62 /* ASLayoutSpecSnapshotTestsHelper.m */; };
ACF6ED611B178DC700DA7C62 /* ASOverlayLayoutSpecSnapshotTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED591B178DC700DA7C62 /* ASOverlayLayoutSpecSnapshotTests.mm */; };
ACF6ED621B178DC700DA7C62 /* ASRatioLayoutSpecSnapshotTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED5A1B178DC700DA7C62 /* ASRatioLayoutSpecSnapshotTests.mm */; };
ACF6ED631B178DC700DA7C62 /* ASStackLayoutSpecSnapshotTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED5B1B178DC700DA7C62 /* ASStackLayoutSpecSnapshotTests.mm */; };
B31A241E1B0114FD0016AE7A /* AsyncDisplayKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 6BDC61F51978FEA400E50D21 /* AsyncDisplayKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
B35061DF1B010EDF0018CF92 /* AsyncDisplayKit-iOS.h in Headers */ = {isa = PBXBuildFile; fileRef = B35061DE1B010EDF0018CF92 /* AsyncDisplayKit-iOS.h */; settings = {ATTRIBUTES = (Public, ); }; };
B35061F31B010EFD0018CF92 /* ASCellNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 055F1A3A19ABD43F004DAFF1 /* ASCellNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
B35061F51B010EFD0018CF92 /* ASCollectionView.h in Headers */ = {isa = PBXBuildFile; fileRef = AC3C4A4F1A1139C100143C57 /* ASCollectionView.h */; settings = {ATTRIBUTES = (Public, ); }; };
B35061F61B010EFD0018CF92 /* ASCollectionView.mm in Sources */ = {isa = PBXBuildFile; fileRef = AC3C4A501A1139C100143C57 /* ASCollectionView.mm */; };
B35061F71B010EFD0018CF92 /* ASCollectionViewProtocols.h in Headers */ = {isa = PBXBuildFile; fileRef = AC3C4A531A113EEC00143C57 /* ASCollectionViewProtocols.h */; settings = {ATTRIBUTES = (Public, ); }; };
B35061F81B010EFD0018CF92 /* ASControlNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 058D09D5195D050800B7D73C /* ASControlNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
B35061F91B010EFD0018CF92 /* ASControlNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 058D09D6195D050800B7D73C /* ASControlNode.m */; };
B35061FA1B010EFD0018CF92 /* ASControlNode+Subclasses.h in Headers */ = {isa = PBXBuildFile; fileRef = 058D09D7195D050800B7D73C /* ASControlNode+Subclasses.h */; settings = {ATTRIBUTES = (Public, ); }; };
B35061FB1B010EFD0018CF92 /* ASDisplayNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 058D09D8195D050800B7D73C /* ASDisplayNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
B35061FC1B010EFD0018CF92 /* ASDisplayNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = 058D09D9195D050800B7D73C /* ASDisplayNode.mm */; };
B35061FD1B010EFD0018CF92 /* ASDisplayNode+Subclasses.h in Headers */ = {isa = PBXBuildFile; fileRef = 058D09DA195D050800B7D73C /* ASDisplayNode+Subclasses.h */; settings = {ATTRIBUTES = (Public, ); }; };
B35061FE1B010EFD0018CF92 /* ASDisplayNodeExtras.h in Headers */ = {isa = PBXBuildFile; fileRef = 058D09DB195D050800B7D73C /* ASDisplayNodeExtras.h */; settings = {ATTRIBUTES = (Public, ); }; };
B35061FF1B010EFD0018CF92 /* ASDisplayNodeExtras.mm in Sources */ = {isa = PBXBuildFile; fileRef = 058D09DC195D050800B7D73C /* ASDisplayNodeExtras.mm */; };
B35062001B010EFD0018CF92 /* ASEditableTextNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 0587F9BB1A7309ED00AFF0BA /* ASEditableTextNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
B35062011B010EFD0018CF92 /* ASEditableTextNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0587F9BC1A7309ED00AFF0BA /* ASEditableTextNode.mm */; };
B35062021B010EFD0018CF92 /* ASImageNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 058D09DD195D050800B7D73C /* ASImageNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
B35062031B010EFD0018CF92 /* ASImageNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = 058D09DE195D050800B7D73C /* ASImageNode.mm */; };
B35062041B010EFD0018CF92 /* ASMultiplexImageNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 0516FA3E1A1563D200B4EBED /* ASMultiplexImageNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
B35062051B010EFD0018CF92 /* ASMultiplexImageNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0516FA3F1A1563D200B4EBED /* ASMultiplexImageNode.mm */; };
B35062061B010EFD0018CF92 /* ASNetworkImageNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 055B9FA61A1C154B00035D6D /* ASNetworkImageNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
B35062071B010EFD0018CF92 /* ASNetworkImageNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = 055B9FA71A1C154B00035D6D /* ASNetworkImageNode.mm */; };
B35062081B010EFD0018CF92 /* ASScrollNode.h in Headers */ = {isa = PBXBuildFile; fileRef = D785F6601A74327E00291744 /* ASScrollNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
B35062091B010EFD0018CF92 /* ASScrollNode.m in Sources */ = {isa = PBXBuildFile; fileRef = D785F6611A74327E00291744 /* ASScrollNode.m */; };
B350620A1B010EFD0018CF92 /* ASTableView.h in Headers */ = {isa = PBXBuildFile; fileRef = 055F1A3219ABD3E3004DAFF1 /* ASTableView.h */; settings = {ATTRIBUTES = (Public, ); }; };
B350620B1B010EFD0018CF92 /* ASTableView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 055F1A3319ABD3E3004DAFF1 /* ASTableView.mm */; };
B350620C1B010EFD0018CF92 /* ASTableViewProtocols.h in Headers */ = {isa = PBXBuildFile; fileRef = 0574D5E119C110610097DC25 /* ASTableViewProtocols.h */; settings = {ATTRIBUTES = (Public, ); }; };
B350620D1B010EFD0018CF92 /* ASTextNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 058D09DF195D050800B7D73C /* ASTextNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
B350620E1B010EFD0018CF92 /* ASTextNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = 058D09E0195D050800B7D73C /* ASTextNode.mm */; };
B350620F1B010EFD0018CF92 /* _ASDisplayLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 058D09E2195D050800B7D73C /* _ASDisplayLayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
B35062101B010EFD0018CF92 /* _ASDisplayLayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 058D09E3195D050800B7D73C /* _ASDisplayLayer.mm */; };
B35062111B010EFD0018CF92 /* _ASDisplayView.h in Headers */ = {isa = PBXBuildFile; fileRef = 058D09E4195D050800B7D73C /* _ASDisplayView.h */; settings = {ATTRIBUTES = (Public, ); }; };
B35062121B010EFD0018CF92 /* _ASDisplayView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 058D09E5195D050800B7D73C /* _ASDisplayView.mm */; };
B35062131B010EFD0018CF92 /* ASBasicImageDownloader.h in Headers */ = {isa = PBXBuildFile; fileRef = 054963471A1EA066000F8E56 /* ASBasicImageDownloader.h */; settings = {ATTRIBUTES = (Public, ); }; };
B35062141B010EFD0018CF92 /* ASBasicImageDownloader.mm in Sources */ = {isa = PBXBuildFile; fileRef = 054963481A1EA066000F8E56 /* ASBasicImageDownloader.mm */; };
B35062151B010EFD0018CF92 /* ASBatchContext.h in Headers */ = {isa = PBXBuildFile; fileRef = 299DA1A71A828D2900162D41 /* ASBatchContext.h */; settings = {ATTRIBUTES = (Public, ); }; };
B35062161B010EFD0018CF92 /* ASBatchContext.mm in Sources */ = {isa = PBXBuildFile; fileRef = 299DA1A81A828D2900162D41 /* ASBatchContext.mm */; };
B35062171B010EFD0018CF92 /* ASDataController.h in Headers */ = {isa = PBXBuildFile; fileRef = 464052191A3F83C40061C0BA /* ASDataController.h */; settings = {ATTRIBUTES = (Public, ); }; };
B35062181B010EFD0018CF92 /* ASDataController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4640521A1A3F83C40061C0BA /* ASDataController.mm */; };
B35062191B010EFD0018CF92 /* ASDealloc2MainObject.h in Headers */ = {isa = PBXBuildFile; fileRef = 05A6D05819D0EB64002DD95E /* ASDealloc2MainObject.h */; settings = {ATTRIBUTES = (Public, ); }; };
B350621A1B010EFD0018CF92 /* ASDealloc2MainObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 05A6D05919D0EB64002DD95E /* ASDealloc2MainObject.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
B350621B1B010EFD0018CF92 /* ASFlowLayoutController.h in Headers */ = {isa = PBXBuildFile; fileRef = 4640521B1A3F83C40061C0BA /* ASFlowLayoutController.h */; settings = {ATTRIBUTES = (Public, ); }; };
B350621C1B010EFD0018CF92 /* ASFlowLayoutController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4640521C1A3F83C40061C0BA /* ASFlowLayoutController.mm */; };
B350621D1B010EFD0018CF92 /* ASHighlightOverlayLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 058D09E6195D050800B7D73C /* ASHighlightOverlayLayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
B350621E1B010EFD0018CF92 /* ASHighlightOverlayLayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 058D09E7195D050800B7D73C /* ASHighlightOverlayLayer.mm */; };
B350621F1B010EFD0018CF92 /* ASImageProtocols.h in Headers */ = {isa = PBXBuildFile; fileRef = 05F20AA31A15733C00DCA68A /* ASImageProtocols.h */; settings = {ATTRIBUTES = (Public, ); }; };
B35062201B010EFD0018CF92 /* ASLayoutController.h in Headers */ = {isa = PBXBuildFile; fileRef = 4640521D1A3F83C40061C0BA /* ASLayoutController.h */; settings = {ATTRIBUTES = (Public, ); }; };
B35062211B010EFD0018CF92 /* ASLayoutRangeType.h in Headers */ = {isa = PBXBuildFile; fileRef = 292C59991A956527007E5DD6 /* ASLayoutRangeType.h */; settings = {ATTRIBUTES = (Public, ); }; };
B35062221B010EFD0018CF92 /* ASMultidimensionalArrayUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 4640521E1A3F83C40061C0BA /* ASMultidimensionalArrayUtils.h */; };
B35062231B010EFD0018CF92 /* ASMultidimensionalArrayUtils.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4640521F1A3F83C40061C0BA /* ASMultidimensionalArrayUtils.mm */; };
B35062241B010EFD0018CF92 /* ASMutableAttributedStringBuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = 058D09E8195D050800B7D73C /* ASMutableAttributedStringBuilder.h */; settings = {ATTRIBUTES = (Public, ); }; };
B35062251B010EFD0018CF92 /* ASMutableAttributedStringBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = 058D09E9195D050800B7D73C /* ASMutableAttributedStringBuilder.m */; };
B35062261B010EFD0018CF92 /* ASRangeController.h in Headers */ = {isa = PBXBuildFile; fileRef = 055F1A3619ABD413004DAFF1 /* ASRangeController.h */; settings = {ATTRIBUTES = (Public, ); }; };
B35062271B010EFD0018CF92 /* ASRangeController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 055F1A3719ABD413004DAFF1 /* ASRangeController.mm */; };
B35062281B010EFD0018CF92 /* ASRangeHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 292C599C1A956527007E5DD6 /* ASRangeHandler.h */; settings = {ATTRIBUTES = (Public, ); }; };
B35062291B010EFD0018CF92 /* ASRangeHandlerPreload.h in Headers */ = {isa = PBXBuildFile; fileRef = 292C599A1A956527007E5DD6 /* ASRangeHandlerPreload.h */; settings = {ATTRIBUTES = (Public, ); }; };
B350622A1B010EFD0018CF92 /* ASRangeHandlerPreload.mm in Sources */ = {isa = PBXBuildFile; fileRef = 292C599B1A956527007E5DD6 /* ASRangeHandlerPreload.mm */; };
B350622B1B010EFD0018CF92 /* ASRangeHandlerRender.h in Headers */ = {isa = PBXBuildFile; fileRef = 292C599D1A956527007E5DD6 /* ASRangeHandlerRender.h */; settings = {ATTRIBUTES = (Public, ); }; };
B350622C1B010EFD0018CF92 /* ASRangeHandlerRender.mm in Sources */ = {isa = PBXBuildFile; fileRef = 292C599E1A956527007E5DD6 /* ASRangeHandlerRender.mm */; };
B350622D1B010EFD0018CF92 /* ASScrollDirection.h in Headers */ = {isa = PBXBuildFile; fileRef = 296A0A311A951715005ACEAA /* ASScrollDirection.h */; settings = {ATTRIBUTES = (Public, ); }; };
B350622E1B010EFD0018CF92 /* ASTextNodeCoreTextAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 058D09EA195D050800B7D73C /* ASTextNodeCoreTextAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
B350622F1B010EFD0018CF92 /* ASTextNodeCoreTextAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 058D09EB195D050800B7D73C /* ASTextNodeCoreTextAdditions.m */; };
B35062301B010EFD0018CF92 /* ASTextNodeRenderer.h in Headers */ = {isa = PBXBuildFile; fileRef = 058D09EC195D050800B7D73C /* ASTextNodeRenderer.h */; settings = {ATTRIBUTES = (Public, ); }; };
B35062311B010EFD0018CF92 /* ASTextNodeRenderer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 058D09ED195D050800B7D73C /* ASTextNodeRenderer.mm */; };
B35062321B010EFD0018CF92 /* ASTextNodeShadower.h in Headers */ = {isa = PBXBuildFile; fileRef = 058D09EE195D050800B7D73C /* ASTextNodeShadower.h */; settings = {ATTRIBUTES = (Public, ); }; };
B35062331B010EFD0018CF92 /* ASTextNodeShadower.m in Sources */ = {isa = PBXBuildFile; fileRef = 058D09EF195D050800B7D73C /* ASTextNodeShadower.m */; };
B35062341B010EFD0018CF92 /* ASTextNodeTextKitHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = 058D09F0195D050800B7D73C /* ASTextNodeTextKitHelpers.h */; settings = {ATTRIBUTES = (Public, ); }; };
B35062351B010EFD0018CF92 /* ASTextNodeTextKitHelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = 058D09F1195D050800B7D73C /* ASTextNodeTextKitHelpers.mm */; };
B35062361B010EFD0018CF92 /* ASTextNodeTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 058D09F2195D050800B7D73C /* ASTextNodeTypes.h */; settings = {ATTRIBUTES = (Public, ); }; };
B35062371B010EFD0018CF92 /* ASTextNodeWordKerner.h in Headers */ = {isa = PBXBuildFile; fileRef = 058D09F3195D050800B7D73C /* ASTextNodeWordKerner.h */; settings = {ATTRIBUTES = (Public, ); }; };
B35062381B010EFD0018CF92 /* ASTextNodeWordKerner.m in Sources */ = {isa = PBXBuildFile; fileRef = 058D09F4195D050800B7D73C /* ASTextNodeWordKerner.m */; };
B35062391B010EFD0018CF92 /* ASThread.h in Headers */ = {isa = PBXBuildFile; fileRef = 058D0A12195D050800B7D73C /* ASThread.h */; settings = {ATTRIBUTES = (Public, ); }; };
B350623A1B010EFD0018CF92 /* NSMutableAttributedString+TextKitAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 058D09F5195D050800B7D73C /* NSMutableAttributedString+TextKitAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
B350623B1B010EFD0018CF92 /* NSMutableAttributedString+TextKitAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 058D09F6195D050800B7D73C /* NSMutableAttributedString+TextKitAdditions.m */; };
B350623C1B010EFD0018CF92 /* _ASAsyncTransaction.h in Headers */ = {isa = PBXBuildFile; fileRef = 058D09F8195D050800B7D73C /* _ASAsyncTransaction.h */; settings = {ATTRIBUTES = (Public, ); }; };
B350623D1B010EFD0018CF92 /* _ASAsyncTransaction.m in Sources */ = {isa = PBXBuildFile; fileRef = 058D09F9195D050800B7D73C /* _ASAsyncTransaction.m */; };
B350623E1B010EFD0018CF92 /* _ASAsyncTransactionContainer+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 058D09FA195D050800B7D73C /* _ASAsyncTransactionContainer+Private.h */; settings = {ATTRIBUTES = (Public, ); }; };
B350623F1B010EFD0018CF92 /* _ASAsyncTransactionContainer.h in Headers */ = {isa = PBXBuildFile; fileRef = 058D09FB195D050800B7D73C /* _ASAsyncTransactionContainer.h */; settings = {ATTRIBUTES = (Public, ); }; };
B35062401B010EFD0018CF92 /* _ASAsyncTransactionContainer.m in Sources */ = {isa = PBXBuildFile; fileRef = 058D09FC195D050800B7D73C /* _ASAsyncTransactionContainer.m */; };
B35062411B010EFD0018CF92 /* _ASAsyncTransactionGroup.h in Headers */ = {isa = PBXBuildFile; fileRef = 058D09FD195D050800B7D73C /* _ASAsyncTransactionGroup.h */; settings = {ATTRIBUTES = (Public, ); }; };
B35062421B010EFD0018CF92 /* _ASAsyncTransactionGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = 058D09FE195D050800B7D73C /* _ASAsyncTransactionGroup.m */; };
B35062431B010EFD0018CF92 /* UIView+ASConvenience.h in Headers */ = {isa = PBXBuildFile; fileRef = 058D09FF195D050800B7D73C /* UIView+ASConvenience.h */; settings = {ATTRIBUTES = (Public, ); }; };
B35062441B010EFD0018CF92 /* UIView+ASConvenience.m in Sources */ = {isa = PBXBuildFile; fileRef = 058D0A00195D050800B7D73C /* UIView+ASConvenience.m */; };
B35062451B010EFD0018CF92 /* ASBatchFetching.h in Headers */ = {isa = PBXBuildFile; fileRef = 296A0A2C1A9516B2005ACEAA /* ASBatchFetching.h */; settings = {ATTRIBUTES = (Private, ); }; };
B35062461B010EFD0018CF92 /* ASBasicImageDownloaderInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 2967F9E11AB0A4CF0072E4AB /* ASBasicImageDownloaderInternal.h */; settings = {ATTRIBUTES = (Private, ); }; };
B35062471B010EFD0018CF92 /* ASBatchFetching.m in Sources */ = {isa = PBXBuildFile; fileRef = 296A0A2D1A9516B2005ACEAA /* ASBatchFetching.m */; };
B35062481B010EFD0018CF92 /* _AS-objc-internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 058D0A02195D050800B7D73C /* _AS-objc-internal.h */; settings = {ATTRIBUTES = (Private, ); }; };
B35062491B010EFD0018CF92 /* _ASCoreAnimationExtras.h in Headers */ = {isa = PBXBuildFile; fileRef = 058D0A03195D050800B7D73C /* _ASCoreAnimationExtras.h */; settings = {ATTRIBUTES = (Private, ); }; };
B350624A1B010EFD0018CF92 /* _ASCoreAnimationExtras.mm in Sources */ = {isa = PBXBuildFile; fileRef = 058D0A04195D050800B7D73C /* _ASCoreAnimationExtras.mm */; };
B350624B1B010EFD0018CF92 /* _ASPendingState.h in Headers */ = {isa = PBXBuildFile; fileRef = 058D0A05195D050800B7D73C /* _ASPendingState.h */; settings = {ATTRIBUTES = (Private, ); }; };
B350624C1B010EFD0018CF92 /* _ASPendingState.m in Sources */ = {isa = PBXBuildFile; fileRef = 058D0A06195D050800B7D73C /* _ASPendingState.m */; };
B350624D1B010EFD0018CF92 /* _ASScopeTimer.h in Headers */ = {isa = PBXBuildFile; fileRef = 058D0A07195D050800B7D73C /* _ASScopeTimer.h */; settings = {ATTRIBUTES = (Private, ); }; };
B350624E1B010EFD0018CF92 /* ASDisplayNode+AsyncDisplay.mm in Sources */ = {isa = PBXBuildFile; fileRef = 058D0A08195D050800B7D73C /* ASDisplayNode+AsyncDisplay.mm */; };
B350624F1B010EFD0018CF92 /* ASDisplayNode+DebugTiming.h in Headers */ = {isa = PBXBuildFile; fileRef = 058D0A09195D050800B7D73C /* ASDisplayNode+DebugTiming.h */; settings = {ATTRIBUTES = (Private, ); }; };
B35062501B010EFD0018CF92 /* ASDisplayNode+DebugTiming.mm in Sources */ = {isa = PBXBuildFile; fileRef = 058D0A0A195D050800B7D73C /* ASDisplayNode+DebugTiming.mm */; };
B35062511B010EFD0018CF92 /* ASDisplayNode+UIViewBridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 058D0A0B195D050800B7D73C /* ASDisplayNode+UIViewBridge.mm */; };
B35062521B010EFD0018CF92 /* ASDisplayNodeInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 058D0A0C195D050800B7D73C /* ASDisplayNodeInternal.h */; settings = {ATTRIBUTES = (Private, ); }; };
B35062531B010EFD0018CF92 /* ASImageNode+CGExtras.h in Headers */ = {isa = PBXBuildFile; fileRef = 058D0A0D195D050800B7D73C /* ASImageNode+CGExtras.h */; settings = {ATTRIBUTES = (Private, ); }; };
B35062541B010EFD0018CF92 /* ASImageNode+CGExtras.m in Sources */ = {isa = PBXBuildFile; fileRef = 058D0A0E195D050800B7D73C /* ASImageNode+CGExtras.m */; };
B35062551B010EFD0018CF92 /* ASSentinel.h in Headers */ = {isa = PBXBuildFile; fileRef = 058D0A10195D050800B7D73C /* ASSentinel.h */; settings = {ATTRIBUTES = (Private, ); }; };
B35062561B010EFD0018CF92 /* ASSentinel.m in Sources */ = {isa = PBXBuildFile; fileRef = 058D0A11195D050800B7D73C /* ASSentinel.m */; };
B35062571B010F070018CF92 /* ASAssert.h in Headers */ = {isa = PBXBuildFile; fileRef = 058D0A43195D058D00B7D73C /* ASAssert.h */; settings = {ATTRIBUTES = (Public, ); }; };
B35062581B010F070018CF92 /* ASAvailability.h in Headers */ = {isa = PBXBuildFile; fileRef = 0516FA3A1A15563400B4EBED /* ASAvailability.h */; settings = {ATTRIBUTES = (Public, ); }; };
B35062591B010F070018CF92 /* ASBaseDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 058D0A44195D058D00B7D73C /* ASBaseDefines.h */; settings = {ATTRIBUTES = (Public, ); }; };
B350625A1B010F070018CF92 /* ASDisplayNodeExtraIvars.h in Headers */ = {isa = PBXBuildFile; fileRef = 058D0A45195D058D00B7D73C /* ASDisplayNodeExtraIvars.h */; settings = {ATTRIBUTES = (Public, ); }; };
B350625B1B010F070018CF92 /* ASEqualityHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = 1950C4481A3BB5C1005C8279 /* ASEqualityHelpers.h */; };
B350625C1B010F070018CF92 /* ASLog.h in Headers */ = {isa = PBXBuildFile; fileRef = 0516FA3B1A15563400B4EBED /* ASLog.h */; settings = {ATTRIBUTES = (Public, ); }; };
B350625D1B0111740018CF92 /* Photos.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 051943141A1575670030A7D0 /* Photos.framework */; };
B350625E1B0111780018CF92 /* AssetsLibrary.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 051943121A1575630030A7D0 /* AssetsLibrary.framework */; };
B350625F1B0111800018CF92 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 058D09AF195D04C000B7D73C /* Foundation.framework */; };
D785F6621A74327E00291744 /* ASScrollNode.h in Headers */ = {isa = PBXBuildFile; fileRef = D785F6601A74327E00291744 /* ASScrollNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
D785F6631A74327E00291744 /* ASScrollNode.m in Sources */ = {isa = PBXBuildFile; fileRef = D785F6611A74327E00291744 /* ASScrollNode.m */; };
DB7121BCD50849C498C886FB /* libPods-AsyncDisplayKitTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = EFA731F0396842FF8AB635EE /* libPods-AsyncDisplayKitTests.a */; };
@ -220,7 +393,6 @@
055F1A3619ABD413004DAFF1 /* ASRangeController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASRangeController.h; sourceTree = "<group>"; };
055F1A3719ABD413004DAFF1 /* ASRangeController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASRangeController.mm; sourceTree = "<group>"; };
055F1A3A19ABD43F004DAFF1 /* ASCellNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCellNode.h; sourceTree = "<group>"; };
055F1A3B19ABD43F004DAFF1 /* ASCellNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCellNode.m; sourceTree = "<group>"; };
056D21501ABCEDA1001107EF /* ASSnapshotTestCase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASSnapshotTestCase.h; sourceTree = "<group>"; };
056D21541ABCEF50001107EF /* ASImageNodeSnapshotTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASImageNodeSnapshotTests.m; sourceTree = "<group>"; };
0574D5E119C110610097DC25 /* ASTableViewProtocols.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ASTableViewProtocols.h; sourceTree = "<group>"; };
@ -230,7 +402,7 @@
057D02C51AC0A66700C7AC3C /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
057D02C61AC0A66700C7AC3C /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
0587F9BB1A7309ED00AFF0BA /* ASEditableTextNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASEditableTextNode.h; sourceTree = "<group>"; };
0587F9BC1A7309ED00AFF0BA /* ASEditableTextNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASEditableTextNode.mm; sourceTree = "<group>"; };
0587F9BC1A7309ED00AFF0BA /* ASEditableTextNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASEditableTextNode.mm; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
058D09AC195D04C000B7D73C /* libAsyncDisplayKit.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libAsyncDisplayKit.a; sourceTree = BUILT_PRODUCTS_DIR; };
058D09AF195D04C000B7D73C /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
058D09B3195D04C000B7D73C /* AsyncDisplayKit-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "AsyncDisplayKit-Prefix.pch"; sourceTree = "<group>"; };
@ -242,15 +414,15 @@
058D09D5195D050800B7D73C /* ASControlNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASControlNode.h; sourceTree = "<group>"; };
058D09D6195D050800B7D73C /* ASControlNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASControlNode.m; sourceTree = "<group>"; };
058D09D7195D050800B7D73C /* ASControlNode+Subclasses.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASControlNode+Subclasses.h"; sourceTree = "<group>"; };
058D09D8195D050800B7D73C /* ASDisplayNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASDisplayNode.h; sourceTree = "<group>"; };
058D09D9195D050800B7D73C /* ASDisplayNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASDisplayNode.mm; sourceTree = "<group>"; };
058D09DA195D050800B7D73C /* ASDisplayNode+Subclasses.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASDisplayNode+Subclasses.h"; sourceTree = "<group>"; };
058D09D8195D050800B7D73C /* ASDisplayNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = ASDisplayNode.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
058D09D9195D050800B7D73C /* ASDisplayNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASDisplayNode.mm; sourceTree = "<group>"; };
058D09DA195D050800B7D73C /* ASDisplayNode+Subclasses.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = "ASDisplayNode+Subclasses.h"; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
058D09DB195D050800B7D73C /* ASDisplayNodeExtras.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASDisplayNodeExtras.h; sourceTree = "<group>"; };
058D09DC195D050800B7D73C /* ASDisplayNodeExtras.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASDisplayNodeExtras.mm; sourceTree = "<group>"; };
058D09DD195D050800B7D73C /* ASImageNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASImageNode.h; sourceTree = "<group>"; };
058D09DE195D050800B7D73C /* ASImageNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASImageNode.mm; sourceTree = "<group>"; };
058D09DE195D050800B7D73C /* ASImageNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASImageNode.mm; sourceTree = "<group>"; };
058D09DF195D050800B7D73C /* ASTextNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASTextNode.h; sourceTree = "<group>"; };
058D09E0195D050800B7D73C /* ASTextNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASTextNode.mm; sourceTree = "<group>"; };
058D09E0195D050800B7D73C /* ASTextNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASTextNode.mm; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
058D09E2195D050800B7D73C /* _ASDisplayLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _ASDisplayLayer.h; sourceTree = "<group>"; };
058D09E3195D050800B7D73C /* _ASDisplayLayer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = _ASDisplayLayer.mm; sourceTree = "<group>"; };
058D09E4195D050800B7D73C /* _ASDisplayView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _ASDisplayView.h; sourceTree = "<group>"; };
@ -316,6 +488,17 @@
05EA6FE61AC0966E00E35788 /* ASSnapshotTestCase.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASSnapshotTestCase.mm; sourceTree = "<group>"; };
05F20AA31A15733C00DCA68A /* ASImageProtocols.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASImageProtocols.h; sourceTree = "<group>"; };
1950C4481A3BB5C1005C8279 /* ASEqualityHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASEqualityHelpers.h; sourceTree = "<group>"; };
204C979D1B362CB3002B1083 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Default-568h@2x.png"; path = "../Default-568h@2x.png"; sourceTree = "<group>"; };
205F0E0D1B371875007741D0 /* UICollectionViewLayout+ASConvenience.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UICollectionViewLayout+ASConvenience.h"; sourceTree = "<group>"; };
205F0E0E1B371875007741D0 /* UICollectionViewLayout+ASConvenience.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UICollectionViewLayout+ASConvenience.m"; sourceTree = "<group>"; };
205F0E111B371BD7007741D0 /* ASScrollDirection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASScrollDirection.m; sourceTree = "<group>"; };
205F0E171B37339C007741D0 /* ASAbstractLayoutController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASAbstractLayoutController.h; sourceTree = "<group>"; };
205F0E181B37339C007741D0 /* ASAbstractLayoutController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASAbstractLayoutController.mm; sourceTree = "<group>"; };
205F0E1B1B373A2C007741D0 /* ASCollectionViewLayoutController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionViewLayoutController.h; sourceTree = "<group>"; };
205F0E1C1B373A2C007741D0 /* ASCollectionViewLayoutController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASCollectionViewLayoutController.mm; sourceTree = "<group>"; };
205F0E1F1B376416007741D0 /* CGRect+ASConvenience.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CGRect+ASConvenience.h"; sourceTree = "<group>"; };
205F0E201B376416007741D0 /* CGRect+ASConvenience.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "CGRect+ASConvenience.m"; sourceTree = "<group>"; };
242995D21B29743C00090100 /* ASBasicImageDownloaderTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASBasicImageDownloaderTests.m; sourceTree = "<group>"; };
2911485B1A77147A005D0878 /* ASControlNodeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASControlNodeTests.m; sourceTree = "<group>"; };
292C59991A956527007E5DD6 /* ASLayoutRangeType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASLayoutRangeType.h; sourceTree = "<group>"; };
292C599A1A956527007E5DD6 /* ASRangeHandlerPreload.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASRangeHandlerPreload.h; sourceTree = "<group>"; };
@ -332,6 +515,8 @@
299DA1A81A828D2900162D41 /* ASBatchContext.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASBatchContext.mm; sourceTree = "<group>"; };
29CDC2E11AAE70D000833CA4 /* ASBasicImageDownloaderContextTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASBasicImageDownloaderContextTests.m; sourceTree = "<group>"; };
3C9C128419E616EF00E942A0 /* ASTableViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASTableViewTests.m; sourceTree = "<group>"; };
430E7C8D1B4C23F100697A4C /* ASIndexPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASIndexPath.h; sourceTree = "<group>"; };
430E7C8E1B4C23F100697A4C /* ASIndexPath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASIndexPath.m; sourceTree = "<group>"; };
464052191A3F83C40061C0BA /* ASDataController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASDataController.h; sourceTree = "<group>"; };
4640521A1A3F83C40061C0BA /* ASDataController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASDataController.mm; sourceTree = "<group>"; };
4640521B1A3F83C40061C0BA /* ASFlowLayoutController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASFlowLayoutController.h; sourceTree = "<group>"; };
@ -340,9 +525,54 @@
4640521E1A3F83C40061C0BA /* ASMultidimensionalArrayUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASMultidimensionalArrayUtils.h; sourceTree = "<group>"; };
4640521F1A3F83C40061C0BA /* ASMultidimensionalArrayUtils.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASMultidimensionalArrayUtils.mm; sourceTree = "<group>"; };
6BDC61F51978FEA400E50D21 /* AsyncDisplayKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AsyncDisplayKit.h; sourceTree = "<group>"; };
9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCollectionViewTests.m; sourceTree = "<group>"; };
AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutChild.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutChild.h; path = AsyncDisplayKit/Layout/ASStackLayoutChild.h; sourceTree = "<group>"; };
AC3C4A4F1A1139C100143C57 /* ASCollectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionView.h; sourceTree = "<group>"; };
AC3C4A501A1139C100143C57 /* ASCollectionView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASCollectionView.mm; sourceTree = "<group>"; };
AC3C4A531A113EEC00143C57 /* ASCollectionViewProtocols.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionViewProtocols.h; sourceTree = "<group>"; };
AC47D9431B3BB41900AAEE9D /* ASStaticLayoutSpecDimension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStaticLayoutSpecDimension.h; path = AsyncDisplayKit/Layout/ASStaticLayoutSpecDimension.h; sourceTree = "<group>"; };
AC47D9441B3BB41900AAEE9D /* ASStaticLayoutSpecDimension.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASStaticLayoutSpecDimension.mm; path = AsyncDisplayKit/Layout/ASStaticLayoutSpecDimension.mm; sourceTree = "<group>"; };
AC6456071B0A335000CF11B8 /* ASCellNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCellNode.m; sourceTree = "<group>"; };
ACF6ED011B17843500DA7C62 /* ASBackgroundLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASBackgroundLayoutSpec.h; path = AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.h; sourceTree = "<group>"; };
ACF6ED021B17843500DA7C62 /* ASBackgroundLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; name = ASBackgroundLayoutSpec.mm; path = AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.mm; sourceTree = "<group>"; };
ACF6ED031B17843500DA7C62 /* ASCenterLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASCenterLayoutSpec.h; path = AsyncDisplayKit/Layout/ASCenterLayoutSpec.h; sourceTree = "<group>"; };
ACF6ED041B17843500DA7C62 /* ASCenterLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; name = ASCenterLayoutSpec.mm; path = AsyncDisplayKit/Layout/ASCenterLayoutSpec.mm; sourceTree = "<group>"; };
ACF6ED071B17843500DA7C62 /* ASDimension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASDimension.h; path = AsyncDisplayKit/Layout/ASDimension.h; sourceTree = "<group>"; };
ACF6ED081B17843500DA7C62 /* ASDimension.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASDimension.mm; path = AsyncDisplayKit/Layout/ASDimension.mm; sourceTree = "<group>"; };
ACF6ED091B17843500DA7C62 /* ASInsetLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASInsetLayoutSpec.h; path = AsyncDisplayKit/Layout/ASInsetLayoutSpec.h; sourceTree = "<group>"; };
ACF6ED0A1B17843500DA7C62 /* ASInsetLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; name = ASInsetLayoutSpec.mm; path = AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm; sourceTree = "<group>"; };
ACF6ED0B1B17843500DA7C62 /* ASLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASLayout.h; path = AsyncDisplayKit/Layout/ASLayout.h; sourceTree = "<group>"; };
ACF6ED0C1B17843500DA7C62 /* ASLayout.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASLayout.mm; path = AsyncDisplayKit/Layout/ASLayout.mm; sourceTree = "<group>"; };
ACF6ED0D1B17843500DA7C62 /* ASLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASLayoutSpec.h; path = AsyncDisplayKit/Layout/ASLayoutSpec.h; sourceTree = "<group>"; };
ACF6ED0E1B17843500DA7C62 /* ASLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; name = ASLayoutSpec.mm; path = AsyncDisplayKit/Layout/ASLayoutSpec.mm; sourceTree = "<group>"; };
ACF6ED111B17843500DA7C62 /* ASLayoutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASLayoutable.h; path = AsyncDisplayKit/Layout/ASLayoutable.h; sourceTree = "<group>"; };
ACF6ED121B17843500DA7C62 /* ASOverlayLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASOverlayLayoutSpec.h; path = AsyncDisplayKit/Layout/ASOverlayLayoutSpec.h; sourceTree = "<group>"; };
ACF6ED131B17843500DA7C62 /* ASOverlayLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; name = ASOverlayLayoutSpec.mm; path = AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm; sourceTree = "<group>"; };
ACF6ED141B17843500DA7C62 /* ASRatioLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASRatioLayoutSpec.h; path = AsyncDisplayKit/Layout/ASRatioLayoutSpec.h; sourceTree = "<group>"; };
ACF6ED151B17843500DA7C62 /* ASRatioLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; name = ASRatioLayoutSpec.mm; path = AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm; sourceTree = "<group>"; };
ACF6ED161B17843500DA7C62 /* ASStackLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutSpec.h; path = AsyncDisplayKit/Layout/ASStackLayoutSpec.h; sourceTree = "<group>"; };
ACF6ED171B17843500DA7C62 /* ASStackLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASStackLayoutSpec.mm; path = AsyncDisplayKit/Layout/ASStackLayoutSpec.mm; sourceTree = "<group>"; };
ACF6ED181B17843500DA7C62 /* ASStaticLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStaticLayoutSpec.h; path = AsyncDisplayKit/Layout/ASStaticLayoutSpec.h; sourceTree = "<group>"; };
ACF6ED191B17843500DA7C62 /* ASStaticLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; name = ASStaticLayoutSpec.mm; path = AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
ACF6ED431B17847A00DA7C62 /* ASInternalHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASInternalHelpers.h; sourceTree = "<group>"; };
ACF6ED441B17847A00DA7C62 /* ASInternalHelpers.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASInternalHelpers.mm; sourceTree = "<group>"; };
ACF6ED451B17847A00DA7C62 /* ASLayoutSpecUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASLayoutSpecUtilities.h; sourceTree = "<group>"; };
ACF6ED461B17847A00DA7C62 /* ASStackLayoutSpecUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASStackLayoutSpecUtilities.h; sourceTree = "<group>"; };
ACF6ED471B17847A00DA7C62 /* ASStackPositionedLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASStackPositionedLayout.h; sourceTree = "<group>"; };
ACF6ED481B17847A00DA7C62 /* ASStackPositionedLayout.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASStackPositionedLayout.mm; sourceTree = "<group>"; };
ACF6ED491B17847A00DA7C62 /* ASStackUnpositionedLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASStackUnpositionedLayout.h; sourceTree = "<group>"; };
ACF6ED4A1B17847A00DA7C62 /* ASStackUnpositionedLayout.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASStackUnpositionedLayout.mm; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
ACF6ED531B178DC700DA7C62 /* ASCenterLayoutSpecSnapshotTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASCenterLayoutSpecSnapshotTests.mm; sourceTree = "<group>"; };
ACF6ED541B178DC700DA7C62 /* ASDimensionTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASDimensionTests.mm; sourceTree = "<group>"; };
ACF6ED551B178DC700DA7C62 /* ASInsetLayoutSpecSnapshotTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASInsetLayoutSpecSnapshotTests.mm; sourceTree = "<group>"; };
ACF6ED571B178DC700DA7C62 /* ASLayoutSpecSnapshotTestsHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASLayoutSpecSnapshotTestsHelper.h; sourceTree = "<group>"; };
ACF6ED581B178DC700DA7C62 /* ASLayoutSpecSnapshotTestsHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = ASLayoutSpecSnapshotTestsHelper.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
ACF6ED591B178DC700DA7C62 /* ASOverlayLayoutSpecSnapshotTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASOverlayLayoutSpecSnapshotTests.mm; sourceTree = "<group>"; };
ACF6ED5A1B178DC700DA7C62 /* ASRatioLayoutSpecSnapshotTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASRatioLayoutSpecSnapshotTests.mm; sourceTree = "<group>"; };
ACF6ED5B1B178DC700DA7C62 /* ASStackLayoutSpecSnapshotTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASStackLayoutSpecSnapshotTests.mm; sourceTree = "<group>"; };
B35061DA1B010EDF0018CF92 /* AsyncDisplayKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AsyncDisplayKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
B35061DD1B010EDF0018CF92 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
B35061DE1B010EDF0018CF92 /* AsyncDisplayKit-iOS.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "AsyncDisplayKit-iOS.h"; sourceTree = "<group>"; };
D3779BCFF841AD3EB56537ED /* Pods-AsyncDisplayKitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AsyncDisplayKitTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-AsyncDisplayKitTests/Pods-AsyncDisplayKitTests.release.xcconfig"; sourceTree = "<group>"; };
D785F6601A74327E00291744 /* ASScrollNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASScrollNode.h; sourceTree = "<group>"; };
D785F6611A74327E00291744 /* ASScrollNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASScrollNode.m; sourceTree = "<group>"; };
@ -382,12 +612,23 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
B35061D61B010EDF0018CF92 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
B350625F1B0111800018CF92 /* Foundation.framework in Frameworks */,
B350625E1B0111780018CF92 /* AssetsLibrary.framework in Frameworks */,
B350625D1B0111740018CF92 /* Photos.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
057D02C01AC0A66700C7AC3C /* AsyncDisplayKitTestHost */ = {
isa = PBXGroup;
children = (
204C979D1B362CB3002B1083 /* Default-568h@2x.png */,
057D02C51AC0A66700C7AC3C /* AppDelegate.h */,
057D02C61AC0A66700C7AC3C /* AppDelegate.m */,
057D02C11AC0A66700C7AC3C /* Supporting Files */,
@ -410,6 +651,7 @@
children = (
058D09B1195D04C000B7D73C /* AsyncDisplayKit */,
058D09C5195D04C000B7D73C /* AsyncDisplayKitTests */,
B35061DB1B010EDF0018CF92 /* AsyncDisplayKit-iOS */,
058D09AE195D04C000B7D73C /* Frameworks */,
058D09AD195D04C000B7D73C /* Products */,
FD40E2760492F0CAAEAD552D /* Pods */,
@ -422,6 +664,7 @@
058D09AC195D04C000B7D73C /* libAsyncDisplayKit.a */,
058D09BC195D04C000B7D73C /* AsyncDisplayKitTests.xctest */,
057D02BF1AC0A66700C7AC3C /* AsyncDisplayKitTestHost.app */,
B35061DA1B010EDF0018CF92 /* AsyncDisplayKit.framework */,
);
name = Products;
sourceTree = "<group>";
@ -443,7 +686,7 @@
isa = PBXGroup;
children = (
055F1A3A19ABD43F004DAFF1 /* ASCellNode.h */,
055F1A3B19ABD43F004DAFF1 /* ASCellNode.m */,
AC6456071B0A335000CF11B8 /* ASCellNode.m */,
AC3C4A4F1A1139C100143C57 /* ASCollectionView.h */,
AC3C4A501A1139C100143C57 /* ASCollectionView.mm */,
AC3C4A531A113EEC00143C57 /* ASCollectionViewProtocols.h */,
@ -473,6 +716,7 @@
6BDC61F51978FEA400E50D21 /* AsyncDisplayKit.h */,
058D09E1195D050800B7D73C /* Details */,
058D0A01195D050800B7D73C /* Private */,
AC6456051B0A333200CF11B8 /* Layout */,
058D09B2195D04C000B7D73C /* Supporting Files */,
);
path = AsyncDisplayKit;
@ -494,9 +738,19 @@
056D21501ABCEDA1001107EF /* ASSnapshotTestCase.h */,
05EA6FE61AC0966E00E35788 /* ASSnapshotTestCase.mm */,
056D21541ABCEF50001107EF /* ASImageNodeSnapshotTests.m */,
ACF6ED531B178DC700DA7C62 /* ASCenterLayoutSpecSnapshotTests.mm */,
ACF6ED551B178DC700DA7C62 /* ASInsetLayoutSpecSnapshotTests.mm */,
ACF6ED591B178DC700DA7C62 /* ASOverlayLayoutSpecSnapshotTests.mm */,
ACF6ED5A1B178DC700DA7C62 /* ASRatioLayoutSpecSnapshotTests.mm */,
ACF6ED5B1B178DC700DA7C62 /* ASStackLayoutSpecSnapshotTests.mm */,
ACF6ED571B178DC700DA7C62 /* ASLayoutSpecSnapshotTestsHelper.h */,
ACF6ED581B178DC700DA7C62 /* ASLayoutSpecSnapshotTestsHelper.m */,
242995D21B29743C00090100 /* ASBasicImageDownloaderTests.m */,
29CDC2E11AAE70D000833CA4 /* ASBasicImageDownloaderContextTests.m */,
296A0A341A951ABF005ACEAA /* ASBatchFetchingTests.m */,
9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.m */,
2911485B1A77147A005D0878 /* ASControlNodeTests.m */,
ACF6ED541B178DC700DA7C62 /* ASDimensionTests.mm */,
058D0A2D195D057000B7D73C /* ASDisplayLayerTests.m */,
058D0A2E195D057000B7D73C /* ASDisplayNodeAppearanceTests.m */,
058D0A2F195D057000B7D73C /* ASDisplayNodeTests.m */,
@ -532,10 +786,14 @@
058D09E3195D050800B7D73C /* _ASDisplayLayer.mm */,
058D09E4195D050800B7D73C /* _ASDisplayView.h */,
058D09E5195D050800B7D73C /* _ASDisplayView.mm */,
205F0E171B37339C007741D0 /* ASAbstractLayoutController.h */,
205F0E181B37339C007741D0 /* ASAbstractLayoutController.mm */,
054963471A1EA066000F8E56 /* ASBasicImageDownloader.h */,
054963481A1EA066000F8E56 /* ASBasicImageDownloader.mm */,
299DA1A71A828D2900162D41 /* ASBatchContext.h */,
299DA1A81A828D2900162D41 /* ASBatchContext.mm */,
205F0E1B1B373A2C007741D0 /* ASCollectionViewLayoutController.h */,
205F0E1C1B373A2C007741D0 /* ASCollectionViewLayoutController.mm */,
464052191A3F83C40061C0BA /* ASDataController.h */,
4640521A1A3F83C40061C0BA /* ASDataController.mm */,
05A6D05819D0EB64002DD95E /* ASDealloc2MainObject.h */,
@ -544,6 +802,8 @@
4640521C1A3F83C40061C0BA /* ASFlowLayoutController.mm */,
058D09E6195D050800B7D73C /* ASHighlightOverlayLayer.h */,
058D09E7195D050800B7D73C /* ASHighlightOverlayLayer.mm */,
430E7C8D1B4C23F100697A4C /* ASIndexPath.h */,
430E7C8E1B4C23F100697A4C /* ASIndexPath.m */,
05F20AA31A15733C00DCA68A /* ASImageProtocols.h */,
4640521D1A3F83C40061C0BA /* ASLayoutController.h */,
292C59991A956527007E5DD6 /* ASLayoutRangeType.h */,
@ -559,6 +819,7 @@
292C599D1A956527007E5DD6 /* ASRangeHandlerRender.h */,
292C599E1A956527007E5DD6 /* ASRangeHandlerRender.mm */,
296A0A311A951715005ACEAA /* ASScrollDirection.h */,
205F0E111B371BD7007741D0 /* ASScrollDirection.m */,
058D09EA195D050800B7D73C /* ASTextNodeCoreTextAdditions.h */,
058D09EB195D050800B7D73C /* ASTextNodeCoreTextAdditions.m */,
058D09EC195D050800B7D73C /* ASTextNodeRenderer.h */,
@ -571,9 +832,13 @@
058D09F3195D050800B7D73C /* ASTextNodeWordKerner.h */,
058D09F4195D050800B7D73C /* ASTextNodeWordKerner.m */,
058D0A12195D050800B7D73C /* ASThread.h */,
205F0E1F1B376416007741D0 /* CGRect+ASConvenience.h */,
205F0E201B376416007741D0 /* CGRect+ASConvenience.m */,
058D09F5195D050800B7D73C /* NSMutableAttributedString+TextKitAdditions.h */,
058D09F6195D050800B7D73C /* NSMutableAttributedString+TextKitAdditions.m */,
058D09F7195D050800B7D73C /* Transactions */,
205F0E0D1B371875007741D0 /* UICollectionViewLayout+ASConvenience.h */,
205F0E0E1B371875007741D0 /* UICollectionViewLayout+ASConvenience.m */,
058D09FF195D050800B7D73C /* UIView+ASConvenience.h */,
058D0A00195D050800B7D73C /* UIView+ASConvenience.m */,
);
@ -615,6 +880,14 @@
058D0A0E195D050800B7D73C /* ASImageNode+CGExtras.m */,
058D0A10195D050800B7D73C /* ASSentinel.h */,
058D0A11195D050800B7D73C /* ASSentinel.m */,
ACF6ED431B17847A00DA7C62 /* ASInternalHelpers.h */,
ACF6ED441B17847A00DA7C62 /* ASInternalHelpers.mm */,
ACF6ED451B17847A00DA7C62 /* ASLayoutSpecUtilities.h */,
ACF6ED461B17847A00DA7C62 /* ASStackLayoutSpecUtilities.h */,
ACF6ED471B17847A00DA7C62 /* ASStackPositionedLayout.h */,
ACF6ED481B17847A00DA7C62 /* ASStackPositionedLayout.mm */,
ACF6ED491B17847A00DA7C62 /* ASStackUnpositionedLayout.h */,
ACF6ED4A1B17847A00DA7C62 /* ASStackUnpositionedLayout.mm */,
);
path = Private;
sourceTree = "<group>";
@ -632,6 +905,56 @@
path = Base;
sourceTree = SOURCE_ROOT;
};
AC6456051B0A333200CF11B8 /* Layout */ = {
isa = PBXGroup;
children = (
ACF6ED011B17843500DA7C62 /* ASBackgroundLayoutSpec.h */,
ACF6ED021B17843500DA7C62 /* ASBackgroundLayoutSpec.mm */,
ACF6ED031B17843500DA7C62 /* ASCenterLayoutSpec.h */,
ACF6ED041B17843500DA7C62 /* ASCenterLayoutSpec.mm */,
ACF6ED071B17843500DA7C62 /* ASDimension.h */,
ACF6ED081B17843500DA7C62 /* ASDimension.mm */,
ACF6ED091B17843500DA7C62 /* ASInsetLayoutSpec.h */,
ACF6ED0A1B17843500DA7C62 /* ASInsetLayoutSpec.mm */,
ACF6ED0B1B17843500DA7C62 /* ASLayout.h */,
ACF6ED0C1B17843500DA7C62 /* ASLayout.mm */,
ACF6ED111B17843500DA7C62 /* ASLayoutable.h */,
ACF6ED0D1B17843500DA7C62 /* ASLayoutSpec.h */,
ACF6ED0E1B17843500DA7C62 /* ASLayoutSpec.mm */,
ACF6ED121B17843500DA7C62 /* ASOverlayLayoutSpec.h */,
ACF6ED131B17843500DA7C62 /* ASOverlayLayoutSpec.mm */,
ACF6ED141B17843500DA7C62 /* ASRatioLayoutSpec.h */,
ACF6ED151B17843500DA7C62 /* ASRatioLayoutSpec.mm */,
AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutChild.h */,
ACF6ED161B17843500DA7C62 /* ASStackLayoutSpec.h */,
ACF6ED171B17843500DA7C62 /* ASStackLayoutSpec.mm */,
ACF6ED181B17843500DA7C62 /* ASStaticLayoutSpec.h */,
ACF6ED191B17843500DA7C62 /* ASStaticLayoutSpec.mm */,
AC47D9431B3BB41900AAEE9D /* ASStaticLayoutSpecDimension.h */,
AC47D9441B3BB41900AAEE9D /* ASStaticLayoutSpecDimension.mm */,
);
name = Layout;
path = ..;
sourceTree = "<group>";
};
B35061DB1B010EDF0018CF92 /* AsyncDisplayKit-iOS */ = {
isa = PBXGroup;
children = (
B35061DE1B010EDF0018CF92 /* AsyncDisplayKit-iOS.h */,
B35061DC1B010EDF0018CF92 /* Supporting Files */,
);
name = "AsyncDisplayKit-iOS";
path = AsyncDisplayKit;
sourceTree = "<group>";
};
B35061DC1B010EDF0018CF92 /* Supporting Files */ = {
isa = PBXGroup;
children = (
B35061DD1B010EDF0018CF92 /* Info.plist */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
FD40E2760492F0CAAEAD552D /* Pods */ = {
isa = PBXGroup;
children = (
@ -648,6 +971,20 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
AC21EC101B3D0BF600C8B19A /* ASStackLayoutChild.h in Headers */,
AC47D9451B3BB41900AAEE9D /* ASStaticLayoutSpecDimension.h in Headers */,
ACF6ED511B17847A00DA7C62 /* ASStackUnpositionedLayout.h in Headers */,
ACF6ED2D1B17843500DA7C62 /* ASRatioLayoutSpec.h in Headers */,
ACF6ED261B17843500DA7C62 /* ASLayoutSpec.h in Headers */,
ACF6ED221B17843500DA7C62 /* ASInsetLayoutSpec.h in Headers */,
ACF6ED201B17843500DA7C62 /* ASDimension.h in Headers */,
ACF6ED2B1B17843500DA7C62 /* ASOverlayLayoutSpec.h in Headers */,
ACF6ED1C1B17843500DA7C62 /* ASCenterLayoutSpec.h in Headers */,
ACF6ED2A1B17843500DA7C62 /* ASLayoutable.h in Headers */,
ACF6ED311B17843500DA7C62 /* ASStaticLayoutSpec.h in Headers */,
ACF6ED241B17843500DA7C62 /* ASLayout.h in Headers */,
ACF6ED2F1B17843500DA7C62 /* ASStackLayoutSpec.h in Headers */,
ACF6ED1A1B17843500DA7C62 /* ASBackgroundLayoutSpec.h in Headers */,
291B63FB1AA53A7A000A71B3 /* ASScrollDirection.h in Headers */,
464052221A3F83C40061C0BA /* ASFlowLayoutController.h in Headers */,
464052241A3F83C40061C0BA /* ASLayoutController.h in Headers */,
@ -660,6 +997,7 @@
058D0A4A195D05CB00B7D73C /* ASDisplayNode.h in Headers */,
1950C4491A3BB5C1005C8279 /* ASEqualityHelpers.h in Headers */,
058D0A4B195D05CB00B7D73C /* ASDisplayNode.mm in Headers */,
430E7C8F1B4C23F100697A4C /* ASIndexPath.h in Headers */,
058D0A4C195D05CB00B7D73C /* ASDisplayNode+Subclasses.h in Headers */,
058D0A4D195D05CB00B7D73C /* ASDisplayNodeExtras.h in Headers */,
058D0A4E195D05CB00B7D73C /* ASDisplayNodeExtras.mm in Headers */,
@ -683,6 +1021,7 @@
058D0A5A195D05DC00B7D73C /* ASMutableAttributedStringBuilder.m in Headers */,
058D0A5B195D05DC00B7D73C /* ASTextNodeCoreTextAdditions.h in Headers */,
058D0A5C195D05DC00B7D73C /* ASTextNodeCoreTextAdditions.m in Headers */,
205F0E191B37339C007741D0 /* ASAbstractLayoutController.h in Headers */,
058D0A5D195D05DC00B7D73C /* ASTextNodeRenderer.h in Headers */,
058D0A5E195D05DC00B7D73C /* ASTextNodeRenderer.mm in Headers */,
058D0A5F195D05DC00B7D73C /* ASTextNodeShadower.h in Headers */,
@ -698,6 +1037,7 @@
058D0A66195D05DC00B7D73C /* NSMutableAttributedString+TextKitAdditions.h in Headers */,
058D0A67195D05DC00B7D73C /* NSMutableAttributedString+TextKitAdditions.m in Headers */,
058D0A68195D05EC00B7D73C /* _ASAsyncTransaction.h in Headers */,
205F0E0F1B371875007741D0 /* UICollectionViewLayout+ASConvenience.h in Headers */,
058D0A69195D05EC00B7D73C /* _ASAsyncTransaction.m in Headers */,
058D0A6A195D05EC00B7D73C /* _ASAsyncTransactionContainer+Private.h in Headers */,
058D0A6B195D05EC00B7D73C /* _ASAsyncTransactionContainer.h in Headers */,
@ -705,8 +1045,10 @@
6BDC61F61979037800E50D21 /* AsyncDisplayKit.h in Headers */,
058D0A6D195D05EC00B7D73C /* _ASAsyncTransactionGroup.h in Headers */,
058D0A6E195D05EC00B7D73C /* _ASAsyncTransactionGroup.m in Headers */,
205F0E1D1B373A2C007741D0 /* ASCollectionViewLayoutController.h in Headers */,
058D0A6F195D05EC00B7D73C /* UIView+ASConvenience.h in Headers */,
058D0A70195D05EC00B7D73C /* UIView+ASConvenience.m in Headers */,
ACF6ED4B1B17847A00DA7C62 /* ASInternalHelpers.h in Headers */,
058D0A82195D060300B7D73C /* ASAssert.h in Headers */,
0516FA3C1A15563400B4EBED /* ASAvailability.h in Headers */,
0516FA3D1A15563400B4EBED /* ASLog.h in Headers */,
@ -715,6 +1057,9 @@
AC3C4A511A1139C100143C57 /* ASCollectionView.h in Headers */,
292C59A01A956527007E5DD6 /* ASRangeHandlerPreload.h in Headers */,
055B9FA81A1C154B00035D6D /* ASNetworkImageNode.h in Headers */,
ACF6ED4E1B17847A00DA7C62 /* ASStackLayoutSpecUtilities.h in Headers */,
ACF6ED4D1B17847A00DA7C62 /* ASLayoutSpecUtilities.h in Headers */,
ACF6ED4F1B17847A00DA7C62 /* ASStackPositionedLayout.h in Headers */,
054963491A1EA066000F8E56 /* ASBasicImageDownloader.h in Headers */,
AC3C4A541A113EEC00143C57 /* ASCollectionViewProtocols.h in Headers */,
D785F6621A74327E00291744 /* ASScrollNode.h in Headers */,
@ -731,6 +1076,7 @@
058D0A79195D05F900B7D73C /* ASDisplayNode+DebugTiming.mm in Headers */,
058D0A7A195D05F900B7D73C /* ASDisplayNode+UIViewBridge.mm in Headers */,
2967F9E21AB0A5190072E4AB /* ASBasicImageDownloaderInternal.h in Headers */,
205F0E211B376416007741D0 /* CGRect+ASConvenience.h in Headers */,
058D0A7B195D05F900B7D73C /* ASDisplayNodeInternal.h in Headers */,
058D0A7C195D05F900B7D73C /* ASImageNode+CGExtras.h in Headers */,
058D0A7D195D05F900B7D73C /* ASImageNode+CGExtras.m in Headers */,
@ -741,6 +1087,82 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
B35061D71B010EDF0018CF92 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
B35062321B010EFD0018CF92 /* ASTextNodeShadower.h in Headers */,
B35062431B010EFD0018CF92 /* UIView+ASConvenience.h in Headers */,
B31A241E1B0114FD0016AE7A /* AsyncDisplayKit.h in Headers */,
B350622D1B010EFD0018CF92 /* ASScrollDirection.h in Headers */,
B35061FB1B010EFD0018CF92 /* ASDisplayNode.h in Headers */,
B35062361B010EFD0018CF92 /* ASTextNodeTypes.h in Headers */,
B35062341B010EFD0018CF92 /* ASTextNodeTextKitHelpers.h in Headers */,
B35061FA1B010EFD0018CF92 /* ASControlNode+Subclasses.h in Headers */,
B35062371B010EFD0018CF92 /* ASTextNodeWordKerner.h in Headers */,
B35062261B010EFD0018CF92 /* ASRangeController.h in Headers */,
B35062111B010EFD0018CF92 /* _ASDisplayView.h in Headers */,
B35061F81B010EFD0018CF92 /* ASControlNode.h in Headers */,
430E7C901B4C23F100697A4C /* ASIndexPath.h in Headers */,
B35062281B010EFD0018CF92 /* ASRangeHandler.h in Headers */,
B35061FD1B010EFD0018CF92 /* ASDisplayNode+Subclasses.h in Headers */,
B35062491B010EFD0018CF92 /* _ASCoreAnimationExtras.h in Headers */,
B35061F31B010EFD0018CF92 /* ASCellNode.h in Headers */,
B35062201B010EFD0018CF92 /* ASLayoutController.h in Headers */,
B35062571B010F070018CF92 /* ASAssert.h in Headers */,
B35062411B010EFD0018CF92 /* _ASAsyncTransactionGroup.h in Headers */,
B350623C1B010EFD0018CF92 /* _ASAsyncTransaction.h in Headers */,
B350625C1B010F070018CF92 /* ASLog.h in Headers */,
B35062551B010EFD0018CF92 /* ASSentinel.h in Headers */,
B350624B1B010EFD0018CF92 /* _ASPendingState.h in Headers */,
B35062391B010EFD0018CF92 /* ASThread.h in Headers */,
B35062131B010EFD0018CF92 /* ASBasicImageDownloader.h in Headers */,
B35062221B010EFD0018CF92 /* ASMultidimensionalArrayUtils.h in Headers */,
B350625B1B010F070018CF92 /* ASEqualityHelpers.h in Headers */,
B35061F71B010EFD0018CF92 /* ASCollectionViewProtocols.h in Headers */,
509E68631B3AEDB4009B9150 /* ASCollectionViewLayoutController.h in Headers */,
B35062241B010EFD0018CF92 /* ASMutableAttributedStringBuilder.h in Headers */,
B350621D1B010EFD0018CF92 /* ASHighlightOverlayLayer.h in Headers */,
B35062171B010EFD0018CF92 /* ASDataController.h in Headers */,
B350625A1B010F070018CF92 /* ASDisplayNodeExtraIvars.h in Headers */,
B350621F1B010EFD0018CF92 /* ASImageProtocols.h in Headers */,
B35061DF1B010EDF0018CF92 /* AsyncDisplayKit-iOS.h in Headers */,
B350620A1B010EFD0018CF92 /* ASTableView.h in Headers */,
B35062451B010EFD0018CF92 /* ASBatchFetching.h in Headers */,
B350620C1B010EFD0018CF92 /* ASTableViewProtocols.h in Headers */,
B35062481B010EFD0018CF92 /* _AS-objc-internal.h in Headers */,
B350623F1B010EFD0018CF92 /* _ASAsyncTransactionContainer.h in Headers */,
B35062081B010EFD0018CF92 /* ASScrollNode.h in Headers */,
B35061F51B010EFD0018CF92 /* ASCollectionView.h in Headers */,
B35062581B010F070018CF92 /* ASAvailability.h in Headers */,
B35062461B010EFD0018CF92 /* ASBasicImageDownloaderInternal.h in Headers */,
B350622B1B010EFD0018CF92 /* ASRangeHandlerRender.h in Headers */,
B350622E1B010EFD0018CF92 /* ASTextNodeCoreTextAdditions.h in Headers */,
B35062061B010EFD0018CF92 /* ASNetworkImageNode.h in Headers */,
B350624D1B010EFD0018CF92 /* _ASScopeTimer.h in Headers */,
509E68651B3AEDC5009B9150 /* CGRect+ASConvenience.h in Headers */,
B350624F1B010EFD0018CF92 /* ASDisplayNode+DebugTiming.h in Headers */,
B35062211B010EFD0018CF92 /* ASLayoutRangeType.h in Headers */,
B35062521B010EFD0018CF92 /* ASDisplayNodeInternal.h in Headers */,
B35061FE1B010EFD0018CF92 /* ASDisplayNodeExtras.h in Headers */,
B35062041B010EFD0018CF92 /* ASMultiplexImageNode.h in Headers */,
B35062021B010EFD0018CF92 /* ASImageNode.h in Headers */,
B35062301B010EFD0018CF92 /* ASTextNodeRenderer.h in Headers */,
509E68611B3AEDA0009B9150 /* ASAbstractLayoutController.h in Headers */,
B350620D1B010EFD0018CF92 /* ASTextNode.h in Headers */,
B35062151B010EFD0018CF92 /* ASBatchContext.h in Headers */,
B350621B1B010EFD0018CF92 /* ASFlowLayoutController.h in Headers */,
B35062291B010EFD0018CF92 /* ASRangeHandlerPreload.h in Headers */,
B35062001B010EFD0018CF92 /* ASEditableTextNode.h in Headers */,
B350623A1B010EFD0018CF92 /* NSMutableAttributedString+TextKitAdditions.h in Headers */,
B350623E1B010EFD0018CF92 /* _ASAsyncTransactionContainer+Private.h in Headers */,
B35062591B010F070018CF92 /* ASBaseDefines.h in Headers */,
B35062191B010EFD0018CF92 /* ASDealloc2MainObject.h in Headers */,
B350620F1B010EFD0018CF92 /* _ASDisplayLayer.h in Headers */,
B35062531B010EFD0018CF92 /* ASImageNode+CGExtras.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
@ -800,6 +1222,24 @@
productReference = 058D09BC195D04C000B7D73C /* AsyncDisplayKitTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
B35061D91B010EDF0018CF92 /* AsyncDisplayKit-iOS */ = {
isa = PBXNativeTarget;
buildConfigurationList = B35061ED1B010EDF0018CF92 /* Build configuration list for PBXNativeTarget "AsyncDisplayKit-iOS" */;
buildPhases = (
B35061D51B010EDF0018CF92 /* Sources */,
B35061D61B010EDF0018CF92 /* Frameworks */,
B35061D71B010EDF0018CF92 /* Headers */,
B35061D81B010EDF0018CF92 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = "AsyncDisplayKit-iOS";
productName = AsyncDisplayKit;
productReference = B35061DA1B010EDF0018CF92 /* AsyncDisplayKit.framework */;
productType = "com.apple.product-type.framework";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
@ -815,6 +1255,9 @@
058D09BB195D04C000B7D73C = {
TestTargetID = 057D02BE1AC0A66700C7AC3C;
};
B35061D91B010EDF0018CF92 = {
CreatedOnToolsVersion = 6.3.1;
};
};
};
buildConfigurationList = 058D09A7195D04C000B7D73C /* Build configuration list for PBXProject "AsyncDisplayKit" */;
@ -833,6 +1276,7 @@
058D09AB195D04C000B7D73C /* AsyncDisplayKit */,
058D09BB195D04C000B7D73C /* AsyncDisplayKitTests */,
057D02BE1AC0A66700C7AC3C /* AsyncDisplayKitTestHost */,
B35061D91B010EDF0018CF92 /* AsyncDisplayKit-iOS */,
);
};
/* End PBXProject section */
@ -842,6 +1286,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
204C979E1B362CB3002B1083 /* Default-568h@2x.png in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -854,6 +1299,13 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
B35061D81B010EDF0018CF92 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
@ -903,18 +1355,32 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
430E7C911B4C23F100697A4C /* ASIndexPath.m in Sources */,
058D0A26195D050800B7D73C /* _ASCoreAnimationExtras.mm in Sources */,
058D0A23195D050800B7D73C /* _ASAsyncTransactionContainer.m in Sources */,
058D0A1E195D050800B7D73C /* ASTextNodeShadower.m in Sources */,
ACF6ED2E1B17843500DA7C62 /* ASRatioLayoutSpec.mm in Sources */,
058D0A18195D050800B7D73C /* _ASDisplayLayer.mm in Sources */,
ACF6ED321B17843500DA7C62 /* ASStaticLayoutSpec.mm in Sources */,
ACF6ED2C1B17843500DA7C62 /* ASOverlayLayoutSpec.mm in Sources */,
058D0A2C195D050800B7D73C /* ASSentinel.m in Sources */,
205F0E221B376416007741D0 /* CGRect+ASConvenience.m in Sources */,
205F0E1A1B37339C007741D0 /* ASAbstractLayoutController.mm in Sources */,
464052211A3F83C40061C0BA /* ASDataController.mm in Sources */,
299DA1AA1A828D2900162D41 /* ASBatchContext.mm in Sources */,
058D0A15195D050800B7D73C /* ASDisplayNodeExtras.mm in Sources */,
ACF6ED501B17847A00DA7C62 /* ASStackPositionedLayout.mm in Sources */,
058D0A1F195D050800B7D73C /* ASTextNodeTextKitHelpers.mm in Sources */,
ACF6ED1B1B17843500DA7C62 /* ASBackgroundLayoutSpec.mm in Sources */,
055F1A3519ABD3E3004DAFF1 /* ASTableView.mm in Sources */,
205F0E121B371BD7007741D0 /* ASScrollDirection.m in Sources */,
AC47D9461B3BB41900AAEE9D /* ASStaticLayoutSpecDimension.mm in Sources */,
ACF6ED271B17843500DA7C62 /* ASLayoutSpec.mm in Sources */,
ACF6ED211B17843500DA7C62 /* ASDimension.mm in Sources */,
464052261A3F83C40061C0BA /* ASMultidimensionalArrayUtils.mm in Sources */,
055B9FA91A1C154B00035D6D /* ASNetworkImageNode.mm in Sources */,
ACF6ED521B17847A00DA7C62 /* ASStackUnpositionedLayout.mm in Sources */,
ACF6ED1D1B17843500DA7C62 /* ASCenterLayoutSpec.mm in Sources */,
058D0A1D195D050800B7D73C /* ASTextNodeRenderer.mm in Sources */,
292C59A41A956527007E5DD6 /* ASRangeHandlerRender.mm in Sources */,
058D0A2A195D050800B7D73C /* ASDisplayNode+UIViewBridge.mm in Sources */,
@ -926,23 +1392,29 @@
058D0A28195D050800B7D73C /* ASDisplayNode+AsyncDisplay.mm in Sources */,
0587F9BE1A7309ED00AFF0BA /* ASEditableTextNode.mm in Sources */,
058D0A21195D050800B7D73C /* NSMutableAttributedString+TextKitAdditions.m in Sources */,
ACF6ED251B17843500DA7C62 /* ASLayout.mm in Sources */,
058D0A25195D050800B7D73C /* UIView+ASConvenience.m in Sources */,
0549634A1A1EA066000F8E56 /* ASBasicImageDownloader.mm in Sources */,
058D0A14195D050800B7D73C /* ASDisplayNode.mm in Sources */,
058D0A1B195D050800B7D73C /* ASMutableAttributedStringBuilder.m in Sources */,
058D0A2B195D050800B7D73C /* ASImageNode+CGExtras.m in Sources */,
058D0A24195D050800B7D73C /* _ASAsyncTransactionGroup.m in Sources */,
055F1A3D19ABD43F004DAFF1 /* ASCellNode.m in Sources */,
058D0A1C195D050800B7D73C /* ASTextNodeCoreTextAdditions.m in Sources */,
058D0A13195D050800B7D73C /* ASControlNode.m in Sources */,
ACF6ED4C1B17847A00DA7C62 /* ASInternalHelpers.mm in Sources */,
058D0A19195D050800B7D73C /* _ASDisplayView.mm in Sources */,
205F0E101B371875007741D0 /* UICollectionViewLayout+ASConvenience.m in Sources */,
05A6D05B19D0EB64002DD95E /* ASDealloc2MainObject.m in Sources */,
058D0A17195D050800B7D73C /* ASTextNode.mm in Sources */,
AC6456091B0A335000CF11B8 /* ASCellNode.m in Sources */,
ACF6ED231B17843500DA7C62 /* ASInsetLayoutSpec.mm in Sources */,
058D0A27195D050800B7D73C /* _ASPendingState.m in Sources */,
0516FA411A1563D200B4EBED /* ASMultiplexImageNode.mm in Sources */,
058D0A16195D050800B7D73C /* ASImageNode.mm in Sources */,
058D0A29195D050800B7D73C /* ASDisplayNode+DebugTiming.mm in Sources */,
205F0E1E1B373A2C007741D0 /* ASCollectionViewLayoutController.mm in Sources */,
058D0A22195D050800B7D73C /* _ASAsyncTransaction.m in Sources */,
ACF6ED301B17843500DA7C62 /* ASStackLayoutSpec.mm in Sources */,
055F1A3919ABD413004DAFF1 /* ASRangeController.mm in Sources */,
296A0A2F1A9516B2005ACEAA /* ASBatchFetching.m in Sources */,
D785F6631A74327E00291744 /* ASScrollNode.m in Sources */,
@ -953,26 +1425,90 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
ACF6ED611B178DC700DA7C62 /* ASOverlayLayoutSpecSnapshotTests.mm in Sources */,
ACF6ED621B178DC700DA7C62 /* ASRatioLayoutSpecSnapshotTests.mm in Sources */,
2911485C1A77147A005D0878 /* ASControlNodeTests.m in Sources */,
296A0A351A951ABF005ACEAA /* ASBatchFetchingTests.m in Sources */,
058D0A3E195D057000B7D73C /* ASTextNodeRendererTests.m in Sources */,
058D0A3D195D057000B7D73C /* ASTextNodeCoreTextAdditionsTests.m in Sources */,
058D0A3C195D057000B7D73C /* ASMutableAttributedStringBuilderTests.m in Sources */,
242995D31B29743C00090100 /* ASBasicImageDownloaderTests.m in Sources */,
058D0A3F195D057000B7D73C /* ASTextNodeShadowerTests.m in Sources */,
ACF6ED631B178DC700DA7C62 /* ASStackLayoutSpecSnapshotTests.mm in Sources */,
29CDC2E21AAE70D000833CA4 /* ASBasicImageDownloaderContextTests.m in Sources */,
056D21551ABCEF50001107EF /* ASImageNodeSnapshotTests.m in Sources */,
05EA6FE71AC0966E00E35788 /* ASSnapshotTestCase.mm in Sources */,
ACF6ED5E1B178DC700DA7C62 /* ASInsetLayoutSpecSnapshotTests.mm in Sources */,
058D0A3B195D057000B7D73C /* ASDisplayNodeTestsHelper.m in Sources */,
058D0A3A195D057000B7D73C /* ASDisplayNodeTests.m in Sources */,
ACF6ED601B178DC700DA7C62 /* ASLayoutSpecSnapshotTestsHelper.m in Sources */,
052EE0661A159FEF002C6279 /* ASMultiplexImageNodeTests.m in Sources */,
ACF6ED5D1B178DC700DA7C62 /* ASDimensionTests.mm in Sources */,
058D0A39195D057000B7D73C /* ASDisplayNodeAppearanceTests.m in Sources */,
058D0A41195D057000B7D73C /* ASTextNodeWordKernerTests.mm in Sources */,
ACF6ED5C1B178DC700DA7C62 /* ASCenterLayoutSpecSnapshotTests.mm in Sources */,
9F06E5CD1B4CAF4200F015D8 /* ASCollectionViewTests.m in Sources */,
058D0A40195D057000B7D73C /* ASTextNodeTests.m in Sources */,
3C9C128519E616EF00E942A0 /* ASTableViewTests.m in Sources */,
058D0A38195D057000B7D73C /* ASDisplayLayerTests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
B35061D51B010EDF0018CF92 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
B350623B1B010EFD0018CF92 /* NSMutableAttributedString+TextKitAdditions.m in Sources */,
B35062401B010EFD0018CF92 /* _ASAsyncTransactionContainer.m in Sources */,
B35062311B010EFD0018CF92 /* ASTextNodeRenderer.mm in Sources */,
B35062051B010EFD0018CF92 /* ASMultiplexImageNode.mm in Sources */,
B35061FC1B010EFD0018CF92 /* ASDisplayNode.mm in Sources */,
B35062181B010EFD0018CF92 /* ASDataController.mm in Sources */,
B35062501B010EFD0018CF92 /* ASDisplayNode+DebugTiming.mm in Sources */,
B35062471B010EFD0018CF92 /* ASBatchFetching.m in Sources */,
B350624E1B010EFD0018CF92 /* ASDisplayNode+AsyncDisplay.mm in Sources */,
B35061F61B010EFD0018CF92 /* ASCollectionView.mm in Sources */,
509E68621B3AEDA5009B9150 /* ASAbstractLayoutController.mm in Sources */,
B350620B1B010EFD0018CF92 /* ASTableView.mm in Sources */,
B350623D1B010EFD0018CF92 /* _ASAsyncTransaction.m in Sources */,
B35062161B010EFD0018CF92 /* ASBatchContext.mm in Sources */,
B350620E1B010EFD0018CF92 /* ASTextNode.mm in Sources */,
B35062141B010EFD0018CF92 /* ASBasicImageDownloader.mm in Sources */,
B350621C1B010EFD0018CF92 /* ASFlowLayoutController.mm in Sources */,
B35062231B010EFD0018CF92 /* ASMultidimensionalArrayUtils.mm in Sources */,
509E68641B3AEDB7009B9150 /* ASCollectionViewLayoutController.mm in Sources */,
B350621E1B010EFD0018CF92 /* ASHighlightOverlayLayer.mm in Sources */,
B35062271B010EFD0018CF92 /* ASRangeController.mm in Sources */,
B35061F91B010EFD0018CF92 /* ASControlNode.m in Sources */,
AC47D9421B3B891B00AAEE9D /* ASCellNode.m in Sources */,
509E68661B3AEDD7009B9150 /* CGRect+ASConvenience.m in Sources */,
B35062561B010EFD0018CF92 /* ASSentinel.m in Sources */,
B350624A1B010EFD0018CF92 /* _ASCoreAnimationExtras.mm in Sources */,
B35062071B010EFD0018CF92 /* ASNetworkImageNode.mm in Sources */,
B35062011B010EFD0018CF92 /* ASEditableTextNode.mm in Sources */,
B35062441B010EFD0018CF92 /* UIView+ASConvenience.m in Sources */,
B350622F1B010EFD0018CF92 /* ASTextNodeCoreTextAdditions.m in Sources */,
B35062031B010EFD0018CF92 /* ASImageNode.mm in Sources */,
B35062091B010EFD0018CF92 /* ASScrollNode.m in Sources */,
B35062251B010EFD0018CF92 /* ASMutableAttributedStringBuilder.m in Sources */,
430E7C921B4C23F100697A4C /* ASIndexPath.m in Sources */,
B35062381B010EFD0018CF92 /* ASTextNodeWordKerner.m in Sources */,
B35062101B010EFD0018CF92 /* _ASDisplayLayer.mm in Sources */,
B35062351B010EFD0018CF92 /* ASTextNodeTextKitHelpers.mm in Sources */,
B35062421B010EFD0018CF92 /* _ASAsyncTransactionGroup.m in Sources */,
B35061FF1B010EFD0018CF92 /* ASDisplayNodeExtras.mm in Sources */,
B35062121B010EFD0018CF92 /* _ASDisplayView.mm in Sources */,
B350624C1B010EFD0018CF92 /* _ASPendingState.m in Sources */,
B35062541B010EFD0018CF92 /* ASImageNode+CGExtras.m in Sources */,
509E68601B3AED8E009B9150 /* ASScrollDirection.m in Sources */,
B350622C1B010EFD0018CF92 /* ASRangeHandlerRender.mm in Sources */,
B350622A1B010EFD0018CF92 /* ASRangeHandlerPreload.mm in Sources */,
B35062511B010EFD0018CF92 /* ASDisplayNode+UIViewBridge.mm in Sources */,
B350621A1B010EFD0018CF92 /* ASDealloc2MainObject.m in Sources */,
B35062331B010EFD0018CF92 /* ASTextNodeShadower.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
@ -1194,6 +1730,63 @@
};
name = Release;
};
B35061EE1B010EDF0018CF92 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_WARN_UNREACHABLE_CODE = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_NO_COMMON_BLOCKS = YES;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
INFOPLIST_FILE = AsyncDisplayKit/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
MTL_ENABLE_DEBUG_INFO = YES;
PRODUCT_NAME = AsyncDisplayKit;
SKIP_INSTALL = YES;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Debug;
};
B35061EF1B010EDF0018CF92 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_WARN_UNREACHABLE_CODE = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_NO_COMMON_BLOCKS = YES;
INFOPLIST_FILE = AsyncDisplayKit/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
MTL_ENABLE_DEBUG_INFO = NO;
PRODUCT_NAME = AsyncDisplayKit;
SKIP_INSTALL = YES;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
@ -1233,6 +1826,15 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
B35061ED1B010EDF0018CF92 /* Build configuration list for PBXNativeTarget "AsyncDisplayKit-iOS" */ = {
isa = XCConfigurationList;
buildConfigurations = (
B35061EE1B010EDF0018CF92 /* Debug */,
B35061EF1B010EDF0018CF92 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 058D09A4195D04C000B7D73C /* Project object */;

View File

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0630"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2C5B669C1B3A796F005926A7"
BuildableName = "AsyncDisplayKit.framework"
BlueprintName = "AsyncDisplayKit-iOS"
ReferencedContainer = "container:AsyncDisplayKit.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<Testables>
</Testables>
</TestAction>
<LaunchAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
buildConfiguration = "Debug"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2C5B669C1B3A796F005926A7"
BuildableName = "AsyncDisplayKit.framework"
BlueprintName = "AsyncDisplayKit-iOS"
ReferencedContainer = "container:AsyncDisplayKit.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2C5B669C1B3A796F005926A7"
BuildableName = "AsyncDisplayKit.framework"
BlueprintName = "AsyncDisplayKit-iOS"
ReferencedContainer = "container:AsyncDisplayKit.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -12,6 +12,7 @@
#import <AsyncDisplayKit/ASDisplayNode+Subclasses.h>
#import <AsyncDisplayKit/ASTextNode.h>
#import <AsyncDisplayKit/ASInsetLayoutSpec.h>
#pragma mark -
#pragma mark ASCellNode
@ -25,6 +26,7 @@
// use UITableViewCell defaults
_selectionStyle = UITableViewCellSelectionStyleDefault;
self.clipsToBounds = YES;
return self;
}
@ -91,8 +93,6 @@
@implementation ASTextCellNode
static const CGFloat kHorizontalPadding = 15.0f;
static const CGFloat kVerticalPadding = 11.0f;
static const CGFloat kFontSize = 18.0f;
- (instancetype)init
@ -106,19 +106,12 @@ static const CGFloat kFontSize = 18.0f;
return self;
}
- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize
- (id<ASLayoutable>)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
CGSize availableSize = CGSizeMake(constrainedSize.width - 2 * kHorizontalPadding,
constrainedSize.height - 2 * kVerticalPadding);
CGSize textNodeSize = [_textNode measure:availableSize];
return CGSizeMake(ceilf(2 * kHorizontalPadding + textNodeSize.width),
ceilf(2 * kVerticalPadding + textNodeSize.height));
}
- (void)layout
{
_textNode.frame = CGRectInset(self.bounds, kHorizontalPadding, kVerticalPadding);
static const CGFloat kHorizontalPadding = 15.0f;
static const CGFloat kVerticalPadding = 11.0f;
UIEdgeInsets insets = UIEdgeInsetsMake(kVerticalPadding, kHorizontalPadding, kVerticalPadding, kHorizontalPadding);
return [ASInsetLayoutSpec newWithInsets:insets child:_textNode];
}
- (void)setText:(NSString *)text
@ -130,7 +123,7 @@ static const CGFloat kFontSize = 18.0f;
_textNode.attributedString = [[NSAttributedString alloc] initWithString:_text
attributes:@{NSFontAttributeName: [UIFont systemFontOfSize:kFontSize]}];
[self invalidateCalculatedSize];
[self invalidateCalculatedLayout];
}
@end

View File

@ -70,6 +70,15 @@
*/
@property (nonatomic, assign) CGFloat leadingScreensForBatching;
/**
* Reload everything from scratch, destroying the working range and all cached nodes.
*
* @param completion block to run on completion of asynchronous loading or nil. If supplied, the block is run on
* the main thread.
* @warning This method is substantially more expensive than UICollectionView's version.
*/
- (void)reloadDataWithCompletion:(void (^)())completion;
/**
* Reload everything from scratch, destroying the working range and all cached nodes.
*
@ -122,6 +131,20 @@
*/
- (CGSize)calculatedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath;
/**
* Determines collection view's current scroll direction. Supports 2-axis collection views.
*
* @returns a bitmask of ASScrollDirection values.
*/
- (ASScrollDirection)scrollDirection;
/**
* Determines collection view's scrollable directions.
*
* @returns a bitmask of ASScrollDirection values.
*/
- (ASScrollDirection)scrollableDirections;
@end

View File

@ -9,13 +9,14 @@
#import "ASCollectionView.h"
#import "ASAssert.h"
#import "ASFlowLayoutController.h"
#import "ASCollectionViewLayoutController.h"
#import "ASRangeController.h"
#import "ASDataController.h"
#import "ASDisplayNodeInternal.h"
#import "ASBatchFetching.h"
#import "UICollectionViewLayout+ASConvenience.h"
const static NSUInteger kASCollectionViewAnimationNone = 0;
const static NSUInteger kASCollectionViewAnimationNone = UITableViewRowAnimationNone;
#pragma mark -
@ -78,11 +79,17 @@ static BOOL _isInterceptedSelector(SEL sel)
- (BOOL)respondsToSelector:(SEL)aSelector
{
ASDisplayNodeAssert(_target, @"target must not be nil"); // catch weak ref's being nilled early
ASDisplayNodeAssert(_interceptor, @"interceptor must not be nil");
return (_isInterceptedSelector(aSelector) || [_target respondsToSelector:aSelector]);
}
- (id)forwardingTargetForSelector:(SEL)aSelector
{
ASDisplayNodeAssert(_target, @"target must not be nil"); // catch weak ref's being nilled early
ASDisplayNodeAssert(_interceptor, @"interceptor must not be nil");
if (_isInterceptedSelector(aSelector)) {
return _interceptor;
}
@ -102,7 +109,7 @@ static BOOL _isInterceptedSelector(SEL sel)
ASDataController *_dataController;
ASRangeController *_rangeController;
ASFlowLayoutController *_layoutController;
ASCollectionViewLayoutController *_layoutController;
BOOL _performingBatchUpdates;
NSMutableArray *_batchUpdateBlocks;
@ -130,11 +137,12 @@ static BOOL _isInterceptedSelector(SEL sel)
{
if (!(self = [super initWithFrame:frame collectionViewLayout:layout]))
return nil;
// FIXME: asyncDataFetching is currently unreliable for some use cases.
// https://github.com/facebook/AsyncDisplayKit/issues/385
asyncDataFetchingEnabled = NO;
ASDisplayNodeAssert([layout isKindOfClass:UICollectionViewFlowLayout.class], @"only flow layouts are currently supported");
ASFlowLayoutDirection direction = (((UICollectionViewFlowLayout *)layout).scrollDirection == UICollectionViewScrollDirectionHorizontal) ? ASFlowLayoutDirectionHorizontal : ASFlowLayoutDirectionVertical;
_layoutController = [[ASFlowLayoutController alloc] initWithScrollOption:direction];
_layoutController = [[ASCollectionViewLayoutController alloc] initWithCollectionView:self];
_rangeController = [[ASRangeController alloc] init];
_rangeController.delegate = self;
@ -159,16 +167,29 @@ static BOOL _isInterceptedSelector(SEL sel)
return self;
}
- (void)dealloc
{
// Sometimes the UIKit classes can call back to their delegate even during deallocation.
// This bug might be iOS 7-specific.
super.delegate = nil;
super.dataSource = nil;
}
#pragma mark -
#pragma mark Overrides.
- (void)reloadData
- (void)reloadDataWithCompletion:(void (^)())completion
{
ASDisplayNodeAssert(self.asyncDelegate, @"ASCollectionView's asyncDelegate property must be set.");
ASDisplayNodePerformBlockOnMainThread(^{
[super reloadData];
});
[_dataController reloadDataWithAnimationOption:kASCollectionViewAnimationNone];
[_dataController reloadDataWithAnimationOptions:kASCollectionViewAnimationNone completion:completion];
}
- (void)reloadData
{
[self reloadDataWithCompletion:nil];
}
- (void)setDataSource:(id<UICollectionViewDataSource>)dataSource
@ -184,13 +205,15 @@ static BOOL _isInterceptedSelector(SEL sel)
- (void)setAsyncDataSource:(id<ASCollectionViewDataSource>)asyncDataSource
{
if (_asyncDataSource == asyncDataSource)
return;
// Note: It's common to check if the value hasn't changed and short-circuit but we aren't doing that here to handle
// the (common) case of nilling the asyncDataSource in the ViewController's dealloc. In this case our _asyncDataSource
// will return as nil (ARC magic) even though the _proxyDataSource still exists. It's really important to nil out
// super.dataSource in this case because calls to _ASTableViewProxy will start failing and cause crashes.
if (asyncDataSource == nil) {
super.dataSource = nil;
_asyncDataSource = nil;
_proxyDataSource = nil;
super.dataSource = nil;
} else {
_asyncDataSource = asyncDataSource;
_proxyDataSource = [[_ASCollectionViewProxy alloc] initWithTarget:_asyncDataSource interceptor:self];
@ -200,13 +223,17 @@ static BOOL _isInterceptedSelector(SEL sel)
- (void)setAsyncDelegate:(id<ASCollectionViewDelegate>)asyncDelegate
{
if (_asyncDelegate == asyncDelegate)
return;
// Note: It's common to check if the value hasn't changed and short-circuit but we aren't doing that here to handle
// the (common) case of nilling the asyncDelegate in the ViewController's dealloc. In this case our _asyncDelegate
// will return as nil (ARC magic) even though the _proxyDelegate still exists. It's really important to nil out
// super.delegate in this case because calls to _ASTableViewProxy will start failing and cause crashes.
if (asyncDelegate == nil) {
// order is important here, the delegate must be callable while nilling super.delegate to avoid random crashes
// in UIScrollViewAccessibility.
super.delegate = nil;
_asyncDelegate = nil;
_proxyDelegate = nil;
super.delegate = nil;
} else {
_asyncDelegate = asyncDelegate;
_proxyDelegate = [[_ASCollectionViewProxy alloc] initWithTarget:_asyncDelegate interceptor:self];
@ -263,42 +290,42 @@ static BOOL _isInterceptedSelector(SEL sel)
- (void)insertSections:(NSIndexSet *)sections
{
[_dataController insertSections:sections withAnimationOption:kASCollectionViewAnimationNone];
[_dataController insertSections:sections withAnimationOptions:kASCollectionViewAnimationNone];
}
- (void)deleteSections:(NSIndexSet *)sections
{
[_dataController deleteSections:sections withAnimationOption:kASCollectionViewAnimationNone];
[_dataController deleteSections:sections withAnimationOptions:kASCollectionViewAnimationNone];
}
- (void)reloadSections:(NSIndexSet *)sections
{
[_dataController reloadSections:sections withAnimationOption:kASCollectionViewAnimationNone];
[_dataController reloadSections:sections withAnimationOptions:kASCollectionViewAnimationNone];
}
- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection
{
[_dataController moveSection:section toSection:newSection withAnimationOption:kASCollectionViewAnimationNone];
[_dataController moveSection:section toSection:newSection withAnimationOptions:kASCollectionViewAnimationNone];
}
- (void)insertItemsAtIndexPaths:(NSArray *)indexPaths
{
[_dataController insertRowsAtIndexPaths:indexPaths withAnimationOption:kASCollectionViewAnimationNone];
[_dataController insertRowsAtIndexPaths:indexPaths withAnimationOptions:kASCollectionViewAnimationNone];
}
- (void)deleteItemsAtIndexPaths:(NSArray *)indexPaths
{
[_dataController deleteRowsAtIndexPaths:indexPaths withAnimationOption:kASCollectionViewAnimationNone];
[_dataController deleteRowsAtIndexPaths:indexPaths withAnimationOptions:kASCollectionViewAnimationNone];
}
- (void)reloadItemsAtIndexPaths:(NSArray *)indexPaths
{
[_dataController reloadRowsAtIndexPaths:indexPaths withAnimationOption:kASCollectionViewAnimationNone];
[_dataController reloadRowsAtIndexPaths:indexPaths withAnimationOptions:kASCollectionViewAnimationNone];
}
- (void)moveItemAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath
{
[_dataController moveRowAtIndexPath:indexPath toIndexPath:newIndexPath withAnimationOption:kASCollectionViewAnimationNone];
[_dataController moveRowAtIndexPath:indexPath toIndexPath:newIndexPath withAnimationOptions:kASCollectionViewAnimationNone];
}
- (ASCellNode *)nodeForItemAtIndexPath:(NSIndexPath *)indexPath
@ -340,24 +367,57 @@ static BOOL _isInterceptedSelector(SEL sel)
- (ASScrollDirection)scrollDirection
{
CGPoint scrollVelocity = [self.panGestureRecognizer velocityInView:self.superview];
return [self scrollDirectionForVelocity:scrollVelocity];
}
- (ASScrollDirection)scrollDirectionForVelocity:(CGPoint)scrollVelocity
{
ASScrollDirection direction = ASScrollDirectionNone;
if (_layoutController.layoutDirection == ASFlowLayoutDirectionHorizontal) {
if (scrollVelocity.x > 0) {
direction = ASScrollDirectionRight;
} else if (scrollVelocity.x < 0) {
direction = ASScrollDirectionLeft;
}
} else {
if (scrollVelocity.y > 0) {
direction = ASScrollDirectionDown;
ASScrollDirection scrollableDirections = [self scrollableDirections];
if (ASScrollDirectionContainsHorizontalDirection(scrollableDirections)) { // Can scroll horizontally.
if (scrollVelocity.x >= 0) {
direction |= ASScrollDirectionRight;
} else {
direction = ASScrollDirectionUp;
direction |= ASScrollDirectionLeft;
}
}
if (ASScrollDirectionContainsVerticalDirection(scrollableDirections)) { // Can scroll vertically.
if (scrollVelocity.y >= 0) {
direction |= ASScrollDirectionDown;
} else {
direction |= ASScrollDirectionUp;
}
}
return direction;
}
- (ASScrollDirection)scrollableDirections
{
if ([self.collectionViewLayout asdk_isFlowLayout]) {
return [self flowLayoutScrollableDirections:(UICollectionViewFlowLayout *)self.collectionViewLayout];
} else {
return [self nonFlowLayoutScrollableDirections];
}
}
- (ASScrollDirection)flowLayoutScrollableDirections:(UICollectionViewFlowLayout *)flowLayout {
return (flowLayout.scrollDirection == UICollectionViewScrollDirectionHorizontal) ? ASScrollDirectionHorizontalDirections : ASScrollDirectionVerticalDirections;
}
- (ASScrollDirection)nonFlowLayoutScrollableDirections
{
ASScrollDirection scrollableDirection = ASScrollDirectionNone;
if (self.contentSize.width > self.bounds.size.width) { // Can scroll horizontally.
scrollableDirection |= ASScrollDirectionHorizontalDirections;
}
if (self.contentSize.height > self.bounds.size.height) { // Can scroll vertically.
scrollableDirection |= ASScrollDirectionVerticalDirections;
}
return scrollableDirection;
}
- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath
{
[_rangeController visibleNodeIndexPathsDidChangeWithScrollDirection:self.scrollDirection];
@ -430,7 +490,7 @@ static BOOL _isInterceptedSelector(SEL sel)
{
CGSize restrainedSize = self.bounds.size;
if (_layoutController.layoutDirection == ASFlowLayoutDirectionHorizontal) {
if (ASScrollDirectionContainsHorizontalDirection([self scrollableDirections])) {
restrainedSize.width = FLT_MAX;
} else {
restrainedSize.height = FLT_MAX;
@ -514,7 +574,7 @@ static BOOL _isInterceptedSelector(SEL sel)
return [_dataController nodesAtIndexPaths:indexPaths];
}
- (void)rangeController:(ASRangeController *)rangeController didInsertNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption
- (void)rangeController:(ASRangeController *)rangeController didInsertNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
{
ASDisplayNodeAssertMainThread();
if (_performingBatchUpdates) {
@ -528,7 +588,7 @@ static BOOL _isInterceptedSelector(SEL sel)
}
}
- (void)rangeController:(ASRangeController *)rangeController didDeleteNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption
- (void)rangeController:(ASRangeController *)rangeController didDeleteNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
{
ASDisplayNodeAssertMainThread();
@ -543,7 +603,7 @@ static BOOL _isInterceptedSelector(SEL sel)
}
}
- (void)rangeController:(ASRangeController *)rangeController didInsertSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOption:(ASDataControllerAnimationOptions)animationOption
- (void)rangeController:(ASRangeController *)rangeController didInsertSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
{
ASDisplayNodeAssertMainThread();
@ -558,7 +618,7 @@ static BOOL _isInterceptedSelector(SEL sel)
}
}
- (void)rangeController:(ASRangeController *)rangeController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOption:(ASDataControllerAnimationOptions)animationOption
- (void)rangeController:(ASRangeController *)rangeController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
{
ASDisplayNodeAssertMainThread();

View File

@ -226,7 +226,7 @@ void _ASEnumerateControlEventsIncludedInMaskWithBlock(ASControlNodeEvent mask, v
if (!eventDispatchTable)
{
// Create the dispatch table for this event.
eventDispatchTable = [NSMapTable strongToStrongObjectsMapTable];
eventDispatchTable = [NSMapTable weakToStrongObjectsMapTable];
[_controlEventDispatchTable setObject:eventDispatchTable forKey:eventKey];
}

View File

@ -13,6 +13,8 @@
#import <AsyncDisplayKit/ASDisplayNode.h>
#import <AsyncDisplayKit/ASThread.h>
#import <AsyncDisplayKit/ASLayout.h>
#import <AsyncDisplayKit/ASLayoutable.h>
/**
* The subclass header _ASDisplayNode+Subclasses_ defines the following methods that either must or can be overriden by
@ -34,7 +36,7 @@
* variables.
*/
@interface ASDisplayNode (Subclassing)
@interface ASDisplayNode (Subclassing) <ASLayoutable>
/** @name View Configuration */
@ -65,6 +67,26 @@
*/
@property (nonatomic, readonly, assign, getter=isInHierarchy) BOOL inHierarchy;
/**
* @abstract Return the calculated layout.
*
* @discussion For node subclasses that implement manual layout (e.g., they have a custom -layout method),
* calculatedLayout may be accessed on subnodes to retrieved cached information about their size.
* This allows -layout to be very fast, saving time on the main thread.
* Note: .calculatedLayout will only be set for nodes that have had -measure: called on them.
* For manual layout, make sure you call -measure: in your implementation of -calculateSizeThatFits:.
*
* For node subclasses that use automatic layout (e.g., they implement -layoutSpecThatFits:),
* it is typically not necessary to use .calculatedLayout at any point. For these nodes,
* the ASLayoutSpec implementation will automatically call -measureWithSizeRange: on all of the subnodes,
* and the ASDisplayNode base class implementation of -layout will automatically make use of .calculatedLayout on the subnodes.
*
* @return Layout that wraps calculated size returned by -calculateSizeThatFits: (in manual layout mode),
* or layout already calculated from layout spec returned by -layoutSpecThatFits: (in automatic layout mode).
*
* @warning Subclasses must not override this; it returns the last cached layout and is never expensive.
*/
@property (nonatomic, readonly, assign) ASLayout *calculatedLayout;
/** @name View Lifecycle */
@ -96,8 +118,39 @@
- (void)layoutDidFinish;
/** @name Sizing */
/** @name Layout calculation */
/**
* @abstract Asks the node to measure a layout based on given size range.
*
* @param constrainedSize The minimum and maximum sizes the receiver should fit in.
*
* @return An ASLayout instance defining the layout of the receiver (and its children, if the box layout model is used).
*
* @discussion Though this method does not set the bounds of the view, it does have side effects--caching both the
* constraint and the result.
*
* @warning Subclasses must not override this; it caches results from -calculateLayoutThatFits:. Calling this method may
* be expensive if result is not cached.
*
* @see [ASDisplayNode(Subclassing) calculateLayoutThatFits:]
*/
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize;
/**
* @abstract Calculate a layout based on given size range.
*
* @param constrainedSize The minimum and maximum sizes the receiver should fit in.
*
* @return An ASLayout instance defining the layout of the receiver (and its children, if the box layout model is used).
*
* @discussion This method is called on a non-main thread. The default implementation calls either -layoutSpecThatFits:
* or -calculateSizeThatFits:, whichever method is overriden. Subclasses rarely need to override this method,
* override -layoutSpecThatFits: or -calculateSizeThatFits: instead.
*
* @note This method should not be called directly outside of ASDisplayNode; use -measure: or -calculatedLayout instead.
*/
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize;
/**
* @abstract Return the calculated size.
@ -105,21 +158,37 @@
* @param constrainedSize The maximum size the receiver should fit in.
*
* @discussion Subclasses that override should expect this method to be called on a non-main thread. The returned size
* is cached by ASDisplayNode for quick access during -layout, via -calculatedSize. Other expensive work that needs to
* is wrapped in an ASLayout and cached for quick access during -layout. Other expensive work that needs to
* be done before display can be performed here, and using ivars to cache any valuable intermediate results is
* encouraged.
*
* @note This method should not be called directly outside of ASDisplayNode; use -measure: or -calculatedSize instead.
* @note Subclasses that override are committed to manual layout. Therefore, -layout: must be overriden to layout all subnodes or subviews.
*
* @note This method should not be called directly outside of ASDisplayNode; use -measure: or -calculatedLayout instead.
*/
- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize;
/**
* @abstract Invalidate previously measured and cached size.
* @abstract Return a layout spec that describes the layout of the receiver and its children.
*
* @discussion Subclasses should call this method to invalidate the previously measured and cached size for the display
* @param constrainedSize The minimum and maximum sizes the receiver should fit in.
*
* @discussion Subclasses that override should expect this method to be called on a non-main thread. The returned layout spec
* is used to calculate an ASLayout and cached by ASDisplayNode for quick access during -layout. Other expensive work that needs to
* be done before display can be performed here, and using ivars to cache any valuable intermediate results is
* encouraged.
*
* @note This method should not be called directly outside of ASDisplayNode; use -measure: or -calculatedLayout instead.
*/
- (id<ASLayoutable>)layoutSpecThatFits:(ASSizeRange)constrainedSize;
/**
* @abstract Invalidate previously measured and cached layout.
*
* @discussion Subclasses should call this method to invalidate the previously measured and cached layout for the display
* node, when the contents of the node change in such a way as to require measuring it again.
*/
- (void)invalidateCalculatedSize;
- (void)invalidateCalculatedLayout;
/** @name Drawing */
@ -190,8 +259,9 @@
*
* @discussion Subclasses may override this method to be notified when they should begin to fetch data. Fetching
* should be done asynchronously. The node is also responsible for managing the memory of any data.
* The data may be remote and accessed via the network, but could also be a local database query.
*/
- (void)fetchRemoteData ASDISPLAYNODE_REQUIRES_SUPER;
- (void)fetchData ASDISPLAYNODE_REQUIRES_SUPER;
/**
* @abstract Indicates that the receiver is about to display its subnodes. This method is not called if there are no
@ -206,8 +276,8 @@
* @abstract Indicates that the receiver is finished displaying its subnodes. This method is not called if there are
* no subnodes present.
*
* @discussion Subclasses may override this method to be notified when subnode display (asynchronous or synchronous) is
* about to begin.
* @discussion Subclasses may override this method to be notified when subnode display (asynchronous or synchronous) has
* completed.
*/
- (void)subnodeDisplayDidFinish:(ASDisplayNode *)subnode ASDISPLAYNODE_REQUIRES_SUPER;
@ -323,18 +393,18 @@
* Provides an opportunity to clear backing store and other memory-intensive intermediates, such as text layout managers
* on the current node.
*
* @discussion Called by -recursivelyClearRendering. Base class implements self.contents = nil, clearing any backing
* @discussion Called by -recursivelyClearContents. Base class implements self.contents = nil, clearing any backing
* store, for asynchronous regeneration when needed.
*/
- (void)clearRendering ASDISPLAYNODE_REQUIRES_SUPER;
- (void)clearContents ASDISPLAYNODE_REQUIRES_SUPER;
/**
* Provides an opportunity to clear any remote data on the current node.
* Provides an opportunity to clear any fetched data (e.g. remote / network or database-queried) on the current node.
*
* @discussion This will not clear data recursively for all subnodes. Either call -recursivelyClearRemoteData or
* selectively clear remote data.
* @discussion This will not clear data recursively for all subnodes. Either call -recursivelyClearFetchedData or
* selectively clear fetched data.
*/
- (void)clearRemoteData ASDISPLAYNODE_REQUIRES_SUPER;
- (void)clearFetchedData ASDISPLAYNODE_REQUIRES_SUPER;
/** @name Placeholders */

View File

@ -11,7 +11,7 @@
#import <AsyncDisplayKit/_ASAsyncTransactionContainer.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
#import <AsyncDisplayKit/ASDealloc2MainObject.h>
#import <AsyncDisplayKit/ASDimension.h>
typedef UIView *(^ASDisplayNodeViewBlock)();
typedef CALayer *(^ASDisplayNodeLayerBlock)();
@ -118,8 +118,8 @@ typedef CALayer *(^ASDisplayNodeLayerBlock)();
/** @name Managing dimensions */
/**
* @abstract Asks the node to calculate and return the size that best fits its subnodes.
/**
* @abstract Asks the node to measure and return the size that best fits its subnodes.
*
* @param constrainedSize The maximum size the receiver should fit in.
*
@ -128,10 +128,12 @@ typedef CALayer *(^ASDisplayNodeLayerBlock)();
* @discussion Though this method does not set the bounds of the view, it does have side effects--caching both the
* constraint and the result.
*
* @warning Subclasses must not override this; it caches results from -calculateSizeThatFits:. Calling this method may
* @warning Subclasses must not override this; it calls -measureWithSizeRange: with zero min size.
* -measureWithSizeRange: caches results from -calculateLayoutThatFits:. Calling this method may
* be expensive if result is not cached.
*
* @see [ASDisplayNode(Subclassing) calculateSizeThatFits:]
* @see [ASDisplayNode(Subclassing) measureWithSizeRange:]
* @see [ASDisplayNode(Subclassing) calculateLayoutThatFits:]
*/
- (CGSize)measure:(CGSize)constrainedSize;
@ -139,21 +141,20 @@ typedef CALayer *(^ASDisplayNodeLayerBlock)();
* @abstract Return the calculated size.
*
* @discussion Ideal for use by subclasses in -layout, having already prompted their subnodes to calculate their size by
* calling -measure: on them in -calculateSizeThatFits:.
* calling -measure: on them in -calculateLayoutThatFits.
*
* @return Size already calculated by calculateSizeThatFits:.
* @return Size already calculated by -calculateLayoutThatFits:.
*
* @warning Subclasses must not override this; it returns the last cached size calculated and is never expensive.
* @warning Subclasses must not override this; it returns the last cached measurement and is never expensive.
*/
@property (nonatomic, readonly, assign) CGSize calculatedSize;
/**
* @abstract Return the constrained size used for calculating size.
* @abstract Return the constrained size range used for calculating layout.
*
* @return The constrained size used by calculateSizeThatFits:.
* @return The minimum and maximum constrained sizes used by calculateLayoutThatFits:.
*/
@property (nonatomic, readonly, assign) CGSize constrainedSizeForCalculatedSize;
@property (nonatomic, readonly, assign) ASSizeRange constrainedSizeForCalculatedLayout;
/** @name Managing the nodes hierarchy */
@ -312,7 +313,7 @@ typedef CALayer *(^ASDisplayNodeLayerBlock)();
- (void)recursivelySetDisplaySuspended:(BOOL)flag;
/**
* @abstract Calls -clearRendering on the receiver and its subnode hierarchy.
* @abstract Calls -clearContents on the receiver and its subnode hierarchy.
*
* @discussion Clears backing stores and other memory-intensive intermediates.
* If the node is removed from a visible hierarchy and then re-added, it will automatically trigger a new asynchronous display,
@ -322,27 +323,27 @@ typedef CALayer *(^ASDisplayNodeLayerBlock)();
* @see displaySuspended and setNeedsDisplay
*/
- (void)recursivelyClearRendering;
- (void)recursivelyClearContents;
/**
* @abstract Calls -clearRemoteData on the receiver and its subnode hierarchy.
* @abstract Calls -clearFetchedData on the receiver and its subnode hierarchy.
*
* @discussion Clears any memory-intensive fetched content.
* This method is used to notify the node that it should purge any content that is both expensive to fetch and to
* retain in memory.
*
* @see clearRemoteData and fetchRemoteData
* @see clearFetchedData and fetchData
*/
- (void)recursivelyClearRemoteData;
- (void)recursivelyClearFetchedData;
/**
* @abstract Calls -fetchRemoteData on the receiver and its subnode hierarchy.
* @abstract Calls -fetchData on the receiver and its subnode hierarchy.
*
* @discussion Fetches content from remote sources for the current node and all subnodes.
*
* @see fetchRemoteData and clearRemoteData
* @see fetchData and clearFetchedData
*/
- (void)recursivelyFetchRemoteData;
- (void)recursivelyFetchData;
/**
* @abstract Toggle displaying a placeholder over the node that covers content until the node and all subnodes are
@ -432,6 +433,16 @@ typedef CALayer *(^ASDisplayNodeLayerBlock)();
*/
- (CGRect)convertRect:(CGRect)rect fromNode:(ASDisplayNode *)node;
/** @name UIResponder methods */
// By default these fall through to the underlying view, but can be overridden.
- (BOOL)canBecomeFirstResponder; // default==NO
- (BOOL)becomeFirstResponder; // default==NO (no-op)
- (BOOL)canResignFirstResponder; // default==YES
- (BOOL)resignFirstResponder; // default==NO (no-op)
- (BOOL)isFirstResponder;
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender;
@end
@ -533,6 +544,9 @@ typedef CALayer *(^ASDisplayNodeLayerBlock)();
@property (atomic, assign) BOOL accessibilityViewIsModal;
@property (atomic, assign) BOOL shouldGroupAccessibilityChildren;
// Accessibility identification support
@property (nonatomic, copy) NSString *accessibilityIdentifier;
@end
/*

View File

@ -18,6 +18,8 @@
#import "_ASScopeTimer.h"
#import "ASDisplayNodeExtras.h"
#import "ASInternalHelpers.h"
@interface ASDisplayNode () <UIGestureRecognizerDelegate>
/**
@ -37,37 +39,16 @@
@implementation ASDisplayNode
@synthesize spacingBefore = _spacingBefore;
@synthesize spacingAfter = _spacingAfter;
@synthesize flexGrow = _flexGrow;
@synthesize flexShrink = _flexShrink;
@synthesize flexBasis = _flexBasis;
@synthesize alignSelf = _alignSelf;
BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector)
{
Method superclassMethod = class_getInstanceMethod([ASDisplayNode class], selector);
Method subclassMethod = class_getInstanceMethod(subclass, selector);
IMP superclassIMP = superclassMethod ? method_getImplementation(superclassMethod) : NULL;
IMP subclassIMP = subclassMethod ? method_getImplementation(subclassMethod) : NULL;
return (superclassIMP != subclassIMP);
}
CGFloat ASDisplayNodeScreenScale()
{
static CGFloat screenScale = 0.0;
static dispatch_once_t onceToken;
ASDispatchOnceOnMainThread(&onceToken, ^{
screenScale = [[UIScreen mainScreen] scale];
});
return screenScale;
}
static void ASDispatchOnceOnMainThread(dispatch_once_t *predicate, dispatch_block_t block)
{
if ([NSThread isMainThread]) {
dispatch_once(predicate, block);
} else {
if (DISPATCH_EXPECT(*predicate == 0L, NO)) {
dispatch_sync(dispatch_get_main_queue(), ^{
dispatch_once(predicate, block);
});
}
}
return ASSubclassOverridesSelector([ASDisplayNode class], subclass, selector);
}
void ASDisplayNodePerformBlockOnMainThread(void (^block)())
@ -89,9 +70,17 @@ void ASDisplayNodePerformBlockOnMainThread(void (^block)())
// Subclasses should never override these
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(calculatedSize)), @"Subclass %@ must not override calculatedSize method", NSStringFromClass(self));
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(calculatedLayout)), @"Subclass %@ must not override calculatedLayout method", NSStringFromClass(self));
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(measure:)), @"Subclass %@ must not override measure method", NSStringFromClass(self));
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(recursivelyClearRendering)), @"Subclass %@ must not override recursivelyClearRendering method", NSStringFromClass(self));
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(recursivelyClearRemoteData)), @"Subclass %@ must not override recursivelyClearRemoteData method", NSStringFromClass(self));
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(measureWithSizeRange:)), @"Subclass %@ must not override measureWithSizeRange method", NSStringFromClass(self));
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(recursivelyClearContents)), @"Subclass %@ must not override recursivelyClearContents method", NSStringFromClass(self));
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(recursivelyClearFetchedData)), @"Subclass %@ must not override recursivelyClearFetchedData method", NSStringFromClass(self));
// At most one of the three layout methods is overridden
ASDisplayNodeAssert((ASDisplayNodeSubclassOverridesSelector(self, @selector(calculateSizeThatFits:)) ? 1 : 0)
+ (ASDisplayNodeSubclassOverridesSelector(self, @selector(layoutSpecThatFits:)) ? 1 : 0)
+ (ASDisplayNodeSubclassOverridesSelector(self, @selector(calculateLayoutThatFits:)) ? 1 : 0) <= 1,
@"Subclass %@ must override at most one of the three layout methods: calculateLayoutThatFits, layoutSpecThatFits or calculateSizeThatFits", NSStringFromClass(self));
}
+ (BOOL)layerBackedNodesEnabled
@ -113,10 +102,10 @@ void ASDisplayNodePerformBlockOnMainThread(void (^block)())
- (void)_initializeInstance
{
_contentsScaleForDisplay = ASDisplayNodeScreenScale();
_contentsScaleForDisplay = ASScreenScale();
_displaySentinel = [[ASSentinel alloc] init];
_flags.isInHierarchy = NO;
_flags.displaysAsynchronously = YES;
@ -138,7 +127,12 @@ void ASDisplayNodePerformBlockOnMainThread(void (^block)())
if (ASDisplayNodeSubclassOverridesSelector([self class], @selector(touchesEnded:withEvent:))) {
overrides |= ASDisplayNodeMethodOverrideTouchesEnded;
}
if (ASDisplayNodeSubclassOverridesSelector([self class], @selector(calculateSizeThatFits:))) {
overrides |= ASDisplayNodeMethodOverrideCalculateSizeThatFits;
}
_methodOverrides = overrides;
_flexBasis = ASRelativeDimensionUnconstrained;
}
- (id)init
@ -437,33 +431,38 @@ void ASDisplayNodePerformBlockOnMainThread(void (^block)())
- (CGSize)measure:(CGSize)constrainedSize
{
ASDN::MutexLocker l(_propertyLock);
return [self __measure:constrainedSize];
return [self measureWithSizeRange:ASSizeRangeMake(CGSizeZero, constrainedSize)].size;
}
- (CGSize)__measure:(CGSize)constrainedSize
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
{
ASDN::MutexLocker l(_propertyLock);
return [self __measureWithSizeRange:constrainedSize];
}
- (ASLayout *)__measureWithSizeRange:(ASSizeRange)constrainedSize
{
ASDisplayNodeAssertThreadAffinity(self);
if (![self __shouldSize])
return CGSizeZero;
return nil;
// only calculate the size if
// - we haven't already
// - the width is different from the last time
// - the height is different from the last time
if (!_flags.isMeasured || !CGSizeEqualToSize(constrainedSize, _constrainedSize)) {
_size = [self calculateSizeThatFits:constrainedSize];
// - the constrained size range is different
if (!_flags.isMeasured || !ASSizeRangeEqualToSizeRange(constrainedSize, _constrainedSize)) {
_layout = [self calculateLayoutThatFits:constrainedSize];
_constrainedSize = constrainedSize;
_flags.isMeasured = YES;
}
ASDisplayNodeAssertTrue(_size.width >= 0.0);
ASDisplayNodeAssertTrue(_size.height >= 0.0);
ASDisplayNodeAssertTrue(_layout.layoutableObject == self);
ASDisplayNodeAssertTrue(_layout.size.width >= 0.0);
ASDisplayNodeAssertTrue(_layout.size.height >= 0.0);
// we generate placeholders at measure: time so that a node is guaranteed to have a placeholder ready to go
// we generate placeholders at measureWithSizeRange: time so that a node is guaranteed to have a placeholder ready to go
// also if a node has no size, it should not have a placeholder
if (self.placeholderEnabled && [self _displaysAsynchronously] && _size.width > 0.0 && _size.height > 0.0) {
if (self.placeholderEnabled && [self _displaysAsynchronously] && _layout.size.width > 0.0 && _layout.size.height > 0.0) {
if (!_placeholderImage) {
_placeholderImage = [self placeholderImage];
}
@ -473,7 +472,7 @@ void ASDisplayNodePerformBlockOnMainThread(void (^block)())
}
}
return _size;
return _layout;
}
- (BOOL)displaysAsynchronously
@ -566,7 +565,7 @@ void ASDisplayNodePerformBlockOnMainThread(void (^block)())
ASDisplayNodeAssertMainThread();
ASDN::MutexLocker l(_propertyLock);
if (CGRectEqualToRect(_layer.bounds, CGRectZero)) {
return; // Performing layout on a zero-bounds view often results in frame calculations with negative sizes after applying margins, which will cause measure: on subnodes to assert.
return; // Performing layout on a zero-bounds view often results in frame calculations with negative sizes after applying margins, which will cause measureWithSizeRange: on subnodes to assert.
}
_placeholderLayer.frame = _layer.bounds;
[self layout];
@ -1216,6 +1215,10 @@ static NSInteger incrementIfFound(NSInteger i) {
{
ASDN::MutexLocker l(_propertyLock);
if (!_pendingDisplayNodes) {
_pendingDisplayNodes = [[NSMutableSet alloc] init];
}
[_pendingDisplayNodes addObject:node];
}
@ -1278,19 +1281,51 @@ static NSInteger incrementIfFound(NSInteger i) {
#pragma mark - For Subclasses
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
{
ASDisplayNodeAssertThreadAffinity(self);
if (_methodOverrides & ASDisplayNodeMethodOverrideCalculateSizeThatFits) {
CGSize size = [self calculateSizeThatFits:constrainedSize.max];
return [ASLayout newWithLayoutableObject:self size:ASSizeRangeClamp(constrainedSize, size)];
} else {
id<ASLayoutable> layoutSpec = [self layoutSpecThatFits:constrainedSize];
ASLayout *layout = [layoutSpec measureWithSizeRange:constrainedSize];
// Make sure layoutableObject of the root layout is `self`, so that the flattened layout will be structurally correct.
if (layout.layoutableObject != self) {
layout.position = CGPointZero;
layout = [ASLayout newWithLayoutableObject:self size:layout.size sublayouts:@[layout]];
}
return [layout flattenedLayoutUsingPredicateBlock:^BOOL(ASLayout *evaluatedLayout) {
return [_subnodes containsObject:evaluatedLayout.layoutableObject];
}];
}
}
- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize
{
ASDisplayNodeAssertThreadAffinity(self);
return CGSizeZero;
}
- (id<ASLayoutable>)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
ASDisplayNodeAssertThreadAffinity(self);
return nil;
}
- (ASLayout *)calculatedLayout
{
ASDisplayNodeAssertThreadAffinity(self);
return _layout;
}
- (CGSize)calculatedSize
{
ASDisplayNodeAssertThreadAffinity(self);
return _size;
return _layout.size;
}
- (CGSize)constrainedSizeForCalculatedSize
- (ASSizeRange)constrainedSizeForCalculatedLayout
{
ASDisplayNodeAssertThreadAffinity(self);
return _constrainedSize;
@ -1301,10 +1336,10 @@ static NSInteger incrementIfFound(NSInteger i) {
return nil;
}
- (void)invalidateCalculatedSize
- (void)invalidateCalculatedLayout
{
ASDisplayNodeAssertThreadAffinity(self);
// This will cause -measure: to actually compute the size instead of returning the previously cached size
// This will cause -measureWithSizeRange: to actually compute the size instead of returning the previously cached size
_flags.isMeasured = NO;
}
@ -1329,49 +1364,63 @@ static NSInteger incrementIfFound(NSInteger i) {
[self __exitedHierarchy];
}
- (void)clearRendering
- (void)clearContents
{
self.layer.contents = nil;
_placeholderLayer.contents = nil;
_placeholderImage = nil;
}
- (void)recursivelyClearRendering
- (void)recursivelyClearContents
{
for (ASDisplayNode *subnode in self.subnodes) {
[subnode recursivelyClearRendering];
[subnode recursivelyClearContents];
}
[self clearRendering];
[self clearContents];
}
- (void)fetchRemoteData
- (void)fetchData
{
// subclass override
}
- (void)recursivelyFetchRemoteData
- (void)recursivelyFetchData
{
for (ASDisplayNode *subnode in self.subnodes) {
[subnode recursivelyFetchRemoteData];
[subnode recursivelyFetchData];
}
[self fetchRemoteData];
[self fetchData];
}
- (void)clearRemoteData
- (void)clearFetchedData
{
// subclass override
}
- (void)recursivelyClearRemoteData
- (void)recursivelyClearFetchedData
{
for (ASDisplayNode *subnode in self.subnodes) {
[subnode recursivelyClearRemoteData];
[subnode recursivelyClearFetchedData];
}
[self clearRemoteData];
[self clearFetchedData];
}
- (void)layout
{
ASDisplayNodeAssertMainThread();
if (!_flags.isMeasured) {
return;
}
// Assume that _layout was flattened and is 1-level deep.
for (ASLayout *subnodeLayout in _layout.sublayouts) {
ASDisplayNodeAssert([_subnodes containsObject:subnodeLayout.layoutableObject], @"Cached sublayouts must only contain subnodes' layout.");
((ASDisplayNode *)subnodeLayout.layoutableObject).frame = CGRectMake(subnodeLayout.position.x,
subnodeLayout.position.y,
subnodeLayout.size.width,
subnodeLayout.size.height);
}
}
- (void)displayWillStart
@ -1382,7 +1431,11 @@ static NSInteger incrementIfFound(NSInteger i) {
[_supernode subnodeDisplayWillStart:self];
if (_placeholderImage && _placeholderLayer && self.layer.contents == nil) {
[CATransaction begin];
[CATransaction setDisableActions:YES];
_placeholderLayer.contents = (id)_placeholderImage.CGImage;
_placeholderLayer.opacity = 1.0;
[CATransaction commit];
[self.layer addSublayer:_placeholderLayer];
}
}
@ -1637,7 +1690,7 @@ static void _recursivelySetDisplaySuspended(ASDisplayNode *node, CALayer *layer,
static dispatch_queue_t asyncSizingQueue = NULL;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
asyncSizingQueue = dispatch_queue_create("com.facebook.AsyncDisplayKit.ASDisplayNode.asyncSizingQueue", DISPATCH_QUEUE_CONCURRENT);
asyncSizingQueue = dispatch_queue_create("org.AsyncDisplayKit.ASDisplayNode.asyncSizingQueue", DISPATCH_QUEUE_CONCURRENT);
// we use the highpri queue to prioritize UI rendering over other async operations
dispatch_set_target_queue(asyncSizingQueue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0));
});
@ -1689,6 +1742,35 @@ static void _recursivelySetDisplaySuspended(ASDisplayNode *node, CALayer *layer,
}
- (BOOL)canBecomeFirstResponder {
return NO;
}
- (BOOL)canResignFirstResponder {
return YES;
}
- (BOOL)isFirstResponder {
ASDisplayNodeAssertMainThread();
return _view != nil && [_view isFirstResponder];
}
// Note: this implicitly loads the view if it hasn't been loaded yet.
- (BOOL)becomeFirstResponder {
ASDisplayNodeAssertMainThread();
return !self.layerBacked && [self canBecomeFirstResponder] && [self.view becomeFirstResponder];
}
- (BOOL)resignFirstResponder {
ASDisplayNodeAssertMainThread();
return !self.layerBacked && [self canResignFirstResponder] && [_view resignFirstResponder];
}
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
ASDisplayNodeAssertMainThread();
return !self.layerBacked && [self.view canPerformAction:action withSender:sender];
}
@end
@implementation ASDisplayNode (Debugging)
@ -1816,12 +1898,12 @@ static const char *ASDisplayNodeAssociatedNodeKey = "ASAssociatedNode";
- (void)reclaimMemory
{
[self clearRendering];
[self clearContents];
}
- (void)recursivelyReclaimMemory
{
[self recursivelyClearRendering];
[self recursivelyClearContents];
}
@end

View File

@ -57,10 +57,10 @@
- (BOOL)isFirstResponder;
//! @abstract Makes the receiver's text view the first responder.
- (void)becomeFirstResponder;
- (BOOL)becomeFirstResponder;
//! @abstract Resigns the receiver's text view from first-responder status, if it has it.
- (void)resignFirstResponder;
- (BOOL)resignFirstResponder;
#pragma mark - Geometry
/**

View File

@ -152,7 +152,7 @@
- (void)layout
{
[super layout];
ASDisplayNodeAssertMainThread();
[self _layoutTextView];
}
@ -290,7 +290,7 @@
[_textKitComponents.textStorage setAttributedString:attributedStringToDisplay];
// Calculated size depends on the seeded text.
[self invalidateCalculatedSize];
[self invalidateCalculatedLayout];
// Update if placeholder is shown.
[self _updateDisplayingPlaceholder];
@ -352,16 +352,26 @@
return [_textKitComponents.textView isFirstResponder];
}
- (void)becomeFirstResponder
{
ASDN::MutexLocker l(_textKitLock);
[_textKitComponents.textView becomeFirstResponder];
- (BOOL)canBecomeFirstResponder {
ASDN::MutexLocker l(_textKitLock);
return [_textKitComponents.textView canBecomeFirstResponder];
}
- (void)resignFirstResponder
- (BOOL)becomeFirstResponder
{
ASDN::MutexLocker l(_textKitLock);
[_textKitComponents.textView resignFirstResponder];
return [_textKitComponents.textView becomeFirstResponder];
}
- (BOOL)canResignFirstResponder {
ASDN::MutexLocker l(_textKitLock);
return [_textKitComponents.textView canResignFirstResponder];
}
- (BOOL)resignFirstResponder
{
ASDN::MutexLocker l(_textKitLock);
return [_textKitComponents.textView resignFirstResponder];
}
#pragma mark - UITextView Delegate
@ -389,7 +399,7 @@
[self _updateDisplayingPlaceholder];
// Invalidate, as our calculated size depends on the textview's seeded text.
[self invalidateCalculatedSize];
[self invalidateCalculatedLayout];
// Delegateify.
[self _delegateDidUpdateText];

View File

@ -17,6 +17,8 @@
#import "ASImageNode+CGExtras.h"
#import "ASInternalHelpers.h"
@interface _ASImageNodeDrawParameters : NSObject
@property (nonatomic, assign, readonly) BOOL cropEnabled;
@ -82,7 +84,7 @@
return nil;
// TODO can this be removed?
self.contentsScale = ASDisplayNodeScreenScale();
self.contentsScale = ASScreenScale();
self.contentMode = UIViewContentModeScaleAspectFill;
self.opaque = NO;
@ -123,7 +125,7 @@
ASDN::MutexUnlocker u(_imageLock);
ASDisplayNodePerformBlockOnMainThread(^{
[self invalidateCalculatedSize];
[self invalidateCalculatedLayout];
[self setNeedsDisplay];
});
}
@ -347,7 +349,7 @@ extern asimagenode_modification_block_t ASImageNodeRoundBorderModificationBlock(
// Draw a border on top.
if (borderWidth > 0.0) {
[borderColor setStroke];
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), borderWidth);
[roundOutline setLineWidth:borderWidth];
[roundOutline stroke];
}

View File

@ -162,9 +162,9 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
}
#pragma mark - ASDisplayNode Overrides
- (void)clearRendering
- (void)clearContents
{
[super clearRendering]; // This actually clears the contents, so we need to do this first for our displayedImageIdentifier to be meaningful.
[super clearContents]; // This actually clears the contents, so we need to do this first for our displayedImageIdentifier to be meaningful.
[self _setDisplayedImageIdentifier:nil withImage:nil];
if (_downloadIdentifier) {
@ -177,12 +177,12 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
{
[super displayWillStart];
[self fetchRemoteData];
[self fetchData];
}
- (void)fetchRemoteData
- (void)fetchData
{
[super fetchRemoteData];
[super fetchData];
[self _loadImageIdentifiers];
}

View File

@ -127,12 +127,12 @@
{
[super displayWillStart];
[self fetchRemoteData];
[self fetchData];
}
- (void)clearRemoteData
- (void)clearFetchedData
{
[super clearRemoteData];
[super clearFetchedData];
{
ASDN::MutexLocker l(_lock);
@ -143,9 +143,9 @@
}
}
- (void)fetchRemoteData
- (void)fetchData
{
[super fetchRemoteData];
[super fetchData];
{
ASDN::MutexLocker l(_lock);

View File

@ -7,6 +7,19 @@
*/
#import "ASScrollNode.h"
#import "_ASDisplayLayer.h"
@interface ASScrollView : UIScrollView
@end
@implementation ASScrollView
+ (Class)layerClass
{
return [_ASDisplayLayer class];
}
@end
@implementation ASScrollNode
@dynamic view;
@ -14,7 +27,7 @@
- (instancetype)init
{
return [super initWithViewBlock:^UIView *{
return [[UIScrollView alloc] init];
return [[ASScrollView alloc] init];
}];
}

View File

@ -70,6 +70,15 @@
*/
@property (nonatomic, assign) CGFloat leadingScreensForBatching;
/**
* Reload everything from scratch, destroying the working range and all cached nodes.
*
* @param completion block to run on completion of asynchronous loading or nil. If supplied, the block is run on
* the main thread.
* @warning This method is substantially more expensive than UITableView's version.
*/
-(void)reloadDataWithCompletion:(void (^)())completion;
/**
* Reload everything from scratch, destroying the working range and all cached nodes.
*

View File

@ -10,7 +10,7 @@
#import "ASAssert.h"
#import "ASDataController.h"
#import "ASFlowLayoutController.h"
#import "ASCollectionViewLayoutController.h"
#import "ASLayoutController.h"
#import "ASRangeController.h"
#import "ASDisplayNodeInternal.h"
@ -77,11 +77,17 @@ static BOOL _isInterceptedSelector(SEL sel)
- (BOOL)respondsToSelector:(SEL)aSelector
{
ASDisplayNodeAssert(_target, @"target must not be nil"); // catch weak ref's being nilled early
ASDisplayNodeAssert(_interceptor, @"interceptor must not be nil");
return (_isInterceptedSelector(aSelector) || [_target respondsToSelector:aSelector]);
}
- (id)forwardingTargetForSelector:(SEL)aSelector
{
ASDisplayNodeAssert(_target, @"target must not be nil"); // catch weak ref's being nilled early
ASDisplayNodeAssert(_interceptor, @"interceptor must not be nil");
if (_isInterceptedSelector(aSelector)) {
return _interceptor;
}
@ -118,6 +124,8 @@ static BOOL _isInterceptedSelector(SEL sel)
BOOL _asyncDataFetchingEnabled;
ASBatchContext *_batchContext;
NSIndexPath *_pendingVisibleIndexPath;
}
@property (atomic, assign) BOOL asyncDataSourceLocked;
@ -126,9 +134,50 @@ static BOOL _isInterceptedSelector(SEL sel)
@implementation ASTableView
/**
@summary Conditionally performs UIView geometry changes in the given block without animation.
Used primarily to circumvent UITableView forcing insertion animations when explicitly told not to via
`UITableViewRowAnimationNone`. More info: https://github.com/facebook/AsyncDisplayKit/pull/445
@param withoutAnimation Set to `YES` to perform given block without animation
@param block Perform UIView geometry changes within the passed block
*/
void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) {
if (withoutAnimation) {
BOOL animationsEnabled = [UIView areAnimationsEnabled];
[UIView setAnimationsEnabled:NO];
block();
[UIView setAnimationsEnabled:animationsEnabled];
} else {
block();
}
}
#pragma mark -
#pragma mark Lifecycle
- (void)configureWithAsyncDataFetching:(BOOL)asyncDataFetchingEnabled
{
_layoutController = [[ASFlowLayoutController alloc] initWithScrollOption:ASFlowLayoutDirectionVertical];
_rangeController = [[ASRangeController alloc] init];
_rangeController.layoutController = _layoutController;
_rangeController.delegate = self;
_dataController = [[ASDataController alloc] initWithAsyncDataFetching:asyncDataFetchingEnabled];
_dataController.dataSource = self;
_dataController.delegate = _rangeController;
_layoutController.dataSource = _dataController;
_asyncDataFetchingEnabled = asyncDataFetchingEnabled;
_asyncDataSourceLocked = NO;
_leadingScreensForBatching = 1.0;
_batchContext = [[ASBatchContext alloc] init];
}
- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style
{
return [self initWithFrame:frame style:style asyncDataFetching:NO];
@ -136,29 +185,36 @@ static BOOL _isInterceptedSelector(SEL sel)
- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style asyncDataFetching:(BOOL)asyncDataFetchingEnabled
{
if (!(self = [super initWithFrame:frame style:style]))
return nil;
_layoutController = [[ASFlowLayoutController alloc] initWithScrollOption:ASFlowLayoutDirectionVertical];
_rangeController = [[ASRangeController alloc] init];
_rangeController.layoutController = _layoutController;
_rangeController.delegate = self;
_dataController = [[ASDataController alloc] initWithAsyncDataFetching:asyncDataFetchingEnabled];
_dataController.dataSource = self;
_dataController.delegate = _rangeController;
_asyncDataFetchingEnabled = asyncDataFetchingEnabled;
_asyncDataSourceLocked = NO;
_leadingScreensForBatching = 1.0;
_batchContext = [[ASBatchContext alloc] init];
// FIXME: asyncDataFetching is currently unreliable for some use cases.
// https://github.com/facebook/AsyncDisplayKit/issues/385
asyncDataFetchingEnabled = NO;
[self configureWithAsyncDataFetching:asyncDataFetchingEnabled];
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
if (!(self = [super initWithCoder:aDecoder]))
return nil;
[self configureWithAsyncDataFetching:NO];
return self;
}
- (void)dealloc
{
// Sometimes the UIKit classes can call back to their delegate even during deallocation.
// This bug might be iOS 7-specific.
super.delegate = nil;
super.dataSource = nil;
}
#pragma mark -
#pragma mark Overrides
@ -175,13 +231,15 @@ static BOOL _isInterceptedSelector(SEL sel)
- (void)setAsyncDataSource:(id<ASTableViewDataSource>)asyncDataSource
{
if (_asyncDataSource == asyncDataSource)
return;
// Note: It's common to check if the value hasn't changed and short-circuit but we aren't doing that here to handle
// the (common) case of nilling the asyncDataSource in the ViewController's dealloc. In this case our _asyncDataSource
// will return as nil (ARC magic) even though the _proxyDataSource still exists. It's really important to nil out
// super.dataSource in this case because calls to _ASTableViewProxy will start failing and cause crashes.
if (asyncDataSource == nil) {
super.dataSource = nil;
_asyncDataSource = nil;
_proxyDataSource = nil;
super.dataSource = nil;
} else {
_asyncDataSource = asyncDataSource;
_proxyDataSource = [[_ASTableViewProxy alloc] initWithTarget:_asyncDataSource interceptor:self];
@ -191,13 +249,17 @@ static BOOL _isInterceptedSelector(SEL sel)
- (void)setAsyncDelegate:(id<ASTableViewDelegate>)asyncDelegate
{
if (_asyncDelegate == asyncDelegate)
return;
// Note: It's common to check if the value hasn't changed and short-circuit but we aren't doing that here to handle
// the (common) case of nilling the asyncDelegate in the ViewController's dealloc. In this case our _asyncDelegate
// will return as nil (ARC magic) even though the _proxyDelegate still exists. It's really important to nil out
// super.delegate in this case because calls to _ASTableViewProxy will start failing and cause crashes.
if (asyncDelegate == nil) {
_asyncDelegate = nil;
_proxyDelegate = nil;
// order is important here, the delegate must be callable while nilling super.delegate to avoid random crashes
// in UIScrollViewAccessibility.
super.delegate = nil;
_asyncDelegate = nil;
_proxyDelegate = nil;
} else {
_asyncDelegate = asyncDelegate;
_proxyDelegate = [[_ASTableViewProxy alloc] initWithTarget:asyncDelegate interceptor:self];
@ -205,13 +267,18 @@ static BOOL _isInterceptedSelector(SEL sel)
}
}
- (void)reloadData
- (void)reloadDataWithCompletion:(void (^)())completion
{
ASDisplayNodeAssert(self.asyncDelegate, @"ASTableView's asyncDelegate property must be set.");
ASDisplayNodePerformBlockOnMainThread(^{
[super reloadData];
});
[_dataController reloadDataWithAnimationOption:UITableViewRowAnimationNone];
[_dataController reloadDataWithAnimationOptions:UITableViewRowAnimationNone completion:completion];
}
- (void)reloadData
{
[self reloadDataWithCompletion:nil];
}
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType
@ -268,42 +335,42 @@ static BOOL _isInterceptedSelector(SEL sel)
- (void)insertSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation
{
[_dataController insertSections:sections withAnimationOption:animation];
[_dataController insertSections:sections withAnimationOptions:animation];
}
- (void)deleteSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation
{
[_dataController deleteSections:sections withAnimationOption:animation];
[_dataController deleteSections:sections withAnimationOptions:animation];
}
- (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation
{
[_dataController reloadSections:sections withAnimationOption:animation];
[_dataController reloadSections:sections withAnimationOptions:animation];
}
- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection
{
[_dataController moveSection:section toSection:newSection withAnimationOption:UITableViewRowAnimationNone];
[_dataController moveSection:section toSection:newSection withAnimationOptions:UITableViewRowAnimationNone];
}
- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation
{
[_dataController insertRowsAtIndexPaths:indexPaths withAnimationOption:animation];
[_dataController insertRowsAtIndexPaths:indexPaths withAnimationOptions:animation];
}
- (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation
{
[_dataController deleteRowsAtIndexPaths:indexPaths withAnimationOption:animation];
[_dataController deleteRowsAtIndexPaths:indexPaths withAnimationOptions:animation];
}
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation
{
[_dataController reloadRowsAtIndexPaths:indexPaths withAnimationOption:animation];
[_dataController reloadRowsAtIndexPaths:indexPaths withAnimationOptions:animation];
}
- (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath
{
[_dataController moveRowAtIndexPath:indexPath toIndexPath:newIndexPath withAnimationOption:UITableViewRowAnimationNone];
[_dataController moveRowAtIndexPath:indexPath toIndexPath:newIndexPath withAnimationOptions:UITableViewRowAnimationNone];
}
#pragma mark -
@ -324,6 +391,11 @@ static BOOL _isInterceptedSelector(SEL sel)
cell.backgroundColor = node.backgroundColor;
cell.selectionStyle = node.selectionStyle;
// the following ensures that we clip the entire cell to it's bounds if node.clipsToBounds is set (the default)
// This is actually a workaround for a bug we are seeing in some rare cases (selected background view
// overlaps other cells if size of ASCellNode has changed.)
cell.clipsToBounds = node.clipsToBounds;
return cell;
}
@ -347,25 +419,19 @@ static BOOL _isInterceptedSelector(SEL sel)
{
CGPoint scrollVelocity = [self.panGestureRecognizer velocityInView:self.superview];
ASScrollDirection direction = ASScrollDirectionNone;
if (_layoutController.layoutDirection == ASFlowLayoutDirectionHorizontal) {
if (scrollVelocity.x > 0) {
direction = ASScrollDirectionRight;
} else if (scrollVelocity.x < 0) {
direction = ASScrollDirectionLeft;
}
if (scrollVelocity.y > 0) {
direction = ASScrollDirectionDown;
} else {
if (scrollVelocity.y > 0) {
direction = ASScrollDirectionDown;
} else {
direction = ASScrollDirectionUp;
}
direction = ASScrollDirectionUp;
}
return direction;
}
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
_pendingVisibleIndexPath = indexPath;
[_rangeController visibleNodeIndexPathsDidChangeWithScrollDirection:self.scrollDirection];
if ([_asyncDelegate respondsToSelector:@selector(tableView:willDisplayNodeForRowAtIndexPath:)]) {
@ -375,6 +441,10 @@ static BOOL _isInterceptedSelector(SEL sel)
- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath*)indexPath
{
if ([_pendingVisibleIndexPath isEqual:indexPath]) {
_pendingVisibleIndexPath = nil;
}
[_rangeController visibleNodeIndexPathsDidChangeWithScrollDirection:self.scrollDirection];
if ([_asyncDelegate respondsToSelector:@selector(tableView:didEndDisplayingNodeForRowAtIndexPath:)]) {
@ -445,7 +515,51 @@ static BOOL _isInterceptedSelector(SEL sel)
- (NSArray *)rangeControllerVisibleNodeIndexPaths:(ASRangeController *)rangeController
{
ASDisplayNodeAssertMainThread();
return [self indexPathsForVisibleRows];
NSArray *visibleIndexPaths = self.indexPathsForVisibleRows;
if ( _pendingVisibleIndexPath ) {
NSMutableSet *indexPaths = [NSMutableSet setWithArray:self.indexPathsForVisibleRows];
BOOL (^isAfter)(NSIndexPath *, NSIndexPath *) = ^BOOL(NSIndexPath *indexPath, NSIndexPath *anchor) {
if (!anchor || !indexPath) {
return NO;
}
if (indexPath.section == anchor.section) {
return (indexPath.row == anchor.row+1); // assumes that indexes are valid
} else if (indexPath.section > anchor.section && indexPath.row == 0) {
if (anchor.row != [_dataController numberOfRowsInSection:anchor.section] -1) {
return NO; // anchor is not at the end of the section
}
NSInteger nextSection = anchor.section+1;
while([_dataController numberOfRowsInSection:nextSection] == 0) {
++nextSection;
}
return indexPath.section == nextSection;
}
return NO;
};
BOOL (^isBefore)(NSIndexPath *, NSIndexPath *) = ^BOOL(NSIndexPath *indexPath, NSIndexPath *anchor) {
return isAfter(anchor, indexPath);
};
if ( [indexPaths containsObject:_pendingVisibleIndexPath]) {
_pendingVisibleIndexPath = nil; // once it has shown up in visibleIndexPaths, we can stop tracking it
} else if (!isBefore(_pendingVisibleIndexPath, visibleIndexPaths.firstObject) &&
!isAfter(_pendingVisibleIndexPath, visibleIndexPaths.lastObject)) {
_pendingVisibleIndexPath = nil; // not contiguous, ignore.
} else {
[indexPaths addObject:_pendingVisibleIndexPath];
visibleIndexPaths = [indexPaths.allObjects sortedArrayUsingSelector:@selector(compare:)];
}
}
return visibleIndexPaths;
}
- (NSArray *)rangeController:(ASRangeController *)rangeController nodesAtIndexPaths:(NSArray *)indexPaths
@ -459,32 +573,44 @@ static BOOL _isInterceptedSelector(SEL sel)
return self.bounds.size;
}
- (void)rangeController:(ASRangeController *)rangeController didInsertNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption
- (void)rangeController:(ASRangeController *)rangeController didInsertNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
{
ASDisplayNodeAssertMainThread();
[super insertRowsAtIndexPaths:indexPaths withRowAnimation:(UITableViewRowAnimation)animationOption];
BOOL preventAnimation = animationOptions == UITableViewRowAnimationNone;
ASPerformBlockWithoutAnimation(preventAnimation, ^{
[super insertRowsAtIndexPaths:indexPaths withRowAnimation:(UITableViewRowAnimation)animationOptions];
});
}
- (void)rangeController:(ASRangeController *)rangeController didDeleteNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption
- (void)rangeController:(ASRangeController *)rangeController didDeleteNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
{
ASDisplayNodeAssertMainThread();
[super deleteRowsAtIndexPaths:indexPaths withRowAnimation:(UITableViewRowAnimation)animationOption];
BOOL preventAnimation = animationOptions == UITableViewRowAnimationNone;
ASPerformBlockWithoutAnimation(preventAnimation, ^{
[super deleteRowsAtIndexPaths:indexPaths withRowAnimation:(UITableViewRowAnimation)animationOptions];
});
}
- (void)rangeController:(ASRangeController *)rangeController didInsertSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOption:(ASDataControllerAnimationOptions)animationOption
- (void)rangeController:(ASRangeController *)rangeController didInsertSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
{
ASDisplayNodeAssertMainThread();
[super insertSections:indexSet withRowAnimation:(UITableViewRowAnimation)animationOption];
BOOL preventAnimation = animationOptions == UITableViewRowAnimationNone;
ASPerformBlockWithoutAnimation(preventAnimation, ^{
[super insertSections:indexSet withRowAnimation:(UITableViewRowAnimation)animationOptions];
});
}
- (void)rangeController:(ASRangeController *)rangeController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOption:(ASDataControllerAnimationOptions)animationOption
- (void)rangeController:(ASRangeController *)rangeController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
{
ASDisplayNodeAssertMainThread();
[super deleteSections:indexSet withRowAnimation:(UITableViewRowAnimation)animationOption];
BOOL preventAnimation = animationOptions == UITableViewRowAnimationNone;
ASPerformBlockWithoutAnimation(preventAnimation, ^{
[super deleteSections:indexSet withRowAnimation:(UITableViewRowAnimation)animationOptions];
});
}
#pragma mark - ASDataControllerDelegate
@ -523,7 +649,7 @@ static BOOL _isInterceptedSelector(SEL sel)
}
}
- (NSUInteger)dataController:(ASDataController *)dataControllre rowsInSection:(NSUInteger)section
- (NSUInteger)dataController:(ASDataController *)dataController rowsInSection:(NSUInteger)section
{
return [_asyncDataSource tableView:self numberOfRowsInSection:section];
}

View File

@ -76,6 +76,8 @@ typedef NS_ENUM(NSUInteger, ASTextNodeHighlightStyle) {
*/
@property (nonatomic, readonly, assign) NSUInteger lineCount;
@property (nonatomic, strong) NSArray *exclusionPaths;
#pragma mark - Placeholders
/**
@ -192,6 +194,11 @@ typedef NS_ENUM(NSUInteger, ASTextNodeHighlightStyle) {
*/
@property (nonatomic, assign) BOOL longPressCancelsTouches;
/**
@abstract if YES will not intercept touches for non-link areas of the text. Default is NO.
*/
@property (nonatomic, assign) BOOL passthroughNonlinkTouches;
@end

View File

@ -87,6 +87,8 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f)
CGFloat _shadowOpacity;
CGFloat _shadowRadius;
NSArray *_exclusionPaths;
NSAttributedString *_composedTruncationString;
NSString *_highlightedLinkAttributeName;
@ -224,12 +226,12 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f)
[self _invalidateRenderer];
}
- (void)clearRendering
- (void)clearContents
{
// We discard the backing store and renderer to prevent the very large
// memory overhead of maintaining these for all text nodes. They can be
// regenerated when layout is necessary.
[super clearRendering]; // ASDisplayNode will set layer.contents = nil
[super clearContents]; // ASDisplayNode will set layer.contents = nil
[self _invalidateRenderer];
}
@ -281,6 +283,7 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f)
truncationString:_composedTruncationString
truncationMode:_truncationMode
maximumLineCount:_maximumLineCount
exclusionPaths:_exclusionPaths
constrainedSize:constrainedSize];
}
return _renderer;
@ -334,8 +337,8 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f)
// We need an entirely new renderer
[self _invalidateRenderer];
// Tell the display node superclasses that the cached sizes are incorrect now
[self invalidateCalculatedSize];
// Tell the display node superclasses that the cached layout is incorrect now
[self invalidateCalculatedLayout];
[self setNeedsDisplay];
@ -349,6 +352,23 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f)
}
}
#pragma mark - Text Layout
- (void)setExclusionPaths:(NSArray *)exclusionPaths
{
if ((_exclusionPaths == nil && exclusionPaths != nil) || (![_exclusionPaths isEqualToArray:exclusionPaths])) {
_exclusionPaths = exclusionPaths;
[self _invalidateRenderer];
[self invalidateCalculatedLayout];
[self setNeedsDisplay];
}
}
- (NSArray *)exclusionPaths
{
return _exclusionPaths;
}
#pragma mark - Drawing
+ (void)drawRect:(CGRect)bounds withParameters:(ASTextNodeDrawParameters *)parameters isCancelled:(asdisplaynode_iscancelled_block_t)isCancelledBlock isRasterizing:(BOOL)isRasterizing
@ -749,6 +769,33 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f)
#pragma mark - Touch Handling
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
if (!_passthroughNonlinkTouches) {
return [super pointInside:point withEvent:event];
}
NSRange range = NSMakeRange(0, 0);
NSString *linkAttributeName = nil;
BOOL inAdditionalTruncationMessage = NO;
id linkAttributeValue = [self _linkAttributeValueAtPoint:point
attributeName:&linkAttributeName
range:&range
inAdditionalTruncationMessage:&inAdditionalTruncationMessage];
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) {
return YES;
} else {
return NO;
}
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesBegan:touches withEvent:event];

View File

@ -24,3 +24,16 @@
#import <AsyncDisplayKit/ASCellNode.h>
#import <AsyncDisplayKit/ASScrollNode.h>
#import <AsyncDisplayKit/ASLayout.h>
#import <AsyncDisplayKit/ASDimension.h>
#import <AsyncDisplayKit/ASLayoutable.h>
#import <AsyncDisplayKit/ASLayoutSpec.h>
#import <AsyncDisplayKit/ASBackgroundLayoutSpec.h>
#import <AsyncDisplayKit/ASCenterLayoutSpec.h>
#import <AsyncDisplayKit/ASInsetLayoutSpec.h>
#import <AsyncDisplayKit/ASOverlayLayoutSpec.h>
#import <AsyncDisplayKit/ASRatioLayoutSpec.h>
#import <AsyncDisplayKit/ASStaticLayoutSpec.h>
#import <AsyncDisplayKit/ASStackLayoutChild.h>
#import <AsyncDisplayKit/ASStackLayoutSpec.h>

View File

@ -0,0 +1,20 @@
/* Copyright (c) 2015-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.
*/
#import <AsyncDisplayKit/ASLayoutController.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
@interface ASAbstractLayoutController : NSObject <ASLayoutController>
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType;
- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType;
@property (nonatomic, assign) ASRangeTuningParameters tuningParameters ASDISPLAYNODE_DEPRECATED;
@end

View File

@ -0,0 +1,95 @@
/* Copyright (c) 2015-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.
*/
#import "ASAbstractLayoutController.h"
#include <vector>
#import "ASAssert.h"
@interface ASAbstractLayoutController () {
std::vector<ASRangeTuningParameters> _tuningParameters;
}
@end
@implementation ASAbstractLayoutController
- (instancetype)init
{
if (!(self = [super init])) {
return nil;
}
_tuningParameters = std::vector<ASRangeTuningParameters>(ASLayoutRangeTypeCount);
_tuningParameters[ASLayoutRangeTypePreload] = {
.leadingBufferScreenfuls = 3,
.trailingBufferScreenfuls = 2
};
_tuningParameters[ASLayoutRangeTypeRender] = {
.leadingBufferScreenfuls = 2,
.trailingBufferScreenfuls = 1
};
return self;
}
#pragma mark - Tuning Parameters
- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType
{
ASDisplayNodeAssert(rangeType < _tuningParameters.size(), @"Requesting a range that is OOB for the configured tuning parameters");
return _tuningParameters[rangeType];
}
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType
{
ASDisplayNodeAssert(rangeType < _tuningParameters.size(), @"Requesting a range that is OOB for the configured tuning parameters");
_tuningParameters[rangeType] = tuningParameters;
}
// Support for the deprecated tuningParameters property
- (ASRangeTuningParameters)tuningParameters
{
return [self tuningParametersForRangeType:ASLayoutRangeTypeRender];
}
// Support for the deprecated tuningParameters property
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters
{
[self setTuningParameters:tuningParameters forRangeType:ASLayoutRangeTypeRender];
}
#pragma mark - Index Path Range Support
// Support for deprecated method
- (BOOL)shouldUpdateForVisibleIndexPath:(NSArray *)indexPaths viewportSize:(CGSize)viewportSize
{
return [self shouldUpdateForVisibleIndexPaths:indexPaths viewportSize:viewportSize rangeType:ASLayoutRangeTypeRender];
}
// Support for the deprecated method
- (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection viewportSize:(CGSize)viewportSize
{
return [self indexPathsForScrolling:scrollDirection viewportSize:viewportSize rangeType:ASLayoutRangeTypeRender];
}
#pragma mark - Abstract
- (BOOL)shouldUpdateForVisibleIndexPaths:(NSArray *)indexPaths viewportSize:(CGSize)viewportSize rangeType:(ASLayoutRangeType)rangeType
{
ASDisplayNodeAssertNotSupported();
return NO;
}
- (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection viewportSize:(CGSize)viewportSize rangeType:(ASLayoutRangeType)rangeType
{
ASDisplayNodeAssertNotSupported();
return nil;
}
@end

View File

@ -20,15 +20,21 @@
/**
* Collection of properties associated with a download request.
*/
typedef void (^ASBasicImageDownloaderContextProgressBlock)(CGFloat);
typedef void (^ASBasicImageDownloaderContextCompletionBlock)(CGImageRef, NSError *);
NSString * const kASBasicImageDownloaderContextCallbackQueue = @"kASBasicImageDownloaderContextCallbackQueue";
NSString * const kASBasicImageDownloaderContextProgressBlock = @"kASBasicImageDownloaderContextProgressBlock";
NSString * const kASBasicImageDownloaderContextCompletionBlock = @"kASBasicImageDownloaderContextCompletionBlock";
@interface ASBasicImageDownloaderContext ()
{
BOOL _invalid;
ASDN::RecursiveMutex _propertyLock;
}
@property (nonatomic, strong) dispatch_queue_t callbackQueue;
@property (nonatomic, copy) void (^downloadProgressBlock)(CGFloat);
@property (nonatomic, copy) void (^completionBlock)(CGImageRef, NSError *);
@property (nonatomic, strong) NSMutableArray *callbackDatas;
@end
@ -63,6 +69,7 @@ static ASDN::RecursiveMutex currentRequestsLock;
{
if (self = [super init]) {
_URL = URL;
_callbackDatas = [NSMutableArray array];
}
return self;
}
@ -87,6 +94,77 @@ static ASDN::RecursiveMutex currentRequestsLock;
return _invalid;
}
- (void)addCallbackData:(NSDictionary *)callbackData
{
ASDN::MutexLocker l(_propertyLock);
[self.callbackDatas addObject:callbackData];
}
- (void)performProgressBlocks:(CGFloat)progress
{
ASDN::MutexLocker l(_propertyLock);
for (NSDictionary *callbackData in self.callbackDatas) {
ASBasicImageDownloaderContextProgressBlock progressBlock = callbackData[kASBasicImageDownloaderContextProgressBlock];
dispatch_queue_t callbackQueue = callbackData[kASBasicImageDownloaderContextCallbackQueue];
if (progressBlock) {
dispatch_async(callbackQueue, ^{
progressBlock(progress);
});
}
}
}
- (void)completeWithImage:(UIImage *)image error:(NSError *)error
{
ASDN::MutexLocker l(_propertyLock);
for (NSDictionary *callbackData in self.callbackDatas) {
ASBasicImageDownloaderContextCompletionBlock completionBlock = callbackData[kASBasicImageDownloaderContextCompletionBlock];
dispatch_queue_t callbackQueue = callbackData[kASBasicImageDownloaderContextCallbackQueue];
if (completionBlock) {
dispatch_async(callbackQueue, ^{
completionBlock(image.CGImage, error);
});
}
}
self.sessionTask = nil;
[self.callbackDatas removeAllObjects];
}
- (NSURLSessionTask *)createSessionTaskIfNecessaryWithBlock:(NSURLSessionTask *(^)())creationBlock {
{
ASDN::MutexLocker l(_propertyLock);
if (self.isCancelled) {
return nil;
}
if (self.sessionTask && (self.sessionTask.state == NSURLSessionTaskStateRunning)) {
return nil;
}
}
NSURLSessionTask *newTask = creationBlock();
{
ASDN::MutexLocker l(_propertyLock);
if (self.isCancelled) {
return nil;
}
if (self.sessionTask && (self.sessionTask.state == NSURLSessionTaskStateRunning)) {
return nil;
}
self.sessionTask = newTask;
return self.sessionTask;
}
}
@end
@ -150,30 +228,29 @@ static const char *kContextKey = NSStringFromClass(ASBasicImageDownloaderContext
// NSURLSessionDownloadTask will do file I/O to create a temp directory. If called on the main thread this will
// cause significant performance issues.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// the downloader may have been invalidated in the time it takes to async dispatch this block
if ([context isCancelled]) {
return;
}
// create download task
NSURLSessionDownloadTask *task = [_session downloadTaskWithURL:URL];
// since creating the task does disk I/O, we should check if it has been invalidated
if ([context isCancelled]) {
return;
}
// associate metadata with it
context.callbackQueue = callbackQueue ?: dispatch_get_main_queue();
context.downloadProgressBlock = downloadProgressBlock;
context.completionBlock = completion;
context.sessionTask = task;
task.originalRequest.asyncdisplaykit_context = context;
NSMutableDictionary *callbackData = [NSMutableDictionary dictionary];
callbackData[kASBasicImageDownloaderContextCallbackQueue] = callbackQueue ?: dispatch_get_main_queue();
// start downloading
[task resume];
if (downloadProgressBlock) {
callbackData[kASBasicImageDownloaderContextProgressBlock] = [downloadProgressBlock copy];
}
context.sessionTask = task;
if (completion) {
callbackData[kASBasicImageDownloaderContextCompletionBlock] = [completion copy];
}
[context addCallbackData:[NSDictionary dictionaryWithDictionary:callbackData]];
// Create new task if necessary
NSURLSessionDownloadTask *task = (NSURLSessionDownloadTask *)[context createSessionTaskIfNecessaryWithBlock:^(){return [_session downloadTaskWithURL:URL];}];
if (task) {
task.originalRequest.asyncdisplaykit_context = context;
// start downloading
[task resume];
}
});
return context;
@ -200,9 +277,7 @@ static const char *kContextKey = NSStringFromClass(ASBasicImageDownloaderContext
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
ASBasicImageDownloaderContext *context = downloadTask.originalRequest.asyncdisplaykit_context;
if (context.downloadProgressBlock) {
context.downloadProgressBlock((CGFloat)totalBytesWritten / (CGFloat)totalBytesExpectedToWrite);
}
[context performProgressBlocks:(CGFloat)totalBytesWritten / (CGFloat)totalBytesExpectedToWrite];
}
// invoked if the download succeeded with no error
@ -214,12 +289,9 @@ static const char *kContextKey = NSStringFromClass(ASBasicImageDownloaderContext
return;
}
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:location]];
if (context.completionBlock) {
dispatch_async(context.callbackQueue, ^{
context.completionBlock(image.CGImage, nil);
});
if (context) {
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:location]];
[context completeWithImage:image error:nil];
}
}
@ -229,9 +301,7 @@ static const char *kContextKey = NSStringFromClass(ASBasicImageDownloaderContext
{
ASBasicImageDownloaderContext *context = task.originalRequest.asyncdisplaykit_context;
if (context && error) {
dispatch_async(context.callbackQueue, ^{
context.completionBlock(NULL, error);
});
[context completeWithImage:nil error:error];
}
}

View File

@ -0,0 +1,18 @@
/* Copyright (c) 2015-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.
*/
#import <AsyncDisplayKit/ASAbstractLayoutController.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
@class ASCollectionView;
@interface ASCollectionViewLayoutController : ASAbstractLayoutController
- (instancetype)initWithCollectionView:(ASCollectionView *)collectionView;
@end

View File

@ -0,0 +1,163 @@
/* Copyright (c) 2015-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.
*/
#import "ASCollectionViewLayoutController.h"
#include <vector>
#import "ASAssert.h"
#import "ASCollectionView.h"
#import "CGRect+ASConvenience.h"
struct ASDirectionalScreenfulBuffer {
CGFloat positiveDirection; // Positive relative to iOS Core Animation layer coordinate space.
CGFloat negativeDirection;
};
typedef struct ASDirectionalScreenfulBuffer ASDirectionalScreenfulBuffer;
ASDirectionalScreenfulBuffer ASDirectionalScreenfulBufferHorizontal(ASScrollDirection scrollDirection,
ASRangeTuningParameters rangeTuningParameters)
{
ASDirectionalScreenfulBuffer horizontalBuffer = {0, 0};
BOOL movingRight = ASScrollDirectionContainsRight(scrollDirection);
horizontalBuffer.positiveDirection = movingRight ? rangeTuningParameters.leadingBufferScreenfuls :
rangeTuningParameters.trailingBufferScreenfuls;
horizontalBuffer.negativeDirection = movingRight ? rangeTuningParameters.trailingBufferScreenfuls :
rangeTuningParameters.leadingBufferScreenfuls;
return horizontalBuffer;
}
ASDirectionalScreenfulBuffer ASDirectionalScreenfulBufferVertical(ASScrollDirection scrollDirection,
ASRangeTuningParameters rangeTuningParameters)
{
ASDirectionalScreenfulBuffer verticalBuffer = {0, 0};
BOOL movingDown = ASScrollDirectionContainsDown(scrollDirection);
verticalBuffer.positiveDirection = movingDown ? rangeTuningParameters.leadingBufferScreenfuls :
rangeTuningParameters.trailingBufferScreenfuls;
verticalBuffer.negativeDirection = movingDown ? rangeTuningParameters.trailingBufferScreenfuls :
rangeTuningParameters.leadingBufferScreenfuls;
return verticalBuffer;
}
struct ASRangeGeometry {
CGRect rangeBounds;
CGRect updateBounds;
};
typedef struct ASRangeGeometry ASRangeGeometry;
#pragma mark -
#pragma mark ASCollectionViewLayoutController
@interface ASCollectionViewLayoutController ()
{
UIScrollView * __weak _scrollView;
UICollectionViewLayout * __strong _collectionViewLayout;
std::vector<CGRect> _updateRangeBoundsIndexedByRangeType;
ASScrollDirection _scrollableDirections;
}
@end
@implementation ASCollectionViewLayoutController
- (instancetype)initWithCollectionView:(ASCollectionView *)collectionView
{
if (!(self = [super init])) {
return nil;
}
_scrollableDirections = [collectionView scrollableDirections];
_scrollView = collectionView;
_collectionViewLayout = [collectionView collectionViewLayout];
_updateRangeBoundsIndexedByRangeType = std::vector<CGRect>(ASLayoutRangeTypeCount);
return self;
}
#pragma mark -
#pragma mark Index Paths in Range
- (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection
viewportSize:(CGSize)viewportSize
rangeType:(ASLayoutRangeType)rangeType
{
ASRangeGeometry rangeGeometry = [self rangeGeometryWithScrollDirection:scrollDirection
rangeTuningParameters:[self tuningParametersForRangeType:rangeType]];
_updateRangeBoundsIndexedByRangeType[rangeType] = rangeGeometry.updateBounds;
return [self indexPathsForItemsWithinRangeBounds:rangeGeometry.rangeBounds];
}
- (ASRangeGeometry)rangeGeometryWithScrollDirection:(ASScrollDirection)scrollDirection
rangeTuningParameters:(ASRangeTuningParameters)rangeTuningParameters
{
CGRect rangeBounds = _scrollView.bounds;
CGRect updateBounds = _scrollView.bounds;
BOOL canScrollHorizontally = ASScrollDirectionContainsHorizontalDirection(_scrollableDirections);
if (canScrollHorizontally) {
ASDirectionalScreenfulBuffer horizontalBuffer = ASDirectionalScreenfulBufferHorizontal(scrollDirection,
rangeTuningParameters);
rangeBounds = asdk_CGRectExpandHorizontally(rangeBounds,
horizontalBuffer.negativeDirection,
horizontalBuffer.positiveDirection);
// Update bounds is at most 95% of the next/previous screenful and at least half of tuning parameter value.
updateBounds = asdk_CGRectExpandHorizontally(updateBounds,
MIN(horizontalBuffer.negativeDirection * 0.5, 0.95),
MIN(horizontalBuffer.positiveDirection * 0.5, 0.95));
}
BOOL canScrollVertically = ASScrollDirectionContainsVerticalDirection(_scrollableDirections);
if (canScrollVertically) {
ASDirectionalScreenfulBuffer verticalBuffer = ASDirectionalScreenfulBufferVertical(scrollDirection,
rangeTuningParameters);
rangeBounds = asdk_CGRectExpandVertically(rangeBounds,
verticalBuffer.negativeDirection,
verticalBuffer.positiveDirection);
// Update bounds is at most 95% of the next/previous screenful and at least half of tuning parameter value.
updateBounds = asdk_CGRectExpandVertically(updateBounds,
MIN(verticalBuffer.negativeDirection * 0.5, 0.95),
MIN(verticalBuffer.positiveDirection * 0.5, 0.95));
}
return {rangeBounds, updateBounds};
}
- (NSSet *)indexPathsForItemsWithinRangeBounds:(CGRect)rangeBounds
{
NSMutableSet *indexPathSet = [[NSMutableSet alloc] init];
NSArray *layoutAttributes = [_collectionViewLayout layoutAttributesForElementsInRect:rangeBounds];
for (UICollectionViewLayoutAttributes *la in layoutAttributes) {
[indexPathSet addObject:la.indexPath];
}
return indexPathSet;
}
#pragma mark -
#pragma mark Should Update Range
- (BOOL)shouldUpdateForVisibleIndexPaths:(NSArray *)indexPaths
viewportSize:(CGSize)viewportSize
rangeType:(ASLayoutRangeType)rangeType
{
CGRect updateRangeBounds = _updateRangeBoundsIndexedByRangeType[rangeType];
if (CGRectIsEmpty(updateRangeBounds)) {
return YES;
}
CGRect currentBounds = _scrollView.bounds;
if (CGRectIsEmpty(currentBounds)) {
currentBounds = CGRectMake(0, 0, viewportSize.width, viewportSize.height);
}
if (CGRectContainsRect(updateRangeBounds, currentBounds)) {
return NO;
} else {
return YES;
}
}
@end

View File

@ -8,7 +8,7 @@
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASDealloc2MainObject.h>
#import "ASFlowLayoutController.h"
@class ASCellNode;
@class ASDataController;
@ -70,26 +70,22 @@ typedef NSUInteger ASDataControllerAnimationOptions;
/**
Called for insertion of elements.
*/
- (void)dataController:(ASDataController *)dataController willInsertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption;
- (void)dataController:(ASDataController *)dataController didInsertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption;
- (void)dataController:(ASDataController *)dataController didInsertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
/**
Called for deletion of elements.
*/
- (void)dataController:(ASDataController *)dataController willDeleteNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption;
- (void)dataController:(ASDataController *)dataController didDeleteNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption;
- (void)dataController:(ASDataController *)dataController didDeleteNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
/**
Called for insertion of sections.
*/
- (void)dataController:(ASDataController *)dataController willInsertSections:(NSArray *)sections atIndexSet:(NSIndexSet *)indexSet withAnimationOption:(ASDataControllerAnimationOptions)animationOption;
- (void)dataController:(ASDataController *)dataController didInsertSections:(NSArray *)sections atIndexSet:(NSIndexSet *)indexSet withAnimationOption:(ASDataControllerAnimationOptions)animationOption;
- (void)dataController:(ASDataController *)dataController didInsertSections:(NSArray *)sections atIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
/**
Called for deletion of sections.
*/
- (void)dataController:(ASDataController *)dataController willDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOption:(ASDataControllerAnimationOptions)animationOption;
- (void)dataController:(ASDataController *)dataController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOption:(ASDataControllerAnimationOptions)animationOption;
- (void)dataController:(ASDataController *)dataController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
@end
@ -101,7 +97,8 @@ typedef NSUInteger ASDataControllerAnimationOptions;
* will be updated asynchronously. The dataSource must be updated to reflect the changes before these methods has been called.
* For each data updatin, the corresponding methods in delegate will be called.
*/
@interface ASDataController : ASDealloc2MainObject
@protocol ASFlowLayoutControllerDataSource;
@interface ASDataController : ASDealloc2MainObject <ASFlowLayoutControllerDataSource>
/**
Data source for fetching data info.
@ -117,19 +114,23 @@ typedef NSUInteger ASDataControllerAnimationOptions;
* Designated iniailizer.
*
* @param asyncDataFetchingEnabled Enable the data fetching in async mode.
*
* @discussion If enabled, we will fetch data through `dataController:nodeAtIndexPath:` and `dataController:rowsInSection:` in background thread.
* Otherwise, the methods will be invoked synchronically in calling thread. Enabling data fetching in async mode could avoid blocking main thread
* while allocating cell on main thread, which is frequently reported issue for handing large scale data. On another hand, the application code
* while allocating cell on main thread, which is frequently reported issue for handling large scale data. On another hand, the application code
* will take the responsibility to avoid data inconsistence. Specifically, we will lock the data source through `dataControllerLockDataSource`,
* and unlock it by `dataControllerUnlockDataSource` after the data fetching. The application should not update the data source while
* the data source is locked.
*/
- (instancetype)initWithAsyncDataFetching:(BOOL)asyncDataFetchingEnabled;
/** @name Initial loading */
/** @name Initial loading
*
* @discussion This method allows choosing an animation style for the first load of content. It is typically used just once,
* for example in viewWillAppear:, to specify an animation option for the information already present in the asyncDataSource.
*/
- (void)initialDataLoadingWithAnimationOption:(ASDataControllerAnimationOptions)animationOption;
- (void)initialDataLoadingWithAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
/** @name Data Updating */
@ -139,23 +140,23 @@ typedef NSUInteger ASDataControllerAnimationOptions;
- (void)endUpdatesWithCompletion:(void (^)(BOOL))completion;
- (void)insertSections:(NSIndexSet *)sections withAnimationOption:(ASDataControllerAnimationOptions)animationOption;
- (void)insertSections:(NSIndexSet *)sections withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
- (void)deleteSections:(NSIndexSet *)sections withAnimationOption:(ASDataControllerAnimationOptions)animationOption;;
- (void)deleteSections:(NSIndexSet *)sections withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
- (void)reloadSections:(NSIndexSet *)sections withAnimationOption:(ASDataControllerAnimationOptions)animationOption;
- (void)reloadSections:(NSIndexSet *)sections withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection withAnimationOption:(ASDataControllerAnimationOptions)animationOption;;
- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption;
- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
- (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption;
- (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption;
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
- (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath withAnimationOption:(ASDataControllerAnimationOptions)animationOption;;
- (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
- (void)reloadDataWithAnimationOption:(ASDataControllerAnimationOptions)animationOption;;
- (void)reloadDataWithAnimationOptions:(ASDataControllerAnimationOptions)animationOptions completion:(void (^)())completion;
/** @name Data Querying */
@ -167,4 +168,6 @@ typedef NSUInteger ASDataControllerAnimationOptions;
- (NSArray *)nodesAtIndexPaths:(NSArray *)indexPaths;
- (NSArray *)completedNodes; // This provides efficient access to the entire _completedNodes multidimensional array.
@end

View File

@ -16,58 +16,22 @@
#import "ASMultidimensionalArrayUtils.h"
#import "ASDisplayNodeInternal.h"
#define INSERT_NODES(multidimensionalArray, indexPath, elements, animationOption) \
{ \
if ([_delegate respondsToSelector:@selector(dataController:willInsertNodes:atIndexPaths:withAnimationOption:)]) { \
[_delegate dataController:self willInsertNodes:elements atIndexPaths:indexPath withAnimationOption:animationOption]; \
} \
ASInsertElementsIntoMultidimensionalArrayAtIndexPaths(multidimensionalArray, indexPath, elements); \
if ([_delegate respondsToSelector:@selector(dataController:didInsertNodes:atIndexPaths:withAnimationOption:)]) { \
[_delegate dataController:self didInsertNodes:elements atIndexPaths:indexPath withAnimationOption:animationOption]; \
} \
}
#define DELETE_NODES(multidimensionalArray, indexPath, animationOption) \
{ \
if ([_delegate respondsToSelector:@selector(dataController:willDeleteNodesAtIndexPaths:withAnimationOption:)]) { \
[_delegate dataController:self willDeleteNodesAtIndexPaths:indexPath withAnimationOption:animationOption]; \
} \
ASDeleteElementsInMultidimensionalArrayAtIndexPaths(multidimensionalArray, indexPath); \
if ([_delegate respondsToSelector:@selector(dataController:didDeleteNodesAtIndexPaths:withAnimationOption:)]) { \
[_delegate dataController:self didDeleteNodesAtIndexPaths:indexPath withAnimationOption:animationOption]; \
} \
}
#define INSERT_SECTIONS(multidimensionalArray, indexSet, sections, animationOption) \
{ \
if ([_delegate respondsToSelector:@selector(dataController:willInsertSections:atIndexSet:withAnimationOption:)]) { \
[_delegate dataController:self willInsertSections:sections atIndexSet:indexSet withAnimationOption:animationOption]; \
} \
[multidimensionalArray insertObjects:sections atIndexes:indexSet]; \
if ([_delegate respondsToSelector:@selector(dataController:didInsertSections:atIndexSet:withAnimationOption:)]) { \
[_delegate dataController:self didInsertSections:sections atIndexSet:indexSet withAnimationOption:animationOption]; \
} \
}
#define DELETE_SECTIONS(multidimensionalArray, indexSet, animationOption) \
{ \
if ([_delegate respondsToSelector:@selector(dataController:willDeleteSectionsAtIndexSet:withAnimationOption:)]) { \
[_delegate dataController:self willDeleteSectionsAtIndexSet:indexSet withAnimationOption:animationOption]; \
} \
[multidimensionalArray removeObjectsAtIndexes:indexSet]; \
if ([_delegate respondsToSelector:@selector(dataController:didDeleteSectionsAtIndexSet:withAnimationOption:)]) { \
[_delegate dataController:self didDeleteSectionsAtIndexSet:indexSet withAnimationOption:animationOption]; \
} \
}
const static NSUInteger kASDataControllerSizingCountPerProcessor = 5;
static void *kASSizingQueueContext = &kASSizingQueueContext;
@interface ASDataController () {
NSMutableArray *_nodes;
NSMutableArray *_pendingBlocks;
NSMutableArray *_completedNodes; // Main thread only. External data access can immediately query this.
NSMutableArray *_editingNodes; // Modified on _editingTransactionQueue only. Updates propogated to _completedNodes.
NSMutableArray *_pendingEditCommandBlocks; // To be run on the main thread. Handles begin/endUpdates tracking.
NSOperationQueue *_editingTransactionQueue; // Serial background queue. Dispatches concurrent layout and manages _editingNodes.
BOOL _asyncDataFetchingEnabled;
BOOL _delegateDidInsertNodes;
BOOL _delegateDidDeleteNodes;
BOOL _delegateDidInsertSections;
BOOL _delegateDidDeleteSections;
}
@property (atomic, assign) NSUInteger batchUpdateCounter;
@ -76,20 +40,46 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
@implementation ASDataController
- (instancetype)initWithAsyncDataFetching:(BOOL)asyncDataFetchingEnabled {
if (self = [super init]) {
_nodes = [NSMutableArray array];
_pendingBlocks = [NSMutableArray array];
_batchUpdateCounter = 0;
_asyncDataFetchingEnabled = asyncDataFetchingEnabled;
}
#pragma mark - Lifecycle
- (instancetype)initWithAsyncDataFetching:(BOOL)asyncDataFetchingEnabled
{
if (!(self = [super init])) {
return nil;
}
_completedNodes = [NSMutableArray array];
_editingNodes = [NSMutableArray array];
_pendingEditCommandBlocks = [NSMutableArray array];
_editingTransactionQueue = [[NSOperationQueue alloc] init];
_editingTransactionQueue.maxConcurrentOperationCount = 1; // Serial queue
_editingTransactionQueue.name = @"org.AsyncDisplayKit.ASDataController.editingTransactionQueue";
_batchUpdateCounter = 0;
_asyncDataFetchingEnabled = asyncDataFetchingEnabled;
return self;
}
#pragma mark - Utils
- (void)setDelegate:(id<ASDataControllerDelegate>)delegate
{
if (_delegate == delegate) {
return;
}
_delegate = delegate;
// Interrogate our delegate to understand its capabilities, optimizing away expensive respondsToSelector: calls later.
_delegateDidInsertNodes = [_delegate respondsToSelector:@selector(dataController:didInsertNodes:atIndexPaths:withAnimationOptions:)];
_delegateDidDeleteNodes = [_delegate respondsToSelector:@selector(dataController:didDeleteNodesAtIndexPaths:withAnimationOptions:)];
_delegateDidInsertSections = [_delegate respondsToSelector:@selector(dataController:didInsertSections:atIndexSet:withAnimationOptions:)];
_delegateDidDeleteSections = [_delegate respondsToSelector:@selector(dataController:didDeleteSectionsAtIndexSet:withAnimationOptions:)];
}
+ (NSUInteger)parallelProcessorCount {
+ (NSUInteger)parallelProcessorCount
{
static NSUInteger parallelProcessorCount;
static dispatch_once_t onceToken;
@ -100,44 +90,187 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
return parallelProcessorCount;
}
+ (dispatch_queue_t)sizingQueue
#pragma mark - Cell Layout
- (void)_layoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
{
static dispatch_queue_t sizingQueue = NULL;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sizingQueue = dispatch_queue_create("com.facebook.AsyncDisplayKit.ASDataController.sizingQueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_set_specific(sizingQueue, kASSizingQueueContext, kASSizingQueueContext, NULL);
dispatch_set_target_queue(sizingQueue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
});
return sizingQueue;
ASDisplayNodeAssert([NSOperationQueue currentQueue] == _editingTransactionQueue, @"Cell node layout must be initiated from edit transaction queue");
if (!nodes.count) {
return;
}
dispatch_group_t layoutGroup = dispatch_group_create();
for (NSUInteger j = 0; j < nodes.count && j < indexPaths.count; j += kASDataControllerSizingCountPerProcessor) {
NSArray *subIndexPaths = [indexPaths subarrayWithRange:NSMakeRange(j, MIN(kASDataControllerSizingCountPerProcessor, indexPaths.count - j))];
// TODO: The current implementation does not make use of different constrained sizes per node.
// There should be a fast-path that avoids all of this object creation.
NSMutableArray *nodeBoundSizes = [[NSMutableArray alloc] initWithCapacity:kASDataControllerSizingCountPerProcessor];
[subIndexPaths enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop) {
[nodeBoundSizes addObject:[NSValue valueWithCGSize:[_dataSource dataController:self constrainedSizeForNodeAtIndexPath:indexPath]]];
}];
dispatch_group_async(layoutGroup, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[subIndexPaths enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop) {
ASCellNode *node = nodes[j + idx];
[node measure:[nodeBoundSizes[idx] CGSizeValue]];
node.frame = CGRectMake(0.0f, 0.0f, node.calculatedSize.width, node.calculatedSize.height);
}];
});
}
// Block the _editingTransactionQueue from executing a new edit transaction until layout is done & _editingNodes array is updated.
dispatch_group_wait(layoutGroup, DISPATCH_TIME_FOREVER);
// Insert finished nodes into data storage
[self _insertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions];
}
+ (BOOL)isSizingQueue {
return kASSizingQueueContext == dispatch_get_specific(kASSizingQueueContext);
- (void)_batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
{
NSUInteger blockSize = [[ASDataController class] parallelProcessorCount] * kASDataControllerSizingCountPerProcessor;
// Processing in batches
for (NSUInteger i = 0; i < indexPaths.count; i += blockSize) {
NSRange batchedRange = NSMakeRange(i, MIN(indexPaths.count - i, blockSize));
NSArray *batchedIndexPaths = [indexPaths subarrayWithRange:batchedRange];
NSArray *batchedNodes = [nodes subarrayWithRange:batchedRange];
[self _layoutNodes:batchedNodes atIndexPaths:batchedIndexPaths withAnimationOptions:animationOptions];
}
}
- (void)asyncUpdateDataWithBlock:(dispatch_block_t)block {
dispatch_async(dispatch_get_main_queue(), ^{
if (_batchUpdateCounter) {
[_pendingBlocks addObject:block];
} else {
block();
}
#pragma mark - Internal Data Querying + Editing
- (void)_insertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
{
if (indexPaths.count == 0)
return;
ASInsertElementsIntoMultidimensionalArrayAtIndexPaths(_editingNodes, indexPaths, nodes);
// Deep copy is critical here, or future edits to the sub-arrays will pollute state between _editing and _complete on different threads.
NSMutableArray *completedNodes = (NSMutableArray *)ASMultidimensionalArrayDeepMutableCopy(_editingNodes);
ASDisplayNodePerformBlockOnMainThread(^{
_completedNodes = completedNodes;
if (_delegateDidInsertNodes)
[_delegate dataController:self didInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions];
});
}
- (void)syncUpdateDataWithBlock:(dispatch_block_t)block {
dispatch_sync(dispatch_get_main_queue(), ^{
if (_batchUpdateCounter) {
[_pendingBlocks addObject:block];
} else {
block();
}
- (void)_deleteNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
{
if (indexPaths.count == 0)
return;
ASDeleteElementsInMultidimensionalArrayAtIndexPaths(_editingNodes, indexPaths);
ASDisplayNodePerformBlockOnMainThread(^{
ASDeleteElementsInMultidimensionalArrayAtIndexPaths(_completedNodes, indexPaths);
if (_delegateDidDeleteNodes)
[_delegate dataController:self didDeleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions];
});
}
- (void)performDataFetchingWithBlock:(dispatch_block_t)block {
- (void)_insertSections:(NSMutableArray *)sections atIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
{
if (indexSet.count == 0)
return;
[_editingNodes insertObjects:sections atIndexes:indexSet];
// Deep copy is critical here, or future edits to the sub-arrays will pollute state between _editing and _complete on different threads.
NSArray *sectionsForCompleted = (NSMutableArray *)ASMultidimensionalArrayDeepMutableCopy(sections);
ASDisplayNodePerformBlockOnMainThread(^{
[_completedNodes insertObjects:sectionsForCompleted atIndexes:indexSet];
if (_delegateDidInsertSections)
[_delegate dataController:self didInsertSections:sections atIndexSet:indexSet withAnimationOptions:animationOptions];
});
}
- (void)_deleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
{
if (indexSet.count == 0)
return;
[_editingNodes removeObjectsAtIndexes:indexSet];
ASDisplayNodePerformBlockOnMainThread(^{
[_completedNodes removeObjectsAtIndexes:indexSet];
if (_delegateDidDeleteSections)
[_delegate dataController:self didDeleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions];
});
}
#pragma mark - Initial Load & Full Reload (External API)
- (void)initialDataLoadingWithAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
{
[self performEditCommandWithBlock:^{
ASDisplayNodeAssertMainThread();
[self accessDataSourceWithBlock:^{
NSMutableArray *indexPaths = [NSMutableArray array];
NSUInteger sectionNum = [_dataSource dataControllerNumberOfSections:self];
// insert sections
[self insertSections:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, sectionNum)] withAnimationOptions:0];
for (NSUInteger i = 0; i < sectionNum; i++) {
NSIndexPath *indexPath = [[NSIndexPath alloc] initWithIndex:i];
NSUInteger rowNum = [_dataSource dataController:self rowsInSection:i];
for (NSUInteger j = 0; j < rowNum; j++) {
[indexPaths addObject:[indexPath indexPathByAddingIndex:j]];
}
}
// insert elements
[self insertRowsAtIndexPaths:indexPaths withAnimationOptions:animationOptions];
}];
}];
}
- (void)reloadDataWithAnimationOptions:(ASDataControllerAnimationOptions)animationOptions completion:(void (^)())completion
{
[self performEditCommandWithBlock:^{
ASDisplayNodeAssertMainThread();
[_editingTransactionQueue waitUntilAllOperationsAreFinished];
[self accessDataSourceWithBlock:^{
NSUInteger sectionCount = [_dataSource dataControllerNumberOfSections:self];
NSMutableArray *updatedNodes = [NSMutableArray array];
NSMutableArray *updatedIndexPaths = [NSMutableArray array];
[self _populateFromEntireDataSourceWithMutableNodes:updatedNodes mutableIndexPaths:updatedIndexPaths];
[_editingTransactionQueue addOperationWithBlock:^{
// Remove everything that existed before the reload, now that we're ready to insert replacements
NSArray *indexPaths = ASIndexPathsForMultidimensionalArray(_editingNodes);
[self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions];
NSMutableIndexSet *indexSet = [[NSMutableIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, _editingNodes.count)];
[self _deleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions];
// Insert each section
NSMutableArray *sections = [NSMutableArray arrayWithCapacity:sectionCount];
for (int i = 0; i < sectionCount; i++) {
[sections addObject:[[NSMutableArray alloc] init]];
}
[self _insertSections:sections atIndexSet:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, sectionCount)] withAnimationOptions:animationOptions];
[self _batchLayoutNodes:updatedNodes atIndexPaths:updatedIndexPaths withAnimationOptions:animationOptions];
if (completion) {
dispatch_async(dispatch_get_main_queue(), completion);
}
}];
}];
}];
}
#pragma mark - Data Source Access (Calling _dataSource)
- (void)accessDataSourceWithBlock:(dispatch_block_t)block
{
if (_asyncDataFetchingEnabled) {
[_dataSource dataControllerLockDataSource];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
@ -151,157 +284,159 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
}
}
#pragma mark - Initial Data Loading
- (void)initialDataLoadingWithAnimationOption:(ASDataControllerAnimationOptions)animationOption {
[self performDataFetchingWithBlock:^{
NSMutableArray *indexPaths = [NSMutableArray array];
NSUInteger sectionNum = [_dataSource dataControllerNumberOfSections:self];
// insert sections
[self insertSections:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, sectionNum)] withAnimationOption:0];
for (NSUInteger i = 0; i < sectionNum; i++) {
NSIndexPath *indexPath = [[NSIndexPath alloc] initWithIndex:i];
NSUInteger rowNum = [_dataSource dataController:self rowsInSection:i];
for (NSUInteger j = 0; j < rowNum; j++) {
[indexPaths addObject:[indexPath indexPathByAddingIndex:j]];
}
- (void)_populateFromDataSourceWithSectionIndexSet:(NSIndexSet *)indexSet mutableNodes:(NSMutableArray *)nodes mutableIndexPaths:(NSMutableArray *)indexPaths
{
[indexSet enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
NSUInteger rowNum = [_dataSource dataController:self rowsInSection:idx];
NSIndexPath *sectionIndex = [[NSIndexPath alloc] initWithIndex:idx];
for (NSUInteger i = 0; i < rowNum; i++) {
NSIndexPath *indexPath = [sectionIndex indexPathByAddingIndex:i];
[indexPaths addObject:indexPath];
[nodes addObject:[_dataSource dataController:self nodeAtIndexPath:indexPath]];
}
// insert elements
[self insertRowsAtIndexPaths:indexPaths withAnimationOption:animationOption];
}];
}
#pragma mark - Data Update
- (void)_populateFromEntireDataSourceWithMutableNodes:(NSMutableArray *)nodes mutableIndexPaths:(NSMutableArray *)indexPaths
{
NSUInteger sectionNum = [_dataSource dataControllerNumberOfSections:self];
for (NSUInteger i = 0; i < sectionNum; i++) {
NSIndexPath *sectionIndexPath = [[NSIndexPath alloc] initWithIndex:i];
NSUInteger rowNum = [_dataSource dataController:self rowsInSection:i];
for (NSUInteger j = 0; j < rowNum; j++) {
NSIndexPath *indexPath = [sectionIndexPath indexPathByAddingIndex:j];
[indexPaths addObject:indexPath];
[nodes addObject:[_dataSource dataController:self nodeAtIndexPath:indexPath]];
}
}
}
#pragma mark - Batching (External API)
- (void)beginUpdates
{
dispatch_async([[self class] sizingQueue], ^{
[self asyncUpdateDataWithBlock:^{
_batchUpdateCounter++;
}];
});
// Begin queuing up edit calls that happen on the main thread.
// This will prevent further operations from being scheduled on _editingTransactionQueue.
// It's fine if there is an in-flight operation on _editingTransactionQueue,
// as once the command queue is unpaused, each edit command will wait for the _editingTransactionQueue to be flushed.
_batchUpdateCounter++;
}
- (void)endUpdates {
- (void)endUpdates
{
[self endUpdatesWithCompletion:NULL];
}
- (void)endUpdatesWithCompletion:(void (^)(BOOL))completion
{
dispatch_async([[self class] sizingQueue], ^{
dispatch_async(dispatch_get_main_queue(), ^{
_batchUpdateCounter--;
_batchUpdateCounter--;
if (!_batchUpdateCounter) {
[_delegate dataControllerBeginUpdates:self];
[_pendingBlocks enumerateObjectsUsingBlock:^(dispatch_block_t block, NSUInteger idx, BOOL *stop) {
block();
}];
[_pendingBlocks removeAllObjects];
[_delegate dataControllerEndUpdates:self completion:completion];
}
});
});
if (_batchUpdateCounter == 0) {
[_delegate dataControllerBeginUpdates:self];
// Running these commands may result in blocking on an _editingTransactionQueue operation that started even before -beginUpdates.
// Each subsequent command in the queue will also wait on the full asynchronous completion of the prior command's edit transaction.
[_pendingEditCommandBlocks enumerateObjectsUsingBlock:^(dispatch_block_t block, NSUInteger idx, BOOL *stop) {
block();
}];
[_pendingEditCommandBlocks removeAllObjects];
[_delegate dataControllerEndUpdates:self completion:completion];
}
}
- (void)insertSections:(NSIndexSet *)indexSet withAnimationOption:(ASDataControllerAnimationOptions)animationOption
- (void)performEditCommandWithBlock:(void (^)(void))block
{
[self performDataFetchingWithBlock:^{
__block int nodeTotalCnt = 0;
NSMutableArray *nodeCounts = [NSMutableArray arrayWithCapacity:indexSet.count];
[indexSet enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
NSUInteger cnt = [_dataSource dataController:self rowsInSection:idx];
[nodeCounts addObject:@(cnt)];
nodeTotalCnt += cnt;
}];
// This method needs to block the thread and synchronously perform the operation if we are not
// queuing commands for begin/endUpdates. If we are queuing, it needs to return immediately.
if (_batchUpdateCounter == 0) {
block();
} else {
[_pendingEditCommandBlocks addObject:block];
}
}
NSMutableArray *nodes = [NSMutableArray arrayWithCapacity:nodeTotalCnt];
NSMutableArray *indexPaths = [NSMutableArray arrayWithCapacity:nodeTotalCnt];
#pragma mark - Section Editing (External API)
__block NSUInteger idx = 0;
[indexSet enumerateIndexesUsingBlock:^(NSUInteger sectionIdx, BOOL *stop) {
NSUInteger cnt = [nodeCounts[idx++] unsignedIntegerValue];
for (int i = 0; i < cnt; i++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:sectionIdx];
[indexPaths addObject:indexPath];
ASCellNode *node = [_dataSource dataController:self nodeAtIndexPath:indexPath];
[nodes addObject:node];
}
}];
dispatch_async([[self class] sizingQueue], ^{
[self syncUpdateDataWithBlock:^{
- (void)insertSections:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
{
[self performEditCommandWithBlock:^{
ASDisplayNodeAssertMainThread();
[_editingTransactionQueue waitUntilAllOperationsAreFinished];
[self accessDataSourceWithBlock:^{
NSMutableArray *updatedNodes = [NSMutableArray array];
NSMutableArray *updatedIndexPaths = [NSMutableArray array];
[self _populateFromDataSourceWithSectionIndexSet:indexSet mutableNodes:updatedNodes mutableIndexPaths:updatedIndexPaths];
[_editingTransactionQueue addOperationWithBlock:^{
NSMutableArray *sectionArray = [NSMutableArray arrayWithCapacity:indexSet.count];
for (NSUInteger i = 0; i < indexSet.count; i++) {
[sectionArray addObject:[NSMutableArray array]];
}
INSERT_SECTIONS(_nodes , indexSet, sectionArray, animationOption);
[self _insertSections:sectionArray atIndexSet:indexSet withAnimationOptions:animationOptions];
[self _batchLayoutNodes:updatedNodes atIndexPaths:updatedIndexPaths withAnimationOptions:animationOptions];
}];
[self _batchInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOption];
});
}];
}];
}
- (void)deleteSections:(NSIndexSet *)indexSet withAnimationOption:(ASDataControllerAnimationOptions)animationOption
- (void)deleteSections:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
{
dispatch_async([[self class] sizingQueue], ^{
[self asyncUpdateDataWithBlock:^{
[self performEditCommandWithBlock:^{
ASDisplayNodeAssertMainThread();
[_editingTransactionQueue waitUntilAllOperationsAreFinished];
[_editingTransactionQueue addOperationWithBlock:^{
// remove elements
NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_nodes, indexSet);
NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes, indexSet);
DELETE_NODES(_nodes, indexPaths, animationOption);
DELETE_SECTIONS(_nodes, indexSet, animationOption);
[self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions];
[self _deleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions];
}];
});
}
- (void)reloadSections:(NSIndexSet *)sections withAnimationOption:(ASDataControllerAnimationOptions)animationOption
{
[self performDataFetchingWithBlock:^{
// We need to keep data query on data source in the calling thread.
NSMutableArray *updatedIndexPaths = [[NSMutableArray alloc] init];
NSMutableArray *updatedNodes = [[NSMutableArray alloc] init];
[sections enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
NSUInteger rowNum = [_dataSource dataController:self rowsInSection:idx];
NSIndexPath *sectionIndex = [[NSIndexPath alloc] initWithIndex:idx];
for (NSUInteger i = 0; i < rowNum; i++) {
NSIndexPath *indexPath = [sectionIndex indexPathByAddingIndex:i];
[updatedIndexPaths addObject:indexPath];
[updatedNodes addObject:[_dataSource dataController:self nodeAtIndexPath:indexPath]];
}
}];
dispatch_async([ASDataController sizingQueue], ^{
[self syncUpdateDataWithBlock:^{
// remove elements
NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_nodes, sections);
DELETE_NODES(_nodes, indexPaths, animationOption);
}];
// reinsert the elements
[self _batchInsertNodes:updatedNodes atIndexPaths:updatedIndexPaths withAnimationOptions:animationOption];
});
}];
}
- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection withAnimationOption:(ASDataControllerAnimationOptions)animationOption
- (void)reloadSections:(NSIndexSet *)sections withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
{
dispatch_async([ASDataController sizingQueue], ^{
[self asyncUpdateDataWithBlock:^{
[self performEditCommandWithBlock:^{
ASDisplayNodeAssertMainThread();
[_editingTransactionQueue waitUntilAllOperationsAreFinished];
[self accessDataSourceWithBlock:^{
NSMutableArray *updatedNodes = [NSMutableArray array];
NSMutableArray *updatedIndexPaths = [NSMutableArray array];
[self _populateFromDataSourceWithSectionIndexSet:sections mutableNodes:updatedNodes mutableIndexPaths:updatedIndexPaths];
// Dispatch to sizing queue in order to guarantee that any in-progress sizing operations from prior edits have completed.
// For example, if an initial -reloadData call is quickly followed by -reloadSections, sizing the initial set may not be done
// at this time. Thus _editingNodes could be empty and crash in ASIndexPathsForMultidimensional[...]
[_editingTransactionQueue addOperationWithBlock:^{
NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes, sections);
[self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions];
// reinsert the elements
[self _batchLayoutNodes:updatedNodes atIndexPaths:updatedIndexPaths withAnimationOptions:animationOptions];
}];
}];
}];
}
- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
{
[self performEditCommandWithBlock:^{
ASDisplayNodeAssertMainThread();
[_editingTransactionQueue waitUntilAllOperationsAreFinished];
[_editingTransactionQueue addOperationWithBlock:^{
// remove elements
NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_nodes, [NSIndexSet indexSetWithIndex:section]);
NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_nodes, indexPaths);
DELETE_NODES(_nodes, indexPaths, animationOption);
NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes, [NSIndexSet indexSetWithIndex:section]);
NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_editingNodes, indexPaths);
[self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions];
// update the section of indexpaths
NSIndexPath *sectionIndexPath = [[NSIndexPath alloc] initWithIndex:newSection];
@ -311,201 +446,137 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
}];
// Don't re-calculate size for moving
INSERT_NODES(_nodes, updatedIndexPaths, nodes, animationOption);
[self _insertNodes:nodes atIndexPaths:updatedIndexPaths withAnimationOptions:animationOptions];
}];
});
}];
}
- (void)_insertNodes:(NSArray *)nodes
atIndexPaths:(NSArray *)indexPaths
withAnimationOption:(ASDataControllerAnimationOptions)animationOption
#pragma mark - Row Editing (External API)
- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
{
if (!nodes.count) {
return;
}
dispatch_group_t layoutGroup = dispatch_group_create();
for (NSUInteger j = 0; j < nodes.count && j < indexPaths.count; j += kASDataControllerSizingCountPerProcessor) {
NSArray *subIndexPaths = [indexPaths subarrayWithRange:NSMakeRange(j, MIN(kASDataControllerSizingCountPerProcessor, indexPaths.count - j))];
NSMutableArray *nodeBoundSizes = [[NSMutableArray alloc] initWithCapacity:kASDataControllerSizingCountPerProcessor];
[subIndexPaths enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop) {
[nodeBoundSizes addObject:[NSValue valueWithCGSize:[_dataSource dataController:self constrainedSizeForNodeAtIndexPath:indexPath]]];
}];
dispatch_group_async(layoutGroup, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[subIndexPaths enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop) {
ASCellNode *node = nodes[j + idx];
[node measure:[nodeBoundSizes[idx] CGSizeValue]];
node.frame = CGRectMake(0.0f, 0.0f, node.calculatedSize.width, node.calculatedSize.height);
[self performEditCommandWithBlock:^{
ASDisplayNodeAssertMainThread();
[_editingTransactionQueue waitUntilAllOperationsAreFinished];
[self accessDataSourceWithBlock:^{
// sort indexPath to avoid messing up the index when inserting in several batches
NSArray *sortedIndexPaths = [indexPaths sortedArrayUsingSelector:@selector(compare:)];
NSMutableArray *nodes = [[NSMutableArray alloc] initWithCapacity:indexPaths.count];
for (NSUInteger i = 0; i < sortedIndexPaths.count; i++) {
[nodes addObject:[_dataSource dataController:self nodeAtIndexPath:sortedIndexPaths[i]]];
}
[_editingTransactionQueue addOperationWithBlock:^{
[self _batchLayoutNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions];
}];
});
}
dispatch_block_t block = ^{
dispatch_group_wait(layoutGroup, DISPATCH_TIME_FOREVER);
[self asyncUpdateDataWithBlock:^{
// updating the cells
INSERT_NODES(_nodes, indexPaths, nodes, animationOption);
}];
};
if ([ASDataController isSizingQueue]) {
block();
} else {
dispatch_async([ASDataController sizingQueue], block);
}
}];
}
- (void)_batchInsertNodes:(NSArray *)nodes
atIndexPaths:(NSArray *)indexPaths
withAnimationOptions:(ASDataControllerAnimationOptions)animationOption
- (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
{
NSUInteger blockSize = [[ASDataController class] parallelProcessorCount] * kASDataControllerSizingCountPerProcessor;
// Processing in batches
for (NSUInteger i = 0; i < indexPaths.count; i += blockSize) {
NSRange batchedRange = NSMakeRange(i, MIN(indexPaths.count - i, blockSize));
NSArray *batchedIndexPaths = [indexPaths subarrayWithRange:batchedRange];
NSArray *batchedNodes = [nodes subarrayWithRange:batchedRange];
[self _insertNodes:batchedNodes atIndexPaths:batchedIndexPaths withAnimationOption:animationOption];
}
}
- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption
{
[self performDataFetchingWithBlock:^{
// sort indexPath to avoid messing up the index when inserting in several batches
[self performEditCommandWithBlock:^{
ASDisplayNodeAssertMainThread();
[_editingTransactionQueue waitUntilAllOperationsAreFinished];
// sort indexPath in order to avoid messing up the index when deleting
NSArray *sortedIndexPaths = [indexPaths sortedArrayUsingSelector:@selector(compare:)];
NSMutableArray *nodes = [[NSMutableArray alloc] initWithCapacity:indexPaths.count];
for (NSUInteger i = 0; i < sortedIndexPaths.count; i++) {
[nodes addObject:[_dataSource dataController:self nodeAtIndexPath:sortedIndexPaths[i]]];
}
[self _batchInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOption];
[_editingTransactionQueue addOperationWithBlock:^{
[self _deleteNodesAtIndexPaths:sortedIndexPaths withAnimationOptions:animationOptions];
}];
}];
}
- (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
{
// sort indexPath in order to avoid messing up the index when deleting
NSArray *sortedIndexPaths = [indexPaths sortedArrayUsingSelector:@selector(compare:)];
dispatch_async([ASDataController sizingQueue], ^{
[self asyncUpdateDataWithBlock:^{
DELETE_NODES(_nodes, sortedIndexPaths, animationOption);
}];
});
}
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption
{
[self performDataFetchingWithBlock:^{
// The reloading operation required reloading the data
// Loading data in the calling thread
NSMutableArray *nodes = [[NSMutableArray alloc] initWithCapacity:indexPaths.count];
[indexPaths sortedArrayUsingSelector:@selector(compare:)];
[indexPaths enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop) {
[nodes addObject:[_dataSource dataController:self nodeAtIndexPath:indexPath]];
}];
dispatch_async([ASDataController sizingQueue], ^{
[self syncUpdateDataWithBlock:^{
DELETE_NODES(_nodes, indexPaths, animationOption);
[self performEditCommandWithBlock:^{
ASDisplayNodeAssertMainThread();
[_editingTransactionQueue waitUntilAllOperationsAreFinished];
// Reloading requires re-fetching the data. Load it on the current calling thread, locking the data source.
[self accessDataSourceWithBlock:^{
NSMutableArray *nodes = [[NSMutableArray alloc] initWithCapacity:indexPaths.count];
[indexPaths sortedArrayUsingSelector:@selector(compare:)];
[indexPaths enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop) {
[nodes addObject:[_dataSource dataController:self nodeAtIndexPath:indexPath]];
}];
[self _batchInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOption];
});
[_editingTransactionQueue addOperationWithBlock:^{
[self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions];
[self _batchLayoutNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions];
}];
}];
}];
}
- (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath withAnimationOption:(ASDataControllerAnimationOptions)animationOption
- (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
{
dispatch_async([ASDataController sizingQueue], ^{
[self asyncUpdateDataWithBlock:^{
NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_nodes, [NSArray arrayWithObject:indexPath]);
[self performEditCommandWithBlock:^{
ASDisplayNodeAssertMainThread();
[_editingTransactionQueue waitUntilAllOperationsAreFinished];
[_editingTransactionQueue addOperationWithBlock:^{
NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_editingNodes, [NSArray arrayWithObject:indexPath]);
NSArray *indexPaths = [NSArray arrayWithObject:indexPath];
DELETE_NODES(_nodes, indexPaths, animationOption);
[self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions];
// Don't re-calculate size for moving
NSArray *newIndexPaths = [NSArray arrayWithObject:newIndexPath];
INSERT_NODES(_nodes, newIndexPaths, nodes, animationOption);
[self _insertNodes:nodes atIndexPaths:newIndexPaths withAnimationOptions:animationOptions];
}];
});
}
- (void)reloadDataWithAnimationOption:(ASDataControllerAnimationOptions)animationOption
{
[self performDataFetchingWithBlock:^{
// Fetching data in calling thread
NSMutableArray *updatedNodes = [[NSMutableArray alloc] init];
NSMutableArray *updatedIndexPaths = [[NSMutableArray alloc] init];
NSUInteger sectionNum = [_dataSource dataControllerNumberOfSections:self];
for (NSUInteger i = 0; i < sectionNum; i++) {
NSIndexPath *sectionIndexPath = [[NSIndexPath alloc] initWithIndex:i];
NSUInteger rowNum = [_dataSource dataController:self rowsInSection:i];
for (NSUInteger j = 0; j < rowNum; j++) {
NSIndexPath *indexPath = [sectionIndexPath indexPathByAddingIndex:j];
[updatedIndexPaths addObject:indexPath];
[updatedNodes addObject:[_dataSource dataController:self nodeAtIndexPath:indexPath]];
}
}
dispatch_async([ASDataController sizingQueue], ^{
[self syncUpdateDataWithBlock:^{
NSArray *indexPaths = ASIndexPathsForMultidimensionalArray(_nodes);
DELETE_NODES(_nodes, indexPaths, animationOption);
NSMutableIndexSet *indexSet = [[NSMutableIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, _nodes.count)];
DELETE_SECTIONS(_nodes, indexSet, animationOption);
// Insert section
NSMutableArray *sections = [[NSMutableArray alloc] initWithCapacity:sectionNum];
for (int i = 0; i < sectionNum; i++) {
[sections addObject:[[NSMutableArray alloc] init]];
}
INSERT_SECTIONS(_nodes, [[NSMutableIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, sectionNum)], sections, animationOption);
}];
[self _batchInsertNodes:updatedNodes atIndexPaths:updatedIndexPaths withAnimationOptions:animationOption];
});
}];
}
#pragma mark - Data Querying
#pragma mark - Data Querying (External API)
- (NSUInteger)numberOfSections
{
ASDisplayNodeAssertMainThread();
return [_nodes count];
return [_completedNodes count];
}
- (NSUInteger)numberOfRowsInSection:(NSUInteger)section
{
ASDisplayNodeAssertMainThread();
return [_nodes[section] count];
return [_completedNodes[section] count];
}
- (ASCellNode *)nodeAtIndexPath:(NSIndexPath *)indexPath
{
ASDisplayNodeAssertMainThread();
return _nodes[indexPath.section][indexPath.row];
return _completedNodes[indexPath.section][indexPath.row];
}
- (NSArray *)nodesAtIndexPaths:(NSArray *)indexPaths
{
ASDisplayNodeAssertMainThread();
return ASFindElementsInMultidimensionalArrayAtIndexPaths(_nodes, [indexPaths sortedArrayUsingSelector:@selector(compare:)]);
return ASFindElementsInMultidimensionalArrayAtIndexPaths(_completedNodes, [indexPaths sortedArrayUsingSelector:@selector(compare:)]);
}
- (NSArray *)completedNodes
{
ASDisplayNodeAssertMainThread();
return _completedNodes;
}
#pragma mark - Dealloc
- (void)dealloc
{
ASDisplayNodeAssertMainThread();
[_completedNodes enumerateObjectsUsingBlock:^(NSMutableArray *section, NSUInteger sectionIndex, BOOL *stop) {
[section enumerateObjectsUsingBlock:^(ASCellNode *node, NSUInteger rowIndex, BOOL *stop) {
if (node.isNodeLoaded) {
if (node.layerBacked) {
[node.layer removeFromSuperlayer];
} else {
[node.view removeFromSuperview];
}
}
}];
}];
}
@end

View File

@ -6,7 +6,7 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import <AsyncDisplayKit/ASLayoutController.h>
#import <AsyncDisplayKit/ASAbstractLayoutController.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
@ -15,19 +15,21 @@ typedef NS_ENUM(NSUInteger, ASFlowLayoutDirection) {
ASFlowLayoutDirectionHorizontal,
};
@protocol ASFlowLayoutControllerDataSource
- (NSArray *)completedNodes; // This provides access to ASDataController's _completedNodes multidimensional array.
@end
/**
* The controller for flow layout.
* An optimized flow layout controller that supports only vertical or horizontal scrolling, not simultaneously two-dimensional scrolling.
* It is used for all ASTableViews, and may be used with ASCollectionView.
*/
@interface ASFlowLayoutController : NSObject <ASLayoutController>
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType;
- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType;
@interface ASFlowLayoutController : ASAbstractLayoutController
@property (nonatomic, readonly, assign) ASFlowLayoutDirection layoutDirection;
@property (nonatomic, readwrite, weak) id <ASFlowLayoutControllerDataSource> dataSource;
- (instancetype)initWithScrollOption:(ASFlowLayoutDirection)layoutDirection;
@property (nonatomic, assign) ASRangeTuningParameters tuningParameters ASDISPLAYNODE_DEPRECATED;
@end

View File

@ -7,162 +7,73 @@
*/
#import "ASFlowLayoutController.h"
#import "ASAssert.h"
#import "ASDisplayNode.h"
#import "ASIndexPath.h"
#include <map>
#include <vector>
#include <cassert>
#import "ASAssert.h"
static const CGFloat kASFlowLayoutControllerRefreshingThreshold = 0.3;
@interface ASFlowLayoutController() {
std::vector<std::vector<CGSize> > _nodeSizes;
std::pair<int, int> _visibleRangeStartPos;
std::pair<int, int> _visibleRangeEndPos;
std::vector<std::pair<int, int>> _rangeStartPos;
std::vector<std::pair<int, int>> _rangeEndPos;
std::vector<ASRangeTuningParameters> _tuningParameters;
@interface ASFlowLayoutController()
{
ASIndexPathRange _visibleRange;
std::vector<ASIndexPathRange> _rangesByType; // All ASLayoutRangeTypes besides visible.
}
@end
@implementation ASFlowLayoutController
- (instancetype)initWithScrollOption:(ASFlowLayoutDirection)layoutDirection {
- (instancetype)initWithScrollOption:(ASFlowLayoutDirection)layoutDirection
{
if (!(self = [super init])) {
return nil;
}
_layoutDirection = layoutDirection;
_tuningParameters = std::vector<ASRangeTuningParameters>(ASLayoutRangeTypeCount);
_tuningParameters[ASLayoutRangeTypePreload] = {
.leadingBufferScreenfuls = 2,
.trailingBufferScreenfuls = 1
};
_tuningParameters[ASLayoutRangeTypeRender] = {
.leadingBufferScreenfuls = 3,
.trailingBufferScreenfuls = 2
};
_rangesByType = std::vector<ASIndexPathRange>(ASLayoutRangeTypeCount);
return self;
}
#pragma mark - Tuning Parameters
- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType
{
ASDisplayNodeAssert(rangeType < _tuningParameters.size(), @"Requesting a range that is OOB for the configured tuning parameters");
return _tuningParameters[rangeType];
}
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType
{
ASDisplayNodeAssert(rangeType < _tuningParameters.size(), @"Requesting a range that is OOB for the configured tuning parameters");
_tuningParameters[rangeType] = tuningParameters;
}
// Support for the deprecated tuningParameters property
- (ASRangeTuningParameters)tuningParameters
{
return [self tuningParametersForRangeType:ASLayoutRangeTypeRender];
}
// Support for the deprecated tuningParameters property
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters
{
[self setTuningParameters:tuningParameters forRangeType:ASLayoutRangeTypeRender];
}
#pragma mark - Editing
- (void)insertNodesAtIndexPaths:(NSArray *)indexPaths withSizes:(NSArray *)nodeSizes
{
ASDisplayNodeAssert(indexPaths.count == nodeSizes.count, @"Inconsistent index paths and node size");
[indexPaths enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop) {
std::vector<CGSize> &v = _nodeSizes[indexPath.section];
v.insert(v.begin() + indexPath.row, [(NSValue *)nodeSizes[idx] CGSizeValue]);
}];
}
- (void)deleteNodesAtIndexPaths:(NSArray *)indexPaths
{
[indexPaths enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop) {
std::vector<CGSize> &v = _nodeSizes[indexPath.section];
v.erase(v.begin() + indexPath.row);
}];
}
- (void)insertSections:(NSArray *)sections atIndexSet:(NSIndexSet *)indexSet
{
__block int cnt = 0;
[indexSet enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
NSArray *nodes = sections[cnt++];
std::vector<CGSize> v;
v.reserve(nodes.count);
for (int i = 0; i < nodes.count; i++) {
v.push_back([nodes[i] CGSizeValue]);
}
_nodeSizes.insert(_nodeSizes.begin() + idx, v);
}];
}
- (void)deleteSectionsAtIndexSet:(NSIndexSet *)indexSet {
[indexSet enumerateIndexesWithOptions:NSEnumerationReverse usingBlock:^(NSUInteger idx, BOOL *stop)
{
_nodeSizes.erase(_nodeSizes.begin() +idx);
}];
}
#pragma mark - Visible Indices
- (BOOL)shouldUpdateForVisibleIndexPaths:(NSArray *)indexPaths viewportSize:(CGSize)viewportSize rangeType:(ASLayoutRangeType)rangeType
{
if (!indexPaths.count) {
if (!indexPaths.count || rangeType >= _rangesByType.size()) {
return NO;
}
std::pair<int, int> rangeStartPos, rangeEndPos;
if (rangeType < _rangeStartPos.size() && rangeType < _rangeEndPos.size()) {
rangeStartPos = _rangeStartPos[rangeType];
rangeEndPos = _rangeEndPos[rangeType];
}
std::pair<int, int> startPos, endPos;
ASFindIndexPathRange(indexPaths, startPos, endPos);
if (rangeStartPos >= startPos || rangeEndPos <= endPos) {
ASIndexPathRange existingRange = _rangesByType[rangeType];
ASIndexPathRange newRange = [self indexPathRangeForIndexPaths:indexPaths];
ASIndexPath maximumStart = ASIndexPathMaximum(existingRange.start, newRange.start);
ASIndexPath minimumEnd = ASIndexPathMinimum(existingRange.end, newRange.end);
if (ASIndexPathEqualToIndexPath(maximumStart, existingRange.start) || ASIndexPathEqualToIndexPath(minimumEnd, existingRange.end)) {
return YES;
}
return ASFlowLayoutDistance(startPos, _visibleRangeStartPos, _nodeSizes) > ASFlowLayoutDistance(_visibleRangeStartPos, rangeStartPos, _nodeSizes) * kASFlowLayoutControllerRefreshingThreshold ||
ASFlowLayoutDistance(endPos, _visibleRangeEndPos, _nodeSizes) > ASFlowLayoutDistance(_visibleRangeEndPos, rangeEndPos, _nodeSizes) * kASFlowLayoutControllerRefreshingThreshold;
}
- (BOOL)shouldUpdateForVisibleIndexPath:(NSArray *)indexPaths
viewportSize:(CGSize)viewportSize
{
return [self shouldUpdateForVisibleIndexPaths:indexPaths viewportSize:viewportSize rangeType:ASLayoutRangeTypeRender];
NSInteger newStartDelta = [self flowLayoutDistanceForRange:ASIndexPathRangeMake(_visibleRange.start, newRange.start)];
NSInteger existingStartDelta = [self flowLayoutDistanceForRange:ASIndexPathRangeMake(_visibleRange.start, existingRange.start)] * kASFlowLayoutControllerRefreshingThreshold;
NSInteger newEndDelta = [self flowLayoutDistanceForRange:ASIndexPathRangeMake(_visibleRange.end, newRange.end)];
NSInteger existingEndDelta = [self flowLayoutDistanceForRange:ASIndexPathRangeMake(_visibleRange.end, existingRange.end)] * kASFlowLayoutControllerRefreshingThreshold;
return (newStartDelta > existingStartDelta) || (newEndDelta > existingEndDelta);
}
- (void)setVisibleNodeIndexPaths:(NSArray *)indexPaths
{
ASFindIndexPathRange(indexPaths, _visibleRangeStartPos, _visibleRangeEndPos);
_visibleRange = [self indexPathRangeForIndexPaths:indexPaths];
}
/**
* IndexPath array for the element in the working range.
*/
- (NSSet *)indexPathsForScrolling:(enum ASScrollDirection)scrollDirection viewportSize:(CGSize)viewportSize rangeType:(ASLayoutRangeType)rangeType
- (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection viewportSize:(CGSize)viewportSize rangeType:(ASLayoutRangeType)rangeType
{
CGFloat viewportScreenMetric;
ASScrollDirection leadingDirection;
@ -183,105 +94,134 @@ static const CGFloat kASFlowLayoutControllerRefreshingThreshold = 0.3;
CGFloat backScreens = scrollDirection == leadingDirection ? tuningParameters.leadingBufferScreenfuls : tuningParameters.trailingBufferScreenfuls;
CGFloat frontScreens = scrollDirection == leadingDirection ? tuningParameters.trailingBufferScreenfuls : tuningParameters.leadingBufferScreenfuls;
std::pair<int, int> startIter = ASFindIndexForRange(_nodeSizes, _visibleRangeStartPos, - backScreens * viewportScreenMetric, _layoutDirection);
std::pair<int, int> endIter = ASFindIndexForRange(_nodeSizes, _visibleRangeEndPos, frontScreens * viewportScreenMetric, _layoutDirection);
ASIndexPath startPath = [self findIndexPathAtDistance:(-backScreens * viewportScreenMetric) fromIndexPath:_visibleRange.start];
ASIndexPath endPath = [self findIndexPathAtDistance:(frontScreens * viewportScreenMetric) fromIndexPath:_visibleRange.end];
ASDisplayNodeAssert(startPath.section <= endPath.section, @"startPath should never begin at a further position than endPath");
NSMutableSet *indexPathSet = [[NSMutableSet alloc] init];
while (startIter != endIter) {
[indexPathSet addObject:[NSIndexPath indexPathForRow:startIter.second inSection:startIter.first]];
startIter.second++;
NSArray *completedNodes = [_dataSource completedNodes];
while (!ASIndexPathEqualToIndexPath(startPath, endPath)) {
[indexPathSet addObject:[NSIndexPath indexPathWithASIndexPath:startPath]];
startPath.row++;
while (startIter.second == _nodeSizes[startIter.first].size() && startIter.first < _nodeSizes.size()) {
startIter.second = 0;
startIter.first++;
// Once we reach the end of the section, advance to the next one. Keep advancing if the next section is zero-sized.
while (startPath.row >= [(NSArray *)completedNodes[startPath.section] count] && startPath.section < completedNodes.count - 1) {
startPath.row = 0;
startPath.section++;
ASDisplayNodeAssert(startPath.section <= endPath.section, @"startPath should never reach a further section than endPath");
}
}
[indexPathSet addObject:[NSIndexPath indexPathForRow:endIter.second inSection:endIter.first]];
[indexPathSet addObject:[NSIndexPath indexPathWithASIndexPath:endPath]];
return indexPathSet;
}
- (NSSet *)indexPathsForScrolling:(enum ASScrollDirection)scrollDirection
viewportSize:(CGSize)viewportSize
{
return [self indexPathsForScrolling:scrollDirection viewportSize:viewportSize rangeType:ASLayoutRangeTypeRender];
}
#pragma mark - Utility
static void ASFindIndexPathRange(NSArray *indexPaths, std::pair<int, int> &startPos, std::pair<int, int> &endPos)
- (ASIndexPathRange)indexPathRangeForIndexPaths:(NSArray *)indexPaths
{
NSIndexPath *initialIndexPath = [indexPaths firstObject];
startPos = endPos = {initialIndexPath.section, initialIndexPath.row};
// Set up an initial value so the MIN and MAX can work in the enumeration.
__block ASIndexPath currentIndexPath = [[indexPaths firstObject] ASIndexPathValue];
__block ASIndexPathRange range;
range.start = currentIndexPath;
range.end = currentIndexPath;
[indexPaths enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop) {
std::pair<int, int> p(indexPath.section, indexPath.row);
startPos = MIN(startPos, p);
endPos = MAX(endPos, p);
currentIndexPath = [indexPath ASIndexPathValue];
range.start = ASIndexPathMinimum(range.start, currentIndexPath);
range.end = ASIndexPathMaximum(range.end, currentIndexPath);
}];
return range;
}
static const std::pair<int, int> ASFindIndexForRange(const std::vector<std::vector<CGSize>> &nodes,
const std::pair<int, int> &pos,
CGFloat range,
ASFlowLayoutDirection layoutDirection)
- (ASIndexPath)findIndexPathAtDistance:(CGFloat)distance fromIndexPath:(ASIndexPath)start
{
std::pair<int, int> cur = pos, pre = pos;
// "end" is the index path we'll advance until we have gone far enough from "start" to reach "distance"
ASIndexPath end = start;
// "previous" will store one iteration before "end", in case we go too far and need to reset "end" to be "previous"
ASIndexPath previous = start;
if (range < 0.0 && cur.first >= 0 && cur.first < nodes.size() && cur.second >= 0 && cur.second < nodes[cur.first].size()) {
// search backward
while (range < 0.0 && cur.first >= 0 && cur.second >= 0) {
pre = cur;
CGSize size = nodes[cur.first][cur.second];
range += layoutDirection == ASFlowLayoutDirectionHorizontal ? size.width : size.height;
cur.second--;
while (cur.second < 0 && cur.first > 0) {
cur.second = (int)nodes[--cur.first].size() - 1;
NSArray *completedNodes = [_dataSource completedNodes];
NSUInteger numberOfSections = [completedNodes count];
NSUInteger numberOfRowsInSection = [(NSArray *)completedNodes[end.section] count];
// If "distance" is negative, advance "end" backwards across rows and sections.
// Otherwise, advance forward. In either case, bring "distance" closer to zero by the dimension of each row passed.
if (distance < 0.0 && end.section >= 0 && end.section < numberOfSections && end.row >= 0 && end.row < numberOfRowsInSection) {
while (distance < 0.0 && end.section >= 0 && end.row >= 0) {
previous = end;
ASDisplayNode *node = completedNodes[end.section][end.row];
CGSize size = node.calculatedSize;
distance += (_layoutDirection == ASFlowLayoutDirectionHorizontal ? size.width : size.height);
end.row--;
// If we've gone to a negative row, set to the last row of the previous section. While loop is required to handle empty sections.
while (end.row < 0 && end.section > 0) {
end.section--;
numberOfRowsInSection = [(NSArray *)completedNodes[end.section] count];
end.row = numberOfRowsInSection - 1;
}
}
if (cur.second < 0) {
cur = pre;
if (end.row < 0) {
end = previous;
}
} else {
// search forward
while (range > 0.0 && cur.first >= 0 && cur.first < nodes.size() && cur.second >= 0 && cur.second < nodes[cur.first].size()) {
pre = cur;
CGSize size = nodes[cur.first][cur.second];
range -= layoutDirection == ASFlowLayoutDirectionHorizontal ? size.width : size.height;
while (distance > 0.0 && end.section >= 0 && end.section < numberOfSections && end.row >= 0 && end.row < numberOfRowsInSection) {
previous = end;
ASDisplayNode *node = completedNodes[end.section][end.row];
CGSize size = node.calculatedSize;
distance -= _layoutDirection == ASFlowLayoutDirectionHorizontal ? size.width : size.height;
cur.second++;
while (cur.second == nodes[cur.first].size() && cur.first < (int)nodes.size() - 1) {
cur.second = 0;
cur.first++;
end.row++;
// If we've gone beyond the section, reset to the beginning of the next section. While loop is required to handle empty sections.
while (end.row >= numberOfRowsInSection && end.section < numberOfSections - 1) {
end.row = 0;
end.section++;
numberOfRowsInSection = [(NSArray *)completedNodes[end.section] count];
}
}
if (cur.second == nodes[cur.first].size()) {
cur = pre;
if (end.row >= numberOfRowsInSection) {
end = previous;
}
}
return cur;
return end;
}
static int ASFlowLayoutDistance(const std::pair<int, int> &start, const std::pair<int, int> &end, const std::vector<std::vector<CGSize>> &nodes)
- (NSInteger)flowLayoutDistanceForRange:(ASIndexPathRange)range
{
if (start == end) {
// This method should only be called with the range in proper order (start comes before end).
ASDisplayNodeAssert(ASIndexPathEqualToIndexPath(ASIndexPathMinimum(range.start, range.end), range.start), @"flowLayoutDistanceForRange: called with invalid range");
if (ASIndexPathEqualToIndexPath(range.start, range.end)) {
return 0;
} else if (start > end) {
return - ASFlowLayoutDistance(end, start, nodes);
}
NSInteger totalRowCount = 0;
NSUInteger numberOfRowsInSection = 0;
NSArray *completedNodes = [_dataSource completedNodes];
int res = 0;
for (int i = start.first; i <= end.first; i++) {
res += (i == end.first ? end.second + 1 : nodes[i].size()) - (i == start.first ? start.second : 0);
for (NSInteger section = range.start.section; section <= range.end.section; section++) {
numberOfRowsInSection = [(NSArray *)completedNodes[section] count];
totalRowCount += numberOfRowsInSection;
if (section == range.start.section) {
// For the start section, make sure we don't count the rows before the start row.
totalRowCount -= range.start.row;
} else if (section == range.end.section) {
// For the start section, make sure we don't count the rows after the end row.
totalRowCount -= (numberOfRowsInSection - (range.end.row + 1));
}
}
return res;
ASDisplayNodeAssert(totalRowCount >= 0, @"totalRowCount in flowLayoutDistanceForRange: should not be negative");
return totalRowCount;
}
@end

View File

@ -10,7 +10,7 @@
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASDisplayNodeInternal.h>
#import "ASInternalHelpers.h"
static const CGFloat kCornerRadius = 2.5;
static const UIEdgeInsets padding = {2, 4, 1.5, 4};
@ -23,7 +23,7 @@ static const UIEdgeInsets padding = {2, 4, 1.5, 4};
+ (id)defaultValueForKey:(NSString *)key
{
if ([key isEqualToString:@"contentsScale"]) {
return @(ASDisplayNodeScreenScale());
return @(ASScreenScale());
} else if ([key isEqualToString:@"highlightColor"]) {
CGFloat components[] = {0, 0, 0, 0.25};
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

View File

@ -0,0 +1,42 @@
/* 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.
*/
#import <AsyncDisplayKit/ASBaseDefines.h>
typedef struct {
NSInteger section;
NSInteger row;
} ASIndexPath;
typedef struct {
ASIndexPath start;
ASIndexPath end;
} ASIndexPathRange;
ASDISPLAYNODE_EXTERN_C_BEGIN
extern ASIndexPath ASIndexPathMake(NSInteger section, NSInteger row);
extern BOOL ASIndexPathEqualToIndexPath(ASIndexPath first, ASIndexPath second);
extern ASIndexPath ASIndexPathMinimum(ASIndexPath first, ASIndexPath second);
extern ASIndexPath ASIndexPathMaximum(ASIndexPath first, ASIndexPath second);
extern ASIndexPathRange ASIndexPathRangeMake(ASIndexPath first, ASIndexPath second);
extern BOOL ASIndexPathRangeEqualToIndexPathRange(ASIndexPathRange first, ASIndexPathRange second);
ASDISPLAYNODE_EXTERN_C_END
@interface NSIndexPath (ASIndexPathAdditions)
+ (NSIndexPath *)indexPathWithASIndexPath:(ASIndexPath)indexPath;
- (ASIndexPath)ASIndexPathValue;
@end

View File

@ -0,0 +1,73 @@
/* 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.
*/
#import "ASIndexPath.h"
#import <UIKit/UIKit.h>
ASIndexPath ASIndexPathMake(NSInteger section, NSInteger row)
{
ASIndexPath indexPath;
indexPath.section = section;
indexPath.row = row;
return indexPath;
}
BOOL ASIndexPathEqualToIndexPath(ASIndexPath first, ASIndexPath second)
{
return (first.section == second.section && first.row == second.row);
}
ASIndexPath ASIndexPathMinimum(ASIndexPath first, ASIndexPath second)
{
if (first.section < second.section) {
return first;
} else if (first.section > second.section) {
return second;
} else {
return (first.row < second.row ? first : second);
}
}
ASIndexPath ASIndexPathMaximum(ASIndexPath first, ASIndexPath second)
{
if (first.section > second.section) {
return first;
} else if (first.section < second.section) {
return second;
} else {
return (first.row > second.row ? first : second);
}
}
ASIndexPathRange ASIndexPathRangeMake(ASIndexPath first, ASIndexPath second)
{
ASIndexPathRange range;
range.start = ASIndexPathMinimum(first, second);
range.end = ASIndexPathMaximum(first, second);
return range;
}
BOOL ASIndexPathRangeEqualToIndexPathRange(ASIndexPathRange first, ASIndexPathRange second)
{
return ASIndexPathEqualToIndexPath(first.start, second.start) && ASIndexPathEqualToIndexPath(first.end, second.end);
}
@implementation NSIndexPath (ASIndexPathAdditions)
+ (NSIndexPath *)indexPathWithASIndexPath:(ASIndexPath)indexPath
{
return [NSIndexPath indexPathForRow:indexPath.row inSection:indexPath.section];;
}
- (ASIndexPath)ASIndexPathValue
{
return ASIndexPathMake(self.section, self.row);
}
@end

View File

@ -27,6 +27,18 @@ typedef struct {
*/
- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType;
- (BOOL)shouldUpdateForVisibleIndexPaths:(NSArray *)indexPaths viewportSize:(CGSize)viewportSize rangeType:(ASLayoutRangeType)rangeType;
- (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection viewportSize:(CGSize)viewportSize rangeType:(ASLayoutRangeType)rangeType;
@property (nonatomic, assign) ASRangeTuningParameters tuningParameters ASDISPLAYNODE_DEPRECATED;
- (BOOL)shouldUpdateForVisibleIndexPath:(NSArray *)indexPath viewportSize:(CGSize)viewportSize ASDISPLAYNODE_DEPRECATED;
- (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection viewportSize:(CGSize)viewportSize ASDISPLAYNODE_DEPRECATED;
@optional
- (void)insertNodesAtIndexPaths:(NSArray *)indexPaths withSizes:(NSArray *)nodeSizes;
- (void)deleteNodesAtIndexPaths:(NSArray *)indexPaths;
@ -37,14 +49,4 @@ typedef struct {
- (void)setVisibleNodeIndexPaths:(NSArray *)indexPaths;
- (BOOL)shouldUpdateForVisibleIndexPaths:(NSArray *)indexPaths viewportSize:(CGSize)viewportSize rangeType:(ASLayoutRangeType)rangeType;
- (NSSet *)indexPathsForScrolling:(enum ASScrollDirection)scrollDirection viewportSize:(CGSize)viewportSize rangeType:(ASLayoutRangeType)rangeType;
@property (nonatomic, assign) ASRangeTuningParameters tuningParameters ASDISPLAYNODE_DEPRECATED;
- (BOOL)shouldUpdateForVisibleIndexPath:(NSArray *)indexPath viewportSize:(CGSize)viewportSize ASDISPLAYNODE_DEPRECATED;
- (NSSet *)indexPathsForScrolling:(enum ASScrollDirection)scrollDirection viewportSize:(CGSize)viewportSize ASDISPLAYNODE_DEPRECATED;
@end

View File

@ -44,7 +44,7 @@ extern NSArray *ASFindElementsInMultidimensionalArrayAtIndexPaths(NSMutableArray
extern NSArray *ASIndexPathsForMultidimensionalArrayAtIndexSet(NSArray *MultidimensionalArray, NSIndexSet *indexSet);
/**
* Reteurn all the index paths of mutable multidimensional array, in ascending order.
* Return all the index paths of mutable multidimensional array, in ascending order.
*/
extern NSArray *ASIndexPathsForMultidimensionalArray(NSArray *MultidimensionalArray);

View File

@ -19,18 +19,20 @@
/**
* Working range controller.
*
* Used internally by ASTableView and potentially by a future ASCollectionView. Observes the visible range, maintains
* a working range, and is responsible for handling AsyncDisplayKit machinery (sizing cell nodes, enqueueing and
* cancelling their asynchronous layout and display, and so on).
* Used internally by ASTableView and ASCollectionView. It is paired with ASDataController.
* It is designed to support custom scrolling containers as well. Observes the visible range, maintains
* "working ranges" to trigger network calls and rendering, and is responsible for driving asynchronous layout of cells.
* This includes cancelling those asynchronous operations as cells fall outside of the working ranges.
*/
@interface ASRangeController : ASDealloc2MainObject <ASDataControllerDelegate>
/**
* Notify the receiver that the visible range has been updated.
* Notify the range controller that the visible range has been updated.
* This is the primary input call that drives updating the working ranges, and triggering their actions.
*
* @see [ASRangeControllerDelegate rangeControllerVisibleNodeIndexPaths:]
*/
- (void)visibleNodeIndexPathsDidChangeWithScrollDirection:(enum ASScrollDirection)scrollDirection;
- (void)visibleNodeIndexPathsDidChangeWithScrollDirection:(ASScrollDirection)scrollDirection;
/**
* Add the sized node for `indexPath` as a subview of `contentView`.
@ -88,43 +90,21 @@
/**
* Called for nodes insertion.
*/
- (void)rangeController:(ASRangeController *)rangeController didInsertNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption;
- (void)rangeController:(ASRangeController *)rangeController didInsertNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
/**
* Called for nodes deletion.
*/
- (void)rangeController:(ASRangeController *)rangeController didDeleteNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption;
- (void)rangeController:(ASRangeController *)rangeController didDeleteNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
/**
* Called for section insertion.
*/
- (void)rangeController:(ASRangeController *)rangeController didInsertSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOption:(ASDataControllerAnimationOptions)animationOption;
- (void)rangeController:(ASRangeController *)rangeController didInsertSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
/**
* Called for section deletion.
*/
- (void)rangeController:(ASRangeController *)rangeController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOption:(ASDataControllerAnimationOptions)animationOption;
@optional
/**
* Called before nodes insertion.
*/
- (void)rangeController:(ASRangeController *)rangeController willInsertNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption;
/**
* Called before nodes deletion.
*/
- (void)rangeController:(ASRangeController *)rangeController willDeleteNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption;
/**
* Called before section insertion.
*/
- (void)rangeController:(ASRangeController *)rangeController willInsertSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOption:(ASDataControllerAnimationOptions)animationOption;
/**
* Called before section deletion.
*/
- (void)rangeController:(ASRangeController *)rangeController willDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOption:(ASDataControllerAnimationOptions)animationOption;
- (void)rangeController:(ASRangeController *)rangeController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
@end

View File

@ -75,6 +75,7 @@
// coalesce these events -- handling them multiple times per runloop is noisy and expensive
_queuedRangeUpdate = YES;
[self performSelector:@selector(updateVisibleNodeIndexPaths)
withObject:nil
afterDelay:0
@ -92,7 +93,9 @@
CGSize viewportSize = [_delegate rangeControllerViewportSize:self];
// the layout controller needs to know what the current visible indices are to calculate range offsets
[_layoutController setVisibleNodeIndexPaths:visibleNodePaths];
if ([_layoutController respondsToSelector:@selector(setVisibleNodeIndexPaths:)]) {
[_layoutController setVisibleNodeIndexPaths:visibleNodePaths];
}
for (NSInteger i = 0; i < ASLayoutRangeTypeCount; i++) {
ASLayoutRangeType rangeType = (ASLayoutRangeType)i;
@ -108,6 +111,7 @@
NSMutableSet *removedIndexPaths = _rangeIsValid ? [[_rangeTypeIndexPaths objectForKey:rangeKey] mutableCopy] : [NSMutableSet set];
[removedIndexPaths minusSet:indexPaths];
[removedIndexPaths minusSet:visibleNodePathsSet];
if (removedIndexPaths.count) {
NSArray *removedNodes = [_delegate rangeController:self nodesAtIndexPaths:[removedIndexPaths allObjects]];
[removedNodes enumerateObjectsUsingBlock:^(ASCellNode *node, NSUInteger idx, BOOL *stop) {
@ -126,7 +130,7 @@
if ([self shouldSkipVisibleNodesForRangeType:rangeType]) {
[addedIndexPaths minusSet:visibleNodePathsSet];
}
if (addedIndexPaths.count) {
NSArray *addedNodes = [_delegate rangeController:self nodesAtIndexPaths:[addedIndexPaths allObjects]];
[addedNodes enumerateObjectsUsingBlock:^(ASCellNode *node, NSUInteger idx, BOOL *stop) {
@ -178,15 +182,7 @@
});
}
- (void)dataController:(ASDataController *)dataController willInsertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption {
ASDisplayNodePerformBlockOnMainThread(^{
if ([_delegate respondsToSelector:@selector(rangeController:willInsertNodesAtIndexPaths:withAnimationOption:)]) {
[_delegate rangeController:self willInsertNodesAtIndexPaths:indexPaths withAnimationOption:animationOption];
}
});
}
- (void)dataController:(ASDataController *)dataController didInsertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption {
- (void)dataController:(ASDataController *)dataController didInsertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions {
ASDisplayNodeAssert(nodes.count == indexPaths.count, @"Invalid index path");
NSMutableArray *nodeSizes = [NSMutableArray arrayWithCapacity:nodes.count];
@ -195,37 +191,19 @@
}];
ASDisplayNodePerformBlockOnMainThread(^{
[_layoutController insertNodesAtIndexPaths:indexPaths withSizes:nodeSizes];
[_delegate rangeController:self didInsertNodesAtIndexPaths:indexPaths withAnimationOption:animationOption];
_rangeIsValid = NO;
[_delegate rangeController:self didInsertNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions];
});
}
- (void)dataController:(ASDataController *)dataController willDeleteNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption {
- (void)dataController:(ASDataController *)dataController didDeleteNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions {
ASDisplayNodePerformBlockOnMainThread(^{
if ([_delegate respondsToSelector:@selector(rangeController:willDeleteNodesAtIndexPaths:withAnimationOption:)]) {
[_delegate rangeController:self willDeleteNodesAtIndexPaths:indexPaths withAnimationOption:animationOption];
}
});
}
- (void)dataController:(ASDataController *)dataController didDeleteNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption {
ASDisplayNodePerformBlockOnMainThread(^{
[_layoutController deleteNodesAtIndexPaths:indexPaths];
[_delegate rangeController:self didDeleteNodesAtIndexPaths:indexPaths withAnimationOption:animationOption];
_rangeIsValid = NO;
[_delegate rangeController:self didDeleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions];
});
}
- (void)dataController:(ASDataController *)dataController willInsertSections:(NSArray *)sections atIndexSet:(NSIndexSet *)indexSet withAnimationOption:(ASDataControllerAnimationOptions)animationOption {
ASDisplayNodePerformBlockOnMainThread(^{
if ([_delegate respondsToSelector:@selector(rangeController:willInsertSectionsAtIndexSet:withAnimationOption:)]) {
[_delegate rangeController:self willInsertSectionsAtIndexSet:indexSet withAnimationOption:animationOption];
}
});
}
- (void)dataController:(ASDataController *)dataController didInsertSections:(NSArray *)sections atIndexSet:(NSIndexSet *)indexSet withAnimationOption:(ASDataControllerAnimationOptions)animationOption {
- (void)dataController:(ASDataController *)dataController didInsertSections:(NSArray *)sections atIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions {
ASDisplayNodeAssert(sections.count == indexSet.count, @"Invalid sections");
NSMutableArray *sectionNodeSizes = [NSMutableArray arrayWithCapacity:sections.count];
@ -239,25 +217,15 @@
}];
ASDisplayNodePerformBlockOnMainThread(^{
[_layoutController insertSections:sectionNodeSizes atIndexSet:indexSet];
[_delegate rangeController:self didInsertSectionsAtIndexSet:indexSet withAnimationOption:animationOption];
_rangeIsValid = NO;
[_delegate rangeController:self didInsertSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions];
});
}
- (void)dataController:(ASDataController *)dataController willDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOption:(ASDataControllerAnimationOptions)animationOption {
- (void)dataController:(ASDataController *)dataController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions {
ASDisplayNodePerformBlockOnMainThread(^{
if ([_delegate respondsToSelector:@selector(rangeController:willDeleteSectionsAtIndexSet:withAnimationOption:)]) {
[_delegate rangeController:self willDeleteSectionsAtIndexSet:indexSet withAnimationOption:animationOption];
}
});
}
- (void)dataController:(ASDataController *)dataController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOption:(ASDataControllerAnimationOptions)animationOption {
ASDisplayNodePerformBlockOnMainThread(^{
[_layoutController deleteSectionsAtIndexSet:indexSet];
[_delegate rangeController:self didDeleteSectionsAtIndexSet:indexSet withAnimationOption:animationOption];
_rangeIsValid = NO;
[_delegate rangeController:self didDeleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions];
});
}

View File

@ -16,13 +16,13 @@
- (void)node:(ASDisplayNode *)node enteredRangeOfType:(ASLayoutRangeType)rangeType
{
ASDisplayNodeAssert(rangeType == ASLayoutRangeTypePreload, @"Preload delegate should not handle other ranges");
[node recursivelyFetchRemoteData];
[node recursivelyFetchData];
}
- (void)node:(ASDisplayNode *)node exitedRangeOfType:(ASLayoutRangeType)rangeType
{
ASDisplayNodeAssert(rangeType == ASLayoutRangeTypePreload, @"Preload delegate should not handle other ranges");
[node recursivelyClearRemoteData];
[node recursivelyClearFetchedData];
}
@end

View File

@ -19,14 +19,15 @@
ASDisplayNodeAssertMainThread();
// we add nodes' views to this invisible window to start async rendering
// TODO: Replace this with directly triggering display https://github.com/facebook/AsyncDisplayKit/issues/315
static UIWindow *workingWindow = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
workingWindow = [[UIWindow alloc] initWithFrame:CGRectZero];
workingWindow.windowLevel = UIWindowLevelNormal - 1000;
workingWindow.userInteractionEnabled = NO;
workingWindow.clipsToBounds = YES;
workingWindow.hidden = YES;
workingWindow.alpha = 0.0;
});
return workingWindow;
}
@ -50,7 +51,7 @@
[node recursivelySetDisplaySuspended:YES];
[node.view removeFromSuperview];
[node recursivelyClearRendering];
[node recursivelyClearContents];
}
@end

View File

@ -8,10 +8,27 @@
#import <Foundation/Foundation.h>
typedef NS_ENUM(NSInteger, ASScrollDirection) {
ASScrollDirectionNone,
ASScrollDirectionRight,
ASScrollDirectionLeft,
ASScrollDirectionUp,
ASScrollDirectionDown,
#import "ASBaseDefines.h"
typedef NS_OPTIONS(NSInteger, ASScrollDirection) {
ASScrollDirectionNone = 0,
ASScrollDirectionRight = 1 << 0,
ASScrollDirectionLeft = 1 << 1,
ASScrollDirectionUp = 1 << 2,
ASScrollDirectionDown = 1 << 3
};
extern const ASScrollDirection ASScrollDirectionHorizontalDirections;
extern const ASScrollDirection ASScrollDirectionVerticalDirections;
ASDISPLAYNODE_EXTERN_C_BEGIN
BOOL ASScrollDirectionContainsVerticalDirection(ASScrollDirection scrollDirection);
BOOL ASScrollDirectionContainsHorizontalDirection(ASScrollDirection scrollDirection);
BOOL ASScrollDirectionContainsRight(ASScrollDirection scrollDirection);
BOOL ASScrollDirectionContainsLeft(ASScrollDirection scrollDirection);
BOOL ASScrollDirectionContainsUp(ASScrollDirection scrollDirection);
BOOL ASScrollDirectionContainsDown(ASScrollDirection scrollDirection);
ASDISPLAYNODE_EXTERN_C_END

View File

@ -0,0 +1,36 @@
/* Copyright (c) 2015-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.
*/
#import "ASScrollDirection.h"
const ASScrollDirection ASScrollDirectionHorizontalDirections = ASScrollDirectionLeft | ASScrollDirectionRight;
const ASScrollDirection ASScrollDirectionVerticalDirections = ASScrollDirectionUp | ASScrollDirectionDown;
BOOL ASScrollDirectionContainsVerticalDirection(ASScrollDirection scrollDirection) {
return (scrollDirection & ASScrollDirectionVerticalDirections) != 0;
}
BOOL ASScrollDirectionContainsHorizontalDirection(ASScrollDirection scrollDirection) {
return (scrollDirection & ASScrollDirectionHorizontalDirections) != 0;
}
BOOL ASScrollDirectionContainsRight(ASScrollDirection scrollDirection) {
return (scrollDirection & ASScrollDirectionRight) != 0;
}
BOOL ASScrollDirectionContainsLeft(ASScrollDirection scrollDirection) {
return (scrollDirection & ASScrollDirectionLeft) != 0;
}
BOOL ASScrollDirectionContainsUp(ASScrollDirection scrollDirection) {
return (scrollDirection & ASScrollDirectionUp) != 0;
}
BOOL ASScrollDirectionContainsDown(ASScrollDirection scrollDirection) {
return (scrollDirection & ASScrollDirectionDown) != 0;
}

View File

@ -45,6 +45,12 @@ typedef NS_ENUM(NSUInteger, ASTextNodeRendererMeasureOption) {
*/
@interface ASTextNodeRenderer : NSObject
- (instancetype)initWithAttributedString:(NSAttributedString *)attributedString
truncationString:(NSAttributedString *)truncationString
truncationMode:(NSLineBreakMode)truncationMode
maximumLineCount:(NSUInteger)maximumLineCount
exclusionPaths:(NSArray *)exclusionPaths
constrainedSize:(CGSize)constrainedSize;
/*
* Designated Initializer
*

View File

@ -39,6 +39,8 @@ static const CGFloat ASTextNodeRendererTextCapHeightPadding = 1.3;
NSLayoutManager *_layoutManager;
NSTextStorage *_textStorage;
NSTextContainer *_textContainer;
NSArray *_exclusionPaths;
}
#pragma mark - Initialization
@ -47,6 +49,7 @@ static const CGFloat ASTextNodeRendererTextCapHeightPadding = 1.3;
truncationString:(NSAttributedString *)truncationString
truncationMode:(NSLineBreakMode)truncationMode
maximumLineCount:(NSUInteger)maximumLineCount
exclusionPaths:(NSArray *)exclusionPaths
constrainedSize:(CGSize)constrainedSize
{
if (self = [super init]) {
@ -57,11 +60,22 @@ static const CGFloat ASTextNodeRendererTextCapHeightPadding = 1.3;
_maximumLineCount = maximumLineCount;
_exclusionPaths = exclusionPaths;
_constrainedSize = constrainedSize;
}
return self;
}
- (instancetype)initWithAttributedString:(NSAttributedString *)attributedString
truncationString:(NSAttributedString *)truncationString
truncationMode:(NSLineBreakMode)truncationMode
maximumLineCount:(NSUInteger)maximumLineCount
constrainedSize:(CGSize)constrainedSize
{
return [self initWithAttributedString:attributedString truncationString:truncationString truncationMode:truncationMode maximumLineCount:maximumLineCount exclusionPaths:nil constrainedSize:constrainedSize];
}
/*
* Use this method to lazily construct the TextKit components.
*/
@ -97,6 +111,8 @@ static const CGFloat ASTextNodeRendererTextCapHeightPadding = 1.3;
// Set maximum number of lines
_textContainer.maximumNumberOfLines = _maximumLineCount;
_textContainer.exclusionPaths = _exclusionPaths;
[_layoutManager addTextContainer:_textContainer];
ASDN::StaticMutexUnlocker gu(mutex);

View File

@ -90,7 +90,7 @@
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
spaceSizes = [[NSMapTable alloc] initWithKeyOptions:NSMapTableStrongMemory valueOptions:NSMapTableStrongMemory capacity:1];
mapQueue = dispatch_queue_create("com.facebook.AsyncDisplayKit.wordKerningQueue", DISPATCH_QUEUE_SERIAL);
mapQueue = dispatch_queue_create("org.AsyncDisplayKit.wordKerningQueue", DISPATCH_QUEUE_SERIAL);
});
CGFloat ordinarySpaceWidth;
UIFont *font = [layoutManager.textStorage attribute:NSFontAttributeName atIndex:characterIndex effectiveRange:NULL];

View File

@ -0,0 +1,18 @@
/* Copyright (c) 2015-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.
*/
#import <CoreGraphics/CoreGraphics.h>
#import "ASBaseDefines.h"
ASDISPLAYNODE_EXTERN_C_BEGIN
CGRect asdk_CGRectExpandHorizontally(CGRect rect, CGFloat negativeMultiplier, CGFloat positiveMultiplier);
CGRect asdk_CGRectExpandVertically(CGRect rect, CGFloat negativeMultiplier, CGFloat positiveMultiplier);
ASDISPLAYNODE_EXTERN_C_END

View File

@ -0,0 +1,31 @@
/* Copyright (c) 2015-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.
*/
#import "CGRect+ASConvenience.h"
CGRect asdk_CGRectExpandHorizontally(CGRect rect, CGFloat negativeMultiplier, CGFloat positiveMultiplier) {
CGFloat negativeDirectionWidth = negativeMultiplier * rect.size.width;
CGFloat positiveDirectionWidth = positiveMultiplier * rect.size.width;
CGFloat width = negativeDirectionWidth + rect.size.width + positiveDirectionWidth;
CGFloat originX = rect.origin.x - negativeDirectionWidth;
return CGRectMake(originX,
rect.origin.y,
width,
rect.size.height);
}
CGRect asdk_CGRectExpandVertically(CGRect rect, CGFloat negativeMultiplier, CGFloat positiveMultiplier) {
CGFloat negativeDirectionHeight = negativeMultiplier * rect.size.height;
CGFloat positiveDirectionHeight = positiveMultiplier * rect.size.height;
CGFloat height = negativeDirectionHeight + rect.size.height + positiveDirectionHeight;
CGFloat originY = rect.origin.y - negativeDirectionHeight;
return CGRectMake(rect.origin.x,
originY,
rect.size.width,
height);
}

View File

@ -0,0 +1,15 @@
/* Copyright (c) 2015-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.
*/
#import <UIKit/UIKit.h>
@interface UICollectionViewLayout (ASConvenience)
- (BOOL)asdk_isFlowLayout;
@end

View File

@ -0,0 +1,17 @@
/* Copyright (c) 2015-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.
*/
#import "UICollectionViewLayout+ASConvenience.h"
@implementation UICollectionViewLayout (ASConvenience)
- (BOOL)asdk_isFlowLayout {
return [self isKindOfClass:UICollectionViewFlowLayout.class];
}
@end

View File

@ -75,6 +75,9 @@
@property (atomic, assign) BOOL shouldGroupAccessibilityChildren;
*/
// Accessibility identification support
@property (nonatomic, copy) NSString *accessibilityIdentifier;
@end
@interface CALayer (ASDisplayNodeLayer)

View File

@ -93,10 +93,13 @@
[super layoutSublayers];
ASDisplayNode *node = self.asyncdisplaykit_node;
// If our associated node is layer-backed, we cannot rely on the view's -layoutSubviews calling the node's -layout implementation, so do it ourselves.
if (node.isLayerBacked) {
ASDisplayNodeAssertMainThread();
if (ASDisplayNodeThreadIsMain()) {
[node __layout];
} else {
ASDisplayNodeFailAssert(@"not reached assertion");
dispatch_async(dispatch_get_main_queue(), ^ {
[node __layout];
});
}
}
@ -126,7 +129,7 @@
static dispatch_queue_t displayQueue = NULL;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
displayQueue = dispatch_queue_create("com.facebook.AsyncDisplayKit.ASDisplayLayer.displayQueue", DISPATCH_QUEUE_CONCURRENT);
displayQueue = dispatch_queue_create("org.AsyncDisplayKit.ASDisplayLayer.displayQueue", DISPATCH_QUEUE_CONCURRENT);
// we use the highpri queue to prioritize UI rendering over other async operations
dispatch_set_target_queue(displayQueue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0));
});

View File

@ -122,19 +122,6 @@
}
}
- (void)layoutSubviews
{
if (ASDisplayNodeThreadIsMain()) {
[_node __layout];
} else {
// FIXME: CRASH This should not be happening because of the way we gate -setNeedsLayout, but it has been seen.
ASDisplayNodeFailAssert(@"not reached assertion");
dispatch_async(dispatch_get_main_queue(), ^ {
[_node __layout];
});
}
}
- (UIViewContentMode)contentMode
{
return ASDisplayNodeUIContentModeFromCAContentsGravity(self.layer.contentsGravity);
@ -256,6 +243,14 @@
[_node tintColorDidChange];
}
- (BOOL)canBecomeFirstResponder {
return [_node canBecomeFirstResponder];
}
- (BOOL)canResignFirstResponder {
return [_node canResignFirstResponder];
}
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
// We forward responder-chain actions to our node if we can't handle them ourselves. See -targetForAction:withSender:.

View File

@ -0,0 +1,25 @@
/*
* 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.
*
*/
#import <AsyncDisplayKit/ASLayoutSpec.h>
/**
Lays out a single layoutable child, then lays out a background layoutable instance behind it stretched to its size.
*/
@interface ASBackgroundLayoutSpec : ASLayoutSpec
/**
@param child A child that is laid out to determine the size of this spec. If this is nil, then this method
returns nil.
@param background A layoutable object that is laid out behind the child. May be nil, in which case the background is omitted.
*/
+ (instancetype)newWithChild:(id<ASLayoutable>)child background:(id<ASLayoutable>)background;
@end

View File

@ -0,0 +1,61 @@
/*
* 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.
*
*/
#import "ASBackgroundLayoutSpec.h"
#import "ASAssert.h"
#import "ASBaseDefines.h"
@interface ASBackgroundLayoutSpec ()
{
id<ASLayoutable> _child;
id<ASLayoutable> _background;
}
@end
@implementation ASBackgroundLayoutSpec
+ (instancetype)newWithChild:(id<ASLayoutable>)child background:(id<ASLayoutable>)background
{
if (child == nil) {
return nil;
}
ASBackgroundLayoutSpec *spec = [super new];
spec->_child = child;
spec->_background = background;
return spec;
}
+ (instancetype)new
{
ASDISPLAYNODE_NOT_DESIGNATED_INITIALIZER();
}
/**
First layout the contents, then fit the background image.
*/
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
{
ASLayout *contentsLayout = [_child measureWithSizeRange:constrainedSize];
NSMutableArray *sublayouts = [NSMutableArray arrayWithCapacity:2];
if (_background) {
// Size background to exactly the same size.
ASLayout *backgroundLayout = [_background measureWithSizeRange:{contentsLayout.size, contentsLayout.size}];
backgroundLayout.position = CGPointZero;
[sublayouts addObject:backgroundLayout];
}
contentsLayout.position = CGPointZero;
[sublayouts addObject:contentsLayout];
return [ASLayout newWithLayoutableObject:self size:contentsLayout.size sublayouts:sublayouts];
}
@end

View File

@ -0,0 +1,46 @@
/*
* 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.
*
*/
#import <AsyncDisplayKit/ASLayoutSpec.h>
typedef NS_OPTIONS(NSUInteger, ASCenterLayoutSpecCenteringOptions) {
/** The child is positioned in {0,0} relatively to the layout bounds */
ASCenterLayoutSpecCenteringNone = 0,
/** The child is centered along the X axis */
ASCenterLayoutSpecCenteringX = 1 << 0,
/** The child is centered along the Y axis */
ASCenterLayoutSpecCenteringY = 1 << 1,
/** Convenience option to center both along the X and Y axis */
ASCenterLayoutSpecCenteringXY = ASCenterLayoutSpecCenteringX | ASCenterLayoutSpecCenteringY
};
typedef NS_OPTIONS(NSUInteger, ASCenterLayoutSpecSizingOptions) {
/** The spec will take up the maximum size possible */
ASCenterLayoutSpecSizingOptionDefault,
/** The spec will take up the minimum size possible along the X axis */
ASCenterLayoutSpecSizingOptionMinimumX = 1 << 0,
/** The spec will take up the minimum size possible along the Y axis */
ASCenterLayoutSpecSizingOptionMinimumY = 1 << 1,
/** Convenience option to take up the minimum size along both the X and Y axis */
ASCenterLayoutSpecSizingOptionMinimumXY = ASCenterLayoutSpecSizingOptionMinimumX | ASCenterLayoutSpecSizingOptionMinimumY,
};
/** Lays out a single layoutable child and position it so that it is centered into the layout bounds. */
@interface ASCenterLayoutSpec : ASLayoutSpec
/**
@param centeringOptions, see ASCenterLayoutSpecCenteringOptions.
@param child The child to center.
*/
+ (instancetype)newWithCenteringOptions:(ASCenterLayoutSpecCenteringOptions)centeringOptions
sizingOptions:(ASCenterLayoutSpecSizingOptions)sizingOptions
child:(id<ASLayoutable>)child;
@end

View File

@ -0,0 +1,78 @@
/*
* 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.
*
*/
#import "ASCenterLayoutSpec.h"
#import "ASInternalHelpers.h"
@implementation ASCenterLayoutSpec
{
ASCenterLayoutSpecCenteringOptions _centeringOptions;
ASCenterLayoutSpecSizingOptions _sizingOptions;
id<ASLayoutable> _child;
}
+ (instancetype)newWithCenteringOptions:(ASCenterLayoutSpecCenteringOptions)centeringOptions
sizingOptions:(ASCenterLayoutSpecSizingOptions)sizingOptions
child:(id<ASLayoutable>)child
{
ASCenterLayoutSpec *spec = [super new];
if (spec) {
spec->_centeringOptions = centeringOptions;
spec->_sizingOptions = sizingOptions;
spec->_child = child;
}
return spec;
}
+ (instancetype)new
{
ASDISPLAYNODE_NOT_DESIGNATED_INITIALIZER();
}
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
{
CGSize size = {
constrainedSize.max.width,
constrainedSize.max.height
};
// Layout the child
const CGSize minChildSize = {
(_centeringOptions & ASCenterLayoutSpecCenteringX) != 0 ? 0 : constrainedSize.min.width,
(_centeringOptions & ASCenterLayoutSpecCenteringY) != 0 ? 0 : constrainedSize.min.height,
};
ASLayout *sublayout = [_child measureWithSizeRange:ASSizeRangeMake(minChildSize, constrainedSize.max)];
// If we have an undetermined height or width, use the child size to define the layout
// size
size = ASSizeRangeClamp(constrainedSize, {
isnan(size.width) ? sublayout.size.width : size.width,
isnan(size.height) ? sublayout.size.height : size.height
});
// If minimum size options are set, attempt to shrink the size to the size of the child
size = ASSizeRangeClamp(constrainedSize, {
MIN(size.width, (_sizingOptions & ASCenterLayoutSpecSizingOptionMinimumX) != 0 ? sublayout.size.width : size.width),
MIN(size.height, (_sizingOptions & ASCenterLayoutSpecSizingOptionMinimumY) != 0 ? sublayout.size.height : size.height)
});
// Compute the centered postion for the child
BOOL shouldCenterAlongX = (_centeringOptions & ASCenterLayoutSpecCenteringX);
BOOL shouldCenterAlongY = (_centeringOptions & ASCenterLayoutSpecCenteringY);
sublayout.position = {
ASRoundPixelValue(shouldCenterAlongX ? (size.width - sublayout.size.width) * 0.5f : 0),
ASRoundPixelValue(shouldCenterAlongY ? (size.height - sublayout.size.height) * 0.5f : 0)
};
return [ASLayout newWithLayoutableObject:self size:size sublayouts:@[sublayout]];
}
@end

View File

@ -0,0 +1,76 @@
/*
* 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.
*
*/
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
/**
A dimension relative to constraints to be provided in the future.
A RelativeDimension can be one of two types:
"Points" - Just a number. It will always resolve to exactly this amount. This is the default type.
"Percent" - Multiplied to a provided parent amount to resolve a final amount.
*/
typedef NS_ENUM(NSInteger, ASRelativeDimensionType) {
ASRelativeDimensionTypePoints,
ASRelativeDimensionTypePercent,
};
typedef struct {
ASRelativeDimensionType type;
CGFloat value;
} ASRelativeDimension;
/** Expresses an inclusive range of sizes. Used to provide a simple constraint to layout. */
typedef struct {
CGSize min;
CGSize max;
} ASSizeRange;
extern ASRelativeDimension const ASRelativeDimensionUnconstrained;
ASDISPLAYNODE_EXTERN_C_BEGIN
#pragma mark ASRelativeDimension
extern ASRelativeDimension ASRelativeDimensionMake(ASRelativeDimensionType type, CGFloat value);
extern ASRelativeDimension ASRelativeDimensionMakeWithPoints(CGFloat points);
extern ASRelativeDimension ASRelativeDimensionMakeWithPercent(CGFloat percent);
extern ASRelativeDimension ASRelativeDimensionCopy(ASRelativeDimension aDimension);
extern BOOL ASRelativeDimensionEqualToRelativeDimension(ASRelativeDimension lhs, ASRelativeDimension rhs);
extern NSString *NSStringFromASRelativeDimension(ASRelativeDimension dimension);
extern CGFloat ASRelativeDimensionResolve(ASRelativeDimension dimension, CGFloat parent);
#pragma mark -
#pragma mark ASSizeRange
extern ASSizeRange ASSizeRangeMake(CGSize min, CGSize max);
/** Clamps the provided CGSize between the [min, max] bounds of this ASSizeRange. */
extern CGSize ASSizeRangeClamp(ASSizeRange sizeRange, CGSize size);
/**
Intersects another size range. If the other size range does not overlap in either dimension, this size range
"wins" by returning a single point within its own range that is closest to the non-overlapping range.
*/
extern ASSizeRange ASSizeRangeIntersect(ASSizeRange sizeRange, ASSizeRange otherSizeRange);
extern BOOL ASSizeRangeEqualToSizeRange(ASSizeRange lhs, ASSizeRange rhs);
extern NSString *NSStringFromASSizeRange(ASSizeRange sizeRange);
ASDISPLAYNODE_EXTERN_C_END

View File

@ -0,0 +1,128 @@
/*
* 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.
*
*/
#import "ASDimension.h"
#import "ASAssert.h"
ASRelativeDimension const ASRelativeDimensionUnconstrained = {};
#pragma mark ASRelativeDimension
ASRelativeDimension ASRelativeDimensionMake(ASRelativeDimensionType type, CGFloat value)
{
if (type == ASRelativeDimensionTypePoints) { ASDisplayNodeCAssertPositiveReal(@"Points", value); }
ASRelativeDimension dimension; dimension.type = type; dimension.value = value; return dimension;
}
ASRelativeDimension ASRelativeDimensionMakeWithPoints(CGFloat points)
{
return ASRelativeDimensionMake(ASRelativeDimensionTypePoints, points);
}
ASRelativeDimension ASRelativeDimensionMakeWithPercent(CGFloat percent)
{
return ASRelativeDimensionMake(ASRelativeDimensionTypePercent, percent);
}
ASRelativeDimension ASRelativeDimensionCopy(ASRelativeDimension aDimension)
{
return ASRelativeDimensionMake(aDimension.type, aDimension.value);
}
BOOL ASRelativeDimensionEqualToRelativeDimension(ASRelativeDimension lhs, ASRelativeDimension rhs)
{
return lhs.type == rhs.type && lhs.value == rhs.value;
}
NSString *NSStringFromASRelativeDimension(ASRelativeDimension dimension)
{
switch (dimension.type) {
case ASRelativeDimensionTypePoints:
return [NSString stringWithFormat:@"%.0fpt", dimension.value];
case ASRelativeDimensionTypePercent:
return [NSString stringWithFormat:@"%.0f%%", dimension.value * 100.0];
}
}
CGFloat ASRelativeDimensionResolve(ASRelativeDimension dimension, CGFloat parent)
{
switch (dimension.type) {
case ASRelativeDimensionTypePoints:
return dimension.value;
case ASRelativeDimensionTypePercent:
return round(dimension.value * parent);
}
}
#pragma mark -
#pragma mark ASSizeRange
ASSizeRange ASSizeRangeMake(CGSize min, CGSize max)
{
ASDisplayNodeCAssertPositiveReal(@"Range min width", min.width);
ASDisplayNodeCAssertPositiveReal(@"Range min height", min.height);
ASDisplayNodeCAssertInfOrPositiveReal(@"Range max width", max.width);
ASDisplayNodeCAssertInfOrPositiveReal(@"Range max height", max.height);
ASDisplayNodeCAssert(min.width <= max.width,
@"Range min width (%f) must not be larger than max width (%f).", min.width, max.width);
ASDisplayNodeCAssert(min.height <= max.height,
@"Range min height (%f) must not be larger than max height (%f).", min.height, max.height);
ASSizeRange sizeRange; sizeRange.min = min; sizeRange.max = max; return sizeRange;
}
CGSize ASSizeRangeClamp(ASSizeRange sizeRange, CGSize size)
{
return CGSizeMake(MAX(sizeRange.min.width, MIN(sizeRange.max.width, size.width)),
MAX(sizeRange.min.height, MIN(sizeRange.max.height, size.height)));
}
struct _Range {
CGFloat min;
CGFloat max;
/**
Intersects another dimension range. If the other range does not overlap, this size range "wins" by returning a
single point within its own range that is closest to the non-overlapping range.
*/
_Range intersect(const _Range &other) const
{
CGFloat newMin = MAX(min, other.min);
CGFloat newMax = MIN(max, other.max);
if (!(newMin > newMax)) {
return {newMin, newMax};
} else {
// No intersection. If we're before the other range, return our max; otherwise our min.
if (min < other.min) {
return {max, max};
} else {
return {min, min};
}
}
}
};
ASSizeRange ASSizeRangeIntersect(ASSizeRange sizeRange, ASSizeRange otherSizeRange)
{
auto w = _Range({sizeRange.min.width, sizeRange.max.width}).intersect({otherSizeRange.min.width, otherSizeRange.max.width});
auto h = _Range({sizeRange.min.height, sizeRange.max.height}).intersect({otherSizeRange.min.height, otherSizeRange.max.height});
return {{w.min, h.min}, {w.max, h.max}};
}
BOOL ASSizeRangeEqualToSizeRange(ASSizeRange lhs, ASSizeRange rhs)
{
return CGSizeEqualToSize(lhs.min, rhs.min) && CGSizeEqualToSize(lhs.max, rhs.max);
}
NSString * NSStringFromASSizeRange(ASSizeRange sizeRange)
{
return [NSString stringWithFormat:@"<ASSizeRange: min=%@, max=%@>",
NSStringFromCGSize(sizeRange.min),
NSStringFromCGSize(sizeRange.max)];
}

View File

@ -0,0 +1,38 @@
/*
* 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.
*
*/
#import <AsyncDisplayKit/ASLayoutSpec.h>
/**
A layout spec that wraps another layoutable child, applying insets around it.
If the child has a size specified as a percentage, the percentage is resolved against this spec's parent
size **after** applying insets.
@example ASOuterLayoutSpec contains an ASInsetLayoutSpec with an ASInnerLayoutSpec. Suppose that:
- ASOuterLayoutSpec is 200pt wide.
- ASInnerLayoutSpec specifies its width as 100%.
- The ASInsetLayoutSpec has insets of 10pt on every side.
ASInnerLayoutSpec will have size 180pt, not 200pt, because it receives a parent size that has been adjusted for insets.
If you're familiar with CSS: ASInsetLayoutSpec's child behaves similarly to "box-sizing: border-box".
An infinite inset is resolved as an inset equal to all remaining space after applying the other insets and child size.
@example An ASInsetLayoutSpec with an infinite left inset and 10px for all other edges will position it's child 10px from the right edge.
*/
@interface ASInsetLayoutSpec : ASLayoutSpec
/**
@param insets The amount of space to inset on each side.
@param child The wrapped child to inset. If nil, this method returns nil.
*/
+ (instancetype)newWithInsets:(UIEdgeInsets)insets child:(id<ASLayoutable>)child;
@end

View File

@ -0,0 +1,108 @@
/*
* 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.
*
*/
#import "ASInsetLayoutSpec.h"
#import "ASAssert.h"
#import "ASBaseDefines.h"
#import "ASInternalHelpers.h"
@interface ASInsetLayoutSpec ()
{
UIEdgeInsets _insets;
id<ASLayoutable> _child;
}
@end
/* Returns f if f is finite, substitute otherwise */
static CGFloat finite(CGFloat f, CGFloat substitute)
{
return isinf(f) ? substitute : f;
}
/* Returns f if f is finite, 0 otherwise */
static CGFloat finiteOrZero(CGFloat f)
{
return finite(f, 0);
}
/* Returns the inset required to center 'inner' in 'outer' */
static CGFloat centerInset(CGFloat outer, CGFloat inner)
{
return ASRoundPixelValue((outer - inner) / 2);
}
@implementation ASInsetLayoutSpec
+ (instancetype)newWithInsets:(UIEdgeInsets)insets child:(id<ASLayoutable>)child
{
if (child == nil) {
return nil;
}
ASInsetLayoutSpec *spec = [super new];
if (spec) {
spec->_insets = insets;
spec->_child = child;
}
return spec;
}
+ (instancetype)new
{
ASDISPLAYNODE_NOT_DESIGNATED_INITIALIZER();
}
/**
Inset will compute a new constrained size for it's child after applying insets and re-positioning
the child to respect the inset.
*/
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
{
const CGFloat insetsX = (finiteOrZero(_insets.left) + finiteOrZero(_insets.right));
const CGFloat insetsY = (finiteOrZero(_insets.top) + finiteOrZero(_insets.bottom));
// if either x-axis inset is infinite, let child be intrinsic width
const CGFloat minWidth = (isinf(_insets.left) || isinf(_insets.right)) ? 0 : constrainedSize.min.width;
// if either y-axis inset is infinite, let child be intrinsic height
const CGFloat minHeight = (isinf(_insets.top) || isinf(_insets.bottom)) ? 0 : constrainedSize.min.height;
const ASSizeRange insetConstrainedSize = {
{
MAX(0, minWidth - insetsX),
MAX(0, minHeight - insetsY),
},
{
MAX(0, constrainedSize.max.width - insetsX),
MAX(0, constrainedSize.max.height - insetsY),
}
};
ASLayout *sublayout = [_child measureWithSizeRange:insetConstrainedSize];
const CGSize computedSize = ASSizeRangeClamp(constrainedSize, {
finite(sublayout.size.width + _insets.left + _insets.right, constrainedSize.max.width),
finite(sublayout.size.height + _insets.top + _insets.bottom, constrainedSize.max.height),
});
const CGFloat x = finite(_insets.left, constrainedSize.max.width -
(finite(_insets.right,
centerInset(constrainedSize.max.width, sublayout.size.width)) + sublayout.size.width));
const CGFloat y = finite(_insets.top,
constrainedSize.max.height -
(finite(_insets.bottom,
centerInset(constrainedSize.max.height, sublayout.size.height)) + sublayout.size.height));
sublayout.position = CGPointMake(x, y);
return [ASLayout newWithLayoutableObject:self size:computedSize sublayouts:@[sublayout]];
}
@end

View File

@ -0,0 +1,65 @@
/*
* 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.
*
*/
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASAssert.h>
#import <AsyncDisplayKit/ASLayoutable.h>
extern CGPoint const CGPointNull;
extern BOOL CGPointIsNull(CGPoint point);
/** Represents a computed immutable layout tree. */
@interface ASLayout : NSObject
@property (nonatomic, weak, readonly) id<ASLayoutable> layoutableObject;
@property (nonatomic, readonly) CGSize size;
/**
* Position in parent. Default to CGPointNull.
*
* @discussion When being used as a sublayout, this property must not equal CGPointNull.
*/
@property (nonatomic, readwrite) CGPoint position;
/**
* Array of ASLayouts. Each must have a valid non-null position.
*/
@property (nonatomic, readonly) NSArray *sublayouts;
+ (instancetype)newWithLayoutableObject:(id<ASLayoutable>)layoutableObject
size:(CGSize)size
position:(CGPoint)position
sublayouts:(NSArray *)sublayouts;
/**
* Convenience that has CGPointNull position.
*/
+ (instancetype)newWithLayoutableObject:(id<ASLayoutable>)layoutableObject
size:(CGSize)size
sublayouts:(NSArray *)sublayouts;
/**
* Convenience that has CGPointNull position and no sublayouts.
*/
+ (instancetype)newWithLayoutableObject:(id<ASLayoutable>)layoutableObject size:(CGSize)size;
/**
* @abstract Evaluates a given predicate block against each object in the receiving layout tree
* and returns a new, 1-level deep layout containing the objects for which the predicate block returns true.
*
* @param predicateBlock The block is applied to a layout to be evaluated.
* The block takes 1 argument: evaluatedLayout - the layout to be evaluated.
* The block returns YES if evaluatedLayout evaluates to true, otherwise NO.
*
* @return A new, 1-level deep layout containing the layouts for which the predicate block returns true.
*/
- (ASLayout *)flattenedLayoutUsingPredicateBlock:(BOOL (^)(ASLayout *evaluatedLayout))predicateBlock;
@end

View File

@ -0,0 +1,94 @@
/*
* 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.
*
*/
#import "ASLayout.h"
#import "ASAssert.h"
#import "ASLayoutSpecUtilities.h"
#import <stack>
CGPoint const CGPointNull = {NAN, NAN};
extern BOOL CGPointIsNull(CGPoint point)
{
return isnan(point.x) && isnan(point.y);
}
@implementation ASLayout
+ (instancetype)newWithLayoutableObject:(id<ASLayoutable>)layoutableObject
size:(CGSize)size
position:(CGPoint)position
sublayouts:(NSArray *)sublayouts
{
ASDisplayNodeAssert(layoutableObject, @"layoutableObject is required.");
for (ASLayout *sublayout in sublayouts) {
ASDisplayNodeAssert(!CGPointIsNull(sublayout.position), @"Invalid position is not allowed in sublayout.");
}
ASLayout *l = [super new];
if (l) {
l->_layoutableObject = layoutableObject;
l->_size = size;
l->_position = position;
l->_sublayouts = [sublayouts copy];
}
return l;
}
+ (instancetype)newWithLayoutableObject:(id<ASLayoutable>)layoutableObject
size:(CGSize)size
sublayouts:(NSArray *)sublayouts
{
return [self newWithLayoutableObject:layoutableObject size:size position:CGPointNull sublayouts:sublayouts];
}
+ (instancetype)newWithLayoutableObject:(id<ASLayoutable>)layoutableObject size:(CGSize)size
{
return [self newWithLayoutableObject:layoutableObject size:size sublayouts:nil];
}
- (ASLayout *)flattenedLayoutUsingPredicateBlock:(BOOL (^)(ASLayout *))predicateBlock
{
NSMutableArray *flattenedSublayouts = [NSMutableArray array];
struct Context {
ASLayout *layout;
CGPoint absolutePosition;
BOOL visited;
};
// Stack of Contexts, used to keep track of sublayouts while traversing this layout in a DFS fashion.
std::stack<Context> stack;
stack.push({self, CGPointMake(0, 0), NO});
while (!stack.empty()) {
Context &context = stack.top();
if (context.visited) {
stack.pop();
} else {
context.visited = YES;
if (predicateBlock(context.layout)) {
[flattenedSublayouts addObject:[ASLayout newWithLayoutableObject:context.layout.layoutableObject
size:context.layout.size
position:context.absolutePosition
sublayouts:nil]];
}
for (ASLayout *sublayout in context.layout.sublayouts) {
stack.push({sublayout, context.absolutePosition + sublayout.position, NO});
}
}
}
return [ASLayout newWithLayoutableObject:_layoutableObject size:_size sublayouts:flattenedSublayouts];
}
@end

View File

@ -0,0 +1,17 @@
/*
* 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.
*
*/
#import <AsyncDisplayKit/ASLayoutable.h>
#import <AsyncDisplayKit/ASLayout.h>
/** A layout spec is an immutable object that describes a layout, loosely inspired by React. */
@interface ASLayoutSpec : NSObject <ASLayoutable>
@end

View File

@ -0,0 +1,44 @@
/*
* 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.
*
*/
#import "ASLayoutSpec.h"
#import "ASAssert.h"
#import "ASBaseDefines.h"
#import "ASInternalHelpers.h"
#import "ASLayout.h"
@implementation ASLayoutSpec
@synthesize spacingBefore = _spacingBefore;
@synthesize spacingAfter = _spacingAfter;
@synthesize flexGrow = _flexGrow;
@synthesize flexShrink = _flexShrink;
@synthesize flexBasis = _flexBasis;
@synthesize alignSelf = _alignSelf;
+ (instancetype)new
{
ASLayoutSpec *spec = [super new];
if (spec) {
spec->_flexBasis = ASRelativeDimensionUnconstrained;
}
return spec;
}
#pragma mark - Layout
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
{
return [ASLayout newWithLayoutableObject:self size:constrainedSize.min];
}
@end

View File

@ -0,0 +1,64 @@
/*
* 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.
*
*/
#import <AsyncDisplayKit/ASDimension.h>
#import <AsyncDisplayKit/ASStackLayoutChild.h>
@class ASLayout;
@protocol ASLayoutable <NSObject>
/**
* @abstract Additional space to place before this object in the stacking direction.
* Used when attached to a stack layout.
*/
@property (nonatomic, readwrite) CGFloat spacingBefore;
/**
* @abstract Additional space to place after this object in the stacking direction.
* Used when attached to a stack layout.
*/
@property (nonatomic, readwrite) CGFloat spacingAfter;
/**
* @abstract If the sum of childrens' stack dimensions is less than the minimum size, should this object grow?
* Used when attached to a stack layout.
*/
@property (nonatomic, readwrite) BOOL flexGrow;
/**
* @abstract If the sum of childrens' stack dimensions is greater than the maximum size, should this object shrink?
* Used when attached to a stack layout.
*/
@property (nonatomic, readwrite) BOOL flexShrink;
/**
* @abstract Specifies the initial size in the stack dimension for this object.
* Default to ASRelativeDimensionUnconstrained.
* Used when attached to a stack layout.
*/
@property (nonatomic, readwrite) ASRelativeDimension flexBasis;
/**
* @abstract Orientation of the object along cross axis, overriding alignItems
* Used when attached to a stack layout.
*/
@property (nonatomic, readwrite) ASStackLayoutAlignSelf alignSelf;
/**
* @abstract Calculate a layout based on given size range.
*
* @param constrainedSize The minimum and maximum sizes the receiver should fit in.
*
* @return An ASLayout instance defining the layout of the receiver and its children.
*/
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize;
@end

View File

@ -0,0 +1,20 @@
/*
* 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.
*
*/
#import <AsyncDisplayKit/ASLayoutSpec.h>
/**
This layout spec lays out a single layoutable child and then overlays a layoutable object on top of it streched to its size
*/
@interface ASOverlayLayoutSpec : ASLayoutSpec
+ (instancetype)newWithChild:(id<ASLayoutable>)child overlay:(id<ASLayoutable>)overlay;
@end

View File

@ -0,0 +1,55 @@
/*
* 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.
*
*/
#import "ASOverlayLayoutSpec.h"
#import "ASAssert.h"
#import "ASBaseDefines.h"
@implementation ASOverlayLayoutSpec
{
id<ASLayoutable> _overlay;
id<ASLayoutable> _child;
}
+ (instancetype)newWithChild:(id<ASLayoutable>)child overlay:(id<ASLayoutable>)overlay
{
ASOverlayLayoutSpec *spec = [super new];
if (spec) {
ASDisplayNodeAssertNotNil(child, @"Child that will be overlayed on shouldn't be nil");
spec->_overlay = overlay;
spec->_child = child;
}
return spec;
}
+ (instancetype)new
{
ASDISPLAYNODE_NOT_DESIGNATED_INITIALIZER();
}
/**
First layout the contents, then fit the overlay on top of it.
*/
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
{
ASLayout *contentsLayout = [_child measureWithSizeRange:constrainedSize];
contentsLayout.position = CGPointZero;
NSMutableArray *sublayouts = [NSMutableArray arrayWithObject:contentsLayout];
if (_overlay) {
ASLayout *overlayLayout = [_overlay measureWithSizeRange:{contentsLayout.size, contentsLayout.size}];
overlayLayout.position = CGPointZero;
[sublayouts addObject:overlayLayout];
}
return [ASLayout newWithLayoutableObject:self size:contentsLayout.size sublayouts:sublayouts];
}
@end

View File

@ -0,0 +1,36 @@
/*
* 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.
*
*/
#import <AsyncDisplayKit/ASLayoutSpec.h>
#import <AsyncDisplayKit/ASLayoutable.h>
/**
Ratio layout spec
For when the content should respect a certain inherent ratio but can be scaled (think photos or videos)
The ratio passed is the ratio of height / width you expect
For a ratio 0.5, the spec will have a flat rectangle shape
_ _ _ _
| |
|_ _ _ _|
For a ratio 2.0, the spec will be twice as tall as it is wide
_ _
| |
| |
| |
|_ _|
**/
@interface ASRatioLayoutSpec : ASLayoutSpec
+ (instancetype)newWithRatio:(CGFloat)ratio child:(id<ASLayoutable>)child;
@end

View File

@ -0,0 +1,75 @@
/*
* 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.
*
*/
#import "ASRatioLayoutSpec.h"
#import <algorithm>
#import <vector>
#import "ASAssert.h"
#import "ASBaseDefines.h"
#import "ASInternalHelpers.h"
@implementation ASRatioLayoutSpec
{
CGFloat _ratio;
id<ASLayoutable> _child;
}
+ (instancetype)newWithRatio:(CGFloat)ratio child:(id<ASLayoutable>)child
{
ASDisplayNodeAssert(ratio > 0, @"Ratio should be strictly positive, but received %f", ratio);
if (child == nil) {
return nil;
}
ASRatioLayoutSpec *spec = [super new];
if (spec) {
spec->_ratio = ratio;
spec->_child = child;
}
return spec;
}
+ (instancetype)new
{
ASDISPLAYNODE_NOT_DESIGNATED_INITIALIZER();
}
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
{
std::vector<CGSize> sizeOptions;
if (!isinf(constrainedSize.max.width)) {
sizeOptions.push_back(ASSizeRangeClamp(constrainedSize, {
constrainedSize.max.width,
ASFloorPixelValue(_ratio * constrainedSize.max.width)
}));
}
if (!isinf(constrainedSize.max.height)) {
sizeOptions.push_back(ASSizeRangeClamp(constrainedSize, {
ASFloorPixelValue(constrainedSize.max.height / _ratio),
constrainedSize.max.height
}));
}
// Choose the size closest to the desired ratio.
const auto &bestSize = std::max_element(sizeOptions.begin(), sizeOptions.end(), [&](const CGSize &a, const CGSize &b){
return fabs((a.height / a.width) - _ratio) > fabs((b.height / b.width) - _ratio);
});
// If there is no max size in *either* dimension, we can't apply the ratio, so just pass our size range through.
const ASSizeRange childRange = (bestSize == sizeOptions.end()) ? constrainedSize : ASSizeRangeMake(*bestSize, *bestSize);
ASLayout *sublayout = [_child measureWithSizeRange:childRange];
sublayout.position = CGPointZero;
return [ASLayout newWithLayoutableObject:self size:sublayout.size sublayouts:@[sublayout]];
}
@end

View File

@ -0,0 +1,22 @@
/*
* Copyright (c) 2015-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.
*
*/
/**
Each child may override their parent stack's cross axis alignment.
@see ASStackLayoutAlignItems
*/
typedef NS_ENUM(NSUInteger, ASStackLayoutAlignSelf) {
/** Inherit alignment value from containing stack. */
ASStackLayoutAlignSelfAuto,
ASStackLayoutAlignSelfStart,
ASStackLayoutAlignSelfEnd,
ASStackLayoutAlignSelfCenter,
ASStackLayoutAlignSelfStretch,
};

View File

@ -0,0 +1,87 @@
/*
* 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.
*
*/
#import <AsyncDisplayKit/ASLayoutSpec.h>
typedef NS_ENUM(NSUInteger, ASStackLayoutDirection) {
ASStackLayoutDirectionVertical,
ASStackLayoutDirectionHorizontal,
};
/** If no children are flexible, how should this spec justify its children in the available space? */
typedef NS_ENUM(NSUInteger, ASStackLayoutJustifyContent) {
/**
On overflow, children overflow out of this spec's bounds on the right/bottom side.
On underflow, children are left/top-aligned within this spec's bounds.
*/
ASStackLayoutJustifyContentStart,
/**
On overflow, children are centered and overflow on both sides.
On underflow, children are centered within this spec's bounds in the stacking direction.
*/
ASStackLayoutJustifyContentCenter,
/**
On overflow, children overflow out of this spec's bounds on the left/top side.
On underflow, children are right/bottom-aligned within this spec's bounds.
*/
ASStackLayoutJustifyContentEnd,
};
typedef NS_ENUM(NSUInteger, ASStackLayoutAlignItems) {
/** Align children to start of cross axis */
ASStackLayoutAlignItemsStart,
/** Align children with end of cross axis */
ASStackLayoutAlignItemsEnd,
/** Center children on cross axis */
ASStackLayoutAlignItemsCenter,
/** Expand children to fill cross axis */
ASStackLayoutAlignItemsStretch,
};
typedef struct {
/** Specifies the direction children are stacked in. */
ASStackLayoutDirection direction;
/** The amount of space between each child. */
CGFloat spacing;
/** How children are aligned if there are no flexible children. */
ASStackLayoutJustifyContent justifyContent;
/** Orientation of children along cross axis */
ASStackLayoutAlignItems alignItems;
} ASStackLayoutSpecStyle;
/**
A simple layout spec that stacks a list of children vertically or horizontally.
- All children are initially laid out with the an infinite available size in the stacking direction.
- In the other direction, this spec's constraint is passed.
- The children's sizes are summed in the stacking direction.
- If this sum is less than this spec's minimum size in stacking direction, children with flexGrow are flexed.
- If it is greater than this spec's maximum size in the stacking direction, children with flexShrink are flexed.
- If, even after flexing, the sum is still greater than this spec's maximum size in the stacking direction,
justifyContent determines how children are laid out.
For example:
- Suppose stacking direction is Vertical, min-width=100, max-width=300, min-height=200, max-height=500.
- All children are laid out with min-width=100, max-width=300, min-height=0, max-height=INFINITY.
- If the sum of the childrens' heights is less than 200, children with flexGrow are flexed larger.
- If the sum of the childrens' heights is greater than 500, children with flexShrink are flexed smaller.
Each child is shrunk by `((sum of heights) - 500)/(number of flexShrink-able children)`.
- If the sum of the childrens' heights is greater than 500 even after flexShrink-able children are flexed,
justifyContent determines how children are laid out.
*/
@interface ASStackLayoutSpec : ASLayoutSpec
/**
@param style Specifies how children are laid out.
@param children ASLayoutable children to be positioned.
*/
+ (instancetype)newWithStyle:(ASStackLayoutSpecStyle)style children:(NSArray *)children;
@end

View File

@ -0,0 +1,59 @@
/*
* 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.
*
*/
#import "ASStackLayoutSpec.h"
#import <numeric>
#import <vector>
#import "ASBaseDefines.h"
#import "ASInternalHelpers.h"
#import "ASLayoutSpecUtilities.h"
#import "ASStackLayoutSpecUtilities.h"
#import "ASStackPositionedLayout.h"
#import "ASStackUnpositionedLayout.h"
@implementation ASStackLayoutSpec
{
ASStackLayoutSpecStyle _style;
std::vector<id<ASLayoutable>> _children;
}
+ (instancetype)newWithStyle:(ASStackLayoutSpecStyle)style children:(NSArray *)children
{
ASStackLayoutSpec *spec = [super new];
if (spec) {
spec->_style = style;
spec->_children = std::vector<id<ASLayoutable>>();
for (id<ASLayoutable> child in children) {
spec->_children.push_back(child);
}
}
return spec;
}
+ (instancetype)new
{
ASDISPLAYNODE_NOT_DESIGNATED_INITIALIZER();
}
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
{
const auto unpositionedLayout = ASStackUnpositionedLayout::compute(_children, _style, constrainedSize);
const auto positionedLayout = ASStackPositionedLayout::compute(unpositionedLayout, _style, constrainedSize);
const CGSize finalSize = directionSize(_style.direction, unpositionedLayout.stackDimensionSum, positionedLayout.crossSize);
NSArray *sublayouts = [NSArray arrayWithObjects:&positionedLayout.sublayouts[0] count:positionedLayout.sublayouts.size()];
return [ASLayout newWithLayoutableObject:self
size:ASSizeRangeClamp(constrainedSize, finalSize)
sublayouts:sublayouts];
}
@end

View File

@ -0,0 +1,46 @@
/*
* 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.
*
*/
#import <AsyncDisplayKit/ASLayoutSpec.h>
#import <AsyncDisplayKit/ASStaticLayoutSpecDimension.h>
@interface ASStaticLayoutSpecChild : NSObject
@property (nonatomic, readonly) CGPoint position;
@property (nonatomic, readonly) id<ASLayoutable> layoutableObject;
/**
If specified, the child's size is restricted according to this size. Percentages are resolved relative to the static layout spec.
*/
@property (nonatomic, readonly) ASRelativeSizeRange size;
+ (instancetype)newWithPosition:(CGPoint)position layoutableObject:(id<ASLayoutable>)layoutableObject size:(ASRelativeSizeRange)size;
/**
Convenience with default size is Unconstrained in both dimensions, which sets the child's min size to zero
and max size to the maximum available space it can consume without overflowing the spec's bounds.
*/
+ (instancetype)newWithPosition:(CGPoint)position layoutableObject:(id<ASLayoutable>)layoutableObject;
@end
/*
A layout spec that positions children at fixed positions.
Computes a size that is the union of all childrens' frames.
*/
@interface ASStaticLayoutSpec : ASLayoutSpec
/**
@param children Children to be positioned at fixed positions, each is of type ASStaticLayoutSpecChild.
*/
+ (instancetype)newWithChildren:(NSArray *)children;
@end

View File

@ -0,0 +1,95 @@
/*
* 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.
*
*/
#import "ASStaticLayoutSpec.h"
#import "ASLayoutSpecUtilities.h"
#import "ASInternalHelpers.h"
@implementation ASStaticLayoutSpecChild
+ (instancetype)newWithPosition:(CGPoint)position layoutableObject:(id<ASLayoutable>)layoutableObject size:(ASRelativeSizeRange)size
{
ASStaticLayoutSpecChild *c = [super new];
if (c) {
c->_position = position;
c->_layoutableObject = layoutableObject;
c->_size = size;
}
return c;
}
+ (instancetype)newWithPosition:(CGPoint)position layoutableObject:(id<ASLayoutable>)layoutableObject
{
return [self newWithPosition:position layoutableObject:layoutableObject size:ASRelativeSizeRangeUnconstrained];
}
@end
@implementation ASStaticLayoutSpec
{
NSArray *_children;
}
+ (instancetype)newWithChildren:(NSArray *)children
{
ASStaticLayoutSpec *spec = [super new];
if (spec) {
spec->_children = children;
}
return spec;
}
+ (instancetype)new
{
ASDISPLAYNODE_NOT_DESIGNATED_INITIALIZER();
}
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
{
CGSize size = {
constrainedSize.max.width,
constrainedSize.max.height
};
NSMutableArray *sublayouts = [NSMutableArray arrayWithCapacity:_children.count];
for (ASStaticLayoutSpecChild *child in _children) {
CGSize autoMaxSize = {
constrainedSize.max.width - child.position.x,
constrainedSize.max.height - child.position.y
};
ASSizeRange childConstraint = ASRelativeSizeRangeEqualToRelativeSizeRange(ASRelativeSizeRangeUnconstrained, child.size)
? ASSizeRangeMake({0, 0}, autoMaxSize)
: ASRelativeSizeRangeResolve(child.size, size);
ASLayout *sublayout = [child.layoutableObject measureWithSizeRange:childConstraint];
sublayout.position = child.position;
[sublayouts addObject:sublayout];
}
if (isnan(size.width)) {
size.width = constrainedSize.min.width;
for (ASLayout *sublayout in sublayouts) {
size.width = MAX(size.width, sublayout.position.x + sublayout.size.width);
}
}
if (isnan(size.height)) {
size.height = constrainedSize.min.height;
for (ASLayout *sublayout in sublayouts) {
size.height = MAX(size.height, sublayout.position.y + sublayout.size.height);
}
}
return [ASLayout newWithLayoutableObject:self
size:ASSizeRangeClamp(constrainedSize, size)
sublayouts:sublayouts];
}
@end

View File

@ -0,0 +1,73 @@
/*
* Copyright (c) 2015-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.
*
*/
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
#import <AsyncDisplayKit/ASDimension.h>
/**
Expresses a size with relative dimensions.
Used by ASStaticLayoutSpec.
*/
typedef struct {
ASRelativeDimension width;
ASRelativeDimension height;
} ASRelativeSize;
/**
Expresses an inclusive range of relative sizes. Used to provide additional constraint to layout.
Used by ASStaticLayoutSpec.
*/
typedef struct {
ASRelativeSize min;
ASRelativeSize max;
} ASRelativeSizeRange;
extern ASRelativeSizeRange const ASRelativeSizeRangeUnconstrained;
ASDISPLAYNODE_EXTERN_C_BEGIN
#pragma mark -
#pragma mark ASRelativeSize
extern ASRelativeSize ASRelativeSizeMake(ASRelativeDimension width, ASRelativeDimension height);
/** Convenience constructor to provide size in Points. */
extern ASRelativeSize ASRelativeSizeMakeWithCGSize(CGSize size);
/** Resolve this relative size relative to a parent size. */
extern CGSize ASRelativeSizeResolve(ASRelativeSize relativeSize, CGSize parentSize);
extern BOOL ASRelativeSizeEqualToRelativeSize(ASRelativeSize lhs, ASRelativeSize rhs);
extern NSString *NSStringFromASRelativeSize(ASRelativeSize size);
#pragma mark -
#pragma mark ASRelativeSizeRange
extern ASRelativeSizeRange ASRelativeSizeRangeMake(ASRelativeSize min, ASRelativeSize max);
#pragma mark Convenience constructors to provide an exact size (min == max).
extern ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactRelativeSize(ASRelativeSize exact);
extern ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactCGSize(CGSize exact);
extern ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactRelativeDimensions(ASRelativeDimension exactWidth,
ASRelativeDimension exactHeight);
extern BOOL ASRelativeSizeRangeEqualToRelativeSizeRange(ASRelativeSizeRange lhs, ASRelativeSizeRange rhs);
/**
Provided a parent size, compute final dimensions for this RelativeSizeRange to arrive at a SizeRange.
*/
extern ASSizeRange ASRelativeSizeRangeResolve(ASRelativeSizeRange relativeSizeRange,
CGSize parentSize);
ASDISPLAYNODE_EXTERN_C_END

View File

@ -0,0 +1,83 @@
/*
* Copyright (c) 2015-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.
*
*/
#import "ASStaticLayoutSpecDimension.h"
#import "ASAssert.h"
ASRelativeSizeRange const ASRelativeSizeRangeUnconstrained = {};
#pragma mark -
#pragma mark ASRelativeSize
ASRelativeSize ASRelativeSizeMake(ASRelativeDimension width, ASRelativeDimension height)
{
ASRelativeSize size; size.width = width; size.height = height; return size;
}
ASRelativeSize ASRelativeSizeMakeWithCGSize(CGSize size)
{
return ASRelativeSizeMake(ASRelativeDimensionMakeWithPoints(size.width),
ASRelativeDimensionMakeWithPoints(size.height));
}
CGSize ASRelativeSizeResolveSize(ASRelativeSize relativeSize, CGSize parentSize)
{
return CGSizeMake(ASRelativeDimensionResolve(relativeSize.width, parentSize.width),
ASRelativeDimensionResolve(relativeSize.height, parentSize.height));
}
BOOL ASRelativeSizeEqualToRelativeSize(ASRelativeSize lhs, ASRelativeSize rhs)
{
return ASRelativeDimensionEqualToRelativeDimension(lhs.width, rhs.width)
&& ASRelativeDimensionEqualToRelativeDimension(lhs.height, rhs.height);
}
NSString *NSStringFromASRelativeSize(ASRelativeSize size)
{
return [NSString stringWithFormat:@"{%@, %@}",
NSStringFromASRelativeDimension(size.width),
NSStringFromASRelativeDimension(size.height)];
}
#pragma mark -
#pragma mark ASRelativeSizeRange
ASRelativeSizeRange ASRelativeSizeRangeMake(ASRelativeSize min, ASRelativeSize max)
{
ASRelativeSizeRange sizeRange; sizeRange.min = min; sizeRange.max = max; return sizeRange;
}
ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactRelativeSize(ASRelativeSize exact)
{
return ASRelativeSizeRangeMake(exact, exact);
}
ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactCGSize(CGSize exact)
{
return ASRelativeSizeRangeMakeWithExactRelativeSize(ASRelativeSizeMakeWithCGSize(exact));
}
ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactRelativeDimensions(ASRelativeDimension exactWidth,
ASRelativeDimension exactHeight)
{
return ASRelativeSizeRangeMakeWithExactRelativeSize(ASRelativeSizeMake(exactWidth, exactHeight));
}
BOOL ASRelativeSizeRangeEqualToRelativeSizeRange(ASRelativeSizeRange lhs, ASRelativeSizeRange rhs)
{
return ASRelativeSizeEqualToRelativeSize(lhs.min, rhs.min) && ASRelativeSizeEqualToRelativeSize(lhs.max, rhs.max);
}
ASSizeRange ASRelativeSizeRangeResolve(ASRelativeSizeRange relativeSizeRange,
CGSize parentSize)
{
return ASSizeRangeMake(ASRelativeSizeResolveSize(relativeSizeRange.min, parentSize),
ASRelativeSizeResolveSize(relativeSizeRange.max, parentSize));
}

View File

@ -87,15 +87,23 @@ static void __ASDisplayLayerDecrementConcurrentDisplayCount(BOOL displayIsAsync,
// Capture these outside the display block so they are retained.
UIColor *backgroundColor = self.backgroundColor;
CGRect bounds = self.bounds;
CGPoint position = self.position;
CGPoint anchorPoint = self.anchorPoint;
// Pretty hacky since full 3D transforms aren't actually supported, but attempt to compute the transformed frame of this node so that we can composite it into approximately the right spot.
CGAffineTransform transform = CATransform3DGetAffineTransform(self.transform);
CGSize scaledBoundsSize = CGSizeApplyAffineTransform(bounds.size, transform);
CGPoint origin = CGPointMake(position.x - scaledBoundsSize.width * anchorPoint.x,
position.y - scaledBoundsSize.height * anchorPoint.y);
CGRect frame = CGRectMake(origin.x, origin.y, bounds.size.width, bounds.size.height);
CGRect frame;
// If this is the root container node, use a frame with a zero origin to draw into. If not, calculate the correct frame using the node's position, transform and anchorPoint.
if (self.shouldRasterizeDescendants) {
frame = CGRectMake(0.0f, 0.0f, bounds.size.width, bounds.size.height);
} else {
CGPoint position = self.position;
CGPoint anchorPoint = self.anchorPoint;
// Pretty hacky since full 3D transforms aren't actually supported, but attempt to compute the transformed frame of this node so that we can composite it into approximately the right spot.
CGAffineTransform transform = CATransform3DGetAffineTransform(self.transform);
CGSize scaledBoundsSize = CGSizeApplyAffineTransform(bounds.size, transform);
CGPoint origin = CGPointMake(position.x - scaledBoundsSize.width * anchorPoint.x,
position.y - scaledBoundsSize.height * anchorPoint.y);
frame = CGRectMake(origin.x, origin.y, bounds.size.width, bounds.size.height);
}
// Get the display block for this node.
asyncdisplaykit_async_transaction_operation_block_t displayBlock = [self _displayBlockWithAsynchronous:NO isCancelledBlock:isCancelledBlock rasterizing:YES];
@ -165,7 +173,7 @@ static void __ASDisplayLayerDecrementConcurrentDisplayCount(BOOL displayIsAsync,
[self _recursivelyRasterizeSelfAndSublayersWithIsCancelledBlock:isCancelledBlock displayBlocks:displayBlocks];
CGFloat contentsScaleForDisplay = self.contentsScaleForDisplay;
BOOL opaque = self.opaque;
BOOL opaque = self.opaque && CGColorGetAlpha(self.backgroundColor.CGColor) == 1.0f;
ASDisplayNodeAssert(self.contentsScaleForDisplay != 0.0, @"Invalid contents scale");
@ -177,7 +185,6 @@ static void __ASDisplayLayerDecrementConcurrentDisplayCount(BOOL displayIsAsync,
}
ASDN_DELAY_FOR_DISPLAY();
UIGraphicsBeginImageContextWithOptions(bounds.size, opaque, contentsScaleForDisplay);
for (dispatch_block_t block in displayBlocks) {

View File

@ -143,16 +143,21 @@
ASDisplayNodeAssert(CATransform3DIsIdentity(self.transform), @"Must be an identity transform");
#endif
if (_layer && ASDisplayNodeThreadIsMain()) {
CGPoint anchorPoint = _layer.anchorPoint;
_layer.bounds = CGRectMake(0, 0, rect.size.width, rect.size.height);
_layer.position = CGPointMake(rect.origin.x + rect.size.width * anchorPoint.x,
rect.origin.y + rect.size.height * anchorPoint.y);
BOOL useLayer = (_layer && ASDisplayNodeThreadIsMain());
CGPoint origin = (useLayer ? _layer.bounds.origin : self.bounds.origin);
CGPoint anchorPoint = (useLayer ? _layer.anchorPoint : self.anchorPoint);
CGRect bounds = (CGRect){ origin, rect.size };
CGPoint position = CGPointMake(rect.origin.x + rect.size.width * anchorPoint.x,
rect.origin.y + rect.size.height * anchorPoint.y);
if (useLayer) {
_layer.bounds = bounds;
_layer.position = position;
} else {
CGPoint anchorPoint = self.anchorPoint;
self.bounds = CGRectMake(0, 0, rect.size.width, rect.size.height);
self.position = CGPointMake(rect.origin.x + rect.size.width * anchorPoint.x,
rect.origin.y + rect.size.height * anchorPoint.y);
self.bounds = bounds;
self.position = position;
}
}
@ -637,6 +642,18 @@
_setToViewOnly(shouldGroupAccessibilityChildren, shouldGroupAccessibilityChildren);
}
- (NSString *)accessibilityIdentifier
{
_bridge_prologue;
return _getFromViewOnly(accessibilityIdentifier);
}
- (void)setAccessibilityIdentifier:(NSString *)accessibilityIdentifier
{
_bridge_prologue;
_setToViewOnly(accessibilityIdentifier, accessibilityIdentifier);
}
@end

View File

@ -17,17 +17,18 @@
#import "ASDisplayNode.h"
#import "ASSentinel.h"
#import "ASThread.h"
#import "ASLayout.h"
BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector);
CGFloat ASDisplayNodeScreenScale();
void ASDisplayNodePerformBlockOnMainThread(void (^block)());
typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides) {
ASDisplayNodeMethodOverrideNone = 0,
ASDisplayNodeMethodOverrideTouchesBegan = 1 << 0,
ASDisplayNodeMethodOverrideTouchesCancelled = 1 << 1,
ASDisplayNodeMethodOverrideTouchesEnded = 1 << 2,
ASDisplayNodeMethodOverrideTouchesMoved = 1 << 3
ASDisplayNodeMethodOverrideTouchesBegan = 1 << 0,
ASDisplayNodeMethodOverrideTouchesCancelled = 1 << 1,
ASDisplayNodeMethodOverrideTouchesEnded = 1 << 2,
ASDisplayNodeMethodOverrideTouchesMoved = 1 << 3,
ASDisplayNodeMethodOverrideCalculateSizeThatFits = 1 << 4
};
@class _ASPendingState;
@ -51,8 +52,8 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides) {
// This is the desired contentsScale, not the scale at which the layer's contents should be displayed
CGFloat _contentsScaleForDisplay;
CGSize _size;
CGSize _constrainedSize;
ASLayout *_layout;
ASSizeRange _constrainedSize;
UIEdgeInsets _hitTestSlop;
NSMutableArray *_subnodes;
@ -118,8 +119,8 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides) {
- (BOOL)__shouldSize;
- (void)__exitedHierarchy;
// Core implementation of -measure:. Must be called with _propertyLock held.
- (CGSize)__measure:(CGSize)constrainedSize;
// Core implementation of -measureWithSizeRange:. Must be called with _propertyLock held.
- (ASLayout *)__measureWithSizeRange:(ASSizeRange)constrainedSize;
- (void)__layout;
- (void)__setSupernode:(ASDisplayNode *)supernode;

View File

@ -0,0 +1,28 @@
/*
* 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.
*
*/
#import <UIKit/UIKit.h>
#import "ASBaseDefines.h"
@class ASLayoutChild;
ASDISPLAYNODE_EXTERN_C_BEGIN
BOOL ASSubclassOverridesSelector(Class superclass, Class subclass, SEL selector);
CGFloat ASScreenScale();
CGFloat ASFloorPixelValue(CGFloat f);
CGFloat ASCeilPixelValue(CGFloat f);
CGFloat ASRoundPixelValue(CGFloat f);
ASDISPLAYNODE_EXTERN_C_END

View File

@ -0,0 +1,63 @@
/*
* 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.
*
*/
#import "ASInternalHelpers.h"
#import <functional>
#import <objc/runtime.h>
#import "ASLayout.h"
BOOL ASSubclassOverridesSelector(Class superclass, Class subclass, SEL selector)
{
Method superclassMethod = class_getInstanceMethod(superclass, selector);
Method subclassMethod = class_getInstanceMethod(subclass, selector);
IMP superclassIMP = superclassMethod ? method_getImplementation(superclassMethod) : NULL;
IMP subclassIMP = subclassMethod ? method_getImplementation(subclassMethod) : NULL;
return (superclassIMP != subclassIMP);
}
static void ASDispatchOnceOnMainThread(dispatch_once_t *predicate, dispatch_block_t block)
{
if ([NSThread isMainThread]) {
dispatch_once(predicate, block);
} else {
if (DISPATCH_EXPECT(*predicate == 0L, NO)) {
dispatch_sync(dispatch_get_main_queue(), ^{
dispatch_once(predicate, block);
});
}
}
}
CGFloat ASScreenScale()
{
static CGFloat _scale;
static dispatch_once_t onceToken;
ASDispatchOnceOnMainThread(&onceToken, ^{
_scale = [UIScreen mainScreen].scale;
});
return _scale;
}
CGFloat ASFloorPixelValue(CGFloat f)
{
return floorf(f * ASScreenScale()) / ASScreenScale();
}
CGFloat ASCeilPixelValue(CGFloat f)
{
return ceilf(f * ASScreenScale()) / ASScreenScale();
}
CGFloat ASRoundPixelValue(CGFloat f)
{
return roundf(f * ASScreenScale()) / ASScreenScale();
}

View File

@ -0,0 +1,105 @@
/*
* 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.
*
*/
#import <algorithm>
#import <functional>
#import <type_traits>
#import <vector>
#import <UIKit/UIKit.h>
namespace AS {
// adopted from http://stackoverflow.com/questions/14945223/map-function-with-c11-constructs
// Takes an iterable, applies a function to every element,
// and returns a vector of the results
//
template <typename T, typename Func>
auto map(const T &iterable, Func &&func) -> std::vector<decltype(func(std::declval<typename T::value_type>()))>
{
// Some convenience type definitions
typedef decltype(func(std::declval<typename T::value_type>())) value_type;
typedef std::vector<value_type> result_type;
// Prepares an output vector of the appropriate size
result_type res(iterable.size());
// Let std::transform apply `func` to all elements
// (use perfect forwarding for the function object)
std::transform(
begin(iterable), end(iterable), res.begin(),
std::forward<Func>(func)
);
return res;
}
template<typename Func>
auto map(id<NSFastEnumeration> collection, Func &&func) -> std::vector<decltype(func(std::declval<id>()))>
{
std::vector<decltype(func(std::declval<id>()))> to;
for (id obj in collection) {
to.push_back(func(obj));
}
return to;
}
template <typename T, typename Func>
auto filter(const T &iterable, Func &&func) -> std::vector<typename T::value_type>
{
std::vector<typename T::value_type> to;
for (auto obj : iterable) {
if (func(obj)) {
to.push_back(obj);
}
}
return to;
}
};
inline CGPoint operator+(const CGPoint &p1, const CGPoint &p2)
{
return { p1.x + p2.x, p1.y + p2.y };
}
inline CGPoint operator-(const CGPoint &p1, const CGPoint &p2)
{
return { p1.x - p2.x, p1.y - p2.y };
}
inline CGSize operator+(const CGSize &s1, const CGSize &s2)
{
return { s1.width + s2.width, s1.height + s2.height };
}
inline CGSize operator-(const CGSize &s1, const CGSize &s2)
{
return { s1.width - s2.width, s1.height - s2.height };
}
inline UIEdgeInsets operator+(const UIEdgeInsets &e1, const UIEdgeInsets &e2)
{
return { e1.top + e2.top, e1.left + e2.left, e1.bottom + e2.bottom, e1.right + e2.right };
}
inline UIEdgeInsets operator-(const UIEdgeInsets &e1, const UIEdgeInsets &e2)
{
return { e1.top - e2.top, e1.left - e2.left, e1.bottom - e2.bottom, e1.right - e2.right };
}
inline UIEdgeInsets operator*(const UIEdgeInsets &e1, const UIEdgeInsets &e2)
{
return { e1.top * e2.top, e1.left * e2.left, e1.bottom * e2.bottom, e1.right * e2.right };
}
inline UIEdgeInsets operator-(const UIEdgeInsets &e)
{
return { -e.top, -e.left, -e.bottom, -e.right };
}

View File

@ -0,0 +1,62 @@
/*
* 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.
*
*/
#import "ASStackLayoutSpec.h"
inline CGFloat stackDimension(const ASStackLayoutDirection direction, const CGSize size)
{
return (direction == ASStackLayoutDirectionVertical) ? size.height : size.width;
}
inline CGFloat crossDimension(const ASStackLayoutDirection direction, const CGSize size)
{
return (direction == ASStackLayoutDirectionVertical) ? size.width : size.height;
}
inline BOOL compareCrossDimension(const ASStackLayoutDirection direction, const CGSize a, const CGSize b)
{
return crossDimension(direction, a) < crossDimension(direction, b);
}
inline CGPoint directionPoint(const ASStackLayoutDirection direction, const CGFloat stack, const CGFloat cross)
{
return (direction == ASStackLayoutDirectionVertical) ? CGPointMake(cross, stack) : CGPointMake(stack, cross);
}
inline CGSize directionSize(const ASStackLayoutDirection direction, const CGFloat stack, const CGFloat cross)
{
return (direction == ASStackLayoutDirectionVertical) ? CGSizeMake(cross, stack) : CGSizeMake(stack, cross);
}
inline ASSizeRange directionSizeRange(const ASStackLayoutDirection direction,
const CGFloat stackMin,
const CGFloat stackMax,
const CGFloat crossMin,
const CGFloat crossMax)
{
return {directionSize(direction, stackMin, crossMin), directionSize(direction, stackMax, crossMax)};
}
inline ASStackLayoutAlignItems alignment(ASStackLayoutAlignSelf childAlignment, ASStackLayoutAlignItems stackAlignment)
{
switch (childAlignment) {
case ASStackLayoutAlignSelfCenter:
return ASStackLayoutAlignItemsCenter;
case ASStackLayoutAlignSelfEnd:
return ASStackLayoutAlignItemsEnd;
case ASStackLayoutAlignSelfStart:
return ASStackLayoutAlignItemsStart;
case ASStackLayoutAlignSelfStretch:
return ASStackLayoutAlignItemsStretch;
case ASStackLayoutAlignSelfAuto:
default:
return stackAlignment;
}
}

View File

@ -0,0 +1,25 @@
/*
* 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.
*
*/
#import "ASLayout.h"
#import "ASDimension.h"
#import "ASStackLayoutSpec.h"
#import "ASStackUnpositionedLayout.h"
/** Represents a set of laid out and positioned stack layout children. */
struct ASStackPositionedLayout {
const std::vector<ASLayout *> sublayouts;
const CGFloat crossSize;
/** Given an unpositioned layout, computes the positions each child should be placed at. */
static ASStackPositionedLayout compute(const ASStackUnpositionedLayout &unpositionedLayout,
const ASStackLayoutSpecStyle &style,
const ASSizeRange &constrainedSize);
};

View File

@ -0,0 +1,75 @@
/*
* 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.
*
*/
#import "ASStackPositionedLayout.h"
#import "ASInternalHelpers.h"
#import "ASLayoutSpecUtilities.h"
#import "ASStackLayoutSpecUtilities.h"
#import "ASLayoutable.h"
static CGFloat crossOffset(const ASStackLayoutSpecStyle &style,
const ASStackUnpositionedItem &l,
const CGFloat crossSize)
{
switch (alignment(l.child.alignSelf, style.alignItems)) {
case ASStackLayoutAlignItemsEnd:
return crossSize - crossDimension(style.direction, l.layout.size);
case ASStackLayoutAlignItemsCenter:
return ASFloorPixelValue((crossSize - crossDimension(style.direction, l.layout.size)) / 2);
case ASStackLayoutAlignItemsStart:
case ASStackLayoutAlignItemsStretch:
return 0;
}
}
static ASStackPositionedLayout stackedLayout(const ASStackLayoutSpecStyle &style,
const CGFloat offset,
const ASStackUnpositionedLayout &unpositionedLayout,
const ASSizeRange &constrainedSize)
{
// The cross dimension is the max of the childrens' cross dimensions (clamped to our constraint below).
const auto it = std::max_element(unpositionedLayout.items.begin(), unpositionedLayout.items.end(),
[&](const ASStackUnpositionedItem &a, const ASStackUnpositionedItem &b){
return compareCrossDimension(style.direction, a.layout.size, b.layout.size);
});
const auto largestChildCrossSize = it == unpositionedLayout.items.end() ? 0 : crossDimension(style.direction, it->layout.size);
const auto minCrossSize = crossDimension(style.direction, constrainedSize.min);
const auto maxCrossSize = crossDimension(style.direction, constrainedSize.max);
const CGFloat crossSize = MIN(MAX(minCrossSize, largestChildCrossSize), maxCrossSize);
CGPoint p = directionPoint(style.direction, offset, 0);
BOOL first = YES;
auto stackedChildren = AS::map(unpositionedLayout.items, [&](const ASStackUnpositionedItem &l) -> ASLayout *{
p = p + directionPoint(style.direction, l.child.spacingBefore, 0);
if (!first) {
p = p + directionPoint(style.direction, style.spacing, 0);
}
first = NO;
l.layout.position = p + directionPoint(style.direction, 0, crossOffset(style, l, crossSize));
p = p + directionPoint(style.direction, stackDimension(style.direction, l.layout.size) + l.child.spacingAfter, 0);
return l.layout;
});
return {stackedChildren, crossSize};
}
ASStackPositionedLayout ASStackPositionedLayout::compute(const ASStackUnpositionedLayout &unpositionedLayout,
const ASStackLayoutSpecStyle &style,
const ASSizeRange &constrainedSize)
{
switch (style.justifyContent) {
case ASStackLayoutJustifyContentStart:
return stackedLayout(style, 0, unpositionedLayout, constrainedSize);
case ASStackLayoutJustifyContentCenter:
return stackedLayout(style, floorf(unpositionedLayout.violation / 2), unpositionedLayout, constrainedSize);
case ASStackLayoutJustifyContentEnd:
return stackedLayout(style, unpositionedLayout.violation, unpositionedLayout, constrainedSize);
}
}

View File

@ -0,0 +1,36 @@
/*
* 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.
*
*/
#import <vector>
#import "ASLayout.h"
#import "ASStackLayoutSpec.h"
struct ASStackUnpositionedItem {
/** The original source child. */
id<ASLayoutable> child;
/** The proposed layout. */
ASLayout *layout;
};
/** Represents a set of stack layout children that have their final layout computed, but are not yet positioned. */
struct ASStackUnpositionedLayout {
/** A set of proposed child layouts, not yet positioned. */
const std::vector<ASStackUnpositionedItem> items;
/** The total size of the children in the stack dimension, including all spacing. */
const CGFloat stackDimensionSum;
/** The amount by which stackDimensionSum violates constraints. If positive, less than min; negative, greater than max. */
const CGFloat violation;
/** Given a set of children, computes the unpositioned layouts for those children. */
static ASStackUnpositionedLayout compute(const std::vector<id<ASLayoutable>> &children,
const ASStackLayoutSpecStyle &style,
const ASSizeRange &sizeRange);
};

View File

@ -0,0 +1,341 @@
/*
* 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.
*
*/
#import "ASStackUnpositionedLayout.h"
#import <numeric>
#import "ASLayoutSpecUtilities.h"
#import "ASStackLayoutSpecUtilities.h"
/**
Sizes the child given the parameters specified, and returns the computed layout.
*/
static ASLayout *crossChildLayout(const id<ASLayoutable> child,
const ASStackLayoutSpecStyle style,
const CGFloat stackMin,
const CGFloat stackMax,
const CGFloat crossMin,
const CGFloat crossMax)
{
const ASStackLayoutAlignItems alignItems = alignment(child.alignSelf, style.alignItems);
// stretched children will have a cross dimension of at least crossMin
const CGFloat childCrossMin = alignItems == ASStackLayoutAlignItemsStretch ? crossMin : 0;
const ASSizeRange childSizeRange = directionSizeRange(style.direction, stackMin, stackMax, childCrossMin, crossMax);
return [child measureWithSizeRange:childSizeRange];
}
/**
Stretches children to lay out along the cross axis according to the alignment stretch settings of the children
(child.alignSelf), and the stack layout's alignment settings (style.alignItems). This does not do the actual alignment
of the items once stretched though; ASStackPositionedLayout will do centering etc.
Finds the maximum cross dimension among child layouts. If that dimension exceeds the minimum cross layout size then
we must stretch any children whose alignItems specify ASStackLayoutAlignItemsStretch.
The diagram below shows 3 children in a horizontal stack. The second child is larger than the minCrossDimension, so
its height is used as the childCrossMax. Any children that are stretchable (which may be all children if
style.alignItems specifies stretch) like the first child must be stretched to match that maximum. All children must be
at least minCrossDimension in cross dimension size, which is shown by the sizing of the third child.
Stack Dimension
+--------------------->
+ +-+-------------+-+-------------+--+---------------+ + + +
| | child. | | | | | | | |
| | alignSelf | | | | | | | |
Cross | | = stretch | | | +-------+-------+ | | |
Dimension | +-----+-------+ | | | | | | | |
| | | | | | | | | |
| | | | | v | | | |
v +-+- - - - - - -+-+ - - - - - - +--+- - - - - - - -+ | | + minCrossDimension
| | | | |
| v | | | | |
+- - - - - - -+ +-------------+ | + childCrossMax
|
+--------------------------------------------------+ + crossMax
@param layouts pre-computed child layouts; modified in-place as needed
@param style the layout style of the overall stack layout
*/
static void stretchChildrenAlongCrossDimension(std::vector<ASStackUnpositionedItem> &layouts,
const ASStackLayoutSpecStyle &style)
{
// Find the maximum cross dimension size among child layouts
const auto it = std::max_element(layouts.begin(), layouts.end(),
[&](const ASStackUnpositionedItem &a, const ASStackUnpositionedItem &b) {
return compareCrossDimension(style.direction, a.layout.size, b.layout.size);
});
const CGFloat childCrossMax = it == layouts.end() ? 0 : crossDimension(style.direction, it->layout.size);
for (auto &l : layouts) {
const ASStackLayoutAlignItems alignItems = alignment(l.child.alignSelf, style.alignItems);
const CGFloat cross = crossDimension(style.direction, l.layout.size);
const CGFloat stack = stackDimension(style.direction, l.layout.size);
// restretch all stretchable children along the cross axis using the new min. set their max size to childCrossMax,
// not crossMax, so that if any of them would choose a larger size just because the min size increased (weird!)
// they are forced to choose the same width as all the other children.
if (alignItems == ASStackLayoutAlignItemsStretch && fabs(cross - childCrossMax) > 0.01) {
l.layout = crossChildLayout(l.child, style, stack, stack, childCrossMax, childCrossMax);
}
}
}
/**
Computes the consumed stack dimension length for the given vector of children and stacking style.
stackDimensionSum
<----------------------->
+-----+ +-------+ +---+
| | | | | |
| | | | | |
+-----+ | | +---+
+-------+
@param children unpositioned layouts for the children of the stack spec
@param style the layout style of the overall stack layout
*/
static CGFloat computeStackDimensionSum(const std::vector<ASStackUnpositionedItem> &children,
const ASStackLayoutSpecStyle &style)
{
// Sum up the childrens' spacing
const CGFloat childSpacingSum = std::accumulate(children.begin(), children.end(),
// Start from default spacing between each child:
children.empty() ? 0 : style.spacing * (children.size() - 1),
[&](CGFloat x, const ASStackUnpositionedItem &l) {
return x + l.child.spacingBefore + l.child.spacingAfter;
});
// Sum up the childrens' dimensions (including spacing) in the stack direction.
const CGFloat childStackDimensionSum = std::accumulate(children.begin(), children.end(), childSpacingSum,
[&](CGFloat x, const ASStackUnpositionedItem &l) {
return x + stackDimension(style.direction, l.layout.size);
});
return childStackDimensionSum;
}
/**
Computes the violation by comparing a stack dimension sum with the overall allowable size range for the stack.
Violation is the distance you would have to add to the unbounded stack-direction length of the stack spec's
children in order to bring the stack within its allowed sizeRange. The diagram below shows 3 horizontal stacks with
the different types of violation.
sizeRange
|------------|
+------+ +-------+ +-------+ +---------+
| | | | | | | | | |
| | | | | | | | (zero violation)
| | | | | | | | | |
+------+ +-------+ +-------+ +---------+
| |
+------+ +-------+ +-------+
| | | | | | | |
| | | | | |<--> (positive violation)
| | | | | | | |
+------+ +-------+ +-------+
| |<------> (negative violation)
+------+ +-------+ +-------+ +---------+ +-----------+
| | | | | | | | | | | |
| | | | | | | | | |
| | | | | | | | | | | |
+------+ +-------+ +-------+ +---------+ +-----------+
@param stackDimensionSum the consumed length of the children in the stack along the stack dimension
@param style layout style to be applied to all children
@param sizeRange the range of allowable sizes for the stack layout spec
*/
static CGFloat computeViolation(const CGFloat stackDimensionSum,
const ASStackLayoutSpecStyle &style,
const ASSizeRange &sizeRange)
{
const CGFloat minStackDimension = stackDimension(style.direction, sizeRange.min);
const CGFloat maxStackDimension = stackDimension(style.direction, sizeRange.max);
if (stackDimensionSum < minStackDimension) {
return minStackDimension - stackDimensionSum;
} else if (stackDimensionSum > maxStackDimension) {
return maxStackDimension - stackDimensionSum;
}
return 0;
}
/** The threshold that determines if a violation has actually occurred. */
static const CGFloat kViolationEpsilon = 0.01;
/**
Returns a lambda that determines if the given unpositioned item's child is flexible in the direction of the violation.
@param violation the amount that the stack layout violates its size range. See header for sign interpretation.
*/
static std::function<BOOL(const ASStackUnpositionedItem &)> isFlexibleInViolationDirection(const CGFloat violation)
{
if (fabs(violation) < kViolationEpsilon) {
return [](const ASStackUnpositionedItem &l) { return NO; };
} else if (violation > 0) {
return [](const ASStackUnpositionedItem &l) { return l.child.flexGrow; };
} else {
return [](const ASStackUnpositionedItem &l) { return l.child.flexShrink; };
}
}
ASDISPLAYNODE_INLINE BOOL isFlexibleInBothDirections(id<ASLayoutable> child)
{
return child.flexGrow && child.flexShrink;
}
/**
If we have a single flexible (both shrinkable and growable) child, and our allowed size range is set to a specific
number then we may avoid the first "intrinsic" size calculation.
*/
ASDISPLAYNODE_INLINE BOOL useOptimizedFlexing(const std::vector<id<ASLayoutable>> &children,
const ASStackLayoutSpecStyle &style,
const ASSizeRange &sizeRange)
{
const NSUInteger flexibleChildren = std::count_if(children.begin(), children.end(), isFlexibleInBothDirections);
return ((flexibleChildren == 1)
&& (stackDimension(style.direction, sizeRange.min) ==
stackDimension(style.direction, sizeRange.max)));
}
/**
The flexible children may have been left not laid out in the initial layout pass, so we may have to go through and size
these children at zero size so that the children layouts are at least present.
*/
static void layoutFlexibleChildrenAtZeroSize(std::vector<ASStackUnpositionedItem> &items,
const ASStackLayoutSpecStyle &style,
const ASSizeRange &sizeRange)
{
for (ASStackUnpositionedItem &item : items) {
if (isFlexibleInBothDirections(item.child)) {
item.layout = crossChildLayout(item.child,
style,
0,
0,
crossDimension(style.direction, sizeRange.min),
crossDimension(style.direction, sizeRange.max));
}
}
}
/**
Flexes children in the stack axis to resolve a min or max stack size violation. First, determines which children are
flexible (see computeViolation and isFlexibleInViolationDirection). Then computes how much to flex each flexible child
and performs re-layout. Note that there may still be a non-zero violation even after flexing.
The actual CSS flexbox spec describes an iterative looping algorithm here, which may be adopted in t5837937:
http://www.w3.org/TR/css3-flexbox/#resolve-flexible-lengths
@param items Reference to unpositioned items from the original, unconstrained layout pass; modified in-place
@param style layout style to be applied to all children
@param sizeRange the range of allowable sizes for the stack layout spec
*/
static void flexChildrenAlongStackDimension(std::vector<ASStackUnpositionedItem> &items,
const ASStackLayoutSpecStyle &style,
const ASSizeRange &sizeRange,
const BOOL useOptimizedFlexing)
{
const CGFloat stackDimensionSum = computeStackDimensionSum(items, style);
const CGFloat violation = computeViolation(stackDimensionSum, style, sizeRange);
// We count the number of children which are flexible in the direction of the violation
std::function<BOOL(const ASStackUnpositionedItem &)> isFlex = isFlexibleInViolationDirection(violation);
const NSUInteger flexibleChildren = std::count_if(items.begin(), items.end(), isFlex);
if (flexibleChildren == 0) {
// If optimized flexing was used then we have to clean up the unsized children, and lay them out at zero size
if (useOptimizedFlexing) {
layoutFlexibleChildrenAtZeroSize(items, style, sizeRange);
}
return;
}
// Each flexible child along the direction of the violation is expanded or contracted equally
const CGFloat violationPerFlexChild = floorf(violation / flexibleChildren);
// If the floor operation above left a remainder we may have a remainder after deducting the adjustments from all the
// contributions of the flexible children.
const CGFloat violationRemainder = violation - (violationPerFlexChild * flexibleChildren);
BOOL isFirstFlex = YES;
for (ASStackUnpositionedItem &item : items) {
if (isFlex(item)) {
const CGFloat originalStackSize = stackDimension(style.direction, item.layout.size);
// The first flexible child is given the additional violation remainder
const CGFloat flexedStackSize = originalStackSize + violationPerFlexChild + (isFirstFlex ? violationRemainder : 0);
item.layout = crossChildLayout(item.child,
style,
MAX(flexedStackSize, 0),
MAX(flexedStackSize, 0),
crossDimension(style.direction, sizeRange.min),
crossDimension(style.direction, sizeRange.max));
isFirstFlex = NO;
}
}
}
/**
Performs the first unconstrained layout of the children, generating the unpositioned items that are then flexed and
stretched.
*/
static std::vector<ASStackUnpositionedItem> layoutChildrenAlongUnconstrainedStackDimension(const std::vector<id<ASLayoutable>> &children,
const ASStackLayoutSpecStyle &style,
const ASSizeRange &sizeRange,
const CGSize size,
const BOOL useOptimizedFlexing)
{
const CGFloat minCrossDimension = crossDimension(style.direction, sizeRange.min);
const CGFloat maxCrossDimension = crossDimension(style.direction, sizeRange.max);
return AS::map(children, [&](id<ASLayoutable> child) -> ASStackUnpositionedItem {
const BOOL isUnconstrainedFlexBasis = ASRelativeDimensionEqualToRelativeDimension(ASRelativeDimensionUnconstrained, child.flexBasis);
const CGFloat exactStackDimension = ASRelativeDimensionResolve(child.flexBasis, stackDimension(style.direction, size));
if (useOptimizedFlexing && isFlexibleInBothDirections(child)) {
return { child, [ASLayout newWithLayoutableObject:child size:{0, 0}] };
} else {
return {
child,
crossChildLayout(child,
style,
isUnconstrainedFlexBasis ? 0 : exactStackDimension,
isUnconstrainedFlexBasis ? INFINITY : exactStackDimension,
minCrossDimension,
maxCrossDimension)
};
}
});
}
ASStackUnpositionedLayout ASStackUnpositionedLayout::compute(const std::vector<id<ASLayoutable>> &children,
const ASStackLayoutSpecStyle &style,
const ASSizeRange &sizeRange)
{
const CGSize size = {
sizeRange.max.width,
sizeRange.max.height
};
// We may be able to avoid some redundant layout passes
const BOOL optimizedFlexing = useOptimizedFlexing(children, style, sizeRange);
// We do a first pass of all the children, generating an unpositioned layout for each with an unbounded range along
// the stack dimension. This allows us to compute the "intrinsic" size of each child and find the available violation
// which determines whether we must grow or shrink the flexible children.
std::vector<ASStackUnpositionedItem> items = layoutChildrenAlongUnconstrainedStackDimension(children,
style,
sizeRange,
size,
optimizedFlexing);
flexChildrenAlongStackDimension(items, style, sizeRange, optimizedFlexing);
stretchChildrenAlongCrossDimension(items, style);
const CGFloat stackDimensionSum = computeStackDimensionSum(items, style);
return {items, stackDimensionSum, computeViolation(stackDimensionSum, style, sizeRange)};
}

View File

@ -48,6 +48,7 @@
BOOL accessibilityElementsHidden;
BOOL accessibilityViewIsModal;
BOOL shouldGroupAccessibilityChildren;
NSString *accessibilityIdentifier;
struct {
// Properties
@ -97,6 +98,7 @@
int setAccessibilityElementsHidden:1;
int setAccessibilityViewIsModal:1;
int setShouldGroupAccessibilityChildren:1;
int setAccessibilityIdentifier:1;
} _flags;
}
@ -187,6 +189,7 @@
accessibilityElementsHidden = NO;
accessibilityViewIsModal = NO;
shouldGroupAccessibilityChildren = NO;
accessibilityIdentifier = nil;
edgeAntialiasingMask = (kCALayerLeftEdge | kCALayerRightEdge | kCALayerTopEdge | kCALayerBottomEdge);
return self;
@ -542,6 +545,19 @@
_flags.setShouldGroupAccessibilityChildren = YES;
}
- (NSString *)accessibilityIdentifier
{
return accessibilityIdentifier;
}
- (void)setAccessibilityIdentifier:(NSString *)newAccessibilityIdentifier
{
_flags.setAccessibilityIdentifier = YES;
if (accessibilityIdentifier != newAccessibilityIdentifier) {
accessibilityIdentifier = [newAccessibilityIdentifier copy];
}
}
- (void)applyToLayer:(CALayer *)layer
{
if (_flags.setAnchorPoint)
@ -775,6 +791,9 @@
if (_flags.setShouldGroupAccessibilityChildren)
view.shouldGroupAccessibilityChildren = shouldGroupAccessibilityChildren;
if (_flags.setAccessibilityIdentifier)
view.accessibilityIdentifier = accessibilityIdentifier;
}
@end

View File

@ -42,6 +42,7 @@
XCTAssert([context isCancelled], @"Context should be cancelled");
}
/* This test is currently unreliable. See https://github.com/facebook/AsyncDisplayKit/issues/459
- (void)testAsyncContextInvalidation
{
NSURL *url = [self randomURL];
@ -56,6 +57,7 @@
[context cancel];
[self waitForExpectationsWithTimeout:30.0 handler:nil];
}
*/
- (void)testContextSessionCanceled
{

View File

@ -0,0 +1,53 @@
//
// ASBasicImageDownloaderTests.m
// AsyncDisplayKit
//
// Created by Victor Mayorov on 10/06/15.
// Copyright (c) 2015 Facebook. All rights reserved.
//
#import <XCTest/XCTest.h>
#import <AsyncDisplayKit/ASBasicImageDownloader.h>
@interface ASBasicImageDownloaderTests : XCTestCase
@end
@implementation ASBasicImageDownloaderTests
- (void)testAsynchronouslyDownloadTheSameURLTwice {
ASBasicImageDownloader *downloader = [ASBasicImageDownloader new];
NSURL *URL = [NSURL URLWithString:@"http://wrongPath/wrongResource.png"];
dispatch_group_t group = dispatch_group_create();
__block BOOL firstDone = NO;
dispatch_group_enter(group);
[downloader downloadImageWithURL:URL
callbackQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
downloadProgressBlock:nil
completion:^(CGImageRef image, NSError *error) {
firstDone = YES;
dispatch_group_leave(group);
}];
__block BOOL secondDone = NO;
dispatch_group_enter(group);
[downloader downloadImageWithURL:URL
callbackQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
downloadProgressBlock:nil
completion:^(CGImageRef image, NSError *error) {
secondDone = YES;
dispatch_group_leave(group);
}];
XCTAssert(0 == dispatch_group_wait(group, dispatch_time(0, 10 * 1000000000)), @"URL loading takes too long");
XCTAssert(firstDone && secondDone, @"Not all handlers has been called");
}
@end

View File

@ -0,0 +1,111 @@
/*
* 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.
*
*/
#import "ASLayoutSpecSnapshotTestsHelper.h"
#import "ASBackgroundLayoutSpec.h"
#import "ASCenterLayoutSpec.h"
#import "ASStackLayoutSpec.h"
static const ASSizeRange kSize = {{100, 120}, {320, 160}};
@interface ASCenterLayoutSpecSnapshotTests : ASLayoutSpecSnapshotTestCase
@end
@implementation ASCenterLayoutSpecSnapshotTests
- (void)setUp
{
[super setUp];
self.recordMode = NO;
}
- (void)testWithOptions
{
[self testWithCenteringOptions:ASCenterLayoutSpecCenteringNone sizingOptions:{}];
[self testWithCenteringOptions:ASCenterLayoutSpecCenteringXY sizingOptions:{}];
[self testWithCenteringOptions:ASCenterLayoutSpecCenteringX sizingOptions:{}];
[self testWithCenteringOptions:ASCenterLayoutSpecCenteringY sizingOptions:{}];
}
- (void)testWithSizingOptions
{
[self testWithCenteringOptions:ASCenterLayoutSpecCenteringNone sizingOptions:ASCenterLayoutSpecSizingOptionDefault];
[self testWithCenteringOptions:ASCenterLayoutSpecCenteringNone sizingOptions:ASCenterLayoutSpecSizingOptionMinimumX];
[self testWithCenteringOptions:ASCenterLayoutSpecCenteringNone sizingOptions:ASCenterLayoutSpecSizingOptionMinimumY];
[self testWithCenteringOptions:ASCenterLayoutSpecCenteringNone sizingOptions:ASCenterLayoutSpecSizingOptionMinimumXY];
}
- (void)testWithCenteringOptions:(ASCenterLayoutSpecCenteringOptions)options
sizingOptions:(ASCenterLayoutSpecSizingOptions)sizingOptions
{
ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor]);
ASStaticSizeDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor greenColor]);
foregroundNode.staticSize = {70, 100};
ASLayoutSpec *layoutSpec =
[ASBackgroundLayoutSpec
newWithChild:
[ASCenterLayoutSpec
newWithCenteringOptions:options
sizingOptions:sizingOptions
child:foregroundNode]
background:backgroundNode];
[self testLayoutSpec:layoutSpec
sizeRange:kSize
subnodes:@[backgroundNode, foregroundNode]
identifier:suffixForCenteringOptions(options, sizingOptions)];
}
static NSString *suffixForCenteringOptions(ASCenterLayoutSpecCenteringOptions centeringOptions,
ASCenterLayoutSpecSizingOptions sizingOptinos)
{
NSMutableString *suffix = [NSMutableString string];
if ((centeringOptions & ASCenterLayoutSpecCenteringX) != 0) {
[suffix appendString:@"CenteringX"];
}
if ((centeringOptions & ASCenterLayoutSpecCenteringY) != 0) {
[suffix appendString:@"CenteringY"];
}
if ((sizingOptinos & ASCenterLayoutSpecSizingOptionMinimumX) != 0) {
[suffix appendString:@"SizingMinimumX"];
}
if ((sizingOptinos & ASCenterLayoutSpecSizingOptionMinimumY) != 0) {
[suffix appendString:@"SizingMinimumY"];
}
return suffix;
}
- (void)testMinimumSizeRangeIsGivenToChildWhenNotCentering
{
ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor]);
ASStaticSizeDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor]);
foregroundNode.staticSize = {10, 10};
foregroundNode.flexGrow = YES;
ASCenterLayoutSpec *layoutSpec =
[ASCenterLayoutSpec
newWithCenteringOptions:ASCenterLayoutSpecCenteringNone
sizingOptions:{}
child:
[ASBackgroundLayoutSpec
newWithChild:[ASStackLayoutSpec newWithStyle:{} children:@[foregroundNode]]
background:backgroundNode]];
[self testLayoutSpec:layoutSpec sizeRange:kSize subnodes:@[backgroundNode, foregroundNode] identifier:nil];
}
@end

View File

@ -0,0 +1,93 @@
//
// ASCollectionViewTests.m
// AsyncDisplayKit
//
// Copyright (c) 2015 Facebook. All rights reserved.
//
#import <XCTest/XCTest.h>
#import <AsyncDisplayKit/ASCollectionView.h>
@interface ASCollectionViewTestDelegate : NSObject <ASCollectionViewDataSource, ASCollectionViewDelegate>
@property (nonatomic, assign) NSInteger numberOfSections;
@property (nonatomic, assign) NSInteger numberOfItemsInSection;
@end
@implementation ASCollectionViewTestDelegate
- (id)initWithNumberOfSections:(NSInteger)numberOfSections numberOfItemsInSection:(NSInteger)numberOfItemsInSection {
if (self = [super init]) {
_numberOfSections = numberOfSections;
_numberOfItemsInSection = numberOfItemsInSection;
}
return self;
}
- (ASCellNode *)collectionView:(ASCollectionView *)collectionView nodeForItemAtIndexPath:(NSIndexPath *)indexPath {
ASTextCellNode *textCellNode = [ASTextCellNode new];
textCellNode.text = indexPath.description;
return textCellNode;
}
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
return self.numberOfSections;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return self.numberOfItemsInSection;
}
@end
@interface ASCollectionViewTestController: UIViewController
@property (nonatomic, strong) ASCollectionViewTestDelegate *asyncDelegate;
@property (nonatomic, strong) ASCollectionView *collectionView;
@end
@implementation ASCollectionViewTestController
- (void)viewDidLoad {
[super viewDidLoad];
self.asyncDelegate = [[ASCollectionViewTestDelegate alloc] initWithNumberOfSections:10 numberOfItemsInSection:10];
self.collectionView = [[ASCollectionView alloc] initWithFrame:self.view.bounds
collectionViewLayout:[UICollectionViewFlowLayout new]];
self.collectionView.asyncDataSource = self.asyncDelegate;
self.collectionView.asyncDelegate = self.asyncDelegate;
[self.view addSubview:self.collectionView];
}
- (void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
self.collectionView.frame = self.view.bounds;
}
@end
@interface ASCollectionViewTests : XCTestCase
@end
@implementation ASCollectionViewTests
- (void)DISABLED_testCollectionViewController {
ASCollectionViewTestController *testController = [[ASCollectionViewTestController alloc] initWithNibName:nil bundle:nil];
UIView *containerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
[containerView addSubview:testController.view];
[testController.collectionView reloadData];
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
}
@end

View File

@ -0,0 +1,86 @@
/*
* 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.
*
*/
#import <XCTest/XCTest.h>
#import "ASDimension.h"
@interface ASDimensionTests : XCTestCase
@end
@implementation ASDimensionTests
- (void)testIntersectingOverlappingSizeRangesReturnsTheirIntersection
{
// range: |---------|
// other: |----------|
// result: |----|
ASSizeRange range = {{0,0}, {10,10}};
ASSizeRange other = {{7,7}, {15,15}};
ASSizeRange result = ASSizeRangeIntersect(range, other);
ASSizeRange expected = {{7,7}, {10,10}};
XCTAssertTrue(ASSizeRangeEqualToSizeRange(result, expected), @"Expected %@ but got %@", NSStringFromASSizeRange(expected), NSStringFromASSizeRange(result));
}
- (void)testIntersectingSizeRangeWithRangeThatContainsItReturnsSameRange
{
// range: |-----|
// other: |---------|
// result: |-----|
ASSizeRange range = {{2,2}, {8,8}};
ASSizeRange other = {{0,0}, {10,10}};
ASSizeRange result = ASSizeRangeIntersect(range, other);
ASSizeRange expected = {{2,2}, {8,8}};
XCTAssertTrue(ASSizeRangeEqualToSizeRange(result, expected), @"Expected %@ but got %@", NSStringFromASSizeRange(expected), NSStringFromASSizeRange(result));
}
- (void)testIntersectingSizeRangeWithRangeContainedWithinItReturnsContainedRange
{
// range: |---------|
// other: |-----|
// result: |-----|
ASSizeRange range = {{0,0}, {10,10}};
ASSizeRange other = {{2,2}, {8,8}};
ASSizeRange result = ASSizeRangeIntersect(range, other);
ASSizeRange expected = {{2,2}, {8,8}};
XCTAssertTrue(ASSizeRangeEqualToSizeRange(result, expected), @"Expected %@ but got %@", NSStringFromASSizeRange(expected), NSStringFromASSizeRange(result));
}
- (void)testIntersectingSizeRangeWithNonOverlappingRangeToRightReturnsSinglePointNearestOtherRange
{
// range: |-----|
// other: |---|
// result: *
ASSizeRange range = {{0,0}, {5,5}};
ASSizeRange other = {{10,10}, {15,15}};
ASSizeRange result = ASSizeRangeIntersect(range, other);
ASSizeRange expected = {{5,5}, {5,5}};
XCTAssertTrue(ASSizeRangeEqualToSizeRange(result, expected), @"Expected %@ but got %@", NSStringFromASSizeRange(expected), NSStringFromASSizeRange(result));
}
- (void)testIntersectingSizeRangeWithNonOverlappingRangeToLeftReturnsSinglePointNearestOtherRange
{
// range: |---|
// other: |-----|
// result: *
ASSizeRange range = {{10,10}, {15,15}};
ASSizeRange other = {{0,0}, {5,5}};
ASSizeRange result = ASSizeRangeIntersect(range, other);
ASSizeRange expected = {{10,10}, {10,10}};
XCTAssertTrue(ASSizeRangeEqualToSizeRange(result, expected), @"Expected %@ but got %@", NSStringFromASSizeRange(expected), NSStringFromASSizeRange(result));
}
@end

View File

@ -71,6 +71,9 @@ for (ASDisplayNode *n in @[ nodes ]) {\
@property (atomic, copy) CGSize(^calculateSizeBlock)(ASTestDisplayNode *node, CGSize size);
@end
@interface ASTestResponderNode : ASTestDisplayNode
@end
@implementation ASTestDisplayNode
- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize
@ -91,9 +94,57 @@ for (ASDisplayNode *n in @[ nodes ]) {\
@interface UIDisplayNodeTestView : UIView
@end
@interface UIResponderNodeTestView : _ASDisplayView
@property(nonatomic) BOOL isFirstResponder;
@end
@implementation UIDisplayNodeTestView
@end
@interface ASTestWindow : UIWindow
@end
@implementation ASTestWindow
- (id)firstResponder {
return self.subviews.firstObject;
}
@end
@implementation ASTestResponderNode
+ (Class)viewClass {
return [UIResponderNodeTestView class];
}
- (BOOL)canBecomeFirstResponder {
return YES;
}
@end
@implementation UIResponderNodeTestView
- (BOOL)becomeFirstResponder {
self.isFirstResponder = YES;
return YES;
}
- (BOOL)canResignFirstResponder {
return YES;
}
- (BOOL)resignFirstResponder {
if (self.isFirstResponder) {
self.isFirstResponder = NO;
return YES;
}
return NO;
}
@end
@interface ASDisplayNodeTests : XCTestCase
@end
@ -102,6 +153,25 @@ for (ASDisplayNode *n in @[ nodes ]) {\
dispatch_queue_t queue;
}
- (void)testOverriddenFirstResponderBehavior {
ASTestDisplayNode *node = [[ASTestResponderNode alloc] init];
XCTAssertTrue([node canBecomeFirstResponder]);
XCTAssertTrue([node becomeFirstResponder]);
}
- (void)testDefaultFirstResponderBehavior {
ASTestDisplayNode *node = [[ASTestDisplayNode alloc] init];
XCTAssertFalse([node canBecomeFirstResponder]);
XCTAssertFalse([node becomeFirstResponder]);
}
- (void)testLayerBackedFirstResponderBehavior {
ASTestDisplayNode *node = [[ASTestResponderNode alloc] init];
node.layerBacked = YES;
XCTAssertTrue([node canBecomeFirstResponder]);
XCTAssertFalse([node becomeFirstResponder]);
}
- (void)setUp
{
[super setUp];
@ -1680,5 +1750,16 @@ static bool stringContainsPointer(NSString *description, const void *p) {
[self checkNameInDescriptionIsLayerBacked:NO];
}
- (void)testBounds
{
ASDisplayNode *node = [[ASDisplayNode alloc] init];
node.bounds = CGRectMake(1, 2, 3, 4);
node.frame = CGRectMake(5, 6, 7, 8);
XCTAssert(node.bounds.origin.x == 1, @"Wrong ASDisplayNode.bounds.origin.x");
XCTAssert(node.bounds.origin.y == 2, @"Wrong ASDisplayNode.bounds.origin.y");
XCTAssert(node.bounds.size.width == 7, @"Wrong ASDisplayNode.bounds.size.width");
XCTAssert(node.bounds.size.height == 8, @"Wrong ASDisplayNode.bounds.size.height");
}
@end

View File

@ -0,0 +1,118 @@
/*
* 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.
*
*/
#import "ASLayoutSpecSnapshotTestsHelper.h"
#import "ASBackgroundLayoutSpec.h"
#import "ASInsetLayoutSpec.h"
typedef NS_OPTIONS(NSUInteger, ASInsetLayoutSpecTestEdge) {
ASInsetLayoutSpecTestEdgeTop = 1 << 0,
ASInsetLayoutSpecTestEdgeLeft = 1 << 1,
ASInsetLayoutSpecTestEdgeBottom = 1 << 2,
ASInsetLayoutSpecTestEdgeRight = 1 << 3,
};
static CGFloat insetForEdge(NSUInteger combination, ASInsetLayoutSpecTestEdge edge, CGFloat insetValue)
{
return combination & edge ? INFINITY : insetValue;
}
static UIEdgeInsets insetsForCombination(NSUInteger combination, CGFloat insetValue)
{
return {
.top = insetForEdge(combination, ASInsetLayoutSpecTestEdgeTop, insetValue),
.left = insetForEdge(combination, ASInsetLayoutSpecTestEdgeLeft, insetValue),
.bottom = insetForEdge(combination, ASInsetLayoutSpecTestEdgeBottom, insetValue),
.right = insetForEdge(combination, ASInsetLayoutSpecTestEdgeRight, insetValue),
};
}
static NSString *nameForInsets(UIEdgeInsets insets)
{
return [NSString stringWithFormat:@"%.f-%.f-%.f-%.f", insets.top, insets.left, insets.bottom, insets.right];
}
@interface ASInsetLayoutSpecSnapshotTests : ASLayoutSpecSnapshotTestCase
@end
@implementation ASInsetLayoutSpecSnapshotTests
- (void)setUp
{
[super setUp];
self.recordMode = NO;
}
- (void)testInsetsWithVariableSize
{
for (NSUInteger combination = 0; combination < 16; combination++) {
UIEdgeInsets insets = insetsForCombination(combination, 10);
ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor grayColor]);
ASStaticSizeDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor greenColor]);
foregroundNode.staticSize = {10, 10};
ASLayoutSpec *layoutSpec =
[ASBackgroundLayoutSpec
newWithChild:[ASInsetLayoutSpec newWithInsets:insets child:foregroundNode]
background:backgroundNode];
static ASSizeRange kVariableSize = {{0, 0}, {300, 300}};
[self testLayoutSpec:layoutSpec
sizeRange:kVariableSize
subnodes:@[backgroundNode, foregroundNode]
identifier:nameForInsets(insets)];
}
}
- (void)testInsetsWithFixedSize
{
for (NSUInteger combination = 0; combination < 16; combination++) {
UIEdgeInsets insets = insetsForCombination(combination, 10);
ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor grayColor]);
ASStaticSizeDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor greenColor]);
foregroundNode.staticSize = {10, 10};
ASLayoutSpec *layoutSpec =
[ASBackgroundLayoutSpec
newWithChild:[ASInsetLayoutSpec newWithInsets:insets child:foregroundNode]
background:backgroundNode];
static ASSizeRange kFixedSize = {{300, 300}, {300, 300}};
[self testLayoutSpec:layoutSpec
sizeRange:kFixedSize
subnodes:@[backgroundNode, foregroundNode]
identifier:nameForInsets(insets)];
}
}
/** Regression test, there was a bug mixing insets with infinite and zero sizes */
- (void)testInsetsWithInfinityAndZeroInsetValue
{
for (NSUInteger combination = 0; combination < 16; combination++) {
UIEdgeInsets insets = insetsForCombination(combination, 0);
ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor grayColor]);
ASStaticSizeDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor greenColor]);
foregroundNode.staticSize = {10, 10};
ASLayoutSpec *layoutSpec =
[ASBackgroundLayoutSpec
newWithChild:[ASInsetLayoutSpec newWithInsets:insets child:foregroundNode]
background:backgroundNode];
static ASSizeRange kFixedSize = {{300, 300}, {300, 300}};
[self testLayoutSpec:layoutSpec
sizeRange:kFixedSize
subnodes:@[backgroundNode, foregroundNode]
identifier:nameForInsets(insets)];
}
}
@end

View File

@ -0,0 +1,47 @@
/*
* Copyright (c) 2015-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.
*
*/
#import "ASSnapshotTestCase.h"
#import "ASDisplayNode+Subclasses.h"
@class ASLayoutSpec;
@interface ASLayoutSpecSnapshotTestCase: ASSnapshotTestCase
/**
Test the layout spec or records a snapshot if recordMode is YES.
@param layoutSpec The layout spec under test or to snapshot
@param sizeRange The size range used to calculate layout of the given layout spec.
@param subnodes An array of ASDisplayNodes used within the layout spec.
@param identifier An optional identifier, used to identify this snapshot test.
@discussion In order to make the layout spec visible, it is embeded to a ASDisplayNode host.
Any subnodes used within the layout spec must be provided.
They will be added to the host in the same order as the array.
*/
- (void)testLayoutSpec:(ASLayoutSpec *)layoutSpec
sizeRange:(ASSizeRange)sizeRange
subnodes:(NSArray *)subnodes
identifier:(NSString *)identifier;
@end
@interface ASStaticSizeDisplayNode : ASDisplayNode
@property (nonatomic) CGSize staticSize;
@end
static inline ASStaticSizeDisplayNode *ASDisplayNodeWithBackgroundColor(UIColor *backgroundColor)
{
ASStaticSizeDisplayNode *node = [[ASStaticSizeDisplayNode alloc] init];
node.layerBacked = YES;
node.backgroundColor = backgroundColor;
node.staticSize = CGSizeZero;
return node;
}

View File

@ -0,0 +1,79 @@
/*
* Copyright (c) 2015-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.
*
*/
#import "ASLayoutSpecSnapshotTestsHelper.h"
#import "ASDisplayNode.h"
#import "ASLayoutSpec.h"
@interface ASTestNode : ASDisplayNode
- (void)setLayoutSpecUnderTest:(ASLayoutSpec *)layoutSpecUnderTest sizeRange:(ASSizeRange)sizeRange;
@end
@implementation ASLayoutSpecSnapshotTestCase
- (void)testLayoutSpec:(ASLayoutSpec *)layoutSpec
sizeRange:(ASSizeRange)sizeRange
subnodes:(NSArray *)subnodes
identifier:(NSString *)identifier
{
ASTestNode *node = [[ASTestNode alloc] init];
for (ASDisplayNode *subnode in subnodes) {
[node addSubnode:subnode];
}
[node setLayoutSpecUnderTest:layoutSpec sizeRange:sizeRange];
ASSnapshotVerifyNode(node, identifier);
}
@end
@implementation ASTestNode
{
ASLayout *_layoutUnderTest;
}
- (instancetype)init
{
if (self = [super init]) {
self.layerBacked = YES;
}
return self;
}
- (void)setLayoutSpecUnderTest:(ASLayoutSpec *)layoutSpecUnderTest sizeRange:(ASSizeRange)sizeRange
{
ASLayout *layout = [layoutSpecUnderTest measureWithSizeRange:sizeRange];
layout.position = CGPointZero;
layout = [ASLayout newWithLayoutableObject:self size:layout.size sublayouts:@[layout]];
_layoutUnderTest = [layout flattenedLayoutUsingPredicateBlock:^BOOL(ASLayout *evaluatedLayout) {
return [self.subnodes containsObject:evaluatedLayout.layoutableObject];
}];
self.frame = CGRectMake(0, 0, _layoutUnderTest.size.width, _layoutUnderTest.size.height);
[self measure:_layoutUnderTest.size];
}
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
{
return _layoutUnderTest;
}
@end
@implementation ASStaticSizeDisplayNode
- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize
{
return _staticSize;
}
@end

View File

@ -29,7 +29,8 @@
- (NSRange)_randomizedRangeForStringBuilder:(ASMutableAttributedStringBuilder *)builder
{
NSUInteger loc = arc4random() % (builder.length - 1);
NSUInteger len = MAX(arc4random() % (builder.length - loc), 1);
NSUInteger len = arc4random() % (builder.length - loc);
len = ((len > 0) ? len : 1);
return NSMakeRange(loc, len);
}

View File

@ -0,0 +1,47 @@
/*
* 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.
*
*/
#import "ASLayoutSpecSnapshotTestsHelper.h"
#import "ASOverlayLayoutSpec.h"
#import "ASCenterLayoutSpec.h"
static const ASSizeRange kSize = {{320, 320}, {320, 320}};
@interface ASOverlayLayoutSpecSnapshotTests : ASLayoutSpecSnapshotTestCase
@end
@implementation ASOverlayLayoutSpecSnapshotTests
- (void)setUp
{
[super setUp];
self.recordMode = NO;
}
- (void)testOverlay
{
ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor blueColor]);
ASStaticSizeDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor blackColor]);
foregroundNode.staticSize = {20, 20};
ASLayoutSpec *layoutSpec =
[ASOverlayLayoutSpec
newWithChild:backgroundNode
overlay:
[ASCenterLayoutSpec
newWithCenteringOptions:ASCenterLayoutSpecCenteringXY
sizingOptions:{}
child:foregroundNode]];
[self testLayoutSpec:layoutSpec sizeRange:kSize subnodes:@[backgroundNode, foregroundNode] identifier: nil];
}
@end

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