mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-16 03:09:56 +00:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
31eef22111
19
AsyncDisplayKit-iOS/AsyncDisplayKit-iOS.h
Normal file
19
AsyncDisplayKit-iOS/AsyncDisplayKit-iOS.h
Normal 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>
|
||||
|
||||
|
||||
26
AsyncDisplayKit-iOS/Info.plist
Normal file
26
AsyncDisplayKit-iOS/Info.plist
Normal 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>
|
||||
@ -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 = [
|
||||
|
||||
@ -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 */;
|
||||
|
||||
@ -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>
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
|
||||
@ -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();
|
||||
|
||||
|
||||
@ -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];
|
||||
}
|
||||
|
||||
|
||||
@ -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 */
|
||||
|
||||
@ -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
|
||||
|
||||
/*
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
/**
|
||||
|
||||
@ -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];
|
||||
|
||||
@ -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];
|
||||
}
|
||||
|
||||
|
||||
@ -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];
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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];
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
@ -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.
|
||||
*
|
||||
|
||||
@ -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];
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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];
|
||||
|
||||
@ -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>
|
||||
|
||||
20
AsyncDisplayKit/Details/ASAbstractLayoutController.h
Normal file
20
AsyncDisplayKit/Details/ASAbstractLayoutController.h
Normal 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
|
||||
95
AsyncDisplayKit/Details/ASAbstractLayoutController.mm
Normal file
95
AsyncDisplayKit/Details/ASAbstractLayoutController.mm
Normal 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
|
||||
@ -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];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
18
AsyncDisplayKit/Details/ASCollectionViewLayoutController.h
Normal file
18
AsyncDisplayKit/Details/ASCollectionViewLayoutController.h
Normal 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
|
||||
163
AsyncDisplayKit/Details/ASCollectionViewLayoutController.mm
Normal file
163
AsyncDisplayKit/Details/ASCollectionViewLayoutController.mm
Normal 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
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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();
|
||||
|
||||
42
AsyncDisplayKit/Details/ASIndexPath.h
Normal file
42
AsyncDisplayKit/Details/ASIndexPath.h
Normal 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
|
||||
73
AsyncDisplayKit/Details/ASIndexPath.m
Normal file
73
AsyncDisplayKit/Details/ASIndexPath.m
Normal 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
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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];
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
36
AsyncDisplayKit/Details/ASScrollDirection.m
Normal file
36
AsyncDisplayKit/Details/ASScrollDirection.m
Normal 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;
|
||||
}
|
||||
@ -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
|
||||
*
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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];
|
||||
|
||||
18
AsyncDisplayKit/Details/CGRect+ASConvenience.h
Normal file
18
AsyncDisplayKit/Details/CGRect+ASConvenience.h
Normal 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
|
||||
31
AsyncDisplayKit/Details/CGRect+ASConvenience.m
Normal file
31
AsyncDisplayKit/Details/CGRect+ASConvenience.m
Normal 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);
|
||||
}
|
||||
@ -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
|
||||
@ -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
|
||||
@ -75,6 +75,9 @@
|
||||
@property (atomic, assign) BOOL shouldGroupAccessibilityChildren;
|
||||
*/
|
||||
|
||||
// Accessibility identification support
|
||||
@property (nonatomic, copy) NSString *accessibilityIdentifier;
|
||||
|
||||
@end
|
||||
|
||||
@interface CALayer (ASDisplayNodeLayer)
|
||||
|
||||
@ -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));
|
||||
});
|
||||
|
||||
@ -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:.
|
||||
|
||||
25
AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.h
Normal file
25
AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.h
Normal 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
|
||||
61
AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.mm
Normal file
61
AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.mm
Normal 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
|
||||
46
AsyncDisplayKit/Layout/ASCenterLayoutSpec.h
Normal file
46
AsyncDisplayKit/Layout/ASCenterLayoutSpec.h
Normal 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
|
||||
78
AsyncDisplayKit/Layout/ASCenterLayoutSpec.mm
Normal file
78
AsyncDisplayKit/Layout/ASCenterLayoutSpec.mm
Normal 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
|
||||
76
AsyncDisplayKit/Layout/ASDimension.h
Normal file
76
AsyncDisplayKit/Layout/ASDimension.h
Normal 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
|
||||
128
AsyncDisplayKit/Layout/ASDimension.mm
Normal file
128
AsyncDisplayKit/Layout/ASDimension.mm
Normal 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)];
|
||||
}
|
||||
38
AsyncDisplayKit/Layout/ASInsetLayoutSpec.h
Normal file
38
AsyncDisplayKit/Layout/ASInsetLayoutSpec.h
Normal 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
|
||||
108
AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm
Normal file
108
AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm
Normal 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
|
||||
65
AsyncDisplayKit/Layout/ASLayout.h
Normal file
65
AsyncDisplayKit/Layout/ASLayout.h
Normal 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
|
||||
94
AsyncDisplayKit/Layout/ASLayout.mm
Normal file
94
AsyncDisplayKit/Layout/ASLayout.mm
Normal 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
|
||||
17
AsyncDisplayKit/Layout/ASLayoutSpec.h
Normal file
17
AsyncDisplayKit/Layout/ASLayoutSpec.h
Normal 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
|
||||
44
AsyncDisplayKit/Layout/ASLayoutSpec.mm
Normal file
44
AsyncDisplayKit/Layout/ASLayoutSpec.mm
Normal 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
|
||||
64
AsyncDisplayKit/Layout/ASLayoutable.h
Normal file
64
AsyncDisplayKit/Layout/ASLayoutable.h
Normal 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
|
||||
20
AsyncDisplayKit/Layout/ASOverlayLayoutSpec.h
Normal file
20
AsyncDisplayKit/Layout/ASOverlayLayoutSpec.h
Normal 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
|
||||
55
AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm
Normal file
55
AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm
Normal 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
|
||||
36
AsyncDisplayKit/Layout/ASRatioLayoutSpec.h
Normal file
36
AsyncDisplayKit/Layout/ASRatioLayoutSpec.h
Normal 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
|
||||
75
AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm
Normal file
75
AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm
Normal 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
|
||||
22
AsyncDisplayKit/Layout/ASStackLayoutChild.h
Normal file
22
AsyncDisplayKit/Layout/ASStackLayoutChild.h
Normal 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,
|
||||
};
|
||||
87
AsyncDisplayKit/Layout/ASStackLayoutSpec.h
Normal file
87
AsyncDisplayKit/Layout/ASStackLayoutSpec.h
Normal 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
|
||||
59
AsyncDisplayKit/Layout/ASStackLayoutSpec.mm
Normal file
59
AsyncDisplayKit/Layout/ASStackLayoutSpec.mm
Normal 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
|
||||
46
AsyncDisplayKit/Layout/ASStaticLayoutSpec.h
Normal file
46
AsyncDisplayKit/Layout/ASStaticLayoutSpec.h
Normal 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
|
||||
95
AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm
Normal file
95
AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm
Normal 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
|
||||
73
AsyncDisplayKit/Layout/ASStaticLayoutSpecDimension.h
Normal file
73
AsyncDisplayKit/Layout/ASStaticLayoutSpecDimension.h
Normal 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
|
||||
83
AsyncDisplayKit/Layout/ASStaticLayoutSpecDimension.mm
Normal file
83
AsyncDisplayKit/Layout/ASStaticLayoutSpecDimension.mm
Normal 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));
|
||||
}
|
||||
@ -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) {
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
28
AsyncDisplayKit/Private/ASInternalHelpers.h
Normal file
28
AsyncDisplayKit/Private/ASInternalHelpers.h
Normal 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
|
||||
63
AsyncDisplayKit/Private/ASInternalHelpers.mm
Normal file
63
AsyncDisplayKit/Private/ASInternalHelpers.mm
Normal 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();
|
||||
}
|
||||
105
AsyncDisplayKit/Private/ASLayoutSpecUtilities.h
Normal file
105
AsyncDisplayKit/Private/ASLayoutSpecUtilities.h
Normal 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 };
|
||||
}
|
||||
|
||||
62
AsyncDisplayKit/Private/ASStackLayoutSpecUtilities.h
Normal file
62
AsyncDisplayKit/Private/ASStackLayoutSpecUtilities.h
Normal 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;
|
||||
}
|
||||
}
|
||||
25
AsyncDisplayKit/Private/ASStackPositionedLayout.h
Normal file
25
AsyncDisplayKit/Private/ASStackPositionedLayout.h
Normal 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);
|
||||
};
|
||||
75
AsyncDisplayKit/Private/ASStackPositionedLayout.mm
Normal file
75
AsyncDisplayKit/Private/ASStackPositionedLayout.mm
Normal 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);
|
||||
}
|
||||
}
|
||||
36
AsyncDisplayKit/Private/ASStackUnpositionedLayout.h
Normal file
36
AsyncDisplayKit/Private/ASStackUnpositionedLayout.h
Normal 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);
|
||||
};
|
||||
341
AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm
Normal file
341
AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm
Normal 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)};
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
{
|
||||
|
||||
53
AsyncDisplayKitTests/ASBasicImageDownloaderTests.m
Normal file
53
AsyncDisplayKitTests/ASBasicImageDownloaderTests.m
Normal 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
|
||||
111
AsyncDisplayKitTests/ASCenterLayoutSpecSnapshotTests.mm
Normal file
111
AsyncDisplayKitTests/ASCenterLayoutSpecSnapshotTests.mm
Normal 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
|
||||
93
AsyncDisplayKitTests/ASCollectionViewTests.m
Normal file
93
AsyncDisplayKitTests/ASCollectionViewTests.m
Normal 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
|
||||
86
AsyncDisplayKitTests/ASDimensionTests.mm
Normal file
86
AsyncDisplayKitTests/ASDimensionTests.mm
Normal 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
|
||||
@ -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
|
||||
|
||||
118
AsyncDisplayKitTests/ASInsetLayoutSpecSnapshotTests.mm
Normal file
118
AsyncDisplayKitTests/ASInsetLayoutSpecSnapshotTests.mm
Normal 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
|
||||
47
AsyncDisplayKitTests/ASLayoutSpecSnapshotTestsHelper.h
Normal file
47
AsyncDisplayKitTests/ASLayoutSpecSnapshotTestsHelper.h
Normal 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;
|
||||
}
|
||||
79
AsyncDisplayKitTests/ASLayoutSpecSnapshotTestsHelper.m
Normal file
79
AsyncDisplayKitTests/ASLayoutSpecSnapshotTestsHelper.m
Normal 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
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
47
AsyncDisplayKitTests/ASOverlayLayoutSpecSnapshotTests.mm
Normal file
47
AsyncDisplayKitTests/ASOverlayLayoutSpecSnapshotTests.mm
Normal 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
Loading…
x
Reference in New Issue
Block a user