mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2026-01-03 19:54:31 +00:00
Merge branch 'master' into compiler-warnings
This commit is contained in:
@@ -139,6 +139,10 @@
|
||||
05A6D05B19D0EB64002DD95E /* ASDealloc2MainObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 05A6D05919D0EB64002DD95E /* ASDealloc2MainObject.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
|
||||
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, ); }; };
|
||||
18C2ED7E1B9B7DE800F627B3 /* ASCollectionNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 18C2ED7C1B9B7DE800F627B3 /* ASCollectionNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
18C2ED7F1B9B7DE800F627B3 /* ASCollectionNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 18C2ED7C1B9B7DE800F627B3 /* ASCollectionNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
18C2ED801B9B7DE800F627B3 /* ASCollectionNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 18C2ED7D1B9B7DE800F627B3 /* ASCollectionNode.m */; };
|
||||
18C2ED831B9B7DE800F627B3 /* ASCollectionNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 18C2ED7D1B9B7DE800F627B3 /* ASCollectionNode.m */; };
|
||||
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, ); }; };
|
||||
@@ -218,18 +222,24 @@
|
||||
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, ); }; };
|
||||
9C204A641B86349B00313849 /* ASBaselinePositionedLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C204A621B86349B00313849 /* ASBaselinePositionedLayout.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
9C204A651B86349B00313849 /* ASBaselinePositionedLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C204A621B86349B00313849 /* ASBaselinePositionedLayout.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
9C204A661B86349B00313849 /* ASBaselinePositionedLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C204A631B86349B00313849 /* ASBaselinePositionedLayout.mm */; };
|
||||
9C204A671B86349B00313849 /* ASBaselinePositionedLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C204A631B86349B00313849 /* ASBaselinePositionedLayout.mm */; };
|
||||
9C204A6A1B87803A00313849 /* ASBaselineLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C204A681B87803A00313849 /* ASBaselineLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
9C204A6B1B87803A00313849 /* ASBaselineLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C204A681B87803A00313849 /* ASBaselineLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
9C3061061B857EC400D0530B /* ASBaselineLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C3061041B857EC400D0530B /* ASBaselineLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
9C3061071B857EC400D0530B /* ASBaselineLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C3061041B857EC400D0530B /* ASBaselineLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
9C3061081B857EC400D0530B /* ASBaselineLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C3061051B857EC400D0530B /* ASBaselineLayoutSpec.mm */; };
|
||||
9C3061091B857EC400D0530B /* ASBaselineLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C3061051B857EC400D0530B /* ASBaselineLayoutSpec.mm */; };
|
||||
9C49C36F1B853957000B0DD5 /* ASStackLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
9C49C3701B853961000B0DD5 /* ASStackLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
9C5FA3511B8F6ADF00A62714 /* ASLayoutOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C5FA34F1B8F6ADF00A62714 /* ASLayoutOptions.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
9C5FA3521B8F6ADF00A62714 /* ASLayoutOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C5FA34F1B8F6ADF00A62714 /* ASLayoutOptions.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
9C5FA3531B8F6ADF00A62714 /* ASLayoutOptions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.mm */; };
|
||||
9C5FA3541B8F6ADF00A62714 /* ASLayoutOptions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.mm */; };
|
||||
9C5FA35F1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C5FA35C1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm */; };
|
||||
9C5FA3601B90C9A500A62714 /* ASLayoutOptionsPrivate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C5FA35C1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm */; };
|
||||
9C65A72A1BA8EA4D0084DA91 /* ASLayoutOptionsPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C65A7291BA8EA4D0084DA91 /* ASLayoutOptionsPrivate.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
9C65A72B1BA8EA4D0084DA91 /* ASLayoutOptionsPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C65A7291BA8EA4D0084DA91 /* ASLayoutOptionsPrivate.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
9C6BB3B21B8CC9C200F13F52 /* ASStaticLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C6BB3B01B8CC9C200F13F52 /* ASStaticLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
9C6BB3B31B8CC9C200F13F52 /* ASStaticLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C6BB3B01B8CC9C200F13F52 /* ASStaticLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
9C8221951BA237B80037F19A /* ASStackBaselinePositionedLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C8221931BA237B80037F19A /* ASStackBaselinePositionedLayout.h */; settings = {ASSET_TAGS = (); }; };
|
||||
9C8221961BA237B80037F19A /* ASStackBaselinePositionedLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C8221931BA237B80037F19A /* ASStackBaselinePositionedLayout.h */; settings = {ASSET_TAGS = (); }; };
|
||||
9C8221971BA237B80037F19A /* ASStackBaselinePositionedLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C8221941BA237B80037F19A /* ASStackBaselinePositionedLayout.mm */; settings = {ASSET_TAGS = (); }; };
|
||||
9C8221981BA237B80037F19A /* ASStackBaselinePositionedLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C8221941BA237B80037F19A /* ASStackBaselinePositionedLayout.mm */; settings = {ASSET_TAGS = (); }; };
|
||||
9CDC18CC1B910E12004965E2 /* ASLayoutablePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9CDC18CB1B910E12004965E2 /* ASLayoutablePrivate.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
9CDC18CD1B910E12004965E2 /* ASLayoutablePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9CDC18CB1B910E12004965E2 /* ASLayoutablePrivate.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
9F06E5CD1B4CAF4200F015D8 /* ASCollectionViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.m */; };
|
||||
AC21EC101B3D0BF600C8B19A /* ASStackLayoutDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutDefines.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
AC3C4A511A1139C100143C57 /* ASCollectionView.h in Headers */ = {isa = PBXBuildFile; fileRef = AC3C4A4F1A1139C100143C57 /* ASCollectionView.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
@@ -464,7 +474,7 @@
|
||||
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; 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; lineEnding = 0; path = ASTextNode.mm; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
|
||||
058D09E0195D050800B7D73C /* ASTextNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASTextNode.mm; sourceTree = "<group>"; };
|
||||
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>"; };
|
||||
@@ -529,6 +539,8 @@
|
||||
05A6D05919D0EB64002DD95E /* ASDealloc2MainObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ASDealloc2MainObject.m; path = ../Details/ASDealloc2MainObject.m; sourceTree = "<group>"; };
|
||||
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>"; };
|
||||
18C2ED7C1B9B7DE800F627B3 /* ASCollectionNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionNode.h; sourceTree = "<group>"; };
|
||||
18C2ED7D1B9B7DE800F627B3 /* ASCollectionNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCollectionNode.m; 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>"; };
|
||||
@@ -567,12 +579,15 @@
|
||||
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>"; };
|
||||
9C204A621B86349B00313849 /* ASBaselinePositionedLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASBaselinePositionedLayout.h; sourceTree = "<group>"; };
|
||||
9C204A631B86349B00313849 /* ASBaselinePositionedLayout.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASBaselinePositionedLayout.mm; sourceTree = "<group>"; };
|
||||
9C204A681B87803A00313849 /* ASBaselineLayoutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASBaselineLayoutable.h; path = AsyncDisplayKit/Layout/ASBaselineLayoutable.h; sourceTree = "<group>"; };
|
||||
9C3061041B857EC400D0530B /* ASBaselineLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASBaselineLayoutSpec.h; path = AsyncDisplayKit/Layout/ASBaselineLayoutSpec.h; sourceTree = "<group>"; };
|
||||
9C3061051B857EC400D0530B /* ASBaselineLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASBaselineLayoutSpec.mm; path = AsyncDisplayKit/Layout/ASBaselineLayoutSpec.mm; sourceTree = "<group>"; };
|
||||
9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutable.h; path = AsyncDisplayKit/Layout/ASStackLayoutable.h; sourceTree = "<group>"; };
|
||||
9C5FA34F1B8F6ADF00A62714 /* ASLayoutOptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASLayoutOptions.h; path = AsyncDisplayKit/Layout/ASLayoutOptions.h; sourceTree = "<group>"; };
|
||||
9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASLayoutOptions.mm; path = AsyncDisplayKit/Layout/ASLayoutOptions.mm; sourceTree = "<group>"; };
|
||||
9C5FA35C1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASLayoutOptionsPrivate.mm; path = AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm; sourceTree = "<group>"; };
|
||||
9C65A7291BA8EA4D0084DA91 /* ASLayoutOptionsPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASLayoutOptionsPrivate.h; sourceTree = "<group>"; };
|
||||
9C6BB3B01B8CC9C200F13F52 /* ASStaticLayoutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStaticLayoutable.h; path = AsyncDisplayKit/Layout/ASStaticLayoutable.h; sourceTree = "<group>"; };
|
||||
9C8221931BA237B80037F19A /* ASStackBaselinePositionedLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASStackBaselinePositionedLayout.h; sourceTree = "<group>"; };
|
||||
9C8221941BA237B80037F19A /* ASStackBaselinePositionedLayout.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASStackBaselinePositionedLayout.mm; sourceTree = "<group>"; };
|
||||
9CDC18CB1B910E12004965E2 /* ASLayoutablePrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASLayoutablePrivate.h; path = AsyncDisplayKit/Layout/ASLayoutablePrivate.h; sourceTree = "<group>"; };
|
||||
9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCollectionViewTests.m; sourceTree = "<group>"; };
|
||||
AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutDefines.h; path = AsyncDisplayKit/Layout/ASStackLayoutDefines.h; sourceTree = "<group>"; };
|
||||
AC3C4A4F1A1139C100143C57 /* ASCollectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionView.h; sourceTree = "<group>"; };
|
||||
@@ -736,6 +751,8 @@
|
||||
children = (
|
||||
055F1A3A19ABD43F004DAFF1 /* ASCellNode.h */,
|
||||
AC6456071B0A335000CF11B8 /* ASCellNode.m */,
|
||||
18C2ED7C1B9B7DE800F627B3 /* ASCollectionNode.h */,
|
||||
18C2ED7D1B9B7DE800F627B3 /* ASCollectionNode.m */,
|
||||
AC3C4A4F1A1139C100143C57 /* ASCollectionView.h */,
|
||||
AC3C4A501A1139C100143C57 /* ASCollectionView.mm */,
|
||||
AC3C4A531A113EEC00143C57 /* ASCollectionViewProtocols.h */,
|
||||
@@ -911,6 +928,9 @@
|
||||
058D0A01195D050800B7D73C /* Private */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9C65A7291BA8EA4D0084DA91 /* ASLayoutOptionsPrivate.h */,
|
||||
9C8221931BA237B80037F19A /* ASStackBaselinePositionedLayout.h */,
|
||||
9C8221941BA237B80037F19A /* ASStackBaselinePositionedLayout.mm */,
|
||||
296A0A2C1A9516B2005ACEAA /* ASBatchFetching.h */,
|
||||
2967F9E11AB0A4CF0072E4AB /* ASBasicImageDownloaderInternal.h */,
|
||||
296A0A2D1A9516B2005ACEAA /* ASBatchFetching.m */,
|
||||
@@ -937,8 +957,6 @@
|
||||
ACF6ED481B17847A00DA7C62 /* ASStackPositionedLayout.mm */,
|
||||
ACF6ED491B17847A00DA7C62 /* ASStackUnpositionedLayout.h */,
|
||||
ACF6ED4A1B17847A00DA7C62 /* ASStackUnpositionedLayout.mm */,
|
||||
9C204A621B86349B00313849 /* ASBaselinePositionedLayout.h */,
|
||||
9C204A631B86349B00313849 /* ASBaselinePositionedLayout.mm */,
|
||||
);
|
||||
path = Private;
|
||||
sourceTree = "<group>";
|
||||
@@ -961,7 +979,6 @@
|
||||
children = (
|
||||
ACF6ED011B17843500DA7C62 /* ASBackgroundLayoutSpec.h */,
|
||||
ACF6ED021B17843500DA7C62 /* ASBackgroundLayoutSpec.mm */,
|
||||
9C204A681B87803A00313849 /* ASBaselineLayoutable.h */,
|
||||
ACF6ED031B17843500DA7C62 /* ASCenterLayoutSpec.h */,
|
||||
ACF6ED041B17843500DA7C62 /* ASCenterLayoutSpec.mm */,
|
||||
ACF6ED071B17843500DA7C62 /* ASDimension.h */,
|
||||
@@ -973,6 +990,7 @@
|
||||
ACF6ED0B1B17843500DA7C62 /* ASLayout.h */,
|
||||
ACF6ED0C1B17843500DA7C62 /* ASLayout.mm */,
|
||||
ACF6ED111B17843500DA7C62 /* ASLayoutable.h */,
|
||||
9CDC18CB1B910E12004965E2 /* ASLayoutablePrivate.h */,
|
||||
ACF6ED0D1B17843500DA7C62 /* ASLayoutSpec.h */,
|
||||
ACF6ED0E1B17843500DA7C62 /* ASLayoutSpec.mm */,
|
||||
ACF6ED121B17843500DA7C62 /* ASOverlayLayoutSpec.h */,
|
||||
@@ -983,10 +1001,12 @@
|
||||
AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutDefines.h */,
|
||||
ACF6ED161B17843500DA7C62 /* ASStackLayoutSpec.h */,
|
||||
ACF6ED171B17843500DA7C62 /* ASStackLayoutSpec.mm */,
|
||||
9C6BB3B01B8CC9C200F13F52 /* ASStaticLayoutable.h */,
|
||||
ACF6ED181B17843500DA7C62 /* ASStaticLayoutSpec.h */,
|
||||
ACF6ED191B17843500DA7C62 /* ASStaticLayoutSpec.mm */,
|
||||
9C3061041B857EC400D0530B /* ASBaselineLayoutSpec.h */,
|
||||
9C3061051B857EC400D0530B /* ASBaselineLayoutSpec.mm */,
|
||||
9C5FA34F1B8F6ADF00A62714 /* ASLayoutOptions.h */,
|
||||
9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.mm */,
|
||||
9C5FA35C1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm */,
|
||||
);
|
||||
name = Layout;
|
||||
path = ..;
|
||||
@@ -1035,23 +1055,26 @@
|
||||
ACF6ED201B17843500DA7C62 /* ASDimension.h in Headers */,
|
||||
ACF6ED2B1B17843500DA7C62 /* ASOverlayLayoutSpec.h in Headers */,
|
||||
ACF6ED1C1B17843500DA7C62 /* ASCenterLayoutSpec.h in Headers */,
|
||||
9C6BB3B21B8CC9C200F13F52 /* ASStaticLayoutable.h in Headers */,
|
||||
ACF6ED2A1B17843500DA7C62 /* ASLayoutable.h in Headers */,
|
||||
ACF6ED311B17843500DA7C62 /* ASStaticLayoutSpec.h in Headers */,
|
||||
ACF6ED241B17843500DA7C62 /* ASLayout.h in Headers */,
|
||||
ACF6ED2F1B17843500DA7C62 /* ASStackLayoutSpec.h in Headers */,
|
||||
9C65A72A1BA8EA4D0084DA91 /* ASLayoutOptionsPrivate.h in Headers */,
|
||||
ACF6ED1A1B17843500DA7C62 /* ASBackgroundLayoutSpec.h in Headers */,
|
||||
291B63FB1AA53A7A000A71B3 /* ASScrollDirection.h in Headers */,
|
||||
464052221A3F83C40061C0BA /* ASFlowLayoutController.h in Headers */,
|
||||
464052241A3F83C40061C0BA /* ASLayoutController.h in Headers */,
|
||||
464052201A3F83C40061C0BA /* ASDataController.h in Headers */,
|
||||
05A6D05A19D0EB64002DD95E /* ASDealloc2MainObject.h in Headers */,
|
||||
9C8221951BA237B80037F19A /* ASStackBaselinePositionedLayout.h in Headers */,
|
||||
0516FA401A1563D200B4EBED /* ASMultiplexImageNode.h in Headers */,
|
||||
9C204A6A1B87803A00313849 /* ASBaselineLayoutable.h in Headers */,
|
||||
058D0A47195D05CB00B7D73C /* ASControlNode.h in Headers */,
|
||||
058D0A48195D05CB00B7D73C /* ASControlNode.m in Headers */,
|
||||
058D0A49195D05CB00B7D73C /* ASControlNode+Subclasses.h in Headers */,
|
||||
058D0A4A195D05CB00B7D73C /* ASDisplayNode.h in Headers */,
|
||||
1950C4491A3BB5C1005C8279 /* ASEqualityHelpers.h in Headers */,
|
||||
9C5FA3511B8F6ADF00A62714 /* ASLayoutOptions.h in Headers */,
|
||||
058D0A4B195D05CB00B7D73C /* ASDisplayNode.mm in Headers */,
|
||||
430E7C8F1B4C23F100697A4C /* ASIndexPath.h in Headers */,
|
||||
058D0A4C195D05CB00B7D73C /* ASDisplayNode+Subclasses.h in Headers */,
|
||||
@@ -1068,7 +1091,6 @@
|
||||
0574D5E219C110940097DC25 /* ASTableViewProtocols.h in Headers */,
|
||||
055F1A3C19ABD43F004DAFF1 /* ASCellNode.h in Headers */,
|
||||
058D0A53195D05DC00B7D73C /* _ASDisplayLayer.h in Headers */,
|
||||
9C204A641B86349B00313849 /* ASBaselinePositionedLayout.h in Headers */,
|
||||
058D0A54195D05DC00B7D73C /* _ASDisplayLayer.mm in Headers */,
|
||||
058D0A55195D05DC00B7D73C /* _ASDisplayView.h in Headers */,
|
||||
058D0A56195D05DC00B7D73C /* _ASDisplayView.mm in Headers */,
|
||||
@@ -1095,12 +1117,13 @@
|
||||
058D0A67195D05DC00B7D73C /* NSMutableAttributedString+TextKitAdditions.m in Headers */,
|
||||
058D0A68195D05EC00B7D73C /* _ASAsyncTransaction.h in Headers */,
|
||||
205F0E0F1B371875007741D0 /* UICollectionViewLayout+ASConvenience.h in Headers */,
|
||||
9C3061061B857EC400D0530B /* ASBaselineLayoutSpec.h in Headers */,
|
||||
058D0A69195D05EC00B7D73C /* _ASAsyncTransaction.m in Headers */,
|
||||
058D0A6A195D05EC00B7D73C /* _ASAsyncTransactionContainer+Private.h in Headers */,
|
||||
058D0A6B195D05EC00B7D73C /* _ASAsyncTransactionContainer.h in Headers */,
|
||||
18C2ED7E1B9B7DE800F627B3 /* ASCollectionNode.h in Headers */,
|
||||
058D0A6C195D05EC00B7D73C /* _ASAsyncTransactionContainer.m in Headers */,
|
||||
6BDC61F61979037800E50D21 /* AsyncDisplayKit.h in Headers */,
|
||||
9CDC18CC1B910E12004965E2 /* ASLayoutablePrivate.h in Headers */,
|
||||
058D0A6D195D05EC00B7D73C /* _ASAsyncTransactionGroup.h in Headers */,
|
||||
058D0A6E195D05EC00B7D73C /* _ASAsyncTransactionGroup.m in Headers */,
|
||||
205F0E1D1B373A2C007741D0 /* ASCollectionViewLayoutController.h in Headers */,
|
||||
@@ -1149,6 +1172,7 @@
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
9C6BB3B31B8CC9C200F13F52 /* ASStaticLayoutable.h in Headers */,
|
||||
B35062321B010EFD0018CF92 /* ASTextNodeShadower.h in Headers */,
|
||||
34EFC7651B701CCC00AD841F /* ASRelativeSize.h in Headers */,
|
||||
B35062431B010EFD0018CF92 /* UIView+ASConvenience.h in Headers */,
|
||||
@@ -1160,17 +1184,16 @@
|
||||
B35061FA1B010EFD0018CF92 /* ASControlNode+Subclasses.h in Headers */,
|
||||
B35062371B010EFD0018CF92 /* ASTextNodeWordKerner.h in Headers */,
|
||||
B35062261B010EFD0018CF92 /* ASRangeController.h in Headers */,
|
||||
9C204A651B86349B00313849 /* ASBaselinePositionedLayout.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 */,
|
||||
18C2ED7F1B9B7DE800F627B3 /* ASCollectionNode.h in Headers */,
|
||||
B35062491B010EFD0018CF92 /* _ASCoreAnimationExtras.h in Headers */,
|
||||
B35061F31B010EFD0018CF92 /* ASCellNode.h in Headers */,
|
||||
34EFC76C1B701CED00AD841F /* ASOverlayLayoutSpec.h in Headers */,
|
||||
B35062201B010EFD0018CF92 /* ASLayoutController.h in Headers */,
|
||||
9C3061071B857EC400D0530B /* ASBaselineLayoutSpec.h in Headers */,
|
||||
B35062571B010F070018CF92 /* ASAssert.h in Headers */,
|
||||
B35062411B010EFD0018CF92 /* _ASAsyncTransactionGroup.h in Headers */,
|
||||
B350623C1B010EFD0018CF92 /* _ASAsyncTransaction.h in Headers */,
|
||||
@@ -1181,6 +1204,7 @@
|
||||
B35062391B010EFD0018CF92 /* ASThread.h in Headers */,
|
||||
B35062131B010EFD0018CF92 /* ASBasicImageDownloader.h in Headers */,
|
||||
34EFC7791B701D3600AD841F /* ASLayoutSpecUtilities.h in Headers */,
|
||||
9C5FA3521B8F6ADF00A62714 /* ASLayoutOptions.h in Headers */,
|
||||
34EFC76A1B701CE600AD841F /* ASLayoutSpec.h in Headers */,
|
||||
B35062221B010EFD0018CF92 /* ASMultidimensionalArrayUtils.h in Headers */,
|
||||
B350625B1B010F070018CF92 /* ASEqualityHelpers.h in Headers */,
|
||||
@@ -1214,7 +1238,6 @@
|
||||
34EFC7631B701CBF00AD841F /* ASCenterLayoutSpec.h in Headers */,
|
||||
B350624D1B010EFD0018CF92 /* _ASScopeTimer.h in Headers */,
|
||||
34EFC7701B701CFA00AD841F /* ASStackLayoutDefines.h in Headers */,
|
||||
9C204A6B1B87803A00313849 /* ASBaselineLayoutable.h in Headers */,
|
||||
509E68651B3AEDC5009B9150 /* CGRect+ASConvenience.h in Headers */,
|
||||
B350624F1B010EFD0018CF92 /* ASDisplayNode+DebugTiming.h in Headers */,
|
||||
B35062211B010EFD0018CF92 /* ASLayoutRangeType.h in Headers */,
|
||||
@@ -1226,6 +1249,7 @@
|
||||
34EFC7611B701C9C00AD841F /* ASBackgroundLayoutSpec.h in Headers */,
|
||||
B35062301B010EFD0018CF92 /* ASTextNodeRenderer.h in Headers */,
|
||||
509E68611B3AEDA0009B9150 /* ASAbstractLayoutController.h in Headers */,
|
||||
9C65A72B1BA8EA4D0084DA91 /* ASLayoutOptionsPrivate.h in Headers */,
|
||||
B350620D1B010EFD0018CF92 /* ASTextNode.h in Headers */,
|
||||
34EFC76E1B701CF400AD841F /* ASRatioLayoutSpec.h in Headers */,
|
||||
B35062151B010EFD0018CF92 /* ASBatchContext.h in Headers */,
|
||||
@@ -1234,9 +1258,11 @@
|
||||
B35062001B010EFD0018CF92 /* ASEditableTextNode.h in Headers */,
|
||||
B350623A1B010EFD0018CF92 /* NSMutableAttributedString+TextKitAdditions.h in Headers */,
|
||||
B350623E1B010EFD0018CF92 /* _ASAsyncTransactionContainer+Private.h in Headers */,
|
||||
9C8221961BA237B80037F19A /* ASStackBaselinePositionedLayout.h in Headers */,
|
||||
B35062591B010F070018CF92 /* ASBaseDefines.h in Headers */,
|
||||
B35062191B010EFD0018CF92 /* ASDealloc2MainObject.h in Headers */,
|
||||
B350620F1B010EFD0018CF92 /* _ASDisplayLayer.h in Headers */,
|
||||
9CDC18CD1B910E12004965E2 /* ASLayoutablePrivate.h in Headers */,
|
||||
B35062531B010EFD0018CF92 /* ASImageNode+CGExtras.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -1437,10 +1463,11 @@
|
||||
058D0A26195D050800B7D73C /* _ASCoreAnimationExtras.mm in Sources */,
|
||||
058D0A23195D050800B7D73C /* _ASAsyncTransactionContainer.m in Sources */,
|
||||
058D0A1E195D050800B7D73C /* ASTextNodeShadower.m in Sources */,
|
||||
9C204A661B86349B00313849 /* ASBaselinePositionedLayout.mm in Sources */,
|
||||
ACF6ED2E1B17843500DA7C62 /* ASRatioLayoutSpec.mm in Sources */,
|
||||
058D0A18195D050800B7D73C /* _ASDisplayLayer.mm in Sources */,
|
||||
ACF6ED321B17843500DA7C62 /* ASStaticLayoutSpec.mm in Sources */,
|
||||
9C5FA3531B8F6ADF00A62714 /* ASLayoutOptions.mm in Sources */,
|
||||
9C5FA35F1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm in Sources */,
|
||||
ACF6ED2C1B17843500DA7C62 /* ASOverlayLayoutSpec.mm in Sources */,
|
||||
058D0A2C195D050800B7D73C /* ASSentinel.m in Sources */,
|
||||
205F0E221B376416007741D0 /* CGRect+ASConvenience.m in Sources */,
|
||||
@@ -1455,6 +1482,7 @@
|
||||
205F0E121B371BD7007741D0 /* ASScrollDirection.m in Sources */,
|
||||
AC47D9461B3BB41900AAEE9D /* ASRelativeSize.mm in Sources */,
|
||||
ACF6ED271B17843500DA7C62 /* ASLayoutSpec.mm in Sources */,
|
||||
9C8221971BA237B80037F19A /* ASStackBaselinePositionedLayout.mm in Sources */,
|
||||
ACF6ED211B17843500DA7C62 /* ASDimension.mm in Sources */,
|
||||
464052261A3F83C40061C0BA /* ASMultidimensionalArrayUtils.mm in Sources */,
|
||||
055B9FA91A1C154B00035D6D /* ASNetworkImageNode.mm in Sources */,
|
||||
@@ -1470,13 +1498,13 @@
|
||||
464052231A3F83C40061C0BA /* ASFlowLayoutController.mm in Sources */,
|
||||
058D0A28195D050800B7D73C /* ASDisplayNode+AsyncDisplay.mm in Sources */,
|
||||
0587F9BE1A7309ED00AFF0BA /* ASEditableTextNode.mm in Sources */,
|
||||
18C2ED801B9B7DE800F627B3 /* ASCollectionNode.m 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 */,
|
||||
9C3061081B857EC400D0530B /* ASBaselineLayoutSpec.mm in Sources */,
|
||||
058D0A2B195D050800B7D73C /* ASImageNode+CGExtras.m in Sources */,
|
||||
058D0A24195D050800B7D73C /* _ASAsyncTransactionGroup.m in Sources */,
|
||||
058D0A1C195D050800B7D73C /* ASTextNodeCoreTextAdditions.m in Sources */,
|
||||
@@ -1538,10 +1566,8 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
9C3061091B857EC400D0530B /* ASBaselineLayoutSpec.mm in Sources */,
|
||||
34EFC7641B701CC600AD841F /* ASCenterLayoutSpec.mm in Sources */,
|
||||
B350623B1B010EFD0018CF92 /* NSMutableAttributedString+TextKitAdditions.m in Sources */,
|
||||
9C204A671B86349B00313849 /* ASBaselinePositionedLayout.mm in Sources */,
|
||||
B35062401B010EFD0018CF92 /* _ASAsyncTransactionContainer.m in Sources */,
|
||||
B35062311B010EFD0018CF92 /* ASTextNodeRenderer.mm in Sources */,
|
||||
B35062051B010EFD0018CF92 /* ASMultiplexImageNode.mm in Sources */,
|
||||
@@ -1552,6 +1578,7 @@
|
||||
B35062471B010EFD0018CF92 /* ASBatchFetching.m in Sources */,
|
||||
B350624E1B010EFD0018CF92 /* ASDisplayNode+AsyncDisplay.mm in Sources */,
|
||||
34EFC76F1B701CF700AD841F /* ASRatioLayoutSpec.mm in Sources */,
|
||||
9C5FA3601B90C9A500A62714 /* ASLayoutOptionsPrivate.mm in Sources */,
|
||||
34EFC7681B701CDE00AD841F /* ASLayout.mm in Sources */,
|
||||
B35061F61B010EFD0018CF92 /* ASCollectionView.mm in Sources */,
|
||||
34EFC75C1B701BD200AD841F /* ASDimension.mm in Sources */,
|
||||
@@ -1567,8 +1594,10 @@
|
||||
B350621C1B010EFD0018CF92 /* ASFlowLayoutController.mm in Sources */,
|
||||
B35062231B010EFD0018CF92 /* ASMultidimensionalArrayUtils.mm in Sources */,
|
||||
509E68641B3AEDB7009B9150 /* ASCollectionViewLayoutController.mm in Sources */,
|
||||
9C5FA3541B8F6ADF00A62714 /* ASLayoutOptions.mm in Sources */,
|
||||
B350621E1B010EFD0018CF92 /* ASHighlightOverlayLayer.mm in Sources */,
|
||||
B35062271B010EFD0018CF92 /* ASRangeController.mm in Sources */,
|
||||
18C2ED831B9B7DE800F627B3 /* ASCollectionNode.m in Sources */,
|
||||
B35061F91B010EFD0018CF92 /* ASControlNode.m in Sources */,
|
||||
AC47D9421B3B891B00AAEE9D /* ASCellNode.m in Sources */,
|
||||
509E68661B3AEDD7009B9150 /* CGRect+ASConvenience.m in Sources */,
|
||||
@@ -1583,6 +1612,7 @@
|
||||
B35062031B010EFD0018CF92 /* ASImageNode.mm in Sources */,
|
||||
B35062091B010EFD0018CF92 /* ASScrollNode.m in Sources */,
|
||||
B35062251B010EFD0018CF92 /* ASMutableAttributedStringBuilder.m in Sources */,
|
||||
9C8221981BA237B80037F19A /* ASStackBaselinePositionedLayout.mm in Sources */,
|
||||
430E7C921B4C23F100697A4C /* ASIndexPath.m in Sources */,
|
||||
34EFC7741B701D0A00AD841F /* ASStaticLayoutSpec.mm in Sources */,
|
||||
B35062381B010EFD0018CF92 /* ASTextNodeWordKerner.m in Sources */,
|
||||
|
||||
@@ -31,13 +31,13 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)viewBlock
|
||||
- (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)viewBlock didLoadBlock:(ASDisplayNodeDidLoadBlock)didLoadBlock
|
||||
{
|
||||
ASDisplayNodeAssertNotSupported();
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock
|
||||
- (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock didLoadBlock:(ASDisplayNodeDidLoadBlock)didLoadBlock
|
||||
{
|
||||
ASDisplayNodeAssertNotSupported();
|
||||
return nil;
|
||||
|
||||
21
AsyncDisplayKit/ASCollectionNode.h
Normal file
21
AsyncDisplayKit/ASCollectionNode.h
Normal file
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// ASCollectionNode.h
|
||||
// AsyncDisplayKit
|
||||
//
|
||||
// Created by Scott Goodson on 9/5/15.
|
||||
// Copyright (c) 2015 Facebook. All rights reserved.
|
||||
//
|
||||
|
||||
#import <AsyncDisplayKit/AsyncDisplayKit.h>
|
||||
|
||||
/**
|
||||
* ASCollectionNode is a node based class that wraps an ASCollectionView. It can be used
|
||||
* as a subnode of another node, and provide room for many (great) features and improvements later on.
|
||||
*/
|
||||
@interface ASCollectionNode : ASDisplayNode
|
||||
|
||||
- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
@property (nonatomic, readonly) ASCollectionView *view;
|
||||
|
||||
@end
|
||||
33
AsyncDisplayKit/ASCollectionNode.m
Normal file
33
AsyncDisplayKit/ASCollectionNode.m
Normal file
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// ASCollectionNode.m
|
||||
// AsyncDisplayKit
|
||||
//
|
||||
// Created by Scott Goodson on 9/5/15.
|
||||
// Copyright (c) 2015 Facebook. All rights reserved.
|
||||
//
|
||||
|
||||
#import "ASCollectionNode.h"
|
||||
|
||||
@implementation ASCollectionNode
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
ASDISPLAYNODE_NOT_DESIGNATED_INITIALIZER();
|
||||
self = [self initWithCollectionViewLayout:nil]; // Will throw an exception for lacking a UICV Layout.
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout
|
||||
{
|
||||
if (self = [super initWithViewBlock:^UIView *{ return [[ASCollectionView alloc] initWithCollectionViewLayout:layout]; }]) {
|
||||
return self;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (ASCollectionView *)view
|
||||
{
|
||||
return (ASCollectionView *)[super view];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -27,6 +27,8 @@
|
||||
*/
|
||||
@interface ASCollectionView : UICollectionView
|
||||
|
||||
- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout;
|
||||
|
||||
@property (nonatomic, weak) id<ASCollectionViewDataSource> asyncDataSource;
|
||||
@property (nonatomic, weak) id<ASCollectionViewDelegate> asyncDelegate; // must not be nil
|
||||
|
||||
|
||||
@@ -126,6 +126,7 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
ASBatchContext *_batchContext;
|
||||
|
||||
CGSize _maxSizeForNodesConstrainedSize;
|
||||
BOOL _ignoreMaxSizeChange;
|
||||
}
|
||||
|
||||
@property (atomic, assign) BOOL asyncDataSourceLocked;
|
||||
@@ -137,6 +138,11 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
#pragma mark -
|
||||
#pragma mark Lifecycle.
|
||||
|
||||
- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout
|
||||
{
|
||||
return [self initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:NO];
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout
|
||||
{
|
||||
return [self initWithFrame:frame collectionViewLayout:layout asyncDataFetching:NO];
|
||||
@@ -174,6 +180,11 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
_collectionViewLayoutImplementsInsetSection = [layout respondsToSelector:@selector(sectionInset)];
|
||||
|
||||
_maxSizeForNodesConstrainedSize = self.bounds.size;
|
||||
// If the initial size is 0, expect a size change very soon which is part of the initial configuration
|
||||
// and should not trigger a relayout.
|
||||
_ignoreMaxSizeChange = CGSizeEqualToSize(_maxSizeForNodesConstrainedSize, CGSizeZero);
|
||||
|
||||
self.backgroundColor = [UIColor whiteColor];
|
||||
|
||||
[self registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"_ASCollectionViewCell"];
|
||||
|
||||
@@ -474,14 +485,22 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
|
||||
if (! CGSizeEqualToSize(_maxSizeForNodesConstrainedSize, self.bounds.size)) {
|
||||
_maxSizeForNodesConstrainedSize = self.bounds.size;
|
||||
[self performBatchAnimated:NO updates:^{
|
||||
[_dataController relayoutAllRows];
|
||||
} completion:nil];
|
||||
|
||||
// First size change occurs during initial configuration. An expensive relayout pass is unnecessary at that time
|
||||
// and should be avoided, assuming that the initial data loading automatically runs shortly afterward.
|
||||
if (_ignoreMaxSizeChange) {
|
||||
_ignoreMaxSizeChange = NO;
|
||||
} else {
|
||||
[self performBatchAnimated:NO updates:^{
|
||||
[_dataController relayoutAllRows];
|
||||
} completion:nil];
|
||||
}
|
||||
}
|
||||
|
||||
// To ensure _maxSizeForNodesConstrainedSize is up-to-date for every usage, this call to super must be done last
|
||||
[super layoutSubviews];
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -13,17 +13,25 @@
|
||||
#import <AsyncDisplayKit/ASDealloc2MainObject.h>
|
||||
#import <AsyncDisplayKit/ASDimension.h>
|
||||
|
||||
#import <AsyncDisplayKit/ASStackLayoutable.h>
|
||||
#import <AsyncDisplayKit/ASLayoutable.h>
|
||||
|
||||
@class ASDisplayNode;
|
||||
|
||||
/**
|
||||
* UIView creation block. Used to create the backing view of a new display node.
|
||||
*/
|
||||
typedef UIView *(^ASDisplayNodeViewBlock)();
|
||||
|
||||
/**
|
||||
* CALayer creation block. Used to create the backing layer of a new display node.
|
||||
*/
|
||||
typedef CALayer *(^ASDisplayNodeLayerBlock)();
|
||||
|
||||
/**
|
||||
* ASDisplayNode loaded callback block. This block is called BEFORE the -didLoad method and is always called on the main thread.
|
||||
*/
|
||||
typedef void (^ASDisplayNodeDidLoadBlock)(ASDisplayNode *node);
|
||||
|
||||
/**
|
||||
* An `ASDisplayNode` is an abstraction over `UIView` and `CALayer` that allows you to perform calculations about a view
|
||||
* hierarchy off the main thread, and could do rendering off the main thread as well.
|
||||
@@ -40,7 +48,7 @@ typedef CALayer *(^ASDisplayNodeLayerBlock)();
|
||||
*
|
||||
*/
|
||||
|
||||
@interface ASDisplayNode : ASDealloc2MainObject <ASStackLayoutable>
|
||||
@interface ASDisplayNode : ASDealloc2MainObject <ASLayoutable>
|
||||
|
||||
|
||||
/** @name Initializing a node object */
|
||||
@@ -65,15 +73,37 @@ typedef CALayer *(^ASDisplayNodeLayerBlock)();
|
||||
*/
|
||||
- (id)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock;
|
||||
|
||||
/**
|
||||
* @abstract Alternative initializer with a block to create the backing view.
|
||||
*
|
||||
* @param viewBlock The block that will be used to create the backing view.
|
||||
* @param didLoadBlock The block that will be called after the view created by the viewBlock is loaded
|
||||
*
|
||||
* @return An ASDisplayNode instance that loads its view with the given block that is guaranteed to run on the main
|
||||
* queue. The view will render synchronously and -layout and touch handling methods on the node will not be called.
|
||||
*/
|
||||
- (id)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock didLoadBlock:(ASDisplayNodeDidLoadBlock)didLoadBlock;
|
||||
|
||||
/**
|
||||
* @abstract Alternative initializer with a block to create the backing layer.
|
||||
*
|
||||
* @param viewBlock The block that will be used to create the backing layer.
|
||||
* @param layerBlock The block that will be used to create the backing layer.
|
||||
*
|
||||
* @return An ASDisplayNode instance that loads its layer with the given block that is guaranteed to run on the main
|
||||
* queue. The layer will render synchronously and -layout and touch handling methods on the node will not be called.
|
||||
*/
|
||||
- (id)initWithLayerBlock:(ASDisplayNodeLayerBlock)viewBlock;
|
||||
- (id)initWithLayerBlock:(ASDisplayNodeLayerBlock)layerBlock;
|
||||
|
||||
/**
|
||||
* @abstract Alternative initializer with a block to create the backing layer.
|
||||
*
|
||||
* @param layerBlock The block that will be used to create the backing layer.
|
||||
* @param didLoadBlock The block that will be called after the layer created by the layerBlock is loaded
|
||||
*
|
||||
* @return An ASDisplayNode instance that loads its layer with the given block that is guaranteed to run on the main
|
||||
* queue. The layer will render synchronously and -layout and touch handling methods on the node will not be called.
|
||||
*/
|
||||
- (id)initWithLayerBlock:(ASDisplayNodeLayerBlock)layerBlock didLoadBlock:(ASDisplayNodeDidLoadBlock)didLoadBlock;
|
||||
|
||||
|
||||
/** @name Properties */
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#import "ASDisplayNode.h"
|
||||
#import "ASDisplayNode+Subclasses.h"
|
||||
#import "ASDisplayNodeInternal.h"
|
||||
#import "ASLayoutOptionsPrivate.h"
|
||||
|
||||
#import <objc/runtime.h>
|
||||
|
||||
@@ -30,6 +31,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
- (void)_staticInitialize;
|
||||
|
||||
@end
|
||||
|
||||
// Conditionally time these scopes to our debug ivars (only exist in debug/profile builds)
|
||||
@@ -41,13 +44,10 @@
|
||||
|
||||
@implementation ASDisplayNode
|
||||
|
||||
@synthesize spacingBefore = _spacingBefore;
|
||||
@synthesize spacingAfter = _spacingAfter;
|
||||
@synthesize flexGrow = _flexGrow;
|
||||
@synthesize flexShrink = _flexShrink;
|
||||
@synthesize flexBasis = _flexBasis;
|
||||
@synthesize alignSelf = _alignSelf;
|
||||
// these dynamic properties all defined in ASLayoutOptionsPrivate.m
|
||||
@dynamic spacingAfter, spacingBefore, flexGrow, flexShrink, flexBasis, alignSelf, ascender, descender, sizeRange, layoutPosition, layoutOptions;
|
||||
@synthesize preferredFrameSize = _preferredFrameSize;
|
||||
@synthesize isFinalLayoutable = _isFinalLayoutable;
|
||||
|
||||
BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector)
|
||||
{
|
||||
@@ -85,25 +85,104 @@ void ASDisplayNodeRespectThreadAffinityOfNode(ASDisplayNode *node, void (^block)
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)initialize
|
||||
/**
|
||||
* Returns ASDisplayNodeFlags for the givern class/instance. instance MAY BE NIL.
|
||||
*
|
||||
* @param c the class, required
|
||||
* @param instance the instance, which may be nil. (If so, the class is inspected instead)
|
||||
* @remarks The instance value is used only if we suspect the class may be dynamic (because it overloads
|
||||
* +respondsToSelector: or -respondsToSelector.) In that case we use our "slow path", calling this
|
||||
* method on each -init and passing the instance value. While this may seem like an unlikely scenario,
|
||||
* it turns our our own internal tests use a dynamic class, so it's worth capturing this edge case.
|
||||
*
|
||||
* @return ASDisplayNode flags.
|
||||
*/
|
||||
static struct ASDisplayNodeFlags GetASDisplayNodeFlags(Class c, ASDisplayNode *instance)
|
||||
{
|
||||
if (self == [ASDisplayNode class]) {
|
||||
return;
|
||||
ASDisplayNodeCAssertNotNil(c, @"class is required");
|
||||
|
||||
struct ASDisplayNodeFlags flags = {0};
|
||||
|
||||
flags.isInHierarchy = NO;
|
||||
flags.displaysAsynchronously = YES;
|
||||
flags.implementsDrawRect = ([c respondsToSelector:@selector(drawRect:withParameters:isCancelled:isRasterizing:)] ? 1 : 0);
|
||||
flags.implementsImageDisplay = ([c respondsToSelector:@selector(displayWithParameters:isCancelled:)] ? 1 : 0);
|
||||
if (instance) {
|
||||
flags.implementsDrawParameters = ([instance respondsToSelector:@selector(drawParametersForAsyncLayer:)] ? 1 : 0);
|
||||
} else {
|
||||
flags.implementsDrawParameters = ([c instancesRespondToSelector:@selector(drawParametersForAsyncLayer:)] ? 1 : 0);
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns ASDisplayNodeMethodOverrides for the given class
|
||||
*
|
||||
* @param c the class, requireed.
|
||||
*
|
||||
* @return ASDisplayNodeMethodOverrides.
|
||||
*/
|
||||
static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
||||
{
|
||||
ASDisplayNodeCAssertNotNil(c, @"class is required");
|
||||
|
||||
ASDisplayNodeMethodOverrides overrides = ASDisplayNodeMethodOverrideNone;
|
||||
if (ASDisplayNodeSubclassOverridesSelector(c, @selector(touchesBegan:withEvent:))) {
|
||||
overrides |= ASDisplayNodeMethodOverrideTouchesBegan;
|
||||
}
|
||||
if (ASDisplayNodeSubclassOverridesSelector(c, @selector(touchesMoved:withEvent:))) {
|
||||
overrides |= ASDisplayNodeMethodOverrideTouchesMoved;
|
||||
}
|
||||
if (ASDisplayNodeSubclassOverridesSelector(c, @selector(touchesCancelled:withEvent:))) {
|
||||
overrides |= ASDisplayNodeMethodOverrideTouchesCancelled;
|
||||
}
|
||||
if (ASDisplayNodeSubclassOverridesSelector(c, @selector(touchesEnded:withEvent:))) {
|
||||
overrides |= ASDisplayNodeMethodOverrideTouchesEnded;
|
||||
}
|
||||
if (ASDisplayNodeSubclassOverridesSelector(c, @selector(layoutSpecThatFits:))) {
|
||||
overrides |= ASDisplayNodeMethodOverrideLayoutSpecThatFits;
|
||||
}
|
||||
|
||||
// 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(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));
|
||||
return overrides;
|
||||
}
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
if (self != [ASDisplayNode class]) {
|
||||
|
||||
// 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(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));
|
||||
}
|
||||
|
||||
// Below we are pre-calculating values per-class and dynamically adding a method (_staticInitialize) to populate these values
|
||||
// when each instance is constructed. These values don't change for each class, so there is significant performance benefit
|
||||
// in doing it here. +initialize is guaranteed to be called before any instance method so it is safe to add this method here.
|
||||
// Note that we take care to detect if the class overrides +respondsToSelector: or -respondsToSelector and take the slow path
|
||||
// (recalculating for each instance) to make sure we are always correct.
|
||||
|
||||
BOOL classOverridesRespondsToSelector = ASSubclassOverridesClassSelector([NSObject class], self, @selector(respondsToSelector:));
|
||||
BOOL instancesOverrideRespondsToSelector = ASSubclassOverridesSelector([NSObject class], self, @selector(respondsToSelector:));
|
||||
struct ASDisplayNodeFlags flags = GetASDisplayNodeFlags(self, nil);
|
||||
ASDisplayNodeMethodOverrides methodOverrides = GetASDisplayNodeMethodOverrides(self);
|
||||
|
||||
IMP staticInitialize = imp_implementationWithBlock(^(ASDisplayNode *node) {
|
||||
node->_flags = (classOverridesRespondsToSelector || instancesOverrideRespondsToSelector) ? GetASDisplayNodeFlags(node.class, node) : flags;
|
||||
node->_methodOverrides = (classOverridesRespondsToSelector) ? GetASDisplayNodeMethodOverrides(node.class) : methodOverrides;
|
||||
});
|
||||
|
||||
class_replaceMethod(self, @selector(_staticInitialize), staticInitialize, "v:@");
|
||||
}
|
||||
|
||||
+ (BOOL)layerBackedNodesEnabled
|
||||
@@ -123,39 +202,16 @@ void ASDisplayNodeRespectThreadAffinityOfNode(ASDisplayNode *node, void (^block)
|
||||
|
||||
#pragma mark - Lifecycle
|
||||
|
||||
- (void)_staticInitialize
|
||||
{
|
||||
ASDisplayNodeAssert(NO, @"_staticInitialize must be overridden");
|
||||
}
|
||||
|
||||
- (void)_initializeInstance
|
||||
{
|
||||
[self _staticInitialize];
|
||||
_contentsScaleForDisplay = ASScreenScale();
|
||||
|
||||
_displaySentinel = [[ASSentinel alloc] init];
|
||||
|
||||
_flags.isInHierarchy = NO;
|
||||
_flags.displaysAsynchronously = YES;
|
||||
|
||||
// As an optimization, it may be worth a caching system that performs these checks once per class in +initialize (see above).
|
||||
_flags.implementsDrawRect = ([[self class] respondsToSelector:@selector(drawRect:withParameters:isCancelled:isRasterizing:)] ? 1 : 0);
|
||||
_flags.implementsImageDisplay = ([[self class] respondsToSelector:@selector(displayWithParameters:isCancelled:)] ? 1 : 0);
|
||||
_flags.implementsDrawParameters = ([self respondsToSelector:@selector(drawParametersForAsyncLayer:)] ? 1 : 0);
|
||||
|
||||
ASDisplayNodeMethodOverrides overrides = ASDisplayNodeMethodOverrideNone;
|
||||
if (ASDisplayNodeSubclassOverridesSelector([self class], @selector(touchesBegan:withEvent:))) {
|
||||
overrides |= ASDisplayNodeMethodOverrideTouchesBegan;
|
||||
}
|
||||
if (ASDisplayNodeSubclassOverridesSelector([self class], @selector(touchesMoved:withEvent:))) {
|
||||
overrides |= ASDisplayNodeMethodOverrideTouchesMoved;
|
||||
}
|
||||
if (ASDisplayNodeSubclassOverridesSelector([self class], @selector(touchesCancelled:withEvent:))) {
|
||||
overrides |= ASDisplayNodeMethodOverrideTouchesCancelled;
|
||||
}
|
||||
if (ASDisplayNodeSubclassOverridesSelector([self class], @selector(touchesEnded:withEvent:))) {
|
||||
overrides |= ASDisplayNodeMethodOverrideTouchesEnded;
|
||||
}
|
||||
if (ASDisplayNodeSubclassOverridesSelector([self class], @selector(layoutSpecThatFits:))) {
|
||||
overrides |= ASDisplayNodeMethodOverrideLayoutSpecThatFits;
|
||||
}
|
||||
_methodOverrides = overrides;
|
||||
|
||||
_flexBasis = ASRelativeDimensionUnconstrained;
|
||||
_preferredFrameSize = CGSizeZero;
|
||||
}
|
||||
|
||||
@@ -200,33 +256,47 @@ void ASDisplayNodeRespectThreadAffinityOfNode(ASDisplayNode *node, void (^block)
|
||||
|
||||
- (id)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock
|
||||
{
|
||||
if (!(self = [super init]))
|
||||
return nil;
|
||||
|
||||
ASDisplayNodeAssertNotNil(viewBlock, @"should initialize with a valid block that returns a UIView");
|
||||
|
||||
[self _initializeInstance];
|
||||
_viewBlock = viewBlock;
|
||||
_flags.synchronous = YES;
|
||||
|
||||
return self;
|
||||
return [self initWithViewBlock:viewBlock didLoadBlock:nil];
|
||||
}
|
||||
|
||||
- (id)initWithLayerBlock:(ASDisplayNodeLayerBlock)layerBlock
|
||||
- (id)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock didLoadBlock:(ASDisplayNodeDidLoadBlock)didLoadBlock
|
||||
{
|
||||
if (!(self = [super init]))
|
||||
return nil;
|
||||
|
||||
ASDisplayNodeAssertNotNil(layerBlock, @"should initialize with a valid block that returns a CALayer");
|
||||
|
||||
|
||||
ASDisplayNodeAssertNotNil(viewBlock, @"should initialize with a valid block that returns a UIView");
|
||||
|
||||
[self _initializeInstance];
|
||||
_layerBlock = layerBlock;
|
||||
_viewBlock = viewBlock;
|
||||
_nodeLoadedBlock = didLoadBlock;
|
||||
_flags.synchronous = YES;
|
||||
_flags.layerBacked = YES;
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- (id)initWithLayerBlock:(ASDisplayNodeLayerBlock)layerBlock
|
||||
{
|
||||
return [self initWithLayerBlock:layerBlock didLoadBlock:nil];
|
||||
}
|
||||
|
||||
- (id)initWithLayerBlock:(ASDisplayNodeLayerBlock)layerBlock didLoadBlock:(ASDisplayNodeDidLoadBlock)didLoadBlock
|
||||
{
|
||||
if (!(self = [super init]))
|
||||
return nil;
|
||||
|
||||
ASDisplayNodeAssertNotNil(layerBlock, @"should initialize with a valid block that returns a CALayer");
|
||||
|
||||
[self _initializeInstance];
|
||||
_layerBlock = layerBlock;
|
||||
_nodeLoadedBlock = didLoadBlock;
|
||||
_flags.synchronous = YES;
|
||||
_flags.layerBacked = YES;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
@@ -368,7 +438,7 @@ void ASDisplayNodeRespectThreadAffinityOfNode(ASDisplayNode *node, void (^block)
|
||||
}
|
||||
{
|
||||
TIME_SCOPED(_debugTimeForDidLoad);
|
||||
[self didLoad];
|
||||
[self __didLoad];
|
||||
}
|
||||
|
||||
if (self.placeholderEnabled) {
|
||||
@@ -600,9 +670,27 @@ void ASDisplayNodeRespectThreadAffinityOfNode(ASDisplayNode *node, void (^block)
|
||||
} else {
|
||||
// This is the root node. Trigger a full measurement pass on *current* thread. Old constrained size is re-used.
|
||||
[self measureWithSizeRange:oldConstrainedSize];
|
||||
CGRect bounds = self.bounds;
|
||||
bounds.size = CGSizeMake(_layout.size.width, _layout.size.height);
|
||||
self.bounds = bounds;
|
||||
|
||||
CGSize oldSize = self.bounds.size;
|
||||
CGSize newSize = _layout.size;
|
||||
|
||||
if (! CGSizeEqualToSize(oldSize, newSize)) {
|
||||
CGRect bounds = self.bounds;
|
||||
bounds.size = newSize;
|
||||
self.bounds = bounds;
|
||||
|
||||
// Frame's origin must be preserved. Since it is computed from bounds size, anchorPoint
|
||||
// and position (see frame setter in ASDisplayNode+UIViewBridge), position needs to be adjusted.
|
||||
BOOL useLayer = (_layer && ASDisplayNodeThreadIsMain());
|
||||
CGPoint anchorPoint = (useLayer ? _layer.anchorPoint : self.anchorPoint);
|
||||
CGPoint oldPosition = (useLayer ? _layer.position : self.position);
|
||||
|
||||
CGFloat xDelta = (newSize.width - oldSize.width) * anchorPoint.x;
|
||||
CGFloat yDelta = (newSize.height - oldSize.height) * anchorPoint.y;
|
||||
CGPoint newPosition = CGPointMake(oldPosition.x + xDelta, oldPosition.y + yDelta);
|
||||
|
||||
useLayer ? _layer.position = newPosition : self.position = newPosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -612,10 +700,10 @@ void ASDisplayNodeRespectThreadAffinityOfNode(ASDisplayNode *node, void (^block)
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
ASDN::MutexLocker l(_propertyLock);
|
||||
if (CGRectEqualToRect(_layer.bounds, CGRectZero)) {
|
||||
if (CGRectEqualToRect(self.bounds, CGRectZero)) {
|
||||
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;
|
||||
_placeholderLayer.frame = self.bounds;
|
||||
[self layout];
|
||||
[self layoutDidFinish];
|
||||
}
|
||||
@@ -1408,6 +1496,15 @@ static NSInteger incrementIfFound(NSInteger i) {
|
||||
_flags.isMeasured = NO;
|
||||
}
|
||||
|
||||
- (void)__didLoad
|
||||
{
|
||||
if (_nodeLoadedBlock) {
|
||||
_nodeLoadedBlock(self);
|
||||
_nodeLoadedBlock = nil;
|
||||
}
|
||||
[self didLoad];
|
||||
}
|
||||
|
||||
- (void)didLoad
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
@@ -1836,6 +1933,11 @@ static void _recursivelySetDisplaySuspended(ASDisplayNode *node, CALayer *layer,
|
||||
return !self.layerBacked && [self.view canPerformAction:action withSender:sender];
|
||||
}
|
||||
|
||||
- (id<ASLayoutable>)finalLayoutable
|
||||
{
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation ASDisplayNode (Debugging)
|
||||
|
||||
@@ -86,13 +86,13 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)viewBlock
|
||||
- (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)viewBlock didLoadBlock:(ASDisplayNodeDidLoadBlock)didLoadBlock
|
||||
{
|
||||
ASDisplayNodeAssertNotSupported();
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock
|
||||
- (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock didLoadBlock:(ASDisplayNodeDidLoadBlock)didLoadBlock
|
||||
{
|
||||
ASDisplayNodeAssertNotSupported();
|
||||
return nil;
|
||||
|
||||
@@ -96,13 +96,13 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)viewBlock
|
||||
- (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)viewBlock didLoadBlock:(ASDisplayNodeDidLoadBlock)didLoadBlock
|
||||
{
|
||||
ASDisplayNodeAssertNotSupported();
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock
|
||||
- (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock didLoadBlock:(ASDisplayNodeDidLoadBlock)didLoadBlock
|
||||
{
|
||||
ASDisplayNodeAssertNotSupported();
|
||||
return nil;
|
||||
|
||||
@@ -99,16 +99,11 @@
|
||||
- (void)beginUpdates;
|
||||
|
||||
/**
|
||||
* Concludes a series of method calls that insert, delete, select, or reload rows and sections of the table view.
|
||||
* Concludes a series of method calls that insert, delete, select, or reload rows and sections of the table view, with animation enabled and no completion block.
|
||||
* You call this method to bracket a series of method calls that begins with beginUpdates and that consists of operations
|
||||
* to insert, delete, select, and reload rows and sections of the table view. When you call endUpdates, ASTableView begins animating
|
||||
* the operations simultaneously. This method is must be called from the main thread. It's important to remeber that the ASTableView will
|
||||
* be processing the updates asynchronously after this call is completed.
|
||||
*
|
||||
* @param animated NO to disable all animations.
|
||||
* @param completion A completion handler block to execute when all of the operations are finished. This block takes a single
|
||||
* Boolean parameter that contains the value YES if all of the related animations completed successfully or
|
||||
* NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread.
|
||||
*/
|
||||
- (void)endUpdates;
|
||||
|
||||
@@ -271,17 +266,6 @@
|
||||
|
||||
@optional
|
||||
|
||||
/**
|
||||
* Provides the constrained size range for measuring the node at the index path.
|
||||
*
|
||||
* @param tableView The sender.
|
||||
*
|
||||
* @param indexPath The index path of the node.
|
||||
*
|
||||
* @returns A constrained size range for layout the node at this index path.
|
||||
*/
|
||||
- (ASSizeRange)tableView:(ASTableView *)tableView constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath;
|
||||
|
||||
/**
|
||||
* Indicator to lock the data source for data fetching in async mode.
|
||||
* We should not update the data source until the data source has been unlocked. Otherwise, it will incur data inconsistence or exception
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#import "ASDisplayNodeInternal.h"
|
||||
#import "ASBatchFetching.h"
|
||||
#import "ASInternalHelpers.h"
|
||||
#import "ASLayout.h"
|
||||
|
||||
//#define LOG(...) NSLog(__VA_ARGS__)
|
||||
#define LOG(...)
|
||||
@@ -104,22 +105,31 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
#pragma mark -
|
||||
#pragma mark ASCellNode<->UITableViewCell bridging.
|
||||
|
||||
@class _ASTableViewCell;
|
||||
|
||||
@protocol _ASTableViewCellDelegate <NSObject>
|
||||
- (void)tableViewCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath didTransitionToState:(UITableViewCellStateMask)state;
|
||||
- (void)willLayoutSubviewsOfTableViewCell:(_ASTableViewCell *)tableViewCell;
|
||||
@end
|
||||
|
||||
@interface _ASTableViewCell : UITableViewCell
|
||||
@property (nonatomic, weak) id<_ASTableViewCellDelegate> delegate;
|
||||
@property (nonatomic) NSIndexPath *indexPath;
|
||||
@property (nonatomic, weak) ASCellNode *node;
|
||||
@end
|
||||
|
||||
@implementation _ASTableViewCell
|
||||
// TODO add assertions to prevent use of view-backed UITableViewCell properties (eg .textLabel)
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[_delegate willLayoutSubviewsOfTableViewCell:self];
|
||||
[super layoutSubviews];
|
||||
}
|
||||
|
||||
- (void)didTransitionToState:(UITableViewCellStateMask)state
|
||||
{
|
||||
[self setNeedsLayout];
|
||||
[self layoutIfNeeded];
|
||||
[super didTransitionToState:state];
|
||||
[_delegate tableViewCell:self atIndexPath:_indexPath didTransitionToState:state];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -146,9 +156,8 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
NSIndexPath *_contentOffsetAdjustmentTopVisibleRow;
|
||||
CGFloat _contentOffsetAdjustment;
|
||||
|
||||
BOOL _asyncDataSourceImplementsConstrainedSizeForNode;
|
||||
|
||||
CGFloat _maxWidthForNodesConstrainedSize;
|
||||
BOOL _ignoreMaxWidthChange;
|
||||
}
|
||||
|
||||
@property (atomic, assign) BOOL asyncDataSourceLocked;
|
||||
@@ -203,6 +212,9 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) {
|
||||
_automaticallyAdjustsContentOffset = NO;
|
||||
|
||||
_maxWidthForNodesConstrainedSize = self.bounds.size.width;
|
||||
// If the initial size is 0, expect a size change very soon which is part of the initial configuration
|
||||
// and should not trigger a relayout.
|
||||
_ignoreMaxWidthChange = (_maxWidthForNodesConstrainedSize == 0);
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style
|
||||
@@ -267,12 +279,10 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) {
|
||||
super.dataSource = nil;
|
||||
_asyncDataSource = nil;
|
||||
_proxyDataSource = nil;
|
||||
_asyncDataSourceImplementsConstrainedSizeForNode = NO;
|
||||
} else {
|
||||
_asyncDataSource = asyncDataSource;
|
||||
_proxyDataSource = [[_ASTableViewProxy alloc] initWithTarget:_asyncDataSource interceptor:self];
|
||||
super.dataSource = (id<UITableViewDataSource>)_proxyDataSource;
|
||||
_asyncDataSourceImplementsConstrainedSizeForNode = ([_asyncDataSource respondsToSelector:@selector(tableView:constrainedSizeForNodeAtIndexPath:)] ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -367,14 +377,22 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) {
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
|
||||
if (_maxWidthForNodesConstrainedSize != self.bounds.size.width) {
|
||||
_maxWidthForNodesConstrainedSize = self.bounds.size.width;
|
||||
[self beginUpdates];
|
||||
[_dataController relayoutAllRows];
|
||||
[self endUpdates];
|
||||
|
||||
// First width change occurs during initial configuration. An expensive relayout pass is unnecessary at that time
|
||||
// and should be avoided, assuming that the initial data loading automatically runs shortly afterward.
|
||||
if (_ignoreMaxWidthChange) {
|
||||
_ignoreMaxWidthChange = NO;
|
||||
} else {
|
||||
[self beginUpdates];
|
||||
[_dataController relayoutAllRows];
|
||||
[self endUpdates];
|
||||
}
|
||||
}
|
||||
|
||||
// To ensure _maxWidthForNodesConstrainedSize is up-to-date for every usage, this call to super must be done last
|
||||
[super layoutSubviews];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
@@ -495,7 +513,7 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) {
|
||||
ASCellNode *node = [_dataController nodeAtIndexPath:indexPath];
|
||||
[_rangeController configureContentView:cell.contentView forCellNode:node];
|
||||
|
||||
cell.indexPath = indexPath;
|
||||
cell.node = node;
|
||||
cell.backgroundColor = node.backgroundColor;
|
||||
cell.selectionStyle = node.selectionStyle;
|
||||
|
||||
@@ -786,12 +804,8 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) {
|
||||
|
||||
- (ASSizeRange)dataController:(ASDataController *)dataController constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
if (_asyncDataSourceImplementsConstrainedSizeForNode) {
|
||||
return [_asyncDataSource tableView:self constrainedSizeForNodeAtIndexPath:indexPath];
|
||||
}
|
||||
|
||||
// Default size range
|
||||
return ASSizeRangeMake(CGSizeZero, CGSizeMake(_maxWidthForNodesConstrainedSize, FLT_MAX));
|
||||
return ASSizeRangeMake(CGSizeMake(_maxWidthForNodesConstrainedSize, 0),
|
||||
CGSizeMake(_maxWidthForNodesConstrainedSize, FLT_MAX));
|
||||
}
|
||||
|
||||
- (void)dataControllerLockDataSource
|
||||
@@ -832,22 +846,31 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) {
|
||||
|
||||
#pragma mark - _ASTableViewCellDelegate
|
||||
|
||||
- (void)tableViewCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath didTransitionToState:(UITableViewCellStateMask)state
|
||||
- (void)willLayoutSubviewsOfTableViewCell:(_ASTableViewCell *)tableViewCell
|
||||
{
|
||||
[self beginUpdates];
|
||||
ASCellNode *node = [_dataController nodeAtIndexPath:indexPath];
|
||||
CGFloat contentViewWidth = tableViewCell.contentView.bounds.size.width;
|
||||
ASCellNode *node = tableViewCell.node;
|
||||
ASSizeRange constrainedSize = node.constrainedSizeForCalculatedLayout;
|
||||
|
||||
ASSizeRange constrainedSize = [self dataController:_dataController constrainedSizeForNodeAtIndexPath:indexPath];
|
||||
if (state != UITableViewCellStateDefaultMask) {
|
||||
// Edit control or delete confirmation was shown and size of content view was changed.
|
||||
// The new size should be taken into consideration.
|
||||
constrainedSize.min.width = MIN(cell.contentView.frame.size.width, constrainedSize.min.width);
|
||||
constrainedSize.max.width = MIN(cell.contentView.frame.size.width, constrainedSize.max.width);
|
||||
// Table view cells should always fill its content view width.
|
||||
// Normally the content view width equals to the constrained size width (which equals to the table view width).
|
||||
// If there is a mismatch between these values, for example after the table view entered or left editing mode,
|
||||
// content view width is preferred and used to re-measure the cell node.
|
||||
if (contentViewWidth != constrainedSize.max.width) {
|
||||
constrainedSize.min.width = contentViewWidth;
|
||||
constrainedSize.max.width = contentViewWidth;
|
||||
|
||||
// Re-measurement is done on main to ensure thread affinity. In the worst case, this is as fast as UIKit's implementation.
|
||||
//
|
||||
// Unloaded nodes *could* be re-measured off the main thread, but only with the assumption that content view width
|
||||
// is the same for all cells (because there is no easy way to get that individual value before the node being assigned to a _ASTableViewCell).
|
||||
// Also, in many cases, some nodes may not need to be re-measured at all, such as when user enters and then immediately leaves editing mode.
|
||||
// To avoid premature optimization and making such assumption, as well as to keep ASTableView simple, re-measurement is strictly done on main.
|
||||
[self beginUpdates];
|
||||
CGSize calculatedSize = [[node measureWithSizeRange:constrainedSize] size];
|
||||
node.frame = CGRectMake(0, 0, calculatedSize.width, calculatedSize.height);
|
||||
[self endUpdates];
|
||||
}
|
||||
|
||||
[node measureWithSizeRange:constrainedSize];
|
||||
node.frame = CGRectMake(0, 0, node.calculatedSize.width, node.calculatedSize.height);
|
||||
[self endUpdates];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
*/
|
||||
|
||||
#import <AsyncDisplayKit/ASControlNode.h>
|
||||
#import <AsyncDisplayKit/ASBaselineLayoutable.h>
|
||||
|
||||
@protocol ASTextNodeDelegate;
|
||||
|
||||
@@ -30,7 +29,7 @@ typedef NS_ENUM(NSUInteger, ASTextNodeHighlightStyle) {
|
||||
@abstract Draws interactive rich text.
|
||||
@discussion Backed by TextKit.
|
||||
*/
|
||||
@interface ASTextNode : ASControlNode <ASBaselineLayoutable>
|
||||
@interface ASTextNode : ASControlNode
|
||||
|
||||
/**
|
||||
@abstract The attributed string to show.
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#import <AsyncDisplayKit/ASTextNodeTextKitHelpers.h>
|
||||
#import <AsyncDisplayKit/ASDisplayNodeExtras.h>
|
||||
|
||||
#import "ASInternalHelpers.h"
|
||||
#import "ASTextNodeRenderer.h"
|
||||
#import "ASTextNodeShadower.h"
|
||||
|
||||
@@ -108,9 +107,6 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f)
|
||||
UILongPressGestureRecognizer *_longPressGestureRecognizer;
|
||||
}
|
||||
|
||||
@synthesize ascender = _ascender;
|
||||
@synthesize descender = _descender;
|
||||
|
||||
#pragma mark - NSObject
|
||||
|
||||
- (instancetype)init
|
||||
@@ -153,13 +149,13 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f)
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)viewBlock
|
||||
- (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)viewBlock didLoadBlock:(ASDisplayNodeDidLoadBlock)didLoadBlock
|
||||
{
|
||||
ASDisplayNodeAssertNotSupported();
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock
|
||||
- (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock didLoadBlock:(ASDisplayNodeDidLoadBlock)didLoadBlock
|
||||
{
|
||||
ASDisplayNodeAssertNotSupported();
|
||||
return nil;
|
||||
@@ -359,9 +355,6 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f)
|
||||
self.isAccessibilityElement = YES;
|
||||
}
|
||||
});
|
||||
|
||||
_ascender = round([[attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * ASScreenScale())/ASScreenScale();
|
||||
_descender = round([[attributedString attribute:NSFontAttributeName atIndex:attributedString.length - 1 effectiveRange:NULL] descender] * ASScreenScale())/ASScreenScale();
|
||||
}
|
||||
|
||||
#pragma mark - Text Layout
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#import <AsyncDisplayKit/ASTableView.h>
|
||||
#import <AsyncDisplayKit/ASCollectionView.h>
|
||||
#import <AsyncDisplayKit/ASCollectionNode.h>
|
||||
#import <AsyncDisplayKit/ASCellNode.h>
|
||||
|
||||
#import <AsyncDisplayKit/ASScrollNode.h>
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#import "ASAssert.h"
|
||||
#import "ASCollectionView.h"
|
||||
#import "CGRect+ASConvenience.h"
|
||||
#import "UICollectionViewLayout+ASConvenience.h"
|
||||
|
||||
struct ASDirectionalScreenfulBuffer {
|
||||
CGFloat positiveDirection; // Positive relative to iOS Core Animation layer coordinate space.
|
||||
@@ -56,7 +57,7 @@ typedef struct ASRangeGeometry ASRangeGeometry;
|
||||
|
||||
@interface ASCollectionViewLayoutController ()
|
||||
{
|
||||
UIScrollView * __weak _scrollView;
|
||||
ASCollectionView * __weak _collectionView;
|
||||
UICollectionViewLayout * __strong _collectionViewLayout;
|
||||
std::vector<CGRect> _updateRangeBoundsIndexedByRangeType;
|
||||
ASScrollDirection _scrollableDirections;
|
||||
@@ -72,7 +73,7 @@ typedef struct ASRangeGeometry ASRangeGeometry;
|
||||
}
|
||||
|
||||
_scrollableDirections = [collectionView scrollableDirections];
|
||||
_scrollView = collectionView;
|
||||
_collectionView = collectionView;
|
||||
_collectionViewLayout = [collectionView collectionViewLayout];
|
||||
_updateRangeBoundsIndexedByRangeType = std::vector<CGRect>(ASLayoutRangeTypeCount);
|
||||
return self;
|
||||
@@ -94,8 +95,13 @@ typedef struct ASRangeGeometry ASRangeGeometry;
|
||||
- (ASRangeGeometry)rangeGeometryWithScrollDirection:(ASScrollDirection)scrollDirection
|
||||
rangeTuningParameters:(ASRangeTuningParameters)rangeTuningParameters
|
||||
{
|
||||
CGRect rangeBounds = _scrollView.bounds;
|
||||
CGRect updateBounds = _scrollView.bounds;
|
||||
CGRect rangeBounds = _collectionView.bounds;
|
||||
CGRect updateBounds = _collectionView.bounds;
|
||||
|
||||
//scrollable directions can change for non-flow layouts
|
||||
if ([_collectionViewLayout asdk_isFlowLayout] == NO) {
|
||||
_scrollableDirections = [_collectionView scrollableDirections];
|
||||
}
|
||||
|
||||
BOOL canScrollHorizontally = ASScrollDirectionContainsHorizontalDirection(_scrollableDirections);
|
||||
if (canScrollHorizontally) {
|
||||
@@ -148,7 +154,7 @@ typedef struct ASRangeGeometry ASRangeGeometry;
|
||||
return YES;
|
||||
}
|
||||
|
||||
CGRect currentBounds = _scrollView.bounds;
|
||||
CGRect currentBounds = _collectionView.bounds;
|
||||
if (CGRectIsEmpty(currentBounds)) {
|
||||
currentBounds = CGRectMake(0, 0, viewportSize.width, viewportSize.height);
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@
|
||||
* End updates.
|
||||
*
|
||||
* @param rangeController Sender.
|
||||
*
|
||||
* @param animated NO if all animations are disabled. YES otherwise.
|
||||
* @param completion Completion block.
|
||||
*/
|
||||
- (void)rangeController:(ASRangeController * )rangeController endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion;
|
||||
@@ -104,6 +104,8 @@
|
||||
*
|
||||
* @param rangeController Sender.
|
||||
*
|
||||
* @param nodes Inserted nodes.
|
||||
*
|
||||
* @param indexPaths Index path of inserted nodes.
|
||||
*
|
||||
* @param animationOptions Animation options. See ASDataControllerAnimationOptions.
|
||||
@@ -115,6 +117,8 @@
|
||||
*
|
||||
* @param rangeController Sender.
|
||||
*
|
||||
* @param nodes Deleted nodes.
|
||||
*
|
||||
* @param indexPaths Index path of deleted nodes.
|
||||
*
|
||||
* @param animationOptions Animation options. See ASDataControllerAnimationOptions.
|
||||
|
||||
@@ -39,8 +39,12 @@
|
||||
|
||||
[node recursivelySetDisplaySuspended:NO];
|
||||
|
||||
// add the node to an off-screen window to force display and preserve its contents
|
||||
[[self.class workingWindow] addSubnode:node];
|
||||
// Add the node's layer to an off-screen window to trigger display and mark its contents as non-volatile.
|
||||
// Use the layer directly to avoid the substantial overhead of UIView heirarchy manipulations.
|
||||
// Any view-backed nodes will still create their views in order to assemble the layer heirarchy, and they will
|
||||
// also assemble a view subtree for the node, but we avoid the much more significant expense triggered by a view
|
||||
// being added or removed from an onscreen window (responder chain setup, will/DidMoveToWindow: recursive calls, etc)
|
||||
[[[[self class] workingWindow] layer] addSublayer:node.layer];
|
||||
}
|
||||
|
||||
- (void)node:(ASDisplayNode *)node exitedRangeOfType:(ASLayoutRangeType)rangeType
|
||||
@@ -48,9 +52,36 @@
|
||||
ASDisplayNodeAssertMainThread();
|
||||
ASDisplayNodeAssert(rangeType == ASLayoutRangeTypeRender, @"Render delegate should not handle other ranges");
|
||||
|
||||
// This code is tricky. There are several possible states a node can be in when it reaches this point.
|
||||
// 1. Layer-backed vs view-backed nodes. AS of this writing, only ASCellNodes arrive here, which are always view-backed —
|
||||
// but we maintain correctness for all ASDisplayNodes, including layer-backed ones.
|
||||
// (Note: it would not make sense to pass in a subnode of a rasterized node here, so that is unsupported).
|
||||
// 2. The node's layer may have been added to the workingWindow previously, or it may have never been added, such as if rangeTuningParameter's leading value is 0.
|
||||
// 3. The node's layer may not be present in the workingWindow, even if it was previously added.
|
||||
// This is a common case, as once the node is added to an active cell contentsView (e.g. visible), it is automatically removed from the workingWindow.
|
||||
// The system does this when addSublayer is called, even if removeFromSuperlayer is never explicitly called.
|
||||
// 4. Lastly and most unusually, it is possible for a node to be offscreen, completely outside the heirarchy, and yet considered within the working range.
|
||||
// This happens if the UITableViewCell is reused after scrolling offscreen. Because the node has already been given the opportunity to display, we do not
|
||||
// proactively re-host it within the workingWindow (improving efficiency). Some time later, it may fall outside the working range, in which case calling
|
||||
// -recursivelyClearContents is critical. If the user scrolls back and it is re-hosted in a UITableViewCell, the content will still exist as it is not cleared
|
||||
// by simply being removed from the cell. The code that usually triggers this condition is the -removeFromSuperview in -[ASRangeController configureContentView:forCellNode:].
|
||||
// Condition #4 is suboptimal in some cases, as it is conceivable that memory warnings could trigger clearing content that is inside the working range. However, enforcing the
|
||||
// preservation of this content could result in the app being killed, which is not likely preferable over briefly seeing placeholders in the event the user scrolls backwards.
|
||||
// Nonetheless, future changes to the implementation will likely eliminate this behavior to simplify debugging and extensibility of working range functionality.
|
||||
|
||||
[node recursivelySetDisplaySuspended:YES];
|
||||
[node.view removeFromSuperview];
|
||||
|
||||
|
||||
if (node.layer.superlayer != [[[self class] workingWindow] layer]) {
|
||||
// In this case, the node has previously passed through the working range (or it is zero), and it has now fallen outside the working range.
|
||||
if (![node isLayerBacked]) {
|
||||
// If the node is view-backed, we need to make sure to remove the view (which is now present in the containing cell contentsView).
|
||||
// Layer-backed nodes will be fully handled by the unconditional removal below.
|
||||
[node.view removeFromSuperview];
|
||||
}
|
||||
}
|
||||
|
||||
// At this point, the node's layer may validly be present either in the workingWindow, or in the contentsView of a cell.
|
||||
[node.layer removeFromSuperlayer];
|
||||
[node recursivelyClearContents];
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
@interface ASBackgroundLayoutSpec : ASLayoutSpec
|
||||
|
||||
@property (nonatomic, strong) id<ASLayoutable> child;
|
||||
@property (nonatomic, strong) id<ASLayoutable> background;
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,11 +14,9 @@
|
||||
#import "ASBaseDefines.h"
|
||||
#import "ASLayout.h"
|
||||
|
||||
static NSString * const kBackgroundChildKey = @"kBackgroundChildKey";
|
||||
|
||||
@interface ASBackgroundLayoutSpec ()
|
||||
{
|
||||
id<ASLayoutable> _child;
|
||||
id<ASLayoutable> _background;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation ASBackgroundLayoutSpec
|
||||
@@ -30,12 +28,11 @@
|
||||
}
|
||||
|
||||
ASDisplayNodeAssertNotNil(child, @"Child cannot be nil");
|
||||
_child = child;
|
||||
_background = background;
|
||||
[self setChild:child];
|
||||
self.background = background;
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
+ (instancetype)backgroundLayoutSpecWithChild:(id<ASLayoutable>)child background:(id<ASLayoutable>)background;
|
||||
{
|
||||
return [[self alloc] initWithChild:child background:background];
|
||||
@@ -46,12 +43,12 @@
|
||||
*/
|
||||
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
|
||||
{
|
||||
ASLayout *contentsLayout = [_child measureWithSizeRange:constrainedSize];
|
||||
ASLayout *contentsLayout = [[self child] measureWithSizeRange:constrainedSize];
|
||||
|
||||
NSMutableArray *sublayouts = [NSMutableArray arrayWithCapacity:2];
|
||||
if (_background) {
|
||||
if (self.background) {
|
||||
// Size background to exactly the same size.
|
||||
ASLayout *backgroundLayout = [_background measureWithSizeRange:{contentsLayout.size, contentsLayout.size}];
|
||||
ASLayout *backgroundLayout = [self.background measureWithSizeRange:{contentsLayout.size, contentsLayout.size}];
|
||||
backgroundLayout.position = CGPointZero;
|
||||
[sublayouts addObject:backgroundLayout];
|
||||
}
|
||||
@@ -63,14 +60,23 @@
|
||||
|
||||
- (void)setBackground:(id<ASLayoutable>)background
|
||||
{
|
||||
ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable");
|
||||
_background = background;
|
||||
[super setChild:background forIdentifier:kBackgroundChildKey];
|
||||
}
|
||||
|
||||
- (void)setChild:(id<ASLayoutable>)child
|
||||
- (id<ASLayoutable>)background
|
||||
{
|
||||
ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable");
|
||||
_child = child;
|
||||
return [super childForIdentifier:kBackgroundChildKey];
|
||||
}
|
||||
|
||||
- (void)setChildren:(NSArray *)children
|
||||
{
|
||||
ASDisplayNodeAssert(NO, @"not supported by this layout spec");
|
||||
}
|
||||
|
||||
- (NSArray *)children
|
||||
{
|
||||
ASDisplayNodeAssert(NO, @"not supported by this layout spec");
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* 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/ASStackLayoutSpec.h>
|
||||
#import <AsyncDisplayKit/ASBaselineLayoutable.h>
|
||||
|
||||
typedef NS_ENUM(NSUInteger, ASBaselineLayoutBaselineAlignment) {
|
||||
/** No baseline alignment. This is only valid for a vertical stack */
|
||||
ASBaselineLayoutBaselineAlignmentNone,
|
||||
/** Align all children to the first baseline. This is only valid for a horizontal stack */
|
||||
ASBaselineLayoutBaselineAlignmentFirst,
|
||||
/** Align all children to the last baseline. This is useful when a text node wraps and you want to align
|
||||
to the bottom baseline. This is only valid for a horizontal stack */
|
||||
ASBaselineLayoutBaselineAlignmentLast,
|
||||
};
|
||||
|
||||
/**
|
||||
A specialized version of a stack layout that aligns its children on a baseline. This spec only works with
|
||||
ASBaselineLayoutable children.
|
||||
|
||||
If the spec is created with a horizontal direction, the children will be laid on a common baseline.
|
||||
If the spec is created with a vertical direction, a child's vertical spacing will be measured from its
|
||||
baseline instead of from the child's bounding box.
|
||||
*/
|
||||
@interface ASBaselineLayoutSpec : ASLayoutSpec <ASBaselineLayoutable>
|
||||
|
||||
/** Specifies the direction children are stacked in. */
|
||||
@property (nonatomic, assign) ASStackLayoutDirection direction;
|
||||
/** The amount of space between each child. */
|
||||
@property (nonatomic, assign) CGFloat spacing;
|
||||
/** The amount of space between each child. */
|
||||
@property (nonatomic, assign) ASStackLayoutJustifyContent justifyContent;
|
||||
/** Orientation of children along cross axis */
|
||||
@property (nonatomic, assign) ASStackLayoutAlignItems alignItems;
|
||||
/** The type of baseline alignment */
|
||||
@property (nonatomic, assign) ASBaselineLayoutBaselineAlignment baselineAlignment;
|
||||
|
||||
- (void)addChild:(id<ASBaselineLayoutable>)child;
|
||||
- (void)addChildren:(NSArray *)children;
|
||||
|
||||
/**
|
||||
@param direction The direction of the stack view (horizontal or vertical)
|
||||
@param spacing The spacing between the children
|
||||
@param baselineAlignment The baseline to align to
|
||||
@param justifyContent If no children are flexible, this describes how to fill any extra space
|
||||
@param alignItems Orientation of the children along the cross axis
|
||||
@param children ASLayoutable children to be positioned.
|
||||
*/
|
||||
+ (instancetype)baselineLayoutSpecWithDirection:(ASStackLayoutDirection)direction
|
||||
spacing:(CGFloat)spacing
|
||||
baselineAlignment:(ASBaselineLayoutBaselineAlignment)baselineAlignment
|
||||
justifyContent:(ASStackLayoutJustifyContent)justifyContent
|
||||
alignItems:(ASStackLayoutAlignItems)alignItems
|
||||
children:(NSArray *)children;
|
||||
|
||||
@end
|
||||
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
* 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 "ASBaselineLayoutSpec.h"
|
||||
#import "ASStackLayoutable.h"
|
||||
|
||||
#import <numeric>
|
||||
#import <vector>
|
||||
|
||||
#import "ASBaseDefines.h"
|
||||
#import "ASInternalHelpers.h"
|
||||
|
||||
#import "ASLayoutSpecUtilities.h"
|
||||
#import "ASStackLayoutSpecUtilities.h"
|
||||
#import "ASStackPositionedLayout.h"
|
||||
#import "ASStackUnpositionedLayout.h"
|
||||
#import "ASBaselinePositionedLayout.h"
|
||||
#import "ASThread.h"
|
||||
|
||||
|
||||
@implementation ASBaselineLayoutSpec
|
||||
{
|
||||
std::vector<id<ASStackLayoutable>> _children;
|
||||
ASDN::RecursiveMutex _propertyLock;
|
||||
}
|
||||
|
||||
@synthesize ascender = _ascender;
|
||||
@synthesize descender = _descender;
|
||||
|
||||
- (instancetype)initWithDirection:(ASStackLayoutDirection)direction spacing:(CGFloat)spacing baselineAlignment:(ASBaselineLayoutBaselineAlignment)baselineAlignment justifyContent:(ASStackLayoutJustifyContent)justifyContent alignItems:(ASStackLayoutAlignItems)alignItems children:(NSArray *)children
|
||||
{
|
||||
if (!(self = [super init])) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
ASDisplayNodeAssert((direction == ASStackLayoutDirectionHorizontal && baselineAlignment != ASBaselineLayoutBaselineAlignmentNone) || direction == ASStackLayoutDirectionVertical, @"baselineAlignment is set to none. If you don't need baseline alignment please use ASStackLayoutSpec");
|
||||
_direction = direction;
|
||||
_alignItems = alignItems;
|
||||
_spacing = spacing;
|
||||
_justifyContent = justifyContent;
|
||||
_baselineAlignment = baselineAlignment;
|
||||
|
||||
_children = std::vector<id<ASStackLayoutable>>();
|
||||
for (id<ASStackLayoutable> child in children) {
|
||||
_children.push_back(child);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
+ (instancetype)baselineLayoutSpecWithDirection:(ASStackLayoutDirection)direction spacing:(CGFloat)spacing baselineAlignment:(ASBaselineLayoutBaselineAlignment)baselineAlignment justifyContent:(ASStackLayoutJustifyContent)justifyContent alignItems:(ASStackLayoutAlignItems)alignItems children:(NSArray *)children
|
||||
{
|
||||
return [[ASBaselineLayoutSpec alloc] initWithDirection:direction spacing:spacing baselineAlignment:baselineAlignment justifyContent:justifyContent alignItems:alignItems children:children];
|
||||
}
|
||||
|
||||
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
|
||||
{
|
||||
ASStackLayoutSpecStyle stackStyle = {.direction = _direction, .spacing = _spacing, .justifyContent = _justifyContent, .alignItems = _alignItems};
|
||||
ASBaselineLayoutSpecStyle style = { .baselineAlignment = _baselineAlignment, .stackLayoutStyle = stackStyle };
|
||||
|
||||
const auto unpositionedLayout = ASStackUnpositionedLayout::compute(_children, stackStyle, constrainedSize);
|
||||
const auto positionedLayout = ASStackPositionedLayout::compute(unpositionedLayout, stackStyle, constrainedSize);
|
||||
const auto baselinePositionedLayout = ASBaselinePositionedLayout::compute(positionedLayout, style, constrainedSize);
|
||||
|
||||
const CGSize finalSize = directionSize(stackStyle.direction, unpositionedLayout.stackDimensionSum, baselinePositionedLayout.crossSize);
|
||||
|
||||
NSArray *sublayouts = [NSArray arrayWithObjects:&baselinePositionedLayout.sublayouts[0] count:baselinePositionedLayout.sublayouts.size()];
|
||||
|
||||
ASDN::MutexLocker l(_propertyLock);
|
||||
_ascender = baselinePositionedLayout.ascender;
|
||||
_descender = baselinePositionedLayout.descender;
|
||||
|
||||
return [ASLayout layoutWithLayoutableObject:self
|
||||
size:ASSizeRangeClamp(constrainedSize, finalSize)
|
||||
sublayouts:sublayouts];
|
||||
}
|
||||
|
||||
- (void)addChild:(id<ASBaselineLayoutable>)child
|
||||
{
|
||||
_children.push_back(child);
|
||||
}
|
||||
|
||||
- (void)addChildren:(NSArray *)children
|
||||
{
|
||||
for (id<ASBaselineLayoutable> child in children) {
|
||||
[self addChild:child];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,23 +0,0 @@
|
||||
/* 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 "ASStackLayoutable.h"
|
||||
|
||||
@protocol ASBaselineLayoutable <ASStackLayoutable>
|
||||
|
||||
/**
|
||||
* @abstract The distance from the top of the layoutable object to its baseline
|
||||
*/
|
||||
@property (nonatomic, readwrite) CGFloat ascender;
|
||||
|
||||
/**
|
||||
* @abstract The distance from the bottom of the layoutable object to its baseline
|
||||
*/
|
||||
@property (nonatomic, readwrite) CGFloat descender;
|
||||
|
||||
@end
|
||||
@@ -39,7 +39,6 @@ typedef NS_OPTIONS(NSUInteger, ASCenterLayoutSpecSizingOptions) {
|
||||
|
||||
@property (nonatomic, assign) ASCenterLayoutSpecCenteringOptions centeringOptions;
|
||||
@property (nonatomic, assign) ASCenterLayoutSpecSizingOptions sizingOptions;
|
||||
@property (nonatomic, strong) id<ASLayoutable> child;
|
||||
|
||||
/**
|
||||
* Initializer.
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
{
|
||||
ASCenterLayoutSpecCenteringOptions _centeringOptions;
|
||||
ASCenterLayoutSpecSizingOptions _sizingOptions;
|
||||
id<ASLayoutable> _child;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCenteringOptions:(ASCenterLayoutSpecCenteringOptions)centeringOptions
|
||||
@@ -30,7 +29,7 @@
|
||||
ASDisplayNodeAssertNotNil(child, @"Child cannot be nil");
|
||||
_centeringOptions = centeringOptions;
|
||||
_sizingOptions = sizingOptions;
|
||||
_child = child;
|
||||
[self setChild:child];
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -41,12 +40,6 @@
|
||||
return [[self alloc] initWithCenteringOptions:centeringOptions sizingOptions:sizingOptions child:child];
|
||||
}
|
||||
|
||||
- (void)setChild:(id<ASLayoutable>)child
|
||||
{
|
||||
ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable");
|
||||
_child = child;
|
||||
}
|
||||
|
||||
- (void)setCenteringOptions:(ASCenterLayoutSpecCenteringOptions)centeringOptions
|
||||
{
|
||||
ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable");
|
||||
@@ -71,7 +64,7 @@
|
||||
(_centeringOptions & ASCenterLayoutSpecCenteringX) != 0 ? 0 : constrainedSize.min.width,
|
||||
(_centeringOptions & ASCenterLayoutSpecCenteringY) != 0 ? 0 : constrainedSize.min.height,
|
||||
};
|
||||
ASLayout *sublayout = [_child measureWithSizeRange:ASSizeRangeMake(minChildSize, constrainedSize.max)];
|
||||
ASLayout *sublayout = [self.child measureWithSizeRange:ASSizeRangeMake(minChildSize, constrainedSize.max)];
|
||||
|
||||
// If we have an undetermined height or width, use the child size to define the layout
|
||||
// size
|
||||
@@ -97,4 +90,15 @@
|
||||
return [ASLayout layoutWithLayoutableObject:self size:size sublayouts:@[sublayout]];
|
||||
}
|
||||
|
||||
- (void)setChildren:(NSArray *)children
|
||||
{
|
||||
ASDisplayNodeAssert(NO, @"not supported by this layout spec");
|
||||
}
|
||||
|
||||
- (NSArray *)children
|
||||
{
|
||||
ASDisplayNodeAssert(NO, @"not supported by this layout spec");
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
*/
|
||||
@interface ASInsetLayoutSpec : ASLayoutSpec
|
||||
|
||||
@property (nonatomic, strong) id<ASLayoutable> child;
|
||||
@property (nonatomic, assign) UIEdgeInsets insets;
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
@interface ASInsetLayoutSpec ()
|
||||
{
|
||||
UIEdgeInsets _insets;
|
||||
id<ASLayoutable> _child;
|
||||
}
|
||||
@end
|
||||
|
||||
@@ -50,7 +49,7 @@ static CGFloat centerInset(CGFloat outer, CGFloat inner)
|
||||
}
|
||||
ASDisplayNodeAssertNotNil(child, @"Child cannot be nil");
|
||||
_insets = insets;
|
||||
_child = child;
|
||||
[self setChild:child];
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -59,12 +58,6 @@ static CGFloat centerInset(CGFloat outer, CGFloat inner)
|
||||
return [[self alloc] initWithInsets:insets child:child];
|
||||
}
|
||||
|
||||
- (void)setChild:(id<ASLayoutable>)child
|
||||
{
|
||||
ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable");
|
||||
_child = child;
|
||||
}
|
||||
|
||||
- (void)setInsets:(UIEdgeInsets)insets
|
||||
{
|
||||
ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable");
|
||||
@@ -95,7 +88,7 @@ static CGFloat centerInset(CGFloat outer, CGFloat inner)
|
||||
MAX(0, constrainedSize.max.height - insetsY),
|
||||
}
|
||||
};
|
||||
ASLayout *sublayout = [_child measureWithSizeRange:insetConstrainedSize];
|
||||
ASLayout *sublayout = [self.child measureWithSizeRange:insetConstrainedSize];
|
||||
|
||||
const CGSize computedSize = ASSizeRangeClamp(constrainedSize, {
|
||||
finite(sublayout.size.width + _insets.left + _insets.right, constrainedSize.max.width),
|
||||
@@ -116,4 +109,15 @@ static CGFloat centerInset(CGFloat outer, CGFloat inner)
|
||||
return [ASLayout layoutWithLayoutableObject:self size:computedSize sublayouts:@[sublayout]];
|
||||
}
|
||||
|
||||
- (void)setChildren:(NSArray *)children
|
||||
{
|
||||
ASDisplayNodeAssert(NO, @"not supported by this layout spec");
|
||||
}
|
||||
|
||||
- (NSArray *)children
|
||||
{
|
||||
ASDisplayNodeAssert(NO, @"not supported by this layout spec");
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
85
AsyncDisplayKit/Layout/ASLayoutOptions.h
Normal file
85
AsyncDisplayKit/Layout/ASLayoutOptions.h
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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 <Foundation/Foundation.h>
|
||||
#import <AsyncDisplayKit/ASLayoutSpec.h>
|
||||
|
||||
@protocol ASLayoutable;
|
||||
|
||||
/**
|
||||
* A store for all of the options defined by ASLayoutSpec subclasses. All implementors of ASLayoutable own a
|
||||
* ASLayoutOptions. When certain layoutSpecs need option values, they are read from this class.
|
||||
*
|
||||
* Unless you wish to create a custom layout spec, ASLayoutOptions can largerly be ignored. Instead you can access
|
||||
* the layout option properties exposed in ASLayoutable directly, which will set the values in ASLayoutOptions
|
||||
* behind the scenes.
|
||||
*/
|
||||
@interface ASLayoutOptions : NSObject <ASStackLayoutable, ASStaticLayoutable, NSCopying>
|
||||
|
||||
/**
|
||||
* Sets the class name for the ASLayoutOptions subclasses that will be created when a node or layoutSpec's options
|
||||
* are first accessed.
|
||||
*
|
||||
* If you create a custom layoutSpec that includes new options, you will want to subclass ASLayoutOptions to add
|
||||
* the new layout options for your layoutSpec(s). In order to make sure your subclass is created instead of an
|
||||
* instance of ASLayoutOptions, call setDefaultLayoutOptionsClass: early in app launch (applicationDidFinishLaunching:)
|
||||
* with your subclass's class.
|
||||
*
|
||||
* @param defaultLayoutOptionsClass The class of ASLayoutOptions that will be lazily created for a node or layout spec.
|
||||
*/
|
||||
+ (void)setDefaultLayoutOptionsClass:(Class)defaultLayoutOptionsClass;
|
||||
|
||||
/**
|
||||
* @return the Class of ASLayoutOptions that will be created for a node or layoutspec. Defaults to [ASLayoutOptions class];
|
||||
*/
|
||||
+ (Class)defaultLayoutOptionsClass;
|
||||
|
||||
#pragma mark - Subclasses should implement these!
|
||||
/**
|
||||
* Initializes a new ASLayoutOptions using the given layoutable to assign any intrinsic option values.
|
||||
* This init function sets a sensible default value for each layout option. If you create a subclass of
|
||||
* ASLayoutOptions, your subclass should do the same.
|
||||
*
|
||||
* @param layoutable The layoutable that will own these options. The layoutable will be used to set any intrinsic
|
||||
* layoutOptions. For example, if the layoutable is an ASTextNode the ascender/descender values will get set.
|
||||
*
|
||||
* @return a new instance of ASLayoutOptions
|
||||
*/
|
||||
- (instancetype)initWithLayoutable:(id<ASLayoutable>)layoutable;
|
||||
|
||||
/**
|
||||
* Copies the values of layoutOptions into self. This is useful when placing a layoutable inside of another. Consider
|
||||
* an ASTextNode that you want to align to the baseline by putting it in an ASStackLayoutSpec. Before that, you want
|
||||
* to inset the ASTextNode by placing it in an ASInsetLayoutSpec. An ASInsetLayoutSpec will not have any information
|
||||
* about the ASTextNode's ascender/descender unless we copy over the layout options from ASTextNode to ASInsetLayoutSpec.
|
||||
* This is done automatically and should not need to be called directly. It is listed here to make sure that any
|
||||
* ASLayoutOptions subclass implements the method.
|
||||
*
|
||||
* @param layoutOptions The layoutOptions to copy from
|
||||
*/
|
||||
- (void)copyIntoOptions:(ASLayoutOptions *)layoutOptions;
|
||||
|
||||
#pragma mark - ASStackLayoutable
|
||||
|
||||
@property (nonatomic, readwrite) CGFloat spacingBefore;
|
||||
@property (nonatomic, readwrite) CGFloat spacingAfter;
|
||||
@property (nonatomic, readwrite) BOOL flexGrow;
|
||||
@property (nonatomic, readwrite) BOOL flexShrink;
|
||||
@property (nonatomic, readwrite) ASRelativeDimension flexBasis;
|
||||
@property (nonatomic, readwrite) ASStackLayoutAlignSelf alignSelf;
|
||||
@property (nonatomic, readwrite) CGFloat ascender;
|
||||
@property (nonatomic, readwrite) CGFloat descender;
|
||||
|
||||
#pragma mark - ASStaticLayoutable
|
||||
|
||||
@property (nonatomic, readwrite) ASRelativeSizeRange sizeRange;
|
||||
@property (nonatomic, readwrite) CGPoint layoutPosition;
|
||||
|
||||
@end
|
||||
262
AsyncDisplayKit/Layout/ASLayoutOptions.mm
Normal file
262
AsyncDisplayKit/Layout/ASLayoutOptions.mm
Normal file
@@ -0,0 +1,262 @@
|
||||
/*
|
||||
* 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 "ASLayoutOptions.h"
|
||||
|
||||
#import <AsyncDisplayKit/ASAssert.h>
|
||||
#import <AsyncDisplayKit/ASThread.h>
|
||||
#import <AsyncDisplayKit/ASTextNode.h>
|
||||
#import "ASInternalHelpers.h"
|
||||
|
||||
@interface ASLayoutOptions()
|
||||
{
|
||||
ASDN::RecursiveMutex _propertyLock;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation ASLayoutOptions
|
||||
|
||||
@synthesize spacingBefore = _spacingBefore;
|
||||
@synthesize spacingAfter = _spacingAfter;
|
||||
@synthesize flexGrow = _flexGrow;
|
||||
@synthesize flexShrink = _flexShrink;
|
||||
@synthesize flexBasis = _flexBasis;
|
||||
@synthesize alignSelf = _alignSelf;
|
||||
|
||||
@synthesize ascender = _ascender;
|
||||
@synthesize descender = _descender;
|
||||
|
||||
@synthesize sizeRange = _sizeRange;
|
||||
@synthesize layoutPosition = _layoutPosition;
|
||||
|
||||
static Class gDefaultLayoutOptionsClass = nil;
|
||||
+ (void)setDefaultLayoutOptionsClass:(Class)defaultLayoutOptionsClass
|
||||
{
|
||||
gDefaultLayoutOptionsClass = defaultLayoutOptionsClass;
|
||||
}
|
||||
|
||||
+ (Class)defaultLayoutOptionsClass
|
||||
{
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
if (gDefaultLayoutOptionsClass == nil) {
|
||||
// If someone is asking for this and it hasn't been customized yet, use the default.
|
||||
gDefaultLayoutOptionsClass = [ASLayoutOptions class];
|
||||
}
|
||||
});
|
||||
return gDefaultLayoutOptionsClass;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
return [self initWithLayoutable:nil];
|
||||
}
|
||||
|
||||
- (instancetype)initWithLayoutable:(id<ASLayoutable>)layoutable;
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
|
||||
self.flexBasis = ASRelativeDimensionUnconstrained;
|
||||
self.sizeRange = ASRelativeSizeRangeMake(ASRelativeSizeMakeWithCGSize(CGSizeZero), ASRelativeSizeMakeWithCGSize(CGSizeZero));
|
||||
self.layoutPosition = CGPointZero;
|
||||
|
||||
// The following properties use a default value of 0 which we do not need to assign.
|
||||
// self.spacingBefore = 0;
|
||||
// self.spacingAfter = 0;
|
||||
// self.flexGrow = NO;
|
||||
// self.flexShrink = NO;
|
||||
// self.alignSelf = ASStackLayoutAlignSelfAuto;
|
||||
// self.ascender = 0;
|
||||
// self.descender = 0;
|
||||
|
||||
[self setValuesFromLayoutable:layoutable];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - NSCopying
|
||||
- (id)copyWithZone:(NSZone *)zone
|
||||
{
|
||||
ASLayoutOptions *copy = [[[self class] alloc] init];
|
||||
[copy copyIntoOptions:self];
|
||||
return copy;
|
||||
}
|
||||
|
||||
- (void)copyIntoOptions:(ASLayoutOptions *)layoutOptions
|
||||
{
|
||||
ASDN::MutexLocker l(_propertyLock);
|
||||
self.flexBasis = layoutOptions.flexBasis;
|
||||
self.spacingAfter = layoutOptions.spacingAfter;
|
||||
self.spacingBefore = layoutOptions.spacingBefore;
|
||||
self.flexGrow = layoutOptions.flexGrow;
|
||||
self.flexShrink = layoutOptions.flexShrink;
|
||||
|
||||
self.ascender = layoutOptions.ascender;
|
||||
self.descender = layoutOptions.descender;
|
||||
|
||||
self.sizeRange = layoutOptions.sizeRange;
|
||||
self.layoutPosition = layoutOptions.layoutPosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an id<ASLayoutable>, set up layout options that are intrinsically defined by the layoutable.
|
||||
*
|
||||
* While this could be done in the layoutable object itself, moving the logic into the ASLayoutOptions class
|
||||
* allows a custom spec to set up defaults without needing to alter the layoutable itself. For example,
|
||||
* image you were creating a custom baseline spec that needed ascender/descender. To assign values automatically
|
||||
* when a text node's attribute string is set, you would need to subclass ASTextNode and assign the values in the
|
||||
* override of setAttributeString. However, assigning the defaults in an ASLayoutOptions subclass's
|
||||
* setValuesFromLayoutable allows you to create a custom spec without the need to create a
|
||||
* subclass of ASTextNode.
|
||||
*
|
||||
* @param layoutable The layoutable object to inspect for default intrinsic layout option values
|
||||
*/
|
||||
- (void)setValuesFromLayoutable:(id<ASLayoutable>)layoutable
|
||||
{
|
||||
ASDN::MutexLocker l(_propertyLock);
|
||||
if ([layoutable isKindOfClass:[ASDisplayNode class]]) {
|
||||
ASDisplayNode *displayNode = (ASDisplayNode *)layoutable;
|
||||
self.sizeRange = ASRelativeSizeRangeMake(ASRelativeSizeMakeWithCGSize(displayNode.preferredFrameSize), ASRelativeSizeMakeWithCGSize(displayNode.preferredFrameSize));
|
||||
|
||||
if ([layoutable isKindOfClass:[ASTextNode class]]) {
|
||||
ASTextNode *textNode = (ASTextNode *)layoutable;
|
||||
NSAttributedString *attributedString = textNode.attributedString;
|
||||
if (attributedString.length > 0) {
|
||||
CGFloat screenScale = ASScreenScale();
|
||||
self.ascender = round([[attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * screenScale)/screenScale;
|
||||
self.descender = round([[attributedString attribute:NSFontAttributeName atIndex:attributedString.length - 1 effectiveRange:NULL] descender] * screenScale)/screenScale;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
- (CGFloat)spacingAfter
|
||||
{
|
||||
ASDN::MutexLocker l(_propertyLock);
|
||||
return _spacingAfter;
|
||||
}
|
||||
|
||||
- (void)setSpacingAfter:(CGFloat)spacingAfter
|
||||
{
|
||||
ASDN::MutexLocker l(_propertyLock);
|
||||
_spacingAfter = spacingAfter;
|
||||
}
|
||||
|
||||
- (CGFloat)spacingBefore
|
||||
{
|
||||
ASDN::MutexLocker l(_propertyLock);
|
||||
return _spacingBefore;
|
||||
}
|
||||
|
||||
- (void)setSpacingBefore:(CGFloat)spacingBefore
|
||||
{
|
||||
ASDN::MutexLocker l(_propertyLock);
|
||||
_spacingBefore = spacingBefore;
|
||||
}
|
||||
|
||||
- (BOOL)flexGrow
|
||||
{
|
||||
ASDN::MutexLocker l(_propertyLock);
|
||||
return _flexGrow;
|
||||
}
|
||||
|
||||
- (void)setFlexGrow:(BOOL)flexGrow
|
||||
{
|
||||
ASDN::MutexLocker l(_propertyLock);
|
||||
_flexGrow = flexGrow;
|
||||
}
|
||||
|
||||
- (BOOL)flexShrink
|
||||
{
|
||||
ASDN::MutexLocker l(_propertyLock);
|
||||
return _flexShrink;
|
||||
}
|
||||
|
||||
- (void)setFlexShrink:(BOOL)flexShrink
|
||||
{
|
||||
ASDN::MutexLocker l(_propertyLock);
|
||||
_flexShrink = flexShrink;
|
||||
}
|
||||
|
||||
- (ASRelativeDimension)flexBasis
|
||||
{
|
||||
ASDN::MutexLocker l(_propertyLock);
|
||||
return _flexBasis;
|
||||
}
|
||||
|
||||
- (void)setFlexBasis:(ASRelativeDimension)flexBasis
|
||||
{
|
||||
ASDN::MutexLocker l(_propertyLock);
|
||||
_flexBasis = flexBasis;
|
||||
}
|
||||
|
||||
- (ASStackLayoutAlignSelf)alignSelf
|
||||
{
|
||||
ASDN::MutexLocker l(_propertyLock);
|
||||
return _alignSelf;
|
||||
}
|
||||
|
||||
- (void)setAlignSelf:(ASStackLayoutAlignSelf)alignSelf
|
||||
{
|
||||
ASDN::MutexLocker l(_propertyLock);
|
||||
_alignSelf = alignSelf;
|
||||
}
|
||||
|
||||
- (CGFloat)ascender
|
||||
{
|
||||
ASDN::MutexLocker l(_propertyLock);
|
||||
return _ascender;
|
||||
}
|
||||
|
||||
- (void)setAscender:(CGFloat)ascender
|
||||
{
|
||||
ASDN::MutexLocker l(_propertyLock);
|
||||
_ascender = ascender;
|
||||
}
|
||||
|
||||
- (CGFloat)descender
|
||||
{
|
||||
ASDN::MutexLocker l(_propertyLock);
|
||||
return _descender;
|
||||
}
|
||||
|
||||
- (void)setDescender:(CGFloat)descender
|
||||
{
|
||||
ASDN::MutexLocker l(_propertyLock);
|
||||
_descender = descender;
|
||||
}
|
||||
|
||||
- (ASRelativeSizeRange)sizeRange
|
||||
{
|
||||
ASDN::MutexLocker l(_propertyLock);
|
||||
return _sizeRange;
|
||||
}
|
||||
|
||||
- (void)setSizeRange:(ASRelativeSizeRange)sizeRange
|
||||
{
|
||||
ASDN::MutexLocker l(_propertyLock);
|
||||
_sizeRange = sizeRange;
|
||||
}
|
||||
|
||||
- (CGPoint)layoutPosition
|
||||
{
|
||||
ASDN::MutexLocker l(_propertyLock);
|
||||
return _layoutPosition;
|
||||
}
|
||||
|
||||
- (void)setLayoutPosition:(CGPoint)layoutPosition
|
||||
{
|
||||
ASDN::MutexLocker l(_propertyLock);
|
||||
_layoutPosition = layoutPosition;
|
||||
}
|
||||
|
||||
@end
|
||||
144
AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm
Normal file
144
AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm
Normal file
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* 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 "ASLayoutOptionsPrivate.h"
|
||||
#import <AsyncDisplayKit/ASDisplayNodeInternal.h>
|
||||
#import "ASThread.h"
|
||||
|
||||
|
||||
/**
|
||||
* Both an ASDisplayNode and an ASLayoutSpec conform to ASLayoutable. There are several properties
|
||||
* in ASLayoutable that are used as layoutOptions when a node or spec is used in a layout spec.
|
||||
* These properties are provided for convenience, as they are forwards to the node or spec's
|
||||
* ASLayoutOptions class. Instead of duplicating the property forwarding in both classes, we
|
||||
* create a define that allows us to easily implement the forwards in one place.
|
||||
*
|
||||
* If you create a custom layout spec, we recommend this stragety if you decide to extend
|
||||
* ASDisplayNode and ASLAyoutSpec to provide convenience properties for any options that your
|
||||
* layoutSpec may require.
|
||||
*/
|
||||
#define ASLayoutOptionsForwarding \
|
||||
- (ASLayoutOptions *)layoutOptions\
|
||||
{\
|
||||
ASDN::MutexLocker l(_layoutOptionsLock);\
|
||||
if (_layoutOptions == nil) {\
|
||||
_layoutOptions = [[[ASLayoutOptions defaultLayoutOptionsClass] alloc] initWithLayoutable:self];\
|
||||
}\
|
||||
return _layoutOptions;\
|
||||
}\
|
||||
\
|
||||
- (CGFloat)spacingBefore\
|
||||
{\
|
||||
return self.layoutOptions.spacingBefore;\
|
||||
}\
|
||||
\
|
||||
- (void)setSpacingBefore:(CGFloat)spacingBefore\
|
||||
{\
|
||||
self.layoutOptions.spacingBefore = spacingBefore;\
|
||||
}\
|
||||
\
|
||||
- (CGFloat)spacingAfter\
|
||||
{\
|
||||
return self.layoutOptions.spacingAfter;\
|
||||
}\
|
||||
\
|
||||
- (void)setSpacingAfter:(CGFloat)spacingAfter\
|
||||
{\
|
||||
self.layoutOptions.spacingAfter = spacingAfter;\
|
||||
}\
|
||||
\
|
||||
- (BOOL)flexGrow\
|
||||
{\
|
||||
return self.layoutOptions.flexGrow;\
|
||||
}\
|
||||
\
|
||||
- (void)setFlexGrow:(BOOL)flexGrow\
|
||||
{\
|
||||
self.layoutOptions.flexGrow = flexGrow;\
|
||||
}\
|
||||
\
|
||||
- (BOOL)flexShrink\
|
||||
{\
|
||||
return self.layoutOptions.flexShrink;\
|
||||
}\
|
||||
\
|
||||
- (void)setFlexShrink:(BOOL)flexShrink\
|
||||
{\
|
||||
self.layoutOptions.flexShrink = flexShrink;\
|
||||
}\
|
||||
\
|
||||
- (ASRelativeDimension)flexBasis\
|
||||
{\
|
||||
return self.layoutOptions.flexBasis;\
|
||||
}\
|
||||
\
|
||||
- (void)setFlexBasis:(ASRelativeDimension)flexBasis\
|
||||
{\
|
||||
self.layoutOptions.flexBasis = flexBasis;\
|
||||
}\
|
||||
\
|
||||
- (ASStackLayoutAlignSelf)alignSelf\
|
||||
{\
|
||||
return self.layoutOptions.alignSelf;\
|
||||
}\
|
||||
\
|
||||
- (void)setAlignSelf:(ASStackLayoutAlignSelf)alignSelf\
|
||||
{\
|
||||
self.layoutOptions.alignSelf = alignSelf;\
|
||||
}\
|
||||
\
|
||||
- (CGFloat)ascender\
|
||||
{\
|
||||
return self.layoutOptions.ascender;\
|
||||
}\
|
||||
\
|
||||
- (void)setAscender:(CGFloat)ascender\
|
||||
{\
|
||||
self.layoutOptions.ascender = ascender;\
|
||||
}\
|
||||
\
|
||||
- (CGFloat)descender\
|
||||
{\
|
||||
return self.layoutOptions.descender;\
|
||||
}\
|
||||
\
|
||||
- (void)setDescender:(CGFloat)descender\
|
||||
{\
|
||||
self.layoutOptions.descender = descender;\
|
||||
}\
|
||||
\
|
||||
- (ASRelativeSizeRange)sizeRange\
|
||||
{\
|
||||
return self.layoutOptions.sizeRange;\
|
||||
}\
|
||||
\
|
||||
- (void)setSizeRange:(ASRelativeSizeRange)sizeRange\
|
||||
{\
|
||||
self.layoutOptions.sizeRange = sizeRange;\
|
||||
}\
|
||||
\
|
||||
- (CGPoint)layoutPosition\
|
||||
{\
|
||||
return self.layoutOptions.layoutPosition;\
|
||||
}\
|
||||
\
|
||||
- (void)setLayoutPosition:(CGPoint)position\
|
||||
{\
|
||||
self.layoutOptions.layoutPosition = position;\
|
||||
}\
|
||||
|
||||
|
||||
@implementation ASDisplayNode(ASLayoutOptions)
|
||||
ASLayoutOptionsForwarding
|
||||
@end
|
||||
|
||||
@implementation ASLayoutSpec(ASLayoutOptions)
|
||||
ASLayoutOptionsForwarding
|
||||
@end
|
||||
@@ -8,18 +8,91 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#import <AsyncDisplayKit/ASStackLayoutable.h>
|
||||
#import <AsyncDisplayKit/ASLayoutable.h>
|
||||
|
||||
/** A layout spec is an immutable object that describes a layout, loosely inspired by React. */
|
||||
@interface ASLayoutSpec : NSObject <ASStackLayoutable>
|
||||
@interface ASLayoutSpec : NSObject <ASLayoutable>
|
||||
|
||||
/**
|
||||
Creation of a layout spec should only happen by a user in layoutSpecThatFits:. During that method, a
|
||||
layout spec can be created and mutated. Once it is passed back to ASDK, the isMutable flag will be
|
||||
set to NO and any further mutations will cause an assert.
|
||||
* Creation of a layout spec should only happen by a user in layoutSpecThatFits:. During that method, a
|
||||
* layout spec can be created and mutated. Once it is passed back to ASDK, the isMutable flag will be
|
||||
* set to NO and any further mutations will cause an assert.
|
||||
*/
|
||||
@property (nonatomic, assign) BOOL isMutable;
|
||||
|
||||
- (instancetype)init;
|
||||
|
||||
/**
|
||||
* Adds a child to this layout spec using a default identifier.
|
||||
*
|
||||
* @param child A child to be added.
|
||||
*
|
||||
* @discussion Every ASLayoutSpec must act on at least one child. The ASLayoutSpec base class takes the
|
||||
* reponsibility of holding on to the spec children. Some layout specs, like ASInsetLayoutSpec,
|
||||
* only require a single child.
|
||||
*
|
||||
* For layout specs that require a known number of children (ASBackgroundLayoutSpec, for example)
|
||||
* a subclass should use this method to set the "primary" child. It can then use setChild:forIdentifier:
|
||||
* to set any other required children. Ideally a subclass would hide this from the user, and use the
|
||||
* setChild:forIdentifier: internally. For example, ASBackgroundLayoutSpec exposes a backgroundChild
|
||||
* property that behind the scenes is calling setChild:forIdentifier:.
|
||||
*/
|
||||
- (void)setChild:(id<ASLayoutable>)child;
|
||||
|
||||
/**
|
||||
* Adds a child with the given identifier to this layout spec.
|
||||
*
|
||||
* @param child A child to be added.
|
||||
*
|
||||
* @param identifier An identifier associated with the child.
|
||||
*
|
||||
* @discussion Every ASLayoutSpec must act on at least one child. The ASLayoutSpec base class takes the
|
||||
* reponsibility of holding on to the spec children. Some layout specs, like ASInsetLayoutSpec,
|
||||
* only require a single child.
|
||||
*
|
||||
* For layout specs that require a known number of children (ASBackgroundLayoutSpec, for example)
|
||||
* a subclass should use the setChild method to set the "primary" child. It can then use this method
|
||||
* to set any other required children. Ideally a subclass would hide this from the user, and use the
|
||||
* setChild:forIdentifier: internally. For example, ASBackgroundLayoutSpec exposes a backgroundChild
|
||||
* property that behind the scenes is calling setChild:forIdentifier:.
|
||||
*/
|
||||
- (void)setChild:(id<ASLayoutable>)child forIdentifier:(NSString *)identifier;
|
||||
|
||||
/**
|
||||
* Adds childen to this layout spec.
|
||||
*
|
||||
* @param children An array of ASLayoutable children to be added.
|
||||
*
|
||||
* @discussion Every ASLayoutSpec must act on at least one child. The ASLayoutSpec base class takes the
|
||||
* reponsibility of holding on to the spec children. Some layout specs, like ASStackLayoutSpec,
|
||||
* can take an unknown number of children. In this case, the this method should be used.
|
||||
* For good measure, in these layout specs it probably makes sense to define
|
||||
* setChild: and setChild:forIdentifier: methods to do something appropriate or to assert.
|
||||
*/
|
||||
- (void)setChildren:(NSArray *)children;
|
||||
|
||||
/**
|
||||
* Get child methods
|
||||
*
|
||||
* There is a corresponding "getChild" method for the above "setChild" methods. If a subclass
|
||||
* has extra layoutable children, it is recommended to make a corresponding get method for that
|
||||
* child. For example, the ASBackgroundLayoutSpec responds to backgroundChild.
|
||||
*
|
||||
* If a get method is called on a spec that doesn't make sense, then the standard is to assert.
|
||||
* For example, calling children on an ASInsetLayoutSpec will assert.
|
||||
*/
|
||||
|
||||
/** Returns the child added to this layout spec using the default identifier. */
|
||||
- (id<ASLayoutable>)child;
|
||||
|
||||
/**
|
||||
* Returns the child added to this layout spec using the given identifier.
|
||||
*
|
||||
* @param identifier An identifier associated withe the child.
|
||||
*/
|
||||
- (id<ASLayoutable>)childForIdentifier:(NSString *)identifier;
|
||||
|
||||
/** Returns all children added to this layout spec. */
|
||||
- (NSArray *)children;
|
||||
|
||||
@end
|
||||
|
||||
@@ -15,22 +15,32 @@
|
||||
|
||||
#import "ASInternalHelpers.h"
|
||||
#import "ASLayout.h"
|
||||
#import "ASLayoutOptions.h"
|
||||
#import "ASLayoutOptionsPrivate.h"
|
||||
#import "ASThread.h"
|
||||
|
||||
#import <objc/runtime.h>
|
||||
|
||||
static NSString * const kDefaultChildKey = @"kDefaultChildKey";
|
||||
static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey";
|
||||
|
||||
@interface ASLayoutSpec()
|
||||
@property (nonatomic, strong) NSMutableDictionary *layoutChildren;
|
||||
@end
|
||||
|
||||
@implementation ASLayoutSpec
|
||||
|
||||
@synthesize spacingBefore = _spacingBefore;
|
||||
@synthesize spacingAfter = _spacingAfter;
|
||||
@synthesize flexGrow = _flexGrow;
|
||||
@synthesize flexShrink = _flexShrink;
|
||||
@synthesize flexBasis = _flexBasis;
|
||||
@synthesize alignSelf = _alignSelf;
|
||||
// these dynamic properties all defined in ASLayoutOptionsPrivate.m
|
||||
@dynamic spacingAfter, spacingBefore, flexGrow, flexShrink, flexBasis, alignSelf, ascender, descender, sizeRange, layoutPosition, layoutOptions;
|
||||
@synthesize layoutChildren = _layoutChildren;
|
||||
@synthesize isFinalLayoutable = _isFinalLayoutable;
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if (!(self = [super init])) {
|
||||
return nil;
|
||||
}
|
||||
_flexBasis = ASRelativeDimensionUnconstrained;
|
||||
_layoutChildren = [NSMutableDictionary dictionary];
|
||||
_isMutable = YES;
|
||||
return self;
|
||||
}
|
||||
@@ -42,4 +52,75 @@
|
||||
return [ASLayout layoutWithLayoutableObject:self size:constrainedSize.min];
|
||||
}
|
||||
|
||||
- (id<ASLayoutable>)finalLayoutable
|
||||
{
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setChild:(id<ASLayoutable>)child;
|
||||
{
|
||||
[self setChild:child forIdentifier:kDefaultChildKey];
|
||||
}
|
||||
|
||||
- (id<ASLayoutable>)layoutableToAddFromLayoutable:(id<ASLayoutable>)child
|
||||
{
|
||||
if (self.isFinalLayoutable == NO) {
|
||||
|
||||
// If you are getting recursion crashes here after implementing finalLayoutable, make sure
|
||||
// that you are setting isFinalLayoutable flag to YES. This must be one BEFORE adding a child
|
||||
// to the new ASLayoutable.
|
||||
//
|
||||
// For example:
|
||||
//- (id<ASLayoutable>)finalLayoutable
|
||||
//{
|
||||
// ASInsetLayoutSpec *insetSpec = [[ASInsetLayoutSpec alloc] init];
|
||||
// insetSpec.insets = UIEdgeInsetsMake(10,10,10,10);
|
||||
// insetSpec.isFinalLayoutable = YES;
|
||||
// [insetSpec setChild:self];
|
||||
// return insetSpec;
|
||||
//}
|
||||
|
||||
id<ASLayoutable> finalLayoutable = [child finalLayoutable];
|
||||
if (finalLayoutable != child) {
|
||||
[finalLayoutable.layoutOptions copyIntoOptions:child.layoutOptions];
|
||||
return finalLayoutable;
|
||||
}
|
||||
}
|
||||
return child;
|
||||
}
|
||||
|
||||
- (void)setChild:(id<ASLayoutable>)child forIdentifier:(NSString *)identifier
|
||||
{
|
||||
ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable");
|
||||
self.layoutChildren[identifier] = [self layoutableToAddFromLayoutable:child];;
|
||||
}
|
||||
|
||||
- (void)setChildren:(NSArray *)children
|
||||
{
|
||||
ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable");
|
||||
|
||||
NSMutableArray *finalChildren = [NSMutableArray arrayWithCapacity:children.count];
|
||||
for (id<ASLayoutable> child in children) {
|
||||
[finalChildren addObject:[self layoutableToAddFromLayoutable:child]];
|
||||
}
|
||||
|
||||
self.layoutChildren[kDefaultChildrenKey] = [NSArray arrayWithArray:finalChildren];
|
||||
}
|
||||
|
||||
- (id<ASLayoutable>)childForIdentifier:(NSString *)identifier
|
||||
{
|
||||
return self.layoutChildren[identifier];
|
||||
}
|
||||
|
||||
- (id<ASLayoutable>)child
|
||||
{
|
||||
return self.layoutChildren[kDefaultChildKey];
|
||||
}
|
||||
|
||||
- (NSArray *)children
|
||||
{
|
||||
return self.layoutChildren[kDefaultChildrenKey];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@@ -9,16 +9,33 @@
|
||||
*/
|
||||
|
||||
#import <AsyncDisplayKit/ASDimension.h>
|
||||
#import <AsyncDisplayKit/ASRelativeSize.h>
|
||||
#import <AsyncDisplayKit/ASStackLayoutDefines.h>
|
||||
#import <AsyncDisplayKit/ASStackLayoutable.h>
|
||||
#import <AsyncDisplayKit/ASStaticLayoutable.h>
|
||||
|
||||
#import <AsyncDisplayKit/ASLayoutablePrivate.h>
|
||||
|
||||
@class ASLayout;
|
||||
@class ASLayoutSpec;
|
||||
|
||||
/**
|
||||
* The ASLayoutable protocol declares a method for measuring the layout of an object. A class must implement the method
|
||||
* so that instances of that class can be used to build layout trees. The protocol also provides information
|
||||
* about how an object should be laid out within an ASStackLayoutSpec.
|
||||
* The ASLayoutable protocol declares a method for measuring the layout of an object. A layout
|
||||
* is defined by an ASLayout return value, and must specify 1) the size (but not position) of the
|
||||
* layoutable object, and 2) the size and position of all of its immediate child objects. The tree
|
||||
* recursion is driven by parents requesting layouts from their children in order to determine their
|
||||
* size, followed by the parents setting the position of the children once the size is known
|
||||
*
|
||||
* The protocol also implements a "family" of Layoutable protocols. These protocols contain layout
|
||||
* options that can be used for specific layout specs. For example, ASStackLayoutSpec has options
|
||||
* defining how a layoutable should shrink or grow based upon available space.
|
||||
*
|
||||
* These layout options are all stored in an ASLayoutOptions class (that is defined in ASLayoutablePrivate).
|
||||
* Generally you needn't worry about the layout options class, as the layoutable protocols allow all direct
|
||||
* access to the options via convenience properties. If you are creating custom layout spec, then you can
|
||||
* extend the backing layout options class to accomodate any new layout options.
|
||||
*/
|
||||
@protocol ASLayoutable <NSObject>
|
||||
@protocol ASLayoutable <ASStackLayoutable, ASStaticLayoutable, ASLayoutablePrivate>
|
||||
|
||||
/**
|
||||
* @abstract Calculate a layout based on given size range.
|
||||
@@ -29,4 +46,64 @@
|
||||
*/
|
||||
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize;
|
||||
|
||||
|
||||
#pragma mark - Layout options from the Layoutable Protocols
|
||||
|
||||
#pragma mark - ASStackLayoutable
|
||||
/**
|
||||
* @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 Used for baseline alignment. The distance from the top of the object to its baseline.
|
||||
*/
|
||||
@property (nonatomic, readwrite) CGFloat ascender;
|
||||
|
||||
/**
|
||||
* @abstract Used for baseline alignment. The distance from the baseline of the object to its bottom.
|
||||
*/
|
||||
@property (nonatomic, readwrite) CGFloat descender;
|
||||
|
||||
#pragma mark - ASStaticLayoutable
|
||||
/**
|
||||
If specified, the child's size is restricted according to this size. Percentages are resolved relative to the static layout spec.
|
||||
*/
|
||||
@property (nonatomic, assign) ASRelativeSizeRange sizeRange;
|
||||
|
||||
/** The position of this object within its parent spec. */
|
||||
@property (nonatomic, assign) CGPoint layoutPosition;
|
||||
|
||||
@end
|
||||
|
||||
47
AsyncDisplayKit/Layout/ASLayoutablePrivate.h
Normal file
47
AsyncDisplayKit/Layout/ASLayoutablePrivate.h
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 <Foundation/Foundation.h>
|
||||
|
||||
@class ASLayoutSpec;
|
||||
@class ASLayoutOptions;
|
||||
@protocol ASLayoutable;
|
||||
|
||||
/**
|
||||
* The base protocol for ASLayoutable. Generally the methods/properties in this class do not need to be
|
||||
* called by the end user and are only called internally. However, there may be a case where the methods are useful.
|
||||
*/
|
||||
@protocol ASLayoutablePrivate <NSObject>
|
||||
|
||||
/**
|
||||
* @abstract This method can be used to give the user a chance to wrap an ASLayoutable in an ASLayoutSpec
|
||||
* just before it is added to a parent ASLayoutSpec. For example, if you wanted an ASTextNode that was always
|
||||
* inside of an ASInsetLayoutSpec, you could subclass ASTextNode and implement finalLayoutable so that it wraps
|
||||
* itself in an inset spec.
|
||||
*
|
||||
* Note that any ASLayoutable other than self that is returned MUST set isFinalLayoutable to YES. Make sure
|
||||
* to do this BEFORE adding a child to the ASLayoutable.
|
||||
*
|
||||
* @return The layoutable that will be added to the parent layout spec. Defaults to self.
|
||||
*/
|
||||
- (id<ASLayoutable>)finalLayoutable;
|
||||
|
||||
/**
|
||||
* A flag to indicate that this ASLayoutable was created in finalLayoutable. This MUST be set to YES
|
||||
* before adding a child to this layoutable.
|
||||
*/
|
||||
@property (nonatomic, assign) BOOL isFinalLayoutable;
|
||||
|
||||
|
||||
/**
|
||||
* The class that holds all of the layoutOptions set on an ASLayoutable.
|
||||
*/
|
||||
@property (nonatomic, strong, readonly) ASLayoutOptions *layoutOptions;
|
||||
@end
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
@interface ASOverlayLayoutSpec : ASLayoutSpec
|
||||
|
||||
@property (nonatomic, strong) id<ASLayoutable> child;
|
||||
@property (nonatomic, strong) id<ASLayoutable> overlay;
|
||||
|
||||
+ (instancetype)overlayLayoutSpecWithChild:(id<ASLayoutable>)child overlay:(id<ASLayoutable>)overlay;
|
||||
|
||||
@@ -14,11 +14,9 @@
|
||||
#import "ASBaseDefines.h"
|
||||
#import "ASLayout.h"
|
||||
|
||||
static NSString * const kOverlayChildKey = @"kOverlayChildKey";
|
||||
|
||||
@implementation ASOverlayLayoutSpec
|
||||
{
|
||||
id<ASLayoutable> _overlay;
|
||||
id<ASLayoutable> _child;
|
||||
}
|
||||
|
||||
- (instancetype)initWithChild:(id<ASLayoutable>)child overlay:(id<ASLayoutable>)overlay
|
||||
{
|
||||
@@ -26,8 +24,8 @@
|
||||
return nil;
|
||||
}
|
||||
ASDisplayNodeAssertNotNil(child, @"Child that will be overlayed on shouldn't be nil");
|
||||
_overlay = overlay;
|
||||
_child = child;
|
||||
self.overlay = overlay;
|
||||
[self setChild:child];
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -36,16 +34,14 @@
|
||||
return [[self alloc] initWithChild:child overlay:overlay];
|
||||
}
|
||||
|
||||
- (void)setChild:(id<ASLayoutable>)child
|
||||
{
|
||||
ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable");
|
||||
_child = child;
|
||||
}
|
||||
|
||||
- (void)setOverlay:(id<ASLayoutable>)overlay
|
||||
{
|
||||
ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable");
|
||||
_overlay = overlay;
|
||||
[super setChild:overlay forIdentifier:kOverlayChildKey];
|
||||
}
|
||||
|
||||
- (id<ASLayoutable>)overlay
|
||||
{
|
||||
return [super childForIdentifier:kOverlayChildKey];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -53,11 +49,11 @@
|
||||
*/
|
||||
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
|
||||
{
|
||||
ASLayout *contentsLayout = [_child measureWithSizeRange:constrainedSize];
|
||||
ASLayout *contentsLayout = [self.child measureWithSizeRange:constrainedSize];
|
||||
contentsLayout.position = CGPointZero;
|
||||
NSMutableArray *sublayouts = [NSMutableArray arrayWithObject:contentsLayout];
|
||||
if (_overlay) {
|
||||
ASLayout *overlayLayout = [_overlay measureWithSizeRange:{contentsLayout.size, contentsLayout.size}];
|
||||
if (self.overlay) {
|
||||
ASLayout *overlayLayout = [self.overlay measureWithSizeRange:{contentsLayout.size, contentsLayout.size}];
|
||||
overlayLayout.position = CGPointZero;
|
||||
[sublayouts addObject:overlayLayout];
|
||||
}
|
||||
@@ -65,4 +61,15 @@
|
||||
return [ASLayout layoutWithLayoutableObject:self size:contentsLayout.size sublayouts:sublayouts];
|
||||
}
|
||||
|
||||
- (void)setChildren:(NSArray *)children
|
||||
{
|
||||
ASDisplayNodeAssert(NO, @"not supported by this layout spec");
|
||||
}
|
||||
|
||||
- (NSArray *)children
|
||||
{
|
||||
ASDisplayNodeAssert(NO, @"not supported by this layout spec");
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -31,8 +31,6 @@
|
||||
**/
|
||||
@interface ASRatioLayoutSpec : ASLayoutSpec
|
||||
|
||||
|
||||
@property (nonatomic, strong) id<ASLayoutable> child;
|
||||
@property (nonatomic, assign) CGFloat ratio;
|
||||
|
||||
+ (instancetype)ratioLayoutSpecWithRatio:(CGFloat)ratio child:(id<ASLayoutable>)child;
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
@implementation ASRatioLayoutSpec
|
||||
{
|
||||
CGFloat _ratio;
|
||||
id<ASLayoutable> _child;
|
||||
}
|
||||
|
||||
+ (instancetype)ratioLayoutSpecWithRatio:(CGFloat)ratio child:(id<ASLayoutable>)child
|
||||
@@ -38,16 +37,10 @@
|
||||
ASDisplayNodeAssertNotNil(child, @"Child cannot be nil");
|
||||
ASDisplayNodeAssert(ratio > 0, @"Ratio should be strictly positive, but received %f", ratio);
|
||||
_ratio = ratio;
|
||||
_child = child;
|
||||
[self setChild:child];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setChild:(id<ASLayoutable>)child
|
||||
{
|
||||
ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable");
|
||||
_child = child;
|
||||
}
|
||||
|
||||
- (void)setRatio:(CGFloat)ratio
|
||||
{
|
||||
ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable");
|
||||
@@ -77,9 +70,20 @@
|
||||
|
||||
// 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];
|
||||
ASLayout *sublayout = [self.child measureWithSizeRange:childRange];
|
||||
sublayout.position = CGPointZero;
|
||||
return [ASLayout layoutWithLayoutableObject:self size:sublayout.size sublayouts:@[sublayout]];
|
||||
}
|
||||
|
||||
- (void)setChildren:(NSArray *)children
|
||||
{
|
||||
ASDisplayNodeAssert(NO, @"not supported by this layout spec");
|
||||
}
|
||||
|
||||
- (NSArray *)children
|
||||
{
|
||||
ASDisplayNodeAssert(NO, @"not supported by this layout spec");
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -44,7 +44,11 @@ typedef NS_ENUM(NSUInteger, ASStackLayoutAlignItems) {
|
||||
/** Center children on cross axis */
|
||||
ASStackLayoutAlignItemsCenter,
|
||||
/** Expand children to fill cross axis */
|
||||
ASStackLayoutAlignItemsStretch
|
||||
ASStackLayoutAlignItemsStretch,
|
||||
/** Children align to their first baseline. Only available for horizontal stack nodes */
|
||||
ASStackLayoutAlignItemsBaselineFirst,
|
||||
/** Children align to their last baseline. Only available for horizontal stack nodes */
|
||||
ASStackLayoutAlignItemsBaselineLast,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -62,4 +66,8 @@ typedef NS_ENUM(NSUInteger, ASStackLayoutAlignSelf) {
|
||||
ASStackLayoutAlignSelfCenter,
|
||||
/** Expand to fill cross axis */
|
||||
ASStackLayoutAlignSelfStretch,
|
||||
/** Children align to their first baseline. Only available for horizontal stack nodes */
|
||||
ASStackLayoutAlignSelfBaselineFirst,
|
||||
/** Children align to their last baseline. Only available for horizontal stack nodes */
|
||||
ASStackLayoutAlignSelfBaselineLast,
|
||||
};
|
||||
|
||||
@@ -43,6 +43,8 @@
|
||||
@property (nonatomic, assign) ASStackLayoutJustifyContent justifyContent;
|
||||
/** Orientation of children along cross axis */
|
||||
@property (nonatomic, assign) ASStackLayoutAlignItems alignItems;
|
||||
/** If YES the vertical spacing between two views is measured from the last baseline of the top view to the top of the bottom view */
|
||||
@property (nonatomic, assign) BOOL baselineRelativeArrangement;
|
||||
|
||||
- (instancetype)init;
|
||||
|
||||
@@ -55,7 +57,4 @@
|
||||
*/
|
||||
+ (instancetype)stackLayoutSpecWithDirection:(ASStackLayoutDirection)direction spacing:(CGFloat)spacing justifyContent:(ASStackLayoutJustifyContent)justifyContent alignItems:(ASStackLayoutAlignItems)alignItems children:(NSArray *)children;
|
||||
|
||||
- (void)addChild:(id<ASStackLayoutable>)child;
|
||||
- (void)addChildren:(NSArray *)children;
|
||||
|
||||
@end
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#import "ASInternalHelpers.h"
|
||||
|
||||
#import "ASLayoutSpecUtilities.h"
|
||||
#import "ASStackBaselinePositionedLayout.h"
|
||||
#import "ASStackLayoutSpecUtilities.h"
|
||||
#import "ASStackPositionedLayout.h"
|
||||
#import "ASStackUnpositionedLayout.h"
|
||||
@@ -24,7 +25,7 @@
|
||||
|
||||
@implementation ASStackLayoutSpec
|
||||
{
|
||||
std::vector<id<ASStackLayoutable>> _children;
|
||||
ASDN::RecursiveMutex _propertyLock;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
@@ -47,26 +48,10 @@
|
||||
_spacing = spacing;
|
||||
_justifyContent = justifyContent;
|
||||
|
||||
_children = std::vector<id<ASStackLayoutable>>();
|
||||
for (id<ASStackLayoutable> child in children) {
|
||||
_children.push_back(child);
|
||||
}
|
||||
[self setChildren:children];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)addChild:(id<ASStackLayoutable>)child
|
||||
{
|
||||
ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable");
|
||||
_children.push_back(child);
|
||||
}
|
||||
|
||||
- (void)addChildren:(NSArray *)children
|
||||
{
|
||||
for (id<ASStackLayoutable> child in children) {
|
||||
[self addChild:child];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setDirection:(ASStackLayoutDirection)direction
|
||||
{
|
||||
ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable");
|
||||
@@ -91,13 +76,52 @@
|
||||
_spacing = spacing;
|
||||
}
|
||||
|
||||
- (void)setBaselineRelativeArrangement:(BOOL)baselineRelativeArrangement
|
||||
{
|
||||
ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable");
|
||||
_baselineRelativeArrangement = baselineRelativeArrangement;
|
||||
}
|
||||
|
||||
- (void)setChild:(id<ASLayoutable>)child forIdentifier:(NSString *)identifier
|
||||
{
|
||||
ASDisplayNodeAssert(NO, @"ASStackLayoutSpec only supports setChildren");
|
||||
}
|
||||
|
||||
- (id<ASLayoutable>)childForIdentifier:(NSString *)identifier
|
||||
{
|
||||
ASDisplayNodeAssert(NO, @"ASStackLayoutSpec only supports children");
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
|
||||
{
|
||||
ASStackLayoutSpecStyle style = {.direction = _direction, .spacing = _spacing, .justifyContent = _justifyContent, .alignItems = _alignItems};
|
||||
const auto unpositionedLayout = ASStackUnpositionedLayout::compute(_children, style, constrainedSize);
|
||||
ASStackLayoutSpecStyle style = {.direction = _direction, .spacing = _spacing, .justifyContent = _justifyContent, .alignItems = _alignItems, .baselineRelativeArrangement = _baselineRelativeArrangement};
|
||||
BOOL needsBaselinePass = _baselineRelativeArrangement || _alignItems == ASStackLayoutAlignItemsBaselineFirst || _alignItems == ASStackLayoutAlignItemsBaselineLast;
|
||||
|
||||
std::vector<id<ASLayoutable>> stackChildren = std::vector<id<ASLayoutable>>();
|
||||
for (id<ASLayoutable> child in self.children) {
|
||||
stackChildren.push_back(child);
|
||||
needsBaselinePass |= child.alignSelf == ASStackLayoutAlignSelfBaselineFirst || child.alignSelf == ASStackLayoutAlignSelfBaselineLast;
|
||||
}
|
||||
|
||||
const auto unpositionedLayout = ASStackUnpositionedLayout::compute(stackChildren, 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()];
|
||||
|
||||
CGSize finalSize = CGSizeZero;
|
||||
NSArray *sublayouts = nil;
|
||||
if (needsBaselinePass) {
|
||||
const auto baselinePositionedLayout = ASStackBaselinePositionedLayout::compute(positionedLayout, style, constrainedSize);
|
||||
ASDN::MutexLocker l(_propertyLock);
|
||||
self.ascender = baselinePositionedLayout.ascender;
|
||||
self.descender = baselinePositionedLayout.descender;
|
||||
|
||||
finalSize = directionSize(style.direction, unpositionedLayout.stackDimensionSum, baselinePositionedLayout.crossSize);
|
||||
sublayouts = [NSArray arrayWithObjects:&baselinePositionedLayout.sublayouts[0] count:baselinePositionedLayout.sublayouts.size()];
|
||||
} else {
|
||||
finalSize = directionSize(style.direction, unpositionedLayout.stackDimensionSum, positionedLayout.crossSize);
|
||||
sublayouts = [NSArray arrayWithObjects:&positionedLayout.sublayouts[0] count:positionedLayout.sublayouts.size()];
|
||||
}
|
||||
|
||||
return [ASLayout layoutWithLayoutableObject:self
|
||||
size:ASSizeRangeClamp(constrainedSize, finalSize)
|
||||
sublayouts:sublayouts];
|
||||
|
||||
@@ -8,9 +8,13 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#import <AsyncDisplayKit/ASLayoutable.h>
|
||||
#import <AsyncDisplayKit/ASDimension.h>
|
||||
#import <AsyncDisplayKit/ASStackLayoutDefines.h>
|
||||
|
||||
@protocol ASStackLayoutable <ASLayoutable>
|
||||
/**
|
||||
* Layout options that can be defined for an ASLayoutable being added to a ASStackLayoutSpec.
|
||||
*/
|
||||
@protocol ASStackLayoutable <NSObject>
|
||||
|
||||
/**
|
||||
* @abstract Additional space to place before this object in the stacking direction.
|
||||
@@ -49,4 +53,14 @@
|
||||
*/
|
||||
@property (nonatomic, readwrite) ASStackLayoutAlignSelf alignSelf;
|
||||
|
||||
/**
|
||||
* @abstract Used for baseline alignment. The distance from the top of the object to its baseline.
|
||||
*/
|
||||
@property (nonatomic, readwrite) CGFloat ascender;
|
||||
|
||||
/**
|
||||
* @abstract Used for baseline alignment. The distance from the baseline of the object to its bottom.
|
||||
*/
|
||||
@property (nonatomic, readwrite) CGFloat descender;
|
||||
|
||||
@end
|
||||
|
||||
@@ -11,43 +11,6 @@
|
||||
#import <AsyncDisplayKit/ASLayoutSpec.h>
|
||||
#import <AsyncDisplayKit/ASRelativeSize.h>
|
||||
|
||||
/**
|
||||
* An ASStaticLayoutSpecChild object wraps an ASLayoutable object and provides position and size information,
|
||||
* to be used as a child of an ASStaticLayoutSpec.
|
||||
*/
|
||||
@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;
|
||||
|
||||
/**
|
||||
* Initializer.
|
||||
*
|
||||
* @param position The position of this child within its parent spec.
|
||||
*
|
||||
* @param layoutableObject The backing ASLayoutable object of this child.
|
||||
*
|
||||
* @param size The size range that this child's size is trstricted according to.
|
||||
*/
|
||||
+ (instancetype)staticLayoutChildWithPosition:(CGPoint)position layoutableObject:(id<ASLayoutable>)layoutableObject size:(ASRelativeSizeRange)size;
|
||||
|
||||
/**
|
||||
* Convenience initializer 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.
|
||||
*
|
||||
* @param position The position of this child within its parent spec.
|
||||
*
|
||||
* @param layoutableObject The backing ASLayoutable object of this child.
|
||||
*/
|
||||
+ (instancetype)staticLayoutChildWithPosition:(CGPoint)position layoutableObject:(id<ASLayoutable>)layoutableObject;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* A layout spec that positions children at fixed positions.
|
||||
*
|
||||
@@ -56,10 +19,8 @@
|
||||
@interface ASStaticLayoutSpec : ASLayoutSpec
|
||||
|
||||
/**
|
||||
@param children Children to be positioned at fixed positions, each is of type ASStaticLayoutSpecChild.
|
||||
@param children Children to be positioned at fixed positions, each conforms to ASStaticLayoutable
|
||||
*/
|
||||
+ (instancetype)staticLayoutSpecWithChildren:(NSArray *)children;
|
||||
|
||||
- (void)addChild:(ASStaticLayoutSpecChild *)child;
|
||||
|
||||
@end
|
||||
|
||||
@@ -11,54 +11,32 @@
|
||||
#import "ASStaticLayoutSpec.h"
|
||||
|
||||
#import "ASLayoutSpecUtilities.h"
|
||||
#import "ASLayoutOptions.h"
|
||||
#import "ASInternalHelpers.h"
|
||||
#import "ASLayout.h"
|
||||
|
||||
@implementation ASStaticLayoutSpecChild
|
||||
|
||||
+ (instancetype)staticLayoutChildWithPosition:(CGPoint)position layoutableObject:(id<ASLayoutable>)layoutableObject size:(ASRelativeSizeRange)size;
|
||||
{
|
||||
ASStaticLayoutSpecChild *c = [[super alloc] init];
|
||||
if (c) {
|
||||
c->_position = position;
|
||||
c->_layoutableObject = layoutableObject;
|
||||
c->_size = size;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
+ (instancetype)staticLayoutChildWithPosition:(CGPoint)position layoutableObject:(id<ASLayoutable>)layoutableObject
|
||||
{
|
||||
return [self staticLayoutChildWithPosition:position layoutableObject:layoutableObject size:ASRelativeSizeRangeUnconstrained];
|
||||
}
|
||||
|
||||
@end
|
||||
#import "ASStaticLayoutable.h"
|
||||
|
||||
@implementation ASStaticLayoutSpec
|
||||
{
|
||||
NSArray *_children;
|
||||
}
|
||||
|
||||
+ (instancetype)staticLayoutSpecWithChildren:(NSArray *)children
|
||||
{
|
||||
return [[self alloc] initWithChildren:children];
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
return [self initWithChildren:@[]];
|
||||
}
|
||||
|
||||
- (instancetype)initWithChildren:(NSArray *)children
|
||||
{
|
||||
if (!(self = [super init])) {
|
||||
return nil;
|
||||
}
|
||||
_children = children;
|
||||
self.children = children;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)addChild:(ASStaticLayoutSpecChild *)child
|
||||
{
|
||||
ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable");
|
||||
_children = [_children arrayByAddingObject:child];
|
||||
}
|
||||
|
||||
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
|
||||
{
|
||||
CGSize size = {
|
||||
@@ -66,17 +44,17 @@
|
||||
constrainedSize.max.height
|
||||
};
|
||||
|
||||
NSMutableArray *sublayouts = [NSMutableArray arrayWithCapacity:_children.count];
|
||||
for (ASStaticLayoutSpecChild *child in _children) {
|
||||
NSMutableArray *sublayouts = [NSMutableArray arrayWithCapacity:self.children.count];
|
||||
for (id<ASLayoutable> child in self.children) {
|
||||
CGSize autoMaxSize = {
|
||||
constrainedSize.max.width - child.position.x,
|
||||
constrainedSize.max.height - child.position.y
|
||||
constrainedSize.max.width - child.layoutPosition.x,
|
||||
constrainedSize.max.height - child.layoutPosition.y
|
||||
};
|
||||
ASSizeRange childConstraint = ASRelativeSizeRangeEqualToRelativeSizeRange(ASRelativeSizeRangeUnconstrained, child.size)
|
||||
ASSizeRange childConstraint = ASRelativeSizeRangeEqualToRelativeSizeRange(ASRelativeSizeRangeUnconstrained, child.sizeRange)
|
||||
? ASSizeRangeMake({0, 0}, autoMaxSize)
|
||||
: ASRelativeSizeRangeResolve(child.size, size);
|
||||
ASLayout *sublayout = [child.layoutableObject measureWithSizeRange:childConstraint];
|
||||
sublayout.position = child.position;
|
||||
: ASRelativeSizeRangeResolve(child.sizeRange, size);
|
||||
ASLayout *sublayout = [child measureWithSizeRange:childConstraint];
|
||||
sublayout.position = child.layoutPosition;
|
||||
[sublayouts addObject:sublayout];
|
||||
}
|
||||
|
||||
@@ -99,4 +77,15 @@
|
||||
sublayouts:sublayouts];
|
||||
}
|
||||
|
||||
- (void)setChild:(id<ASLayoutable>)child forIdentifier:(NSString *)identifier
|
||||
{
|
||||
ASDisplayNodeAssert(NO, @"ASStackLayoutSpec only supports setChildren");
|
||||
}
|
||||
|
||||
- (id<ASLayoutable>)childForIdentifier:(NSString *)identifier
|
||||
{
|
||||
ASDisplayNodeAssert(NO, @"ASStackLayoutSpec only supports children");
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
26
AsyncDisplayKit/Layout/ASStaticLayoutable.h
Normal file
26
AsyncDisplayKit/Layout/ASStaticLayoutable.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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/ASRelativeSize.h>
|
||||
|
||||
/**
|
||||
* Layout options that can be defined for an ASLayoutable being added to a ASStaticLayoutSpec.
|
||||
*/
|
||||
@protocol ASStaticLayoutable
|
||||
|
||||
/**
|
||||
If specified, the child's size is restricted according to this size. Percentages are resolved relative to the static layout spec.
|
||||
*/
|
||||
@property (nonatomic, assign) ASRelativeSizeRange sizeRange;
|
||||
|
||||
/** The position of this object within its parent spec. */
|
||||
@property (nonatomic, assign) CGPoint layoutPosition;
|
||||
|
||||
@end
|
||||
@@ -83,6 +83,13 @@ static void __ASDisplayLayerDecrementConcurrentDisplayCount(BOOL displayIsAsync,
|
||||
if (self.isHidden || self.alpha <= 0.0) {
|
||||
return;
|
||||
}
|
||||
|
||||
BOOL rasterizingFromAscendent = [self __rasterizedContainerNode] != nil;
|
||||
|
||||
// if super node is rasterizing descendents, subnodes will not have had layout calls becase they don't have layers
|
||||
if (rasterizingFromAscendent) {
|
||||
[self __layout];
|
||||
}
|
||||
|
||||
// Capture these outside the display block so they are retained.
|
||||
UIColor *backgroundColor = self.backgroundColor;
|
||||
@@ -121,6 +128,11 @@ static void __ASDisplayLayerDecrementConcurrentDisplayCount(BOOL displayIsAsync,
|
||||
|
||||
CGContextTranslateCTM(context, frame.origin.x, frame.origin.y);
|
||||
|
||||
//support cornerRadius
|
||||
if (rasterizingFromAscendent && self.cornerRadius && self.clipsToBounds) {
|
||||
[[UIBezierPath bezierPathWithRoundedRect:bounds cornerRadius:self.cornerRadius] addClip];
|
||||
}
|
||||
|
||||
// Fill background if any.
|
||||
CGColorRef backgroundCGColor = backgroundColor.CGColor;
|
||||
if (backgroundColor && CGColorGetAlpha(backgroundCGColor) > 0.0) {
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#import "ASDisplayNode.h"
|
||||
#import "ASSentinel.h"
|
||||
#import "ASThread.h"
|
||||
#import "ASLayoutOptions.h"
|
||||
|
||||
BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector);
|
||||
void ASDisplayNodePerformBlockOnMainThread(void (^block)());
|
||||
@@ -59,6 +60,7 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides) {
|
||||
|
||||
ASDisplayNodeViewBlock _viewBlock;
|
||||
ASDisplayNodeLayerBlock _layerBlock;
|
||||
ASDisplayNodeDidLoadBlock _nodeLoadedBlock;
|
||||
Class _viewClass;
|
||||
Class _layerClass;
|
||||
UIView *_view;
|
||||
@@ -72,7 +74,7 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides) {
|
||||
|
||||
_ASPendingState *_pendingViewState;
|
||||
|
||||
struct {
|
||||
struct ASDisplayNodeFlags {
|
||||
// public properties
|
||||
unsigned synchronous:1;
|
||||
unsigned layerBacked:1;
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
ASDISPLAYNODE_EXTERN_C_BEGIN
|
||||
|
||||
BOOL ASSubclassOverridesSelector(Class superclass, Class subclass, SEL selector);
|
||||
BOOL ASSubclassOverridesClassSelector(Class superclass, Class subclass, SEL selector);
|
||||
|
||||
CGFloat ASScreenScale();
|
||||
|
||||
|
||||
@@ -24,6 +24,15 @@ BOOL ASSubclassOverridesSelector(Class superclass, Class subclass, SEL selector)
|
||||
return (superclassIMP != subclassIMP);
|
||||
}
|
||||
|
||||
BOOL ASSubclassOverridesClassSelector(Class superclass, Class subclass, SEL selector)
|
||||
{
|
||||
Method superclassMethod = class_getClassMethod(superclass, selector);
|
||||
Method subclassMethod = class_getClassMethod(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]) {
|
||||
|
||||
33
AsyncDisplayKit/Private/ASLayoutOptionsPrivate.h
Normal file
33
AsyncDisplayKit/Private/ASLayoutOptionsPrivate.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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/ASDisplayNode.h>
|
||||
#import <AsyncDisplayKit/ASLayoutSpec.h>
|
||||
#import <AsyncDisplayKit/ASThread.h>
|
||||
|
||||
@interface ASDisplayNode(ASLayoutOptions)<ASLayoutable>
|
||||
@end
|
||||
|
||||
@interface ASDisplayNode()
|
||||
{
|
||||
ASLayoutOptions *_layoutOptions;
|
||||
ASDN::RecursiveMutex _layoutOptionsLock;
|
||||
}
|
||||
@end
|
||||
|
||||
@interface ASLayoutSpec(ASLayoutOptions)<ASLayoutable>
|
||||
@end
|
||||
|
||||
@interface ASLayoutSpec()
|
||||
{
|
||||
ASLayoutOptions *_layoutOptions;
|
||||
ASDN::RecursiveMutex _layoutOptionsLock;
|
||||
}
|
||||
@end
|
||||
@@ -10,25 +10,16 @@
|
||||
|
||||
#import "ASLayout.h"
|
||||
#import "ASDimension.h"
|
||||
#import "ASBaselineLayoutSpec.h"
|
||||
#import "ASStackPositionedLayout.h"
|
||||
|
||||
typedef struct {
|
||||
/** Describes how the stack will be laid out */
|
||||
ASStackLayoutSpecStyle stackLayoutStyle;
|
||||
|
||||
/** The type of baseline alignment */
|
||||
ASBaselineLayoutBaselineAlignment baselineAlignment;
|
||||
} ASBaselineLayoutSpecStyle;
|
||||
|
||||
struct ASBaselinePositionedLayout {
|
||||
struct ASStackBaselinePositionedLayout {
|
||||
const std::vector<ASLayout *> sublayouts;
|
||||
const CGFloat crossSize;
|
||||
const CGFloat ascender;
|
||||
const CGFloat descender;
|
||||
|
||||
/** Given a positioned layout, computes each child position using baseline alignment. */
|
||||
static ASBaselinePositionedLayout compute(const ASStackPositionedLayout &positionedLayout,
|
||||
const ASBaselineLayoutSpecStyle &style,
|
||||
const ASSizeRange &constrainedSize);
|
||||
static ASStackBaselinePositionedLayout compute(const ASStackPositionedLayout &positionedLayout,
|
||||
const ASStackLayoutSpecStyle &style,
|
||||
const ASSizeRange &constrainedSize);
|
||||
};
|
||||
@@ -8,39 +8,41 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#import "ASBaselinePositionedLayout.h"
|
||||
#import "ASStackBaselinePositionedLayout.h"
|
||||
|
||||
#import "ASLayoutSpecUtilities.h"
|
||||
#import "ASStackLayoutSpecUtilities.h"
|
||||
#import "ASLayoutOptions.h"
|
||||
|
||||
static CGFloat baselineForItem(const ASBaselineLayoutSpecStyle &style,
|
||||
static CGFloat baselineForItem(const ASStackLayoutSpecStyle &style,
|
||||
const ASLayout *layout) {
|
||||
|
||||
__weak id<ASBaselineLayoutable> child = (id<ASBaselineLayoutable>) layout.layoutableObject;
|
||||
switch (style.baselineAlignment) {
|
||||
case ASBaselineLayoutBaselineAlignmentNone:
|
||||
return 0;
|
||||
case ASBaselineLayoutBaselineAlignmentFirst:
|
||||
__weak id<ASLayoutable> child = layout.layoutableObject;
|
||||
switch (style.alignItems) {
|
||||
case ASStackLayoutAlignItemsBaselineFirst:
|
||||
return child.ascender;
|
||||
case ASBaselineLayoutBaselineAlignmentLast:
|
||||
case ASStackLayoutAlignItemsBaselineLast:
|
||||
return layout.size.height + child.descender;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static CGFloat baselineOffset(const ASBaselineLayoutSpecStyle &style,
|
||||
static CGFloat baselineOffset(const ASStackLayoutSpecStyle &style,
|
||||
const ASLayout *l,
|
||||
const CGFloat maxAscender,
|
||||
const CGFloat maxBaseline)
|
||||
{
|
||||
if (style.stackLayoutStyle.direction == ASStackLayoutDirectionHorizontal) {
|
||||
__weak id<ASBaselineLayoutable> child = (id<ASBaselineLayoutable>)l.layoutableObject;
|
||||
switch (style.baselineAlignment) {
|
||||
case ASBaselineLayoutBaselineAlignmentFirst:
|
||||
if (style.direction == ASStackLayoutDirectionHorizontal) {
|
||||
__weak id<ASLayoutable> child = l.layoutableObject;
|
||||
switch (style.alignItems) {
|
||||
case ASStackLayoutAlignItemsBaselineFirst:
|
||||
return maxAscender - child.ascender;
|
||||
case ASBaselineLayoutBaselineAlignmentLast:
|
||||
case ASStackLayoutAlignItemsBaselineLast:
|
||||
return maxBaseline - baselineForItem(style, l);
|
||||
case ASBaselineLayoutBaselineAlignmentNone:
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -55,12 +57,10 @@ static CGFloat maxDimensionForLayout(const ASLayout *l,
|
||||
return maxDimension;
|
||||
}
|
||||
|
||||
ASBaselinePositionedLayout ASBaselinePositionedLayout::compute(const ASStackPositionedLayout &positionedLayout,
|
||||
const ASBaselineLayoutSpecStyle &style,
|
||||
const ASSizeRange &constrainedSize)
|
||||
ASStackBaselinePositionedLayout ASStackBaselinePositionedLayout::compute(const ASStackPositionedLayout &positionedLayout,
|
||||
const ASStackLayoutSpecStyle &style,
|
||||
const ASSizeRange &constrainedSize)
|
||||
{
|
||||
ASStackLayoutSpecStyle stackStyle = style.stackLayoutStyle;
|
||||
|
||||
/* Step 1: Look at each child and determine the distance from the top of the child node it's baseline.
|
||||
For example, let's say we have the following two text nodes and want to align them to the first baseline:
|
||||
|
||||
@@ -91,9 +91,9 @@ ASBaselinePositionedLayout ASBaselinePositionedLayout::compute(const ASStackPosi
|
||||
our layoutSpec to have it so that it can be baseline aligned with another text node or baseline layout spec.
|
||||
*/
|
||||
const auto ascenderIt = std::max_element(positionedLayout.sublayouts.begin(), positionedLayout.sublayouts.end(), [&](const ASLayout *a, const ASLayout *b){
|
||||
return ((id<ASBaselineLayoutable>)a.layoutableObject).ascender < ((id<ASBaselineLayoutable>)b.layoutableObject).ascender;
|
||||
return a.layoutableObject.ascender < b.layoutableObject.ascender;
|
||||
});
|
||||
const CGFloat maxAscender = baselineIt == positionedLayout.sublayouts.end() ? 0 : ((id<ASBaselineLayoutable>)(*ascenderIt).layoutableObject).ascender;
|
||||
const CGFloat maxAscender = baselineIt == positionedLayout.sublayouts.end() ? 0 : (*ascenderIt).layoutableObject.ascender;
|
||||
|
||||
/*
|
||||
Step 3: Take each child and update its layout position based on the baseline offset.
|
||||
@@ -106,14 +106,14 @@ ASBaselinePositionedLayout ASBaselinePositionedLayout::compute(const ASStackPosi
|
||||
CGPoint p = CGPointZero;
|
||||
BOOL first = YES;
|
||||
auto stackedChildren = AS::map(positionedLayout.sublayouts, [&](ASLayout *l) -> ASLayout *{
|
||||
__weak id<ASBaselineLayoutable> child = (id<ASBaselineLayoutable>) l.layoutableObject;
|
||||
p = p + directionPoint(stackStyle.direction, child.spacingBefore, 0);
|
||||
__weak id<ASLayoutable> child = l.layoutableObject;
|
||||
p = p + directionPoint(style.direction, child.spacingBefore, 0);
|
||||
if (first) {
|
||||
// if this is the first item use the previously computed start point
|
||||
p = l.position;
|
||||
} else {
|
||||
// otherwise add the stack spacing
|
||||
p = p + directionPoint(stackStyle.direction, stackStyle.spacing, 0);
|
||||
p = p + directionPoint(style.direction, style.spacing, 0);
|
||||
}
|
||||
first = NO;
|
||||
|
||||
@@ -123,10 +123,10 @@ ASBaselinePositionedLayout ASBaselinePositionedLayout::compute(const ASStackPosi
|
||||
// If we are a vertical stack, add the item's descender (it is negative) to the offset for the next node. This will ensure we are spacing
|
||||
// node from baselines and not bounding boxes.
|
||||
CGFloat spacingAfterBaseline = 0;
|
||||
if (stackStyle.direction == ASStackLayoutDirectionVertical) {
|
||||
if (style.direction == ASStackLayoutDirectionVertical) {
|
||||
spacingAfterBaseline = child.descender;
|
||||
}
|
||||
p = p + directionPoint(stackStyle.direction, stackDimension(stackStyle.direction, l.size) + child.spacingAfter + spacingAfterBaseline, 0);
|
||||
p = p + directionPoint(style.direction, stackDimension(style.direction, l.size) + child.spacingAfter + spacingAfterBaseline, 0);
|
||||
|
||||
return l;
|
||||
});
|
||||
@@ -142,21 +142,21 @@ ASBaselinePositionedLayout ASBaselinePositionedLayout::compute(const ASStackPosi
|
||||
*/
|
||||
const auto it = std::max_element(stackedChildren.begin(), stackedChildren.end(),
|
||||
[&](ASLayout *a, ASLayout *b) {
|
||||
return maxDimensionForLayout(a, stackStyle) < maxDimensionForLayout(b, stackStyle);
|
||||
return maxDimensionForLayout(a, style) < maxDimensionForLayout(b, style);
|
||||
});
|
||||
const auto largestChildCrossSize = it == stackedChildren.end() ? 0 : maxDimensionForLayout(*it, stackStyle);
|
||||
const auto minCrossSize = crossDimension(stackStyle.direction, constrainedSize.min);
|
||||
const auto maxCrossSize = crossDimension(stackStyle.direction, constrainedSize.max);
|
||||
const auto largestChildCrossSize = it == stackedChildren.end() ? 0 : maxDimensionForLayout(*it, style);
|
||||
const auto minCrossSize = crossDimension(style.direction, constrainedSize.min);
|
||||
const auto maxCrossSize = crossDimension(style.direction, constrainedSize.max);
|
||||
const CGFloat crossSize = MIN(MAX(minCrossSize, largestChildCrossSize), maxCrossSize);
|
||||
|
||||
/*
|
||||
Step 5: finally, we must find the smallest descender (descender is negative). This is since ASBaselineLayoutSpec implements
|
||||
ASBaselineLayoutable and needs an ascender and descender to lay itself out properly.
|
||||
ASLayoutable and needs an ascender and descender to lay itself out properly.
|
||||
*/
|
||||
const auto descenderIt = std::max_element(stackedChildren.begin(), stackedChildren.end(), [&](const ASLayout *a, const ASLayout *b){
|
||||
return a.position.y + a.size.height < b.position.y + b.size.height;
|
||||
});
|
||||
const CGFloat minDescender = descenderIt == stackedChildren.end() ? 0 : ((id<ASBaselineLayoutable>)(*descenderIt).layoutableObject).descender;
|
||||
const CGFloat minDescender = descenderIt == stackedChildren.end() ? 0 : (*descenderIt).layoutableObject.descender;
|
||||
|
||||
return {stackedChildren, crossSize, maxAscender, minDescender};
|
||||
}
|
||||
@@ -15,6 +15,7 @@ typedef struct {
|
||||
CGFloat spacing;
|
||||
ASStackLayoutJustifyContent justifyContent;
|
||||
ASStackLayoutAlignItems alignItems;
|
||||
BOOL baselineRelativeArrangement;
|
||||
} ASStackLayoutSpecStyle;
|
||||
|
||||
inline CGFloat stackDimension(const ASStackLayoutDirection direction, const CGSize size)
|
||||
@@ -62,6 +63,10 @@ inline ASStackLayoutAlignItems alignment(ASStackLayoutAlignSelf childAlignment,
|
||||
return ASStackLayoutAlignItemsStart;
|
||||
case ASStackLayoutAlignSelfStretch:
|
||||
return ASStackLayoutAlignItemsStretch;
|
||||
case ASStackLayoutAlignSelfBaselineFirst:
|
||||
return ASStackLayoutAlignItemsBaselineFirst;
|
||||
case ASStackLayoutAlignSelfBaselineLast:
|
||||
return ASStackLayoutAlignItemsBaselineLast;
|
||||
case ASStackLayoutAlignSelfAuto:
|
||||
default:
|
||||
return stackAlignment;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#import "ASLayoutSpecUtilities.h"
|
||||
#import "ASStackLayoutSpecUtilities.h"
|
||||
#import "ASLayoutable.h"
|
||||
#import "ASLayoutOptions.h"
|
||||
|
||||
static CGFloat crossOffset(const ASStackLayoutSpecStyle &style,
|
||||
const ASStackUnpositionedItem &l,
|
||||
@@ -24,6 +25,8 @@ static CGFloat crossOffset(const ASStackLayoutSpecStyle &style,
|
||||
return crossSize - crossDimension(style.direction, l.layout.size);
|
||||
case ASStackLayoutAlignItemsCenter:
|
||||
return ASFloorPixelValue((crossSize - crossDimension(style.direction, l.layout.size)) / 2);
|
||||
case ASStackLayoutAlignItemsBaselineFirst:
|
||||
case ASStackLayoutAlignItemsBaselineLast:
|
||||
case ASStackLayoutAlignItemsStart:
|
||||
case ASStackLayoutAlignItemsStretch:
|
||||
return 0;
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
struct ASStackUnpositionedItem {
|
||||
/** The original source child. */
|
||||
id<ASStackLayoutable> child;
|
||||
id<ASLayoutable> child;
|
||||
/** The proposed layout. */
|
||||
ASLayout *layout;
|
||||
};
|
||||
@@ -31,7 +31,7 @@ struct ASStackUnpositionedLayout {
|
||||
const CGFloat violation;
|
||||
|
||||
/** Given a set of children, computes the unpositioned layouts for those children. */
|
||||
static ASStackUnpositionedLayout compute(const std::vector<id<ASStackLayoutable>> &children,
|
||||
static ASStackUnpositionedLayout compute(const std::vector<id<ASLayoutable>> &children,
|
||||
const ASStackLayoutSpecStyle &style,
|
||||
const ASSizeRange &sizeRange);
|
||||
};
|
||||
|
||||
@@ -14,11 +14,12 @@
|
||||
|
||||
#import "ASLayoutSpecUtilities.h"
|
||||
#import "ASStackLayoutSpecUtilities.h"
|
||||
#import "ASLayoutOptions.h"
|
||||
|
||||
/**
|
||||
Sizes the child given the parameters specified, and returns the computed layout.
|
||||
*/
|
||||
static ASLayout *crossChildLayout(const id<ASStackLayoutable> child,
|
||||
static ASLayout *crossChildLayout(const id<ASLayoutable> child,
|
||||
const ASStackLayoutSpecStyle style,
|
||||
const CGFloat stackMin,
|
||||
const CGFloat stackMax,
|
||||
@@ -186,7 +187,7 @@ static std::function<BOOL(const ASStackUnpositionedItem &)> isFlexibleInViolatio
|
||||
}
|
||||
}
|
||||
|
||||
ASDISPLAYNODE_INLINE BOOL isFlexibleInBothDirections(id<ASStackLayoutable> child)
|
||||
ASDISPLAYNODE_INLINE BOOL isFlexibleInBothDirections(id<ASLayoutable> child)
|
||||
{
|
||||
return child.flexGrow && child.flexShrink;
|
||||
}
|
||||
@@ -195,7 +196,7 @@ ASDISPLAYNODE_INLINE BOOL isFlexibleInBothDirections(id<ASStackLayoutable> child
|
||||
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<ASStackLayoutable>> &children,
|
||||
ASDISPLAYNODE_INLINE BOOL useOptimizedFlexing(const std::vector<id<ASLayoutable>> &children,
|
||||
const ASStackLayoutSpecStyle &style,
|
||||
const ASSizeRange &sizeRange)
|
||||
{
|
||||
@@ -283,7 +284,7 @@ static void flexChildrenAlongStackDimension(std::vector<ASStackUnpositionedItem>
|
||||
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<ASStackLayoutable>> &children,
|
||||
static std::vector<ASStackUnpositionedItem> layoutChildrenAlongUnconstrainedStackDimension(const std::vector<id<ASLayoutable>> &children,
|
||||
const ASStackLayoutSpecStyle &style,
|
||||
const ASSizeRange &sizeRange,
|
||||
const CGSize size,
|
||||
@@ -292,7 +293,7 @@ static std::vector<ASStackUnpositionedItem> layoutChildrenAlongUnconstrainedStac
|
||||
const CGFloat minCrossDimension = crossDimension(style.direction, sizeRange.min);
|
||||
const CGFloat maxCrossDimension = crossDimension(style.direction, sizeRange.max);
|
||||
|
||||
return AS::map(children, [&](id<ASStackLayoutable> child) -> ASStackUnpositionedItem {
|
||||
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));
|
||||
|
||||
@@ -312,7 +313,7 @@ static std::vector<ASStackUnpositionedItem> layoutChildrenAlongUnconstrainedStac
|
||||
});
|
||||
}
|
||||
|
||||
ASStackUnpositionedLayout ASStackUnpositionedLayout::compute(const std::vector<id<ASStackLayoutable>> &children,
|
||||
ASStackUnpositionedLayout ASStackUnpositionedLayout::compute(const std::vector<id<ASLayoutable>> &children,
|
||||
const ASStackLayoutSpecStyle &style,
|
||||
const ASSizeRange &sizeRange)
|
||||
{
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#import "ASBackgroundLayoutSpec.h"
|
||||
#import "ASCenterLayoutSpec.h"
|
||||
#import "ASStackLayoutSpec.h"
|
||||
#import "ASLayoutOptions.h"
|
||||
|
||||
static const ASSizeRange kSize = {{100, 120}, {320, 160}};
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#import "ASBackgroundLayoutSpec.h"
|
||||
#import "ASRatioLayoutSpec.h"
|
||||
#import "ASInsetLayoutSpec.h"
|
||||
#import "ASLayoutOptions.h"
|
||||
|
||||
@interface ASStackLayoutSpecSnapshotTests : ASLayoutSpecSnapshotTestCase
|
||||
@end
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#import "ASTableView.h"
|
||||
#import "ASDisplayNode+Subclasses.h"
|
||||
|
||||
#define NumberOfSections 10
|
||||
#define NumberOfRowsPerSection 20
|
||||
@@ -56,8 +57,24 @@
|
||||
|
||||
@end
|
||||
|
||||
@interface ASTableViewFilledDataSource : NSObject <ASTableViewDataSource, ASTableViewDelegate>
|
||||
@interface ASTestTextCellNode : ASTextCellNode
|
||||
/** Calculated by counting how many times -layoutSpecThatFits: is called on the main thread. */
|
||||
@property (atomic) int numberOfLayoutsOnMainThread;
|
||||
@end
|
||||
|
||||
@implementation ASTestTextCellNode
|
||||
|
||||
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
|
||||
{
|
||||
if ([NSThread isMainThread]) {
|
||||
_numberOfLayoutsOnMainThread++;
|
||||
}
|
||||
return [super layoutSpecThatFits:constrainedSize];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface ASTableViewFilledDataSource : NSObject <ASTableViewDataSource, ASTableViewDelegate>
|
||||
@end
|
||||
|
||||
@implementation ASTableViewFilledDataSource
|
||||
@@ -74,7 +91,7 @@
|
||||
|
||||
- (ASCellNode *)tableView:(ASTableView *)tableView nodeForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
ASTextCellNode *textCellNode = [ASTextCellNode new];
|
||||
ASTestTextCellNode *textCellNode = [ASTestTextCellNode new];
|
||||
textCellNode.text = indexPath.description;
|
||||
|
||||
return textCellNode;
|
||||
@@ -202,34 +219,69 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testRelayoutAllRows
|
||||
- (void)testRelayoutAllRowsWithNonZeroSizeInitially
|
||||
{
|
||||
// Initial width of the table view is 0 and all nodes are measured with this size.
|
||||
ASTestTableView *tableView = [[ASTestTableView alloc] initWithFrame:CGRectMake(0, 0, 0, 500)
|
||||
// Initial width of the table view is non-zero and all nodes are measured with this size.
|
||||
// Any subsequence size change must trigger a relayout.
|
||||
CGSize tableViewFinalSize = CGSizeMake(100, 500);
|
||||
// Width and height are swapped so that a later size change will simulate a rotation
|
||||
ASTestTableView *tableView = [[ASTestTableView alloc] initWithFrame:CGRectMake(0, 0, tableViewFinalSize.height, tableViewFinalSize.width)
|
||||
style:UITableViewStylePlain
|
||||
asyncDataFetching:YES];
|
||||
CGSize tableViewFinalSize = CGSizeMake(100, 500);
|
||||
|
||||
ASTableViewFilledDataSource *dataSource = [ASTableViewFilledDataSource new];
|
||||
|
||||
tableView.asyncDelegate = dataSource;
|
||||
tableView.asyncDataSource = dataSource;
|
||||
|
||||
// Trigger layout measurement on all nodes
|
||||
[tableView reloadData];
|
||||
|
||||
[self triggerSizeChangeAndAssertRelayoutAllRowsForTableView:tableView newSize:tableViewFinalSize];
|
||||
}
|
||||
|
||||
- (void)testRelayoutAllRowsWithZeroSizeInitially
|
||||
{
|
||||
// Initial width of the table view is 0. The first size change is part of the initial config.
|
||||
// Any subsequence size change after that must trigger a relayout.
|
||||
CGSize tableViewFinalSize = CGSizeMake(100, 500);
|
||||
ASTestTableView *tableView = [[ASTestTableView alloc] initWithFrame:CGRectZero
|
||||
style:UITableViewStylePlain
|
||||
asyncDataFetching:YES];
|
||||
ASTableViewFilledDataSource *dataSource = [ASTableViewFilledDataSource new];
|
||||
|
||||
tableView.asyncDelegate = dataSource;
|
||||
tableView.asyncDataSource = dataSource;
|
||||
|
||||
// Initial configuration
|
||||
UIView *superview = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
|
||||
[superview addSubview:tableView];
|
||||
// Width and height are swapped so that a later size change will simulate a rotation
|
||||
tableView.frame = CGRectMake(0, 0, tableViewFinalSize.height, tableViewFinalSize.width);
|
||||
// Trigger layout measurement on all nodes
|
||||
[tableView layoutIfNeeded];
|
||||
|
||||
[self triggerSizeChangeAndAssertRelayoutAllRowsForTableView:tableView newSize:tableViewFinalSize];
|
||||
}
|
||||
|
||||
- (void)triggerSizeChangeAndAssertRelayoutAllRowsForTableView:(ASTableView *)tableView newSize:(CGSize)newSize
|
||||
{
|
||||
XCTestExpectation *nodesMeasuredUsingNewConstrainedSizeExpectation = [self expectationWithDescription:@"nodesMeasuredUsingNewConstrainedSize"];
|
||||
|
||||
[tableView beginUpdates];
|
||||
|
||||
tableView.frame = CGRectMake(0, 0, tableViewFinalSize.width, tableViewFinalSize.height);
|
||||
CGRect frame = tableView.frame;
|
||||
frame.size = newSize;
|
||||
tableView.frame = frame;
|
||||
[tableView layoutIfNeeded];
|
||||
|
||||
XCTestExpectation *nodesMeasuredUsingNewConstrainedSizeExpectation = [self expectationWithDescription:@"nodesMeasuredUsingNewConstrainedSize"];
|
||||
|
||||
[tableView endUpdatesAnimated:NO completion:^(BOOL completed) {
|
||||
for (int section = 0; section < NumberOfSections; section++) {
|
||||
for (int row = 0; row < NumberOfRowsPerSection; row++) {
|
||||
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section];
|
||||
ASCellNode *node = [tableView nodeForRowAtIndexPath:indexPath];
|
||||
XCTAssertEqual(node.constrainedSizeForCalculatedLayout.max.width, tableViewFinalSize.width);
|
||||
ASTestTextCellNode *node = (ASTestTextCellNode *)[tableView nodeForRowAtIndexPath:indexPath];
|
||||
XCTAssertEqual(node.numberOfLayoutsOnMainThread, 1);
|
||||
XCTAssertEqual(node.constrainedSizeForCalculatedLayout.max.width, newSize.width);
|
||||
}
|
||||
}
|
||||
[nodesMeasuredUsingNewConstrainedSizeExpectation fulfill];
|
||||
@@ -242,4 +294,138 @@
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)testRelayoutVisibleRowsWhenEditingModeIsChanged
|
||||
{
|
||||
CGSize tableViewSize = CGSizeMake(100, 500);
|
||||
ASTestTableView *tableView = [[ASTestTableView alloc] initWithFrame:CGRectMake(0, 0, tableViewSize.width, tableViewSize.height)
|
||||
style:UITableViewStylePlain
|
||||
asyncDataFetching:YES];
|
||||
ASTableViewFilledDataSource *dataSource = [ASTableViewFilledDataSource new];
|
||||
|
||||
tableView.asyncDelegate = dataSource;
|
||||
tableView.asyncDataSource = dataSource;
|
||||
|
||||
XCTestExpectation *reloadDataExpectation = [self expectationWithDescription:@"reloadData"];
|
||||
[tableView reloadDataWithCompletion:^{
|
||||
for (int section = 0; section < NumberOfSections; section++) {
|
||||
for (int row = 0; row < NumberOfRowsPerSection; row++) {
|
||||
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section];
|
||||
ASTestTextCellNode *node = (ASTestTextCellNode *)[tableView nodeForRowAtIndexPath:indexPath];
|
||||
XCTAssertEqual(node.numberOfLayoutsOnMainThread, 0);
|
||||
XCTAssertEqual(node.constrainedSizeForCalculatedLayout.max.width, tableViewSize.width);
|
||||
}
|
||||
}
|
||||
[reloadDataExpectation fulfill];
|
||||
}];
|
||||
[self waitForExpectationsWithTimeout:5 handler:^(NSError *error) {
|
||||
if (error) {
|
||||
XCTFail(@"Expectation failed: %@", error);
|
||||
}
|
||||
}];
|
||||
|
||||
NSArray *visibleNodes = [tableView visibleNodes];
|
||||
XCTAssertGreaterThan(visibleNodes.count, 0);
|
||||
|
||||
// Cause table view to enter editing mode.
|
||||
// Visibile nodes should be re-measured on main thread with the new (smaller) content view width.
|
||||
// Other nodes are untouched.
|
||||
XCTestExpectation *relayoutAfterEnablingEditingExpectation = [self expectationWithDescription:@"relayoutAfterEnablingEditing"];
|
||||
[tableView beginUpdates];
|
||||
[tableView setEditing:YES];
|
||||
[tableView endUpdatesAnimated:YES completion:^(BOOL completed) {
|
||||
for (int section = 0; section < NumberOfSections; section++) {
|
||||
for (int row = 0; row < NumberOfRowsPerSection; row++) {
|
||||
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section];
|
||||
ASTestTextCellNode *node = (ASTestTextCellNode *)[tableView nodeForRowAtIndexPath:indexPath];
|
||||
if ([visibleNodes containsObject:node]) {
|
||||
XCTAssertEqual(node.numberOfLayoutsOnMainThread, 1);
|
||||
XCTAssertLessThan(node.constrainedSizeForCalculatedLayout.max.width, tableViewSize.width);
|
||||
} else {
|
||||
XCTAssertEqual(node.numberOfLayoutsOnMainThread, 0);
|
||||
XCTAssertEqual(node.constrainedSizeForCalculatedLayout.max.width, tableViewSize.width);
|
||||
}
|
||||
}
|
||||
}
|
||||
[relayoutAfterEnablingEditingExpectation fulfill];
|
||||
}];
|
||||
[self waitForExpectationsWithTimeout:5 handler:^(NSError *error) {
|
||||
if (error) {
|
||||
XCTFail(@"Expectation failed: %@", error);
|
||||
}
|
||||
}];
|
||||
|
||||
// Cause table view to leave editing mode.
|
||||
// Visibile nodes should be re-measured again.
|
||||
// All nodes should have max constrained width equals to the table view width.
|
||||
XCTestExpectation *relayoutAfterDisablingEditingExpectation = [self expectationWithDescription:@"relayoutAfterDisablingEditing"];
|
||||
[tableView beginUpdates];
|
||||
[tableView setEditing:NO];
|
||||
[tableView endUpdatesAnimated:YES completion:^(BOOL completed) {
|
||||
for (int section = 0; section < NumberOfSections; section++) {
|
||||
for (int row = 0; row < NumberOfRowsPerSection; row++) {
|
||||
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section];
|
||||
ASTestTextCellNode *node = (ASTestTextCellNode *)[tableView nodeForRowAtIndexPath:indexPath];
|
||||
BOOL visible = [visibleNodes containsObject:node];
|
||||
XCTAssertEqual(node.numberOfLayoutsOnMainThread, visible ? 2: 0);
|
||||
XCTAssertEqual(node.constrainedSizeForCalculatedLayout.max.width, tableViewSize.width);
|
||||
}
|
||||
}
|
||||
[relayoutAfterDisablingEditingExpectation fulfill];
|
||||
}];
|
||||
[self waitForExpectationsWithTimeout:5 handler:^(NSError *error) {
|
||||
if (error) {
|
||||
XCTFail(@"Expectation failed: %@", error);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)testRelayoutRowsAfterEditingModeIsChangedAndTheyBecomeVisible
|
||||
{
|
||||
CGSize tableViewSize = CGSizeMake(100, 500);
|
||||
ASTestTableView *tableView = [[ASTestTableView alloc] initWithFrame:CGRectMake(0, 0, tableViewSize.width, tableViewSize.height)
|
||||
style:UITableViewStylePlain
|
||||
asyncDataFetching:YES];
|
||||
ASTableViewFilledDataSource *dataSource = [ASTableViewFilledDataSource new];
|
||||
|
||||
tableView.asyncDelegate = dataSource;
|
||||
tableView.asyncDataSource = dataSource;
|
||||
|
||||
XCTestExpectation *reloadDataExpectation = [self expectationWithDescription:@"reloadData"];
|
||||
[tableView reloadDataWithCompletion:^{
|
||||
for (int section = 0; section < NumberOfSections; section++) {
|
||||
for (int row = 0; row < NumberOfRowsPerSection; row++) {
|
||||
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section];
|
||||
ASTestTextCellNode *node = (ASTestTextCellNode *)[tableView nodeForRowAtIndexPath:indexPath];
|
||||
XCTAssertEqual(node.numberOfLayoutsOnMainThread, 0);
|
||||
XCTAssertEqual(node.constrainedSizeForCalculatedLayout.max.width, tableViewSize.width);
|
||||
}
|
||||
}
|
||||
[reloadDataExpectation fulfill];
|
||||
}];
|
||||
[self waitForExpectationsWithTimeout:5 handler:^(NSError *error) {
|
||||
if (error) {
|
||||
XCTFail(@"Expectation failed: %@", error);
|
||||
}
|
||||
}];
|
||||
|
||||
// Cause table view to enter editing mode and then scroll to the bottom.
|
||||
// The last node should be re-measured on main thread with the new (smaller) content view width.
|
||||
NSIndexPath *lastRowIndexPath = [NSIndexPath indexPathForRow:(NumberOfRowsPerSection - 1) inSection:(NumberOfSections - 1)];
|
||||
XCTestExpectation *relayoutExpectation = [self expectationWithDescription:@"relayout"];
|
||||
[tableView beginUpdates];
|
||||
[tableView setEditing:YES];
|
||||
[tableView setContentOffset:CGPointMake(0, CGFLOAT_MAX) animated:YES];
|
||||
[tableView endUpdatesAnimated:YES completion:^(BOOL completed) {
|
||||
ASTestTextCellNode *node = (ASTestTextCellNode *)[tableView nodeForRowAtIndexPath:lastRowIndexPath];
|
||||
XCTAssertEqual(node.numberOfLayoutsOnMainThread, 1);
|
||||
XCTAssertLessThan(node.constrainedSizeForCalculatedLayout.max.width, tableViewSize.width);
|
||||
[relayoutExpectation fulfill];
|
||||
}];
|
||||
[self waitForExpectationsWithTimeout:5 handler:^(NSError *error) {
|
||||
if (error) {
|
||||
XCTFail(@"Expectation failed: %@", error);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
BIN
examples/HorizontalWithinVerticalScrolling/Default-568h@2x.png
Normal file
BIN
examples/HorizontalWithinVerticalScrolling/Default-568h@2x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
BIN
examples/HorizontalWithinVerticalScrolling/Default-667h@2x.png
Normal file
BIN
examples/HorizontalWithinVerticalScrolling/Default-667h@2x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
BIN
examples/HorizontalWithinVerticalScrolling/Default-736h@3x.png
Normal file
BIN
examples/HorizontalWithinVerticalScrolling/Default-736h@3x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
3
examples/HorizontalWithinVerticalScrolling/Podfile
Normal file
3
examples/HorizontalWithinVerticalScrolling/Podfile
Normal file
@@ -0,0 +1,3 @@
|
||||
source 'https://github.com/CocoaPods/Specs.git'
|
||||
platform :ios, '8.0'
|
||||
pod 'AsyncDisplayKit', :path => '../..'
|
||||
@@ -0,0 +1,358 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
05561CFD19D4F94A00CBA93C /* HorizontalScrollCellNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = 05561CFC19D4F94A00CBA93C /* HorizontalScrollCellNode.mm */; };
|
||||
0585428019D4DBE100606EA6 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 0585427F19D4DBE100606EA6 /* Default-568h@2x.png */; };
|
||||
05E2128719D4DB510098F589 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 05E2128619D4DB510098F589 /* main.m */; };
|
||||
05E2128A19D4DB510098F589 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 05E2128919D4DB510098F589 /* AppDelegate.m */; };
|
||||
05E2128D19D4DB510098F589 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 05E2128C19D4DB510098F589 /* ViewController.m */; };
|
||||
18C2ED861B9B8CE700F627B3 /* RandomCoreGraphicsNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 18C2ED851B9B8CE700F627B3 /* RandomCoreGraphicsNode.m */; };
|
||||
3EC0CDCBA10D483D9F386E5E /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3D24B17D1E4A4E7A9566C5E9 /* libPods.a */; };
|
||||
6C2C82AC19EE274300767484 /* Default-667h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 6C2C82AA19EE274300767484 /* Default-667h@2x.png */; };
|
||||
6C2C82AD19EE274300767484 /* Default-736h@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 6C2C82AB19EE274300767484 /* Default-736h@3x.png */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
05561CFB19D4F94A00CBA93C /* HorizontalScrollCellNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HorizontalScrollCellNode.h; sourceTree = "<group>"; };
|
||||
05561CFC19D4F94A00CBA93C /* HorizontalScrollCellNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = HorizontalScrollCellNode.mm; sourceTree = "<group>"; };
|
||||
0585427F19D4DBE100606EA6 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Default-568h@2x.png"; path = "../Default-568h@2x.png"; sourceTree = "<group>"; };
|
||||
05E2128119D4DB510098F589 /* Sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Sample.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
05E2128519D4DB510098F589 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
05E2128619D4DB510098F589 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
|
||||
05E2128819D4DB510098F589 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
|
||||
05E2128919D4DB510098F589 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
|
||||
05E2128B19D4DB510098F589 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; };
|
||||
05E2128C19D4DB510098F589 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = "<group>"; };
|
||||
088AA6578212BE9BFBB07B70 /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = "<group>"; };
|
||||
18C2ED841B9B8CE700F627B3 /* RandomCoreGraphicsNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RandomCoreGraphicsNode.h; sourceTree = "<group>"; };
|
||||
18C2ED851B9B8CE700F627B3 /* RandomCoreGraphicsNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RandomCoreGraphicsNode.m; sourceTree = "<group>"; };
|
||||
3D24B17D1E4A4E7A9566C5E9 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
6C2C82AA19EE274300767484 /* Default-667h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-667h@2x.png"; sourceTree = SOURCE_ROOT; };
|
||||
6C2C82AB19EE274300767484 /* Default-736h@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-736h@3x.png"; sourceTree = SOURCE_ROOT; };
|
||||
C068F1D3F0CC317E895FCDAB /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
05E2127E19D4DB510098F589 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
3EC0CDCBA10D483D9F386E5E /* libPods.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
05E2127819D4DB510098F589 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
05E2128319D4DB510098F589 /* Sample */,
|
||||
05E2128219D4DB510098F589 /* Products */,
|
||||
1A943BF0259746F18D6E423F /* Frameworks */,
|
||||
1AE410B73DA5C3BD087ACDD7 /* Pods */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
05E2128219D4DB510098F589 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
05E2128119D4DB510098F589 /* Sample.app */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
05E2128319D4DB510098F589 /* Sample */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
05E2128819D4DB510098F589 /* AppDelegate.h */,
|
||||
05E2128919D4DB510098F589 /* AppDelegate.m */,
|
||||
05E2128B19D4DB510098F589 /* ViewController.h */,
|
||||
05E2128C19D4DB510098F589 /* ViewController.m */,
|
||||
05561CFB19D4F94A00CBA93C /* HorizontalScrollCellNode.h */,
|
||||
05561CFC19D4F94A00CBA93C /* HorizontalScrollCellNode.mm */,
|
||||
18C2ED841B9B8CE700F627B3 /* RandomCoreGraphicsNode.h */,
|
||||
18C2ED851B9B8CE700F627B3 /* RandomCoreGraphicsNode.m */,
|
||||
05E2128419D4DB510098F589 /* Supporting Files */,
|
||||
);
|
||||
path = Sample;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
05E2128419D4DB510098F589 /* Supporting Files */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0585427F19D4DBE100606EA6 /* Default-568h@2x.png */,
|
||||
6C2C82AA19EE274300767484 /* Default-667h@2x.png */,
|
||||
6C2C82AB19EE274300767484 /* Default-736h@3x.png */,
|
||||
05E2128519D4DB510098F589 /* Info.plist */,
|
||||
05E2128619D4DB510098F589 /* main.m */,
|
||||
);
|
||||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1A943BF0259746F18D6E423F /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
3D24B17D1E4A4E7A9566C5E9 /* libPods.a */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1AE410B73DA5C3BD087ACDD7 /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C068F1D3F0CC317E895FCDAB /* Pods.debug.xcconfig */,
|
||||
088AA6578212BE9BFBB07B70 /* Pods.release.xcconfig */,
|
||||
);
|
||||
name = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
05E2128019D4DB510098F589 /* Sample */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 05E212A419D4DB510098F589 /* Build configuration list for PBXNativeTarget "Sample" */;
|
||||
buildPhases = (
|
||||
E080B80F89C34A25B3488E26 /* Check Pods Manifest.lock */,
|
||||
05E2127D19D4DB510098F589 /* Sources */,
|
||||
05E2127E19D4DB510098F589 /* Frameworks */,
|
||||
05E2127F19D4DB510098F589 /* Resources */,
|
||||
F012A6F39E0149F18F564F50 /* Copy Pods Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = Sample;
|
||||
productName = Sample;
|
||||
productReference = 05E2128119D4DB510098F589 /* Sample.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
05E2127919D4DB510098F589 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0600;
|
||||
ORGANIZATIONNAME = Facebook;
|
||||
TargetAttributes = {
|
||||
05E2128019D4DB510098F589 = {
|
||||
CreatedOnToolsVersion = 6.0.1;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 05E2127C19D4DB510098F589 /* Build configuration list for PBXProject "Sample" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 05E2127819D4DB510098F589;
|
||||
productRefGroup = 05E2128219D4DB510098F589 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
05E2128019D4DB510098F589 /* Sample */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
05E2127F19D4DB510098F589 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0585428019D4DBE100606EA6 /* Default-568h@2x.png in Resources */,
|
||||
6C2C82AC19EE274300767484 /* Default-667h@2x.png in Resources */,
|
||||
6C2C82AD19EE274300767484 /* Default-736h@3x.png in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
E080B80F89C34A25B3488E26 /* Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Check Pods Manifest.lock";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
F012A6F39E0149F18F564F50 /* Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Copy Pods Resources";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
05E2127D19D4DB510098F589 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
18C2ED861B9B8CE700F627B3 /* RandomCoreGraphicsNode.m in Sources */,
|
||||
05561CFD19D4F94A00CBA93C /* HorizontalScrollCellNode.mm in Sources */,
|
||||
05E2128D19D4DB510098F589 /* ViewController.m in Sources */,
|
||||
05E2128A19D4DB510098F589 /* AppDelegate.m in Sources */,
|
||||
05E2128719D4DB510098F589 /* main.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
05E212A219D4DB510098F589 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
05E212A319D4DB510098F589 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = YES;
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
05E212A519D4DB510098F589 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = C068F1D3F0CC317E895FCDAB /* Pods.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
INFOPLIST_FILE = Sample/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 7.1;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
05E212A619D4DB510098F589 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 088AA6578212BE9BFBB07B70 /* Pods.release.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
INFOPLIST_FILE = Sample/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 7.1;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
05E2127C19D4DB510098F589 /* Build configuration list for PBXProject "Sample" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
05E212A219D4DB510098F589 /* Debug */,
|
||||
05E212A319D4DB510098F589 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
05E212A419D4DB510098F589 /* Build configuration list for PBXNativeTarget "Sample" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
05E212A519D4DB510098F589 /* Debug */,
|
||||
05E212A619D4DB510098F589 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 05E2127919D4DB510098F589 /* Project object */;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:Sample.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
@@ -0,0 +1,88 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0620"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "05E2128019D4DB510098F589"
|
||||
BuildableName = "Sample.app"
|
||||
BlueprintName = "Sample"
|
||||
ReferencedContainer = "container:Sample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
buildConfiguration = "Debug">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "05E2128019D4DB510098F589"
|
||||
BuildableName = "Sample.app"
|
||||
BlueprintName = "Sample"
|
||||
ReferencedContainer = "container:Sample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Debug"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "05E2128019D4DB510098F589"
|
||||
BuildableName = "Sample.app"
|
||||
BlueprintName = "Sample"
|
||||
ReferencedContainer = "container:Sample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Release"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "05E2128019D4DB510098F589"
|
||||
BuildableName = "Sample.app"
|
||||
BlueprintName = "Sample"
|
||||
ReferencedContainer = "container:Sample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
1
examples/HorizontalWithinVerticalScrolling/Sample.xcworkspace/contents.xcworkspacedata
generated
Normal file
1
examples/HorizontalWithinVerticalScrolling/Sample.xcworkspace/contents.xcworkspacedata
generated
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?><Workspace version='1.0'><FileRef location='group:Sample.xcodeproj'/><FileRef location='group:Pods/Pods.xcodeproj'/></Workspace>
|
||||
@@ -0,0 +1,20 @@
|
||||
/* This file provided by Facebook is for non-commercial testing and evaluation
|
||||
* purposes only. Facebook reserves all rights not expressly granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#define UseAutomaticLayout 1
|
||||
|
||||
@interface AppDelegate : UIResponder <UIApplicationDelegate>
|
||||
|
||||
@property (strong, nonatomic) UIWindow *window;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,27 @@
|
||||
/* This file provided by Facebook is for non-commercial testing and evaluation
|
||||
* purposes only. Facebook reserves all rights not expressly granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#import "AppDelegate.h"
|
||||
|
||||
#import "ViewController.h"
|
||||
|
||||
@implementation AppDelegate
|
||||
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
|
||||
self.window.backgroundColor = [UIColor whiteColor];
|
||||
self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:[[ViewController alloc] init]];
|
||||
[self.window makeKeyAndVisible];
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,22 @@
|
||||
/* This file provided by Facebook is for non-commercial testing and evaluation
|
||||
* purposes only. Facebook reserves all rights not expressly granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#import <AsyncDisplayKit/AsyncDisplayKit.h>
|
||||
|
||||
/**
|
||||
* This ASCellNode contains an ASCollectionNode. It intelligently interacts with a containing ASCollectionView or ASTableView,
|
||||
* to preload and clean up contents as the user scrolls around both vertically and horizontally — in a way that minimizes memory usage.
|
||||
*/
|
||||
@interface HorizontalScrollCellNode : ASCellNode <ASCollectionViewDelegate, ASCollectionViewDataSource>
|
||||
|
||||
- (instancetype)initWithElementSize:(CGSize)size;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,100 @@
|
||||
/* This file provided by Facebook is for non-commercial testing and evaluation
|
||||
* purposes only. Facebook reserves all rights not expressly granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#import "HorizontalScrollCellNode.h"
|
||||
#import "RandomCoreGraphicsNode.h"
|
||||
#import "AppDelegate.h"
|
||||
|
||||
#import <AsyncDisplayKit/ASDisplayNode+Subclasses.h>
|
||||
|
||||
#import <AsyncDisplayKit/ASStackLayoutSpec.h>
|
||||
#import <AsyncDisplayKit/ASInsetLayoutSpec.h>
|
||||
|
||||
static const CGFloat kOuterPadding = 16.0f;
|
||||
static const CGFloat kInnerPadding = 10.0f;
|
||||
|
||||
@interface HorizontalScrollCellNode ()
|
||||
{
|
||||
ASCollectionNode *_collectionNode;
|
||||
CGSize _elementSize;
|
||||
ASDisplayNode *_divider;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation HorizontalScrollCellNode
|
||||
|
||||
- (instancetype)initWithElementSize:(CGSize)size
|
||||
{
|
||||
if (!(self = [super init]))
|
||||
return nil;
|
||||
|
||||
_elementSize = size;
|
||||
|
||||
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
|
||||
flowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
|
||||
flowLayout.itemSize = _elementSize;
|
||||
flowLayout.minimumInteritemSpacing = kInnerPadding;
|
||||
_collectionNode = [[ASCollectionNode alloc] initWithCollectionViewLayout:flowLayout];
|
||||
[self addSubnode:_collectionNode];
|
||||
|
||||
// hairline cell separator
|
||||
_divider = [[ASDisplayNode alloc] init];
|
||||
_divider.backgroundColor = [UIColor lightGrayColor];
|
||||
[self addSubnode:_divider];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)didLoad
|
||||
{
|
||||
[super didLoad];
|
||||
_collectionNode.view.asyncDelegate = self;
|
||||
_collectionNode.view.asyncDataSource = self;
|
||||
}
|
||||
|
||||
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
|
||||
{
|
||||
return 5;
|
||||
}
|
||||
|
||||
- (ASCellNode *)collectionView:(ASCollectionView *)collectionView nodeForItemAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
RandomCoreGraphicsNode *elementNode = [[RandomCoreGraphicsNode alloc] init];
|
||||
elementNode.preferredFrameSize = _elementSize;
|
||||
return elementNode;
|
||||
}
|
||||
|
||||
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
|
||||
{
|
||||
_collectionNode.preferredFrameSize = CGSizeMake(self.bounds.size.width, _elementSize.height);
|
||||
|
||||
ASInsetLayoutSpec *insetSpec = [[ASInsetLayoutSpec alloc] init];
|
||||
insetSpec.insets = UIEdgeInsetsMake(kOuterPadding, 0.0, kOuterPadding, 0.0);
|
||||
insetSpec.child = _collectionNode;
|
||||
|
||||
return insetSpec;
|
||||
}
|
||||
|
||||
// With box model, you don't need to override this method, unless you want to add custom logic.
|
||||
- (void)layout
|
||||
{
|
||||
[super layout];
|
||||
|
||||
_collectionNode.view.contentInset = UIEdgeInsetsMake(0.0, kOuterPadding, 0.0, kOuterPadding);
|
||||
|
||||
// Manually layout the divider.
|
||||
CGFloat pixelHeight = 1.0f / [[UIScreen mainScreen] scale];
|
||||
_divider.frame = CGRectMake(0.0f, 0.0f, self.calculatedSize.width, pixelHeight);
|
||||
}
|
||||
|
||||
@end
|
||||
36
examples/HorizontalWithinVerticalScrolling/Sample/Info.plist
Normal file
36
examples/HorizontalWithinVerticalScrolling/Sample/Info.plist
Normal file
@@ -0,0 +1,36 @@
|
||||
<?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.AsyncDisplayKit.$(PRODUCT_NAME:rfc1034identifier)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>armv7</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// RandomCoreGraphicsNode.h
|
||||
// Sample
|
||||
//
|
||||
// Created by Scott Goodson on 9/5/15.
|
||||
// Copyright (c) 2015 Facebook. All rights reserved.
|
||||
//
|
||||
|
||||
#import <AsyncDisplayKit/AsyncDisplayKit.h>
|
||||
|
||||
@interface RandomCoreGraphicsNode : ASCellNode
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,44 @@
|
||||
//
|
||||
// RandomCoreGraphicsNode.m
|
||||
// Sample
|
||||
//
|
||||
// Created by Scott Goodson on 9/5/15.
|
||||
// Copyright (c) 2015 Facebook. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RandomCoreGraphicsNode.h"
|
||||
#import <AsyncDisplayKit/ASDisplayNode+Subclasses.h>
|
||||
|
||||
@implementation RandomCoreGraphicsNode
|
||||
|
||||
+ (UIColor *)randomColor
|
||||
{
|
||||
CGFloat hue = ( arc4random() % 256 / 256.0 ); // 0.0 to 1.0
|
||||
CGFloat saturation = ( arc4random() % 128 / 256.0 ) + 0.5; // 0.5 to 1.0, away from white
|
||||
CGFloat brightness = ( arc4random() % 128 / 256.0 ) + 0.5; // 0.5 to 1.0, away from black
|
||||
return [UIColor colorWithHue:hue saturation:saturation brightness:brightness alpha:1];
|
||||
}
|
||||
|
||||
+ (void)drawRect:(CGRect)bounds withParameters:(id<NSObject>)parameters isCancelled:(asdisplaynode_iscancelled_block_t)isCancelledBlock isRasterizing:(BOOL)isRasterizing
|
||||
{
|
||||
CGFloat locations[3];
|
||||
NSMutableArray *colors = [NSMutableArray arrayWithCapacity:3];
|
||||
[colors addObject:(id)[[RandomCoreGraphicsNode randomColor] CGColor]];
|
||||
locations[0] = 0.0;
|
||||
[colors addObject:(id)[[RandomCoreGraphicsNode randomColor] CGColor]];
|
||||
locations[1] = 1.0;
|
||||
[colors addObject:(id)[[RandomCoreGraphicsNode randomColor] CGColor]];
|
||||
locations[2] = ( arc4random() % 256 / 256.0 );
|
||||
|
||||
|
||||
CGContextRef ctx = UIGraphicsGetCurrentContext();
|
||||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (CFArrayRef)colors, locations);
|
||||
|
||||
CGGradientDrawingOptions drawingOptions;
|
||||
CGContextDrawLinearGradient(ctx, gradient, CGPointZero, CGPointMake(bounds.size.width, bounds.size.height), drawingOptions);
|
||||
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,16 @@
|
||||
/* This file provided by Facebook is for non-commercial testing and evaluation
|
||||
* purposes only. Facebook reserves all rights not expressly granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface ViewController : UIViewController
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,84 @@
|
||||
/* This file provided by Facebook is for non-commercial testing and evaluation
|
||||
* purposes only. Facebook reserves all rights not expressly granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#import <AsyncDisplayKit/AsyncDisplayKit.h>
|
||||
#import <AsyncDisplayKit/ASAssert.h>
|
||||
|
||||
#import "ViewController.h"
|
||||
#import "HorizontalScrollCellNode.h"
|
||||
|
||||
@interface ViewController () <ASTableViewDataSource, ASTableViewDelegate>
|
||||
{
|
||||
ASTableView *_tableView;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation ViewController
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark UIViewController.
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if (!(self = [super init]))
|
||||
return nil;
|
||||
|
||||
_tableView = [[ASTableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
|
||||
_tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
|
||||
_tableView.asyncDataSource = self;
|
||||
_tableView.asyncDelegate = self;
|
||||
|
||||
self.title = @"Horizontal Scrolling Gradients";
|
||||
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRedo
|
||||
target:self
|
||||
action:@selector(reloadEverything)];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)reloadEverything
|
||||
{
|
||||
[_tableView reloadData];
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
[self.view addSubview:_tableView];
|
||||
}
|
||||
|
||||
- (void)viewWillLayoutSubviews
|
||||
{
|
||||
_tableView.frame = self.view.bounds;
|
||||
}
|
||||
|
||||
- (BOOL)prefersStatusBarHidden
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark ASTableView.
|
||||
|
||||
- (ASCellNode *)tableView:(ASTableView *)tableView nodeForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
HorizontalScrollCellNode *node = [[HorizontalScrollCellNode alloc] initWithElementSize:CGSizeMake(100, 100)];
|
||||
return node;
|
||||
}
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
|
||||
{
|
||||
return 100;
|
||||
}
|
||||
|
||||
@end
|
||||
20
examples/HorizontalWithinVerticalScrolling/Sample/main.m
Normal file
20
examples/HorizontalWithinVerticalScrolling/Sample/main.m
Normal file
@@ -0,0 +1,20 @@
|
||||
/* This file provided by Facebook is for non-commercial testing and evaluation
|
||||
* purposes only. Facebook reserves all rights not expressly granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "AppDelegate.h"
|
||||
|
||||
int main(int argc, char * argv[]) {
|
||||
@autoreleasepool {
|
||||
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
|
||||
}
|
||||
}
|
||||
@@ -58,7 +58,9 @@
|
||||
1A943BF0259746F18D6E423F /* Frameworks */,
|
||||
1AE410B73DA5C3BD087ACDD7 /* Pods */,
|
||||
);
|
||||
indentWidth = 2;
|
||||
sourceTree = "<group>";
|
||||
tabWidth = 2;
|
||||
};
|
||||
05E2128219D4DB510098F589 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
|
||||
@@ -140,7 +140,7 @@ static const CGFloat kInnerPadding = 10.0f;
|
||||
ASStackLayoutSpec *stackSpec = [[ASStackLayoutSpec alloc] init];
|
||||
stackSpec.direction = ASStackLayoutDirectionHorizontal;
|
||||
stackSpec.spacing = kInnerPadding;
|
||||
[stackSpec addChildren:!_swappedTextAndImage ? @[_imageNode, _textNode] : @[_textNode, _imageNode]];
|
||||
[stackSpec setChildren:!_swappedTextAndImage ? @[_imageNode, _textNode] : @[_textNode, _imageNode]];
|
||||
|
||||
ASInsetLayoutSpec *insetSpec = [[ASInsetLayoutSpec alloc] init];
|
||||
insetSpec.insets = UIEdgeInsetsMake(kOuterPadding, kOuterPadding, kOuterPadding, kOuterPadding);
|
||||
|
||||
Reference in New Issue
Block a user