[ASCornerLayoutSpec] New layout spec class for declarative corner element layout. (#657)
* Add new layout spec class with snapshot testing. Update examples and CHANGELOG.md * Code review updates. * Open curly bracket in a new line.
@@ -33,6 +33,9 @@
|
|||||||
05EA6FE71AC0966E00E35788 /* ASSnapshotTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = 05EA6FE61AC0966E00E35788 /* ASSnapshotTestCase.m */; };
|
05EA6FE71AC0966E00E35788 /* ASSnapshotTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = 05EA6FE61AC0966E00E35788 /* ASSnapshotTestCase.m */; };
|
||||||
18C2ED7F1B9B7DE800F627B3 /* 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, ); }; };
|
||||||
18C2ED831B9B7DE800F627B3 /* ASCollectionNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = 18C2ED7D1B9B7DE800F627B3 /* ASCollectionNode.mm */; };
|
18C2ED831B9B7DE800F627B3 /* ASCollectionNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = 18C2ED7D1B9B7DE800F627B3 /* ASCollectionNode.mm */; };
|
||||||
|
1A6C000D1FAB4E2100D05926 /* ASCornerLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = 1A6C000B1FAB4E2000D05926 /* ASCornerLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
|
1A6C000E1FAB4E2100D05926 /* ASCornerLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A6C000C1FAB4E2100D05926 /* ASCornerLayoutSpec.mm */; };
|
||||||
|
1A6C00111FAB4EDD00D05926 /* ASCornerLayoutSpecSnapshotTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A6C000F1FAB4ED400D05926 /* ASCornerLayoutSpecSnapshotTests.mm */; };
|
||||||
242995D31B29743C00090100 /* ASBasicImageDownloaderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 242995D21B29743C00090100 /* ASBasicImageDownloaderTests.m */; };
|
242995D31B29743C00090100 /* ASBasicImageDownloaderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 242995D21B29743C00090100 /* ASBasicImageDownloaderTests.m */; };
|
||||||
2538B6F31BC5D2A2003CA0B4 /* ASCollectionViewFlowLayoutInspectorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 2538B6F21BC5D2A2003CA0B4 /* ASCollectionViewFlowLayoutInspectorTests.m */; };
|
2538B6F31BC5D2A2003CA0B4 /* ASCollectionViewFlowLayoutInspectorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 2538B6F21BC5D2A2003CA0B4 /* ASCollectionViewFlowLayoutInspectorTests.m */; };
|
||||||
254C6B521BF8FE6D003EC431 /* ASTextKitTruncationTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 254C6B511BF8FE6D003EC431 /* ASTextKitTruncationTests.mm */; };
|
254C6B521BF8FE6D003EC431 /* ASTextKitTruncationTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 254C6B511BF8FE6D003EC431 /* ASTextKitTruncationTests.mm */; };
|
||||||
@@ -307,8 +310,8 @@
|
|||||||
B350625C1B010F070018CF92 /* ASLog.h in Headers */ = {isa = PBXBuildFile; fileRef = 0516FA3B1A15563400B4EBED /* ASLog.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
B350625C1B010F070018CF92 /* ASLog.h in Headers */ = {isa = PBXBuildFile; fileRef = 0516FA3B1A15563400B4EBED /* ASLog.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
B350625D1B0111740018CF92 /* Photos.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 051943141A1575670030A7D0 /* Photos.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
|
B350625D1B0111740018CF92 /* Photos.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 051943141A1575670030A7D0 /* Photos.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
|
||||||
B350625E1B0111780018CF92 /* AssetsLibrary.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 051943121A1575630030A7D0 /* AssetsLibrary.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
|
B350625E1B0111780018CF92 /* AssetsLibrary.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 051943121A1575630030A7D0 /* AssetsLibrary.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
|
||||||
BB5FC3D11F9C9389007F191E /* ASTabBarControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BB5FC3D01F9C9389007F191E /* ASTabBarControllerTests.m */; };
|
|
||||||
BB5FC3CE1F9BA689007F191E /* ASNavigationControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BB5FC3CD1F9BA688007F191E /* ASNavigationControllerTests.m */; };
|
BB5FC3CE1F9BA689007F191E /* ASNavigationControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BB5FC3CD1F9BA688007F191E /* ASNavigationControllerTests.m */; };
|
||||||
|
BB5FC3D11F9C9389007F191E /* ASTabBarControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BB5FC3D01F9C9389007F191E /* ASTabBarControllerTests.m */; };
|
||||||
C78F7E2B1BF7809800CDEAFC /* ASTableNode.h in Headers */ = {isa = PBXBuildFile; fileRef = B0F880581BEAEC7500D17647 /* ASTableNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
C78F7E2B1BF7809800CDEAFC /* ASTableNode.h in Headers */ = {isa = PBXBuildFile; fileRef = B0F880581BEAEC7500D17647 /* ASTableNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
CC034A091E60BEB400626263 /* ASDisplayNode+Convenience.h in Headers */ = {isa = PBXBuildFile; fileRef = CC034A071E60BEB400626263 /* ASDisplayNode+Convenience.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
CC034A091E60BEB400626263 /* ASDisplayNode+Convenience.h in Headers */ = {isa = PBXBuildFile; fileRef = CC034A071E60BEB400626263 /* ASDisplayNode+Convenience.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
CC034A0A1E60BEB400626263 /* ASDisplayNode+Convenience.m in Sources */ = {isa = PBXBuildFile; fileRef = CC034A081E60BEB400626263 /* ASDisplayNode+Convenience.m */; };
|
CC034A0A1E60BEB400626263 /* ASDisplayNode+Convenience.m in Sources */ = {isa = PBXBuildFile; fileRef = CC034A081E60BEB400626263 /* ASDisplayNode+Convenience.m */; };
|
||||||
@@ -572,6 +575,9 @@
|
|||||||
18C2ED7C1B9B7DE800F627B3 /* ASCollectionNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionNode.h; sourceTree = "<group>"; };
|
18C2ED7C1B9B7DE800F627B3 /* ASCollectionNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionNode.h; sourceTree = "<group>"; };
|
||||||
18C2ED7D1B9B7DE800F627B3 /* ASCollectionNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASCollectionNode.mm; sourceTree = "<group>"; };
|
18C2ED7D1B9B7DE800F627B3 /* ASCollectionNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASCollectionNode.mm; sourceTree = "<group>"; };
|
||||||
1950C4481A3BB5C1005C8279 /* ASEqualityHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASEqualityHelpers.h; sourceTree = "<group>"; };
|
1950C4481A3BB5C1005C8279 /* ASEqualityHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASEqualityHelpers.h; sourceTree = "<group>"; };
|
||||||
|
1A6C000B1FAB4E2000D05926 /* ASCornerLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCornerLayoutSpec.h; sourceTree = "<group>"; };
|
||||||
|
1A6C000C1FAB4E2100D05926 /* ASCornerLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASCornerLayoutSpec.mm; sourceTree = "<group>"; };
|
||||||
|
1A6C000F1FAB4ED400D05926 /* ASCornerLayoutSpecSnapshotTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASCornerLayoutSpecSnapshotTests.mm; sourceTree = "<group>"; };
|
||||||
205F0E0D1B371875007741D0 /* UICollectionViewLayout+ASConvenience.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UICollectionViewLayout+ASConvenience.h"; sourceTree = "<group>"; };
|
205F0E0D1B371875007741D0 /* UICollectionViewLayout+ASConvenience.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UICollectionViewLayout+ASConvenience.h"; sourceTree = "<group>"; };
|
||||||
205F0E0E1B371875007741D0 /* UICollectionViewLayout+ASConvenience.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UICollectionViewLayout+ASConvenience.m"; sourceTree = "<group>"; };
|
205F0E0E1B371875007741D0 /* UICollectionViewLayout+ASConvenience.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UICollectionViewLayout+ASConvenience.m"; sourceTree = "<group>"; };
|
||||||
205F0E111B371BD7007741D0 /* ASScrollDirection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASScrollDirection.m; sourceTree = "<group>"; };
|
205F0E111B371BD7007741D0 /* ASScrollDirection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASScrollDirection.m; sourceTree = "<group>"; };
|
||||||
@@ -785,8 +791,8 @@
|
|||||||
B30BF6501C5964B0004FCD53 /* ASLayoutManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASLayoutManager.h; path = TextKit/ASLayoutManager.h; sourceTree = "<group>"; };
|
B30BF6501C5964B0004FCD53 /* ASLayoutManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASLayoutManager.h; path = TextKit/ASLayoutManager.h; sourceTree = "<group>"; };
|
||||||
B30BF6511C5964B0004FCD53 /* ASLayoutManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ASLayoutManager.m; path = TextKit/ASLayoutManager.m; sourceTree = "<group>"; };
|
B30BF6511C5964B0004FCD53 /* ASLayoutManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ASLayoutManager.m; path = TextKit/ASLayoutManager.m; sourceTree = "<group>"; };
|
||||||
B35061DA1B010EDF0018CF92 /* AsyncDisplayKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AsyncDisplayKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
B35061DA1B010EDF0018CF92 /* AsyncDisplayKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AsyncDisplayKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
BB5FC3D01F9C9389007F191E /* ASTabBarControllerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ASTabBarControllerTests.m; sourceTree = "<group>"; };
|
|
||||||
BB5FC3CD1F9BA688007F191E /* ASNavigationControllerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ASNavigationControllerTests.m; sourceTree = "<group>"; };
|
BB5FC3CD1F9BA688007F191E /* ASNavigationControllerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ASNavigationControllerTests.m; sourceTree = "<group>"; };
|
||||||
|
BB5FC3D01F9C9389007F191E /* ASTabBarControllerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ASTabBarControllerTests.m; sourceTree = "<group>"; };
|
||||||
BDC2D162BD55A807C1475DA5 /* Pods-AsyncDisplayKitTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AsyncDisplayKitTests.profile.xcconfig"; path = "Pods/Target Support Files/Pods-AsyncDisplayKitTests/Pods-AsyncDisplayKitTests.profile.xcconfig"; sourceTree = "<group>"; };
|
BDC2D162BD55A807C1475DA5 /* Pods-AsyncDisplayKitTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AsyncDisplayKitTests.profile.xcconfig"; path = "Pods/Target Support Files/Pods-AsyncDisplayKitTests/Pods-AsyncDisplayKitTests.profile.xcconfig"; sourceTree = "<group>"; };
|
||||||
CC034A071E60BEB400626263 /* ASDisplayNode+Convenience.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASDisplayNode+Convenience.h"; sourceTree = "<group>"; };
|
CC034A071E60BEB400626263 /* ASDisplayNode+Convenience.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASDisplayNode+Convenience.h"; sourceTree = "<group>"; };
|
||||||
CC034A081E60BEB400626263 /* ASDisplayNode+Convenience.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "ASDisplayNode+Convenience.m"; sourceTree = "<group>"; };
|
CC034A081E60BEB400626263 /* ASDisplayNode+Convenience.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "ASDisplayNode+Convenience.m"; sourceTree = "<group>"; };
|
||||||
@@ -1187,6 +1193,7 @@
|
|||||||
7AB338681C55B97B0055FDE8 /* ASRelativeLayoutSpecSnapshotTests.mm */,
|
7AB338681C55B97B0055FDE8 /* ASRelativeLayoutSpecSnapshotTests.mm */,
|
||||||
ACF6ED551B178DC700DA7C62 /* ASInsetLayoutSpecSnapshotTests.mm */,
|
ACF6ED551B178DC700DA7C62 /* ASInsetLayoutSpecSnapshotTests.mm */,
|
||||||
ACF6ED591B178DC700DA7C62 /* ASOverlayLayoutSpecSnapshotTests.mm */,
|
ACF6ED591B178DC700DA7C62 /* ASOverlayLayoutSpecSnapshotTests.mm */,
|
||||||
|
1A6C000F1FAB4ED400D05926 /* ASCornerLayoutSpecSnapshotTests.mm */,
|
||||||
696FCB301D6E46050093471E /* ASBackgroundLayoutSpecSnapshotTests.mm */,
|
696FCB301D6E46050093471E /* ASBackgroundLayoutSpecSnapshotTests.mm */,
|
||||||
ACF6ED5A1B178DC700DA7C62 /* ASRatioLayoutSpecSnapshotTests.mm */,
|
ACF6ED5A1B178DC700DA7C62 /* ASRatioLayoutSpecSnapshotTests.mm */,
|
||||||
ACF6ED5B1B178DC700DA7C62 /* ASStackLayoutSpecSnapshotTests.mm */,
|
ACF6ED5B1B178DC700DA7C62 /* ASStackLayoutSpecSnapshotTests.mm */,
|
||||||
@@ -1524,6 +1531,8 @@
|
|||||||
ACF6ED021B17843500DA7C62 /* ASBackgroundLayoutSpec.mm */,
|
ACF6ED021B17843500DA7C62 /* ASBackgroundLayoutSpec.mm */,
|
||||||
ACF6ED031B17843500DA7C62 /* ASCenterLayoutSpec.h */,
|
ACF6ED031B17843500DA7C62 /* ASCenterLayoutSpec.h */,
|
||||||
ACF6ED041B17843500DA7C62 /* ASCenterLayoutSpec.mm */,
|
ACF6ED041B17843500DA7C62 /* ASCenterLayoutSpec.mm */,
|
||||||
|
1A6C000B1FAB4E2000D05926 /* ASCornerLayoutSpec.h */,
|
||||||
|
1A6C000C1FAB4E2100D05926 /* ASCornerLayoutSpec.mm */,
|
||||||
ACF6ED071B17843500DA7C62 /* ASDimension.h */,
|
ACF6ED071B17843500DA7C62 /* ASDimension.h */,
|
||||||
ACF6ED081B17843500DA7C62 /* ASDimension.mm */,
|
ACF6ED081B17843500DA7C62 /* ASDimension.mm */,
|
||||||
690C35631E055C7B00069B91 /* ASDimensionInternal.h */,
|
690C35631E055C7B00069B91 /* ASDimensionInternal.h */,
|
||||||
@@ -1717,6 +1726,7 @@
|
|||||||
isa = PBXHeadersBuildPhase;
|
isa = PBXHeadersBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
1A6C000D1FAB4E2100D05926 /* ASCornerLayoutSpec.h in Headers */,
|
||||||
E54E00721F1D3828000B30D7 /* ASPagerNode+Beta.h in Headers */,
|
E54E00721F1D3828000B30D7 /* ASPagerNode+Beta.h in Headers */,
|
||||||
E5B225281F1790D6001E1431 /* ASHashing.h in Headers */,
|
E5B225281F1790D6001E1431 /* ASHashing.h in Headers */,
|
||||||
CC034A131E649F1300626263 /* AsyncDisplayKit+IGListKitMethods.h in Headers */,
|
CC034A131E649F1300626263 /* AsyncDisplayKit+IGListKitMethods.h in Headers */,
|
||||||
@@ -2069,13 +2079,16 @@
|
|||||||
files = (
|
files = (
|
||||||
);
|
);
|
||||||
inputPaths = (
|
inputPaths = (
|
||||||
|
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||||
|
"${PODS_ROOT}/Manifest.lock",
|
||||||
);
|
);
|
||||||
name = "[CP] Check Pods Manifest.lock";
|
name = "[CP] Check Pods Manifest.lock";
|
||||||
outputPaths = (
|
outputPaths = (
|
||||||
|
"$(DERIVED_FILE_DIR)/Pods-AsyncDisplayKitTests-checkManifestLockResult.txt",
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n";
|
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||||
showEnvVarsInLog = 0;
|
showEnvVarsInLog = 0;
|
||||||
};
|
};
|
||||||
3B9D88CDF51B429C8409E4B6 /* [CP] Copy Pods Resources */ = {
|
3B9D88CDF51B429C8409E4B6 /* [CP] Copy Pods Resources */ = {
|
||||||
@@ -2174,6 +2187,7 @@
|
|||||||
ACF6ED621B178DC700DA7C62 /* ASRatioLayoutSpecSnapshotTests.mm in Sources */,
|
ACF6ED621B178DC700DA7C62 /* ASRatioLayoutSpecSnapshotTests.mm in Sources */,
|
||||||
7AB338691C55B97B0055FDE8 /* ASRelativeLayoutSpecSnapshotTests.mm in Sources */,
|
7AB338691C55B97B0055FDE8 /* ASRelativeLayoutSpecSnapshotTests.mm in Sources */,
|
||||||
CCDD148B1EEDCD9D0020834E /* ASCollectionModernDataSourceTests.m in Sources */,
|
CCDD148B1EEDCD9D0020834E /* ASCollectionModernDataSourceTests.m in Sources */,
|
||||||
|
1A6C00111FAB4EDD00D05926 /* ASCornerLayoutSpecSnapshotTests.mm in Sources */,
|
||||||
254C6B541BF8FF2A003EC431 /* ASTextKitTests.mm in Sources */,
|
254C6B541BF8FF2A003EC431 /* ASTextKitTests.mm in Sources */,
|
||||||
05EA6FE71AC0966E00E35788 /* ASSnapshotTestCase.m in Sources */,
|
05EA6FE71AC0966E00E35788 /* ASSnapshotTestCase.m in Sources */,
|
||||||
ACF6ED631B178DC700DA7C62 /* ASStackLayoutSpecSnapshotTests.mm in Sources */,
|
ACF6ED631B178DC700DA7C62 /* ASStackLayoutSpecSnapshotTests.mm in Sources */,
|
||||||
@@ -2334,6 +2348,7 @@
|
|||||||
68FC85E51CE29B7E00EDD713 /* ASTabBarController.m in Sources */,
|
68FC85E51CE29B7E00EDD713 /* ASTabBarController.m in Sources */,
|
||||||
CCCCCCDC1EC3EF060087FE10 /* ASTextLine.m in Sources */,
|
CCCCCCDC1EC3EF060087FE10 /* ASTextLine.m in Sources */,
|
||||||
34EFC7741B701D0A00AD841F /* ASAbsoluteLayoutSpec.mm in Sources */,
|
34EFC7741B701D0A00AD841F /* ASAbsoluteLayoutSpec.mm in Sources */,
|
||||||
|
1A6C000E1FAB4E2100D05926 /* ASCornerLayoutSpec.mm in Sources */,
|
||||||
CCCCCCE81EC3F0FC0087FE10 /* NSAttributedString+ASText.m in Sources */,
|
CCCCCCE81EC3F0FC0087FE10 /* NSAttributedString+ASText.m in Sources */,
|
||||||
690C35621E055C5D00069B91 /* ASDimensionInternal.mm in Sources */,
|
690C35621E055C5D00069B91 /* ASDimensionInternal.mm in Sources */,
|
||||||
909C4C761F09C98B00D6B76F /* ASTextNode2.mm in Sources */,
|
909C4C761F09C98B00D6B76F /* ASTextNode2.mm in Sources */,
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
- Updated to be backwards compatible with Xcode 8. [Adlai Holler](https://github.com/Adlai-Holler)
|
- Updated to be backwards compatible with Xcode 8. [Adlai Holler](https://github.com/Adlai-Holler)
|
||||||
- [API CHANGES] `ASPerformMainThreadDeallocation` and `ASPerformBackgroundDeallocation` functions take `id *` instead of `id` and they're now more reliable. Also, in Swift, `ASDeallocQueue.sharedDeallocationQueue() -> ASDeallocQueue.sharedDeallocationQueue`. [Adlai Holler](https://github.com/Adlai-Holler) [#651](https://github.com/TextureGroup/Texture/pull/651)
|
- [API CHANGES] `ASPerformMainThreadDeallocation` and `ASPerformBackgroundDeallocation` functions take `id *` instead of `id` and they're now more reliable. Also, in Swift, `ASDeallocQueue.sharedDeallocationQueue() -> ASDeallocQueue.sharedDeallocationQueue`. [Adlai Holler](https://github.com/Adlai-Holler) [#651](https://github.com/TextureGroup/Texture/pull/651)
|
||||||
- [Collection/Table] Added direct support for mapping section indexes between data spaces. [Adlai Holler](https://github.com/Adlai-Holler) [#651](https://github.com/TextureGroup/Texture/pull/660)
|
- [Collection/Table] Added direct support for mapping section indexes between data spaces. [Adlai Holler](https://github.com/Adlai-Holler) [#651](https://github.com/TextureGroup/Texture/pull/660)
|
||||||
|
- [ASCornerLayoutSpec] New layout spec class for declarative corner element layout. [#657](https://github.com/TextureGroup/Texture/pull/657) [huangkun](https://github.com/huang-kun)
|
||||||
|
|
||||||
## 2.6
|
## 2.6
|
||||||
- [Xcode 9] Updated to require Xcode 9 (to fix warnings) [Garrett Moon](https://github.com/garrettmoon)
|
- [Xcode 9] Updated to require Xcode 9 (to fix warnings) [Garrett Moon](https://github.com/garrettmoon)
|
||||||
|
|||||||
@@ -79,6 +79,7 @@
|
|||||||
#import <AsyncDisplayKit/ASLayoutSpec.h>
|
#import <AsyncDisplayKit/ASLayoutSpec.h>
|
||||||
#import <AsyncDisplayKit/ASBackgroundLayoutSpec.h>
|
#import <AsyncDisplayKit/ASBackgroundLayoutSpec.h>
|
||||||
#import <AsyncDisplayKit/ASCenterLayoutSpec.h>
|
#import <AsyncDisplayKit/ASCenterLayoutSpec.h>
|
||||||
|
#import <AsyncDisplayKit/ASCornerLayoutSpec.h>
|
||||||
#import <AsyncDisplayKit/ASRelativeLayoutSpec.h>
|
#import <AsyncDisplayKit/ASRelativeLayoutSpec.h>
|
||||||
#import <AsyncDisplayKit/ASInsetLayoutSpec.h>
|
#import <AsyncDisplayKit/ASInsetLayoutSpec.h>
|
||||||
#import <AsyncDisplayKit/ASOverlayLayoutSpec.h>
|
#import <AsyncDisplayKit/ASOverlayLayoutSpec.h>
|
||||||
|
|||||||
79
Source/Layout/ASCornerLayoutSpec.h
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
//
|
||||||
|
// ASCornerLayoutSpec.h
|
||||||
|
// Texture
|
||||||
|
//
|
||||||
|
// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <AsyncDisplayKit/ASLayoutSpec.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
The corner location for positioning corner element.
|
||||||
|
*/
|
||||||
|
typedef NS_ENUM(NSInteger, ASCornerLayoutLocation) {
|
||||||
|
ASCornerLayoutLocationTopLeft,
|
||||||
|
ASCornerLayoutLocationTopRight,
|
||||||
|
ASCornerLayoutLocationBottomLeft,
|
||||||
|
ASCornerLayoutLocationBottomRight,
|
||||||
|
};
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
/**
|
||||||
|
A layout spec that positions a corner element which relatives to the child element.
|
||||||
|
|
||||||
|
@warning Both child element and corner element must have valid preferredSize for layout calculation.
|
||||||
|
*/
|
||||||
|
@interface ASCornerLayoutSpec : ASLayoutSpec
|
||||||
|
|
||||||
|
/**
|
||||||
|
A layout spec that positions a corner element which relatives to the child element.
|
||||||
|
|
||||||
|
@param child A child that is laid out to determine the size of this spec.
|
||||||
|
@param corner A layoutElement object that is laid out to a corner on the child.
|
||||||
|
@param location The corner position option.
|
||||||
|
@return An ASCornerLayoutSpec object with a given child and an layoutElement that act as corner.
|
||||||
|
*/
|
||||||
|
- (instancetype)initWithChild:(id <ASLayoutElement>)child corner:(id <ASLayoutElement>)corner location:(ASCornerLayoutLocation)location AS_WARN_UNUSED_RESULT;
|
||||||
|
|
||||||
|
/**
|
||||||
|
A layout spec that positions a corner element which relatives to the child element.
|
||||||
|
|
||||||
|
@param child A child that is laid out to determine the size of this spec.
|
||||||
|
@param corner A layoutElement object that is laid out to a corner on the child.
|
||||||
|
@param location The corner position option.
|
||||||
|
@return An ASCornerLayoutSpec object with a given child and an layoutElement that act as corner.
|
||||||
|
*/
|
||||||
|
+ (instancetype)cornerLayoutSpecWithChild:(id <ASLayoutElement>)child corner:(id <ASLayoutElement>)corner location:(ASCornerLayoutLocation)location AS_WARN_UNUSED_RESULT;
|
||||||
|
|
||||||
|
/**
|
||||||
|
A layoutElement object that is laid out to a corner on the child.
|
||||||
|
*/
|
||||||
|
@property (nonatomic, strong) id <ASLayoutElement> corner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
The corner position option.
|
||||||
|
*/
|
||||||
|
@property (nonatomic, assign) ASCornerLayoutLocation cornerLocation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
The point which offsets from the corner location. Use this property to make delta
|
||||||
|
distance from the default corner location. Default is CGPointZero.
|
||||||
|
*/
|
||||||
|
@property (nonatomic, assign) CGPoint offset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Whether should include corner element into layout size calculation. If included,
|
||||||
|
the layout size will be the union size of both child and corner; If not included,
|
||||||
|
the layout size will be only child's size. Default is NO.
|
||||||
|
*/
|
||||||
|
@property (nonatomic, assign) BOOL wrapsCorner;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
169
Source/Layout/ASCornerLayoutSpec.mm
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
//
|
||||||
|
// ASCornerLayoutSpec.mm
|
||||||
|
// Texture
|
||||||
|
//
|
||||||
|
// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <AsyncDisplayKit/ASCornerLayoutSpec.h>
|
||||||
|
#import <AsyncDisplayKit/ASLayout.h>
|
||||||
|
#import <AsyncDisplayKit/ASLayoutSpec+Subclasses.h>
|
||||||
|
#import <AsyncDisplayKit/ASDisplayNode.h>
|
||||||
|
|
||||||
|
CGPoint as_calculatedCornerOriginIn(CGRect baseFrame, CGSize cornerSize, ASCornerLayoutLocation cornerLocation, CGPoint offset)
|
||||||
|
{
|
||||||
|
CGPoint cornerOrigin = CGPointZero;
|
||||||
|
CGPoint baseOrigin = baseFrame.origin;
|
||||||
|
CGSize baseSize = baseFrame.size;
|
||||||
|
|
||||||
|
switch (cornerLocation) {
|
||||||
|
case ASCornerLayoutLocationTopLeft:
|
||||||
|
cornerOrigin.x = baseOrigin.x - cornerSize.width / 2;
|
||||||
|
cornerOrigin.y = baseOrigin.y - cornerSize.height / 2;
|
||||||
|
break;
|
||||||
|
case ASCornerLayoutLocationTopRight:
|
||||||
|
cornerOrigin.x = baseOrigin.x + baseSize.width - cornerSize.width / 2;
|
||||||
|
cornerOrigin.y = baseOrigin.y - cornerSize.height / 2;
|
||||||
|
break;
|
||||||
|
case ASCornerLayoutLocationBottomLeft:
|
||||||
|
cornerOrigin.x = baseOrigin.x - cornerSize.width / 2;
|
||||||
|
cornerOrigin.y = baseOrigin.y + baseSize.height - cornerSize.height / 2;
|
||||||
|
break;
|
||||||
|
case ASCornerLayoutLocationBottomRight:
|
||||||
|
cornerOrigin.x = baseOrigin.x + baseSize.width - cornerSize.width / 2;
|
||||||
|
cornerOrigin.y = baseOrigin.y + baseSize.height - cornerSize.height / 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
cornerOrigin.x += offset.x;
|
||||||
|
cornerOrigin.y += offset.y;
|
||||||
|
|
||||||
|
return cornerOrigin;
|
||||||
|
}
|
||||||
|
|
||||||
|
static NSUInteger const kBaseChildIndex = 0;
|
||||||
|
static NSUInteger const kCornerChildIndex = 1;
|
||||||
|
|
||||||
|
@interface ASCornerLayoutSpec()
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation ASCornerLayoutSpec
|
||||||
|
|
||||||
|
- (instancetype)initWithChild:(id <ASLayoutElement>)child corner:(id <ASLayoutElement>)corner location:(ASCornerLayoutLocation)location
|
||||||
|
{
|
||||||
|
self = [super init];
|
||||||
|
if (self) {
|
||||||
|
self.child = child;
|
||||||
|
self.corner = corner;
|
||||||
|
self.cornerLocation = location;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (instancetype)cornerLayoutSpecWithChild:(id <ASLayoutElement>)child corner:(id <ASLayoutElement>)corner location:(ASCornerLayoutLocation)location
|
||||||
|
{
|
||||||
|
return [[self alloc] initWithChild:child corner:corner location:location];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Children
|
||||||
|
|
||||||
|
- (void)setChild:(id<ASLayoutElement>)child
|
||||||
|
{
|
||||||
|
ASDisplayNodeAssertNotNil(child, @"Child shouldn't be nil.");
|
||||||
|
[super setChild:child atIndex:kBaseChildIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id<ASLayoutElement>)child
|
||||||
|
{
|
||||||
|
return [super childAtIndex:kBaseChildIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setCorner:(id<ASLayoutElement>)corner
|
||||||
|
{
|
||||||
|
ASDisplayNodeAssertNotNil(corner, @"Corner element cannot be nil.");
|
||||||
|
[super setChild:corner atIndex:kCornerChildIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id<ASLayoutElement>)corner
|
||||||
|
{
|
||||||
|
return [super childAtIndex:kCornerChildIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Calculation
|
||||||
|
|
||||||
|
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
|
||||||
|
{
|
||||||
|
CGSize size = {
|
||||||
|
ASPointsValidForSize(constrainedSize.max.width) == NO ? ASLayoutElementParentDimensionUndefined : constrainedSize.max.width,
|
||||||
|
ASPointsValidForSize(constrainedSize.max.height) == NO ? ASLayoutElementParentDimensionUndefined : constrainedSize.max.height
|
||||||
|
};
|
||||||
|
|
||||||
|
id <ASLayoutElement> child = self.child;
|
||||||
|
id <ASLayoutElement> corner = self.corner;
|
||||||
|
|
||||||
|
// Element validation
|
||||||
|
[self _validateElement:child];
|
||||||
|
[self _validateElement:corner];
|
||||||
|
|
||||||
|
CGRect childFrame = CGRectZero;
|
||||||
|
CGRect cornerFrame = CGRectZero;
|
||||||
|
|
||||||
|
// Layout child
|
||||||
|
ASLayout *childLayout = [child layoutThatFits:constrainedSize parentSize:size];
|
||||||
|
childFrame.size = childLayout.size;
|
||||||
|
|
||||||
|
// Layout corner
|
||||||
|
ASLayout *cornerLayout = [corner layoutThatFits:constrainedSize parentSize:size];
|
||||||
|
cornerFrame.size = cornerLayout.size;
|
||||||
|
|
||||||
|
// Calculate corner's position
|
||||||
|
CGPoint relativePosition = as_calculatedCornerOriginIn(childFrame, cornerFrame.size, _cornerLocation, _offset);
|
||||||
|
|
||||||
|
// Update corner's position
|
||||||
|
cornerFrame.origin = relativePosition;
|
||||||
|
|
||||||
|
// Calculate size
|
||||||
|
CGRect frame = childFrame;
|
||||||
|
if (_wrapsCorner) {
|
||||||
|
frame = CGRectUnion(childFrame, cornerFrame);
|
||||||
|
frame.size = ASSizeRangeClamp(constrainedSize, frame.size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shift sublayouts' positions if they are off the bounds.
|
||||||
|
if (frame.origin.x != 0) {
|
||||||
|
CGFloat deltaX = frame.origin.x;
|
||||||
|
childFrame.origin.x -= deltaX;
|
||||||
|
cornerFrame.origin.x -= deltaX;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (frame.origin.y != 0) {
|
||||||
|
CGFloat deltaY = frame.origin.y;
|
||||||
|
childFrame.origin.y -= deltaY;
|
||||||
|
cornerFrame.origin.y -= deltaY;
|
||||||
|
}
|
||||||
|
|
||||||
|
childLayout.position = childFrame.origin;
|
||||||
|
cornerLayout.position = cornerFrame.origin;
|
||||||
|
|
||||||
|
return [ASLayout layoutWithLayoutElement:self size:frame.size sublayouts:@[childLayout, cornerLayout]];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)_validateElement:(id <ASLayoutElement>)element
|
||||||
|
{
|
||||||
|
// Validate non-nil element
|
||||||
|
if (element == nil) {
|
||||||
|
ASDisplayNodeAssertNotNil(element, @"[%@]: Must have a non-nil child/corner for layout calculation.", self.class);
|
||||||
|
}
|
||||||
|
// Validate preferredSize if needed
|
||||||
|
CGSize size = element.style.preferredSize;
|
||||||
|
if (!CGSizeEqualToSize(size, CGSizeZero) && !ASIsCGSizeValidForSize(size) && (size.width < 0 || (size.height < 0))) {
|
||||||
|
ASDisplayNodeFailAssert(@"[%@]: Should give a valid preferredSize value for %@ before corner's position calculation.", self.class, element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
219
Tests/ASCornerLayoutSpecSnapshotTests.mm
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
//
|
||||||
|
// ASCornerLayoutSpecSnapshotTests.mm
|
||||||
|
// Texture
|
||||||
|
//
|
||||||
|
// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "ASLayoutSpecSnapshotTestsHelper.h"
|
||||||
|
#import <AsyncDisplayKit/ASCornerLayoutSpec.h>
|
||||||
|
#import <AsyncDisplayKit/ASBackgroundLayoutSpec.h>
|
||||||
|
|
||||||
|
typedef NS_ENUM(NSInteger, ASCornerLayoutSpecSnapshotTestsOffsetOption) {
|
||||||
|
ASCornerLayoutSpecSnapshotTestsOffsetOptionCenter,
|
||||||
|
ASCornerLayoutSpecSnapshotTestsOffsetOptionInner,
|
||||||
|
ASCornerLayoutSpecSnapshotTestsOffsetOptionOuter,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@interface ASCornerLayoutSpecSnapshotTests : ASLayoutSpecSnapshotTestCase
|
||||||
|
|
||||||
|
@property (nonatomic, strong) UIColor *boxColor;
|
||||||
|
@property (nonatomic, strong) UIColor *baseColor;
|
||||||
|
@property (nonatomic, strong) UIColor *cornerColor;
|
||||||
|
@property (nonatomic, strong) UIColor *contextColor;
|
||||||
|
|
||||||
|
@property (nonatomic, assign) CGSize baseSize;
|
||||||
|
@property (nonatomic, assign) CGSize cornerSize;
|
||||||
|
@property (nonatomic, assign) CGSize contextSize;
|
||||||
|
|
||||||
|
@property (nonatomic, assign) ASSizeRange contextSizeRange;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
@implementation ASCornerLayoutSpecSnapshotTests
|
||||||
|
|
||||||
|
- (void)setUp
|
||||||
|
{
|
||||||
|
[super setUp];
|
||||||
|
|
||||||
|
self.recordMode = NO;
|
||||||
|
|
||||||
|
_boxColor = [UIColor greenColor];
|
||||||
|
_baseColor = [UIColor blueColor];
|
||||||
|
_cornerColor = [UIColor orangeColor];
|
||||||
|
_contextColor = [UIColor lightGrayColor];
|
||||||
|
|
||||||
|
_baseSize = CGSizeMake(60, 60);
|
||||||
|
_cornerSize = CGSizeMake(20, 20);
|
||||||
|
_contextSize = CGSizeMake(120, 120);
|
||||||
|
|
||||||
|
_contextSizeRange = ASSizeRangeMake(CGSizeZero, _contextSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testCornerSpecForAllLocations
|
||||||
|
{
|
||||||
|
ASCornerLayoutSpecSnapshotTestsOffsetOption center = ASCornerLayoutSpecSnapshotTestsOffsetOptionCenter;
|
||||||
|
|
||||||
|
[self testCornerSpecWithLocation:ASCornerLayoutLocationTopLeft offsetOption:center wrapsCorner:NO];
|
||||||
|
[self testCornerSpecWithLocation:ASCornerLayoutLocationTopLeft offsetOption:center wrapsCorner:YES];
|
||||||
|
|
||||||
|
[self testCornerSpecWithLocation:ASCornerLayoutLocationTopRight offsetOption:center wrapsCorner:NO];
|
||||||
|
[self testCornerSpecWithLocation:ASCornerLayoutLocationTopRight offsetOption:center wrapsCorner:YES];
|
||||||
|
|
||||||
|
[self testCornerSpecWithLocation:ASCornerLayoutLocationBottomLeft offsetOption:center wrapsCorner:NO];
|
||||||
|
[self testCornerSpecWithLocation:ASCornerLayoutLocationBottomLeft offsetOption:center wrapsCorner:YES];
|
||||||
|
|
||||||
|
[self testCornerSpecWithLocation:ASCornerLayoutLocationBottomRight offsetOption:center wrapsCorner:NO];
|
||||||
|
[self testCornerSpecWithLocation:ASCornerLayoutLocationBottomRight offsetOption:center wrapsCorner:YES];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testCornerSpecForAllLocationsWithInnerOffset
|
||||||
|
{
|
||||||
|
ASCornerLayoutSpecSnapshotTestsOffsetOption inner = ASCornerLayoutSpecSnapshotTestsOffsetOptionInner;
|
||||||
|
|
||||||
|
[self testCornerSpecWithLocation:ASCornerLayoutLocationTopLeft offsetOption:inner wrapsCorner:NO];
|
||||||
|
[self testCornerSpecWithLocation:ASCornerLayoutLocationTopLeft offsetOption:inner wrapsCorner:YES];
|
||||||
|
|
||||||
|
[self testCornerSpecWithLocation:ASCornerLayoutLocationTopRight offsetOption:inner wrapsCorner:NO];
|
||||||
|
[self testCornerSpecWithLocation:ASCornerLayoutLocationTopRight offsetOption:inner wrapsCorner:YES];
|
||||||
|
|
||||||
|
[self testCornerSpecWithLocation:ASCornerLayoutLocationBottomLeft offsetOption:inner wrapsCorner:NO];
|
||||||
|
[self testCornerSpecWithLocation:ASCornerLayoutLocationBottomLeft offsetOption:inner wrapsCorner:YES];
|
||||||
|
|
||||||
|
[self testCornerSpecWithLocation:ASCornerLayoutLocationBottomRight offsetOption:inner wrapsCorner:NO];
|
||||||
|
[self testCornerSpecWithLocation:ASCornerLayoutLocationBottomRight offsetOption:inner wrapsCorner:YES];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testCornerSpecForAllLocationsWithOuterOffset
|
||||||
|
{
|
||||||
|
ASCornerLayoutSpecSnapshotTestsOffsetOption outer = ASCornerLayoutSpecSnapshotTestsOffsetOptionOuter;
|
||||||
|
|
||||||
|
[self testCornerSpecWithLocation:ASCornerLayoutLocationTopLeft offsetOption:outer wrapsCorner:NO];
|
||||||
|
[self testCornerSpecWithLocation:ASCornerLayoutLocationTopLeft offsetOption:outer wrapsCorner:YES];
|
||||||
|
|
||||||
|
[self testCornerSpecWithLocation:ASCornerLayoutLocationTopRight offsetOption:outer wrapsCorner:NO];
|
||||||
|
[self testCornerSpecWithLocation:ASCornerLayoutLocationTopRight offsetOption:outer wrapsCorner:YES];
|
||||||
|
|
||||||
|
[self testCornerSpecWithLocation:ASCornerLayoutLocationBottomLeft offsetOption:outer wrapsCorner:NO];
|
||||||
|
[self testCornerSpecWithLocation:ASCornerLayoutLocationBottomLeft offsetOption:outer wrapsCorner:YES];
|
||||||
|
|
||||||
|
[self testCornerSpecWithLocation:ASCornerLayoutLocationBottomRight offsetOption:outer wrapsCorner:NO];
|
||||||
|
[self testCornerSpecWithLocation:ASCornerLayoutLocationBottomRight offsetOption:outer wrapsCorner:YES];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testCornerSpecWithLocation:(ASCornerLayoutLocation)location
|
||||||
|
offsetOption:(ASCornerLayoutSpecSnapshotTestsOffsetOption)offsetOption
|
||||||
|
wrapsCorner:(BOOL)wrapsCorner
|
||||||
|
{
|
||||||
|
ASDisplayNode *baseNode = ASDisplayNodeWithBackgroundColor(_baseColor, _baseSize);
|
||||||
|
ASDisplayNode *cornerNode = ASDisplayNodeWithBackgroundColor(_cornerColor, _cornerSize);
|
||||||
|
ASDisplayNode *debugBoxNode = ASDisplayNodeWithBackgroundColor(_boxColor);
|
||||||
|
|
||||||
|
baseNode.style.layoutPosition = CGPointMake((_contextSize.width - _baseSize.width) / 2,
|
||||||
|
(_contextSize.height - _baseSize.height) / 2);
|
||||||
|
|
||||||
|
ASCornerLayoutSpec *cornerSpec = [ASCornerLayoutSpec cornerLayoutSpecWithChild:baseNode
|
||||||
|
corner:cornerNode
|
||||||
|
location:location];
|
||||||
|
|
||||||
|
CGPoint delta = (CGPoint){ _cornerSize.width / 2, _cornerSize.height / 2 };
|
||||||
|
cornerSpec.offset = [self offsetForOption:offsetOption location:location delta:delta];
|
||||||
|
cornerSpec.wrapsCorner = wrapsCorner;
|
||||||
|
|
||||||
|
ASBackgroundLayoutSpec *backgroundSpec = [ASBackgroundLayoutSpec backgroundLayoutSpecWithChild:cornerSpec
|
||||||
|
background:debugBoxNode];
|
||||||
|
|
||||||
|
[self testLayoutSpec:backgroundSpec
|
||||||
|
sizeRange:_contextSizeRange
|
||||||
|
subnodes:@[debugBoxNode, baseNode, cornerNode]
|
||||||
|
identifier:[self suffixWithLocation:location option:offsetOption wrapsCorner:wrapsCorner]];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (CGPoint)offsetForOption:(ASCornerLayoutSpecSnapshotTestsOffsetOption)option
|
||||||
|
location:(ASCornerLayoutLocation)location
|
||||||
|
delta:(CGPoint)delta
|
||||||
|
{
|
||||||
|
CGFloat x = delta.x;
|
||||||
|
CGFloat y = delta.y;
|
||||||
|
|
||||||
|
switch (option) {
|
||||||
|
|
||||||
|
case ASCornerLayoutSpecSnapshotTestsOffsetOptionCenter:
|
||||||
|
return CGPointZero;
|
||||||
|
|
||||||
|
case ASCornerLayoutSpecSnapshotTestsOffsetOptionInner:
|
||||||
|
|
||||||
|
switch (location) {
|
||||||
|
case ASCornerLayoutLocationTopLeft: return (CGPoint){ x, y };
|
||||||
|
case ASCornerLayoutLocationTopRight: return (CGPoint){ -x, y };
|
||||||
|
case ASCornerLayoutLocationBottomLeft: return (CGPoint){ x, -y };
|
||||||
|
case ASCornerLayoutLocationBottomRight: return (CGPoint){ -x, -y };
|
||||||
|
}
|
||||||
|
|
||||||
|
case ASCornerLayoutSpecSnapshotTestsOffsetOptionOuter:
|
||||||
|
|
||||||
|
switch (location) {
|
||||||
|
case ASCornerLayoutLocationTopLeft: return (CGPoint){ -x, -y };
|
||||||
|
case ASCornerLayoutLocationTopRight: return (CGPoint){ x, -y };
|
||||||
|
case ASCornerLayoutLocationBottomLeft: return (CGPoint){ -x, y };
|
||||||
|
case ASCornerLayoutLocationBottomRight: return (CGPoint){ x, y };
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)suffixWithLocation:(ASCornerLayoutLocation)location
|
||||||
|
option:(ASCornerLayoutSpecSnapshotTestsOffsetOption)option
|
||||||
|
wrapsCorner:(BOOL)wrapsCorner
|
||||||
|
{
|
||||||
|
NSMutableString *desc = [NSMutableString string];
|
||||||
|
|
||||||
|
switch (location) {
|
||||||
|
case ASCornerLayoutLocationTopLeft:
|
||||||
|
[desc appendString:@"topLeft"];
|
||||||
|
break;
|
||||||
|
case ASCornerLayoutLocationTopRight:
|
||||||
|
[desc appendString:@"topRight"];
|
||||||
|
break;
|
||||||
|
case ASCornerLayoutLocationBottomLeft:
|
||||||
|
[desc appendString:@"bottomLeft"];
|
||||||
|
break;
|
||||||
|
case ASCornerLayoutLocationBottomRight:
|
||||||
|
[desc appendString:@"bottomRight"];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
[desc appendString:@"_"];
|
||||||
|
|
||||||
|
switch (option) {
|
||||||
|
case ASCornerLayoutSpecSnapshotTestsOffsetOptionCenter:
|
||||||
|
[desc appendString:@"center"];
|
||||||
|
break;
|
||||||
|
case ASCornerLayoutSpecSnapshotTestsOffsetOptionInner:
|
||||||
|
[desc appendString:@"inner"];
|
||||||
|
break;
|
||||||
|
case ASCornerLayoutSpecSnapshotTestsOffsetOptionOuter:
|
||||||
|
[desc appendString:@"outer"];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
[desc appendString:@"_"];
|
||||||
|
|
||||||
|
if (wrapsCorner) {
|
||||||
|
[desc appendString:@"fullSize"];
|
||||||
|
} else {
|
||||||
|
[desc appendString:@"childSize"];
|
||||||
|
}
|
||||||
|
|
||||||
|
return desc.copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
After Width: | Height: | Size: 697 B |
|
After Width: | Height: | Size: 697 B |
|
After Width: | Height: | Size: 780 B |
|
After Width: | Height: | Size: 780 B |
|
After Width: | Height: | Size: 774 B |
|
After Width: | Height: | Size: 774 B |
|
After Width: | Height: | Size: 785 B |
|
After Width: | Height: | Size: 785 B |
|
After Width: | Height: | Size: 667 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 667 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 667 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 667 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 701 B |
|
After Width: | Height: | Size: 900 B |
|
After Width: | Height: | Size: 701 B |
|
After Width: | Height: | Size: 943 B |
|
After Width: | Height: | Size: 708 B |
|
After Width: | Height: | Size: 888 B |
|
After Width: | Height: | Size: 707 B |
|
After Width: | Height: | Size: 927 B |
BIN
docs/static/images/example-app-screenshots/ASCornerLayoutSpec.png
vendored
Normal file
|
After Width: | Height: | Size: 122 KiB |
@@ -143,3 +143,140 @@ class FlexibleSeparatorSurroundingContent : LayoutExampleNode {
|
|||||||
return "try rotating me!"
|
return "try rotating me!"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CornerLayoutSample : PhotoWithOutsetIconOverlay {
|
||||||
|
let photoNode1 = ASImageNode()
|
||||||
|
let photoNode2 = ASImageNode()
|
||||||
|
let dotNode = ASImageNode()
|
||||||
|
let badgeTextNode = ASTextNode()
|
||||||
|
let badgeImageNode = ASImageNode()
|
||||||
|
|
||||||
|
struct ImageSize {
|
||||||
|
static let avatar = CGSize(width: 100, height: 100)
|
||||||
|
static let icon = CGSize(width: 26, height: 26)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ImageColor {
|
||||||
|
static let avatar = UIColor.lightGray
|
||||||
|
static let icon = UIColor.red
|
||||||
|
}
|
||||||
|
|
||||||
|
required init() {
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
let avatarImage = UIImage.draw(size: ImageSize.avatar, fillColor: ImageColor.avatar) { () -> UIBezierPath in
|
||||||
|
return UIBezierPath(roundedRect: CGRect(origin: CGPoint.zero, size: ImageSize.avatar), cornerRadius: ImageSize.avatar.width / 20)
|
||||||
|
}
|
||||||
|
|
||||||
|
let iconImage = UIImage.draw(size: ImageSize.icon, fillColor: ImageColor.icon) { () -> UIBezierPath in
|
||||||
|
return UIBezierPath(ovalIn: CGRect(origin: CGPoint.zero, size: ImageSize.icon))
|
||||||
|
}
|
||||||
|
|
||||||
|
photoNode1.image = avatarImage
|
||||||
|
photoNode2.image = avatarImage
|
||||||
|
dotNode.image = iconImage
|
||||||
|
|
||||||
|
badgeTextNode.attributedText = NSAttributedString.attributedString(string: " 999+ ", fontSize: 20, color: .white)
|
||||||
|
|
||||||
|
badgeImageNode.image = UIImage.as_resizableRoundedImage(withCornerRadius: 12, cornerColor: .clear, fill: .red)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
|
||||||
|
photoNode.style.preferredSize = ImageSize.avatar
|
||||||
|
iconNode.style.preferredSize = ImageSize.icon
|
||||||
|
|
||||||
|
let badgeSpec = ASBackgroundLayoutSpec(child: badgeTextNode, background: badgeImageNode)
|
||||||
|
let cornerSpec1 = ASCornerLayoutSpec(child: photoNode1, corner: dotNode, location: .topRight)
|
||||||
|
let cornerSpec2 = ASCornerLayoutSpec(child: photoNode2, corner: badgeSpec, location: .topRight)
|
||||||
|
let cornerSpec3 = ASCornerLayoutSpec(child: photoNode, corner: iconNode, location: .topRight)
|
||||||
|
|
||||||
|
cornerSpec1.offset = CGPoint(x: -3, y: 3)
|
||||||
|
|
||||||
|
let stackSpec = ASStackLayoutSpec.vertical()
|
||||||
|
stackSpec.spacing = 40
|
||||||
|
stackSpec.children = [cornerSpec1, cornerSpec2, cornerSpec3]
|
||||||
|
|
||||||
|
return stackSpec
|
||||||
|
}
|
||||||
|
|
||||||
|
override class func title() -> String {
|
||||||
|
return "Declarative way for Corner image Layout"
|
||||||
|
}
|
||||||
|
|
||||||
|
override class func descriptionTitle() -> String? {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class UserProfileSample : LayoutExampleNode {
|
||||||
|
|
||||||
|
let badgeNode = ASImageNode()
|
||||||
|
let avatarNode = ASImageNode()
|
||||||
|
let usernameNode = ASTextNode()
|
||||||
|
let subtitleNode = ASTextNode()
|
||||||
|
|
||||||
|
struct ImageSize {
|
||||||
|
static let avatar = CGSize(width: 44, height: 44)
|
||||||
|
static let badge = CGSize(width: 15, height: 15)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ImageColor {
|
||||||
|
static let avatar = UIColor.lightGray
|
||||||
|
static let badge = UIColor.red
|
||||||
|
}
|
||||||
|
|
||||||
|
required init() {
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
avatarNode.image = UIImage.draw(size: ImageSize.avatar, fillColor: ImageColor.avatar) { () -> UIBezierPath in
|
||||||
|
return UIBezierPath(ovalIn: CGRect(origin: CGPoint.zero, size: ImageSize.avatar))
|
||||||
|
}
|
||||||
|
|
||||||
|
badgeNode.image = UIImage.draw(size: ImageSize.badge, fillColor: ImageColor.badge) { () -> UIBezierPath in
|
||||||
|
return UIBezierPath(ovalIn: CGRect(origin: CGPoint.zero, size: ImageSize.badge))
|
||||||
|
}
|
||||||
|
|
||||||
|
makeSingleLine(for: usernameNode, with: "Hello world", fontSize: 17, textColor: .black)
|
||||||
|
makeSingleLine(for: subtitleNode, with: "This is a long long subtitle, with a long long appended string.", fontSize: 14, textColor: .lightGray)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func makeSingleLine(for node: ASTextNode, with text: String, fontSize: CGFloat, textColor: UIColor) {
|
||||||
|
node.attributedText = NSAttributedString.attributedString(string: text, fontSize: fontSize, color: textColor)
|
||||||
|
node.maximumNumberOfLines = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
|
||||||
|
let avatarBox = ASCornerLayoutSpec(child: avatarNode, corner: badgeNode, location: .bottomRight)
|
||||||
|
avatarBox.offset = CGPoint(x: -6, y: -6)
|
||||||
|
|
||||||
|
let textBox = ASStackLayoutSpec.vertical()
|
||||||
|
textBox.justifyContent = .spaceAround
|
||||||
|
textBox.children = [usernameNode, subtitleNode]
|
||||||
|
|
||||||
|
let profileBox = ASStackLayoutSpec.horizontal()
|
||||||
|
profileBox.spacing = 10
|
||||||
|
profileBox.children = [avatarBox, textBox]
|
||||||
|
|
||||||
|
// Apply text truncation
|
||||||
|
let elems: [ASLayoutElement] = [usernameNode, subtitleNode, textBox, profileBox]
|
||||||
|
for elem in elems {
|
||||||
|
elem.style.flexShrink = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
let insetBox = ASInsetLayoutSpec(
|
||||||
|
insets: UIEdgeInsets(top: 120, left: 20, bottom: CGFloat.infinity, right: 20),
|
||||||
|
child: profileBox
|
||||||
|
)
|
||||||
|
|
||||||
|
return insetBox
|
||||||
|
}
|
||||||
|
|
||||||
|
override class func title() -> String {
|
||||||
|
return "Common user profile layout."
|
||||||
|
}
|
||||||
|
|
||||||
|
override class func descriptionTitle() -> String? {
|
||||||
|
return "For corner image layout and text truncation."
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -26,7 +26,9 @@ class OverviewViewController: ASViewController<ASTableNode> {
|
|||||||
HeaderWithRightAndLeftItems.self,
|
HeaderWithRightAndLeftItems.self,
|
||||||
PhotoWithInsetTextOverlay.self,
|
PhotoWithInsetTextOverlay.self,
|
||||||
PhotoWithOutsetIconOverlay.self,
|
PhotoWithOutsetIconOverlay.self,
|
||||||
FlexibleSeparatorSurroundingContent.self
|
FlexibleSeparatorSurroundingContent.self,
|
||||||
|
CornerLayoutSample.self,
|
||||||
|
UserProfileSample.self
|
||||||
]
|
]
|
||||||
|
|
||||||
super.init(node: tableNode)
|
super.init(node: tableNode)
|
||||||
|
|||||||
@@ -75,6 +75,20 @@ extension UIImage {
|
|||||||
return roundedImage ?? self
|
return roundedImage ?? self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class func draw(size: CGSize, fillColor: UIColor, shapeClosure: () -> UIBezierPath) -> UIImage {
|
||||||
|
UIGraphicsBeginImageContext(size)
|
||||||
|
|
||||||
|
let path = shapeClosure()
|
||||||
|
path.addClip()
|
||||||
|
|
||||||
|
fillColor.setFill()
|
||||||
|
path.fill()
|
||||||
|
|
||||||
|
let image = UIGraphicsGetImageFromCurrentImageContext()
|
||||||
|
UIGraphicsEndImageContext()
|
||||||
|
|
||||||
|
return image!
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension NSAttributedString {
|
extension NSAttributedString {
|
||||||
|
|||||||
@@ -220,13 +220,16 @@
|
|||||||
files = (
|
files = (
|
||||||
);
|
);
|
||||||
inputPaths = (
|
inputPaths = (
|
||||||
|
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||||
|
"${PODS_ROOT}/Manifest.lock",
|
||||||
);
|
);
|
||||||
name = "[CP] Check Pods Manifest.lock";
|
name = "[CP] Check Pods Manifest.lock";
|
||||||
outputPaths = (
|
outputPaths = (
|
||||||
|
"$(DERIVED_FILE_DIR)/Pods-Sample-checkManifestLockResult.txt",
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
shellPath = /bin/sh;
|
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";
|
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||||
showEnvVarsInLog = 0;
|
showEnvVarsInLog = 0;
|
||||||
};
|
};
|
||||||
/* End PBXShellScriptBuildPhase section */
|
/* End PBXShellScriptBuildPhase section */
|
||||||
|
|||||||
10
examples/LayoutSpecExamples/Sample.xcworkspace/contents.xcworkspacedata
generated
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Workspace
|
||||||
|
version = "1.0">
|
||||||
|
<FileRef
|
||||||
|
location = "group:Sample.xcodeproj">
|
||||||
|
</FileRef>
|
||||||
|
<FileRef
|
||||||
|
location = "group:Pods/Pods.xcodeproj">
|
||||||
|
</FileRef>
|
||||||
|
</Workspace>
|
||||||
@@ -33,3 +33,9 @@
|
|||||||
|
|
||||||
@interface FlexibleSeparatorSurroundingContent : LayoutExampleNode
|
@interface FlexibleSeparatorSurroundingContent : LayoutExampleNode
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@interface CornerLayoutExample : PhotoWithOutsetIconOverlay
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface UserProfileSample : LayoutExampleNode
|
||||||
|
@end
|
||||||
|
|||||||
@@ -260,6 +260,188 @@
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@interface CornerLayoutExample ()
|
||||||
|
@property (nonatomic, strong) ASImageNode *dotNode;
|
||||||
|
@property (nonatomic, strong) ASImageNode *photoNode1;
|
||||||
|
@property (nonatomic, strong) ASTextNode *badgeTextNode;
|
||||||
|
@property (nonatomic, strong) ASImageNode *badgeImageNode;
|
||||||
|
@property (nonatomic, strong) ASImageNode *photoNode2;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation CornerLayoutExample
|
||||||
|
|
||||||
|
static CGFloat const kSampleAvatarSize = 100;
|
||||||
|
static CGFloat const kSampleIconSize = 26;
|
||||||
|
static CGFloat const kSampleBadgeCornerRadius = 12;
|
||||||
|
|
||||||
|
+ (NSString *)title
|
||||||
|
{
|
||||||
|
return @"Declarative way for Corner image Layout";
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (NSString *)descriptionTitle
|
||||||
|
{
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)init
|
||||||
|
{
|
||||||
|
self = [super init];
|
||||||
|
if (self) {
|
||||||
|
UIImage *avatarImage = [self avatarImageWithSize:CGSizeMake(kSampleAvatarSize, kSampleAvatarSize)];
|
||||||
|
UIImage *cornerImage = [self cornerImageWithSize:CGSizeMake(kSampleIconSize, kSampleIconSize)];
|
||||||
|
|
||||||
|
NSAttributedString *numberText = [NSAttributedString attributedStringWithString:@" 999+ " fontSize:20 color:UIColor.whiteColor];
|
||||||
|
|
||||||
|
_dotNode = [ASImageNode new];
|
||||||
|
_dotNode.image = cornerImage;
|
||||||
|
|
||||||
|
_photoNode1 = [ASImageNode new];
|
||||||
|
_photoNode1.image = avatarImage;
|
||||||
|
|
||||||
|
_badgeTextNode = [ASTextNode new];
|
||||||
|
_badgeTextNode.attributedText = numberText;
|
||||||
|
|
||||||
|
_badgeImageNode = [ASImageNode new];
|
||||||
|
_badgeImageNode.image = [UIImage as_resizableRoundedImageWithCornerRadius:kSampleBadgeCornerRadius
|
||||||
|
cornerColor:UIColor.clearColor
|
||||||
|
fillColor:UIColor.redColor];
|
||||||
|
|
||||||
|
_photoNode2 = [ASImageNode new];
|
||||||
|
_photoNode2.image = avatarImage;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
|
||||||
|
{
|
||||||
|
|
||||||
|
ASBackgroundLayoutSpec *badgeSpec = [ASBackgroundLayoutSpec backgroundLayoutSpecWithChild:_badgeTextNode
|
||||||
|
background:_badgeImageNode];
|
||||||
|
|
||||||
|
ASCornerLayoutSpec *cornerSpec1 = [ASCornerLayoutSpec cornerLayoutSpecWithChild:_photoNode1 corner:_dotNode location:ASCornerLayoutLocationTopRight];
|
||||||
|
cornerSpec1.offset = CGPointMake(-3, 3);
|
||||||
|
|
||||||
|
ASCornerLayoutSpec *cornerSpec2 = [ASCornerLayoutSpec cornerLayoutSpecWithChild:_photoNode2 corner:badgeSpec location:ASCornerLayoutLocationTopRight];
|
||||||
|
|
||||||
|
self.photoNode.style.preferredSize = CGSizeMake(kSampleAvatarSize, kSampleAvatarSize);
|
||||||
|
self.iconNode.style.preferredSize = CGSizeMake(kSampleIconSize, kSampleIconSize);
|
||||||
|
|
||||||
|
ASCornerLayoutSpec *cornerSpec3 = [ASCornerLayoutSpec cornerLayoutSpecWithChild:self.photoNode corner:self.iconNode location:ASCornerLayoutLocationTopRight];
|
||||||
|
|
||||||
|
ASStackLayoutSpec *stackSpec = [ASStackLayoutSpec verticalStackLayoutSpec];
|
||||||
|
stackSpec.spacing = 40;
|
||||||
|
stackSpec.children = @[cornerSpec1, cornerSpec2, cornerSpec3];
|
||||||
|
|
||||||
|
return stackSpec;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UIImage *)avatarImageWithSize:(CGSize)size
|
||||||
|
{
|
||||||
|
return [UIImage imageWithSize:size fillColor:UIColor.lightGrayColor shapeBlock:^UIBezierPath *{
|
||||||
|
CGRect rect = (CGRect){ CGPointZero, size };
|
||||||
|
return [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:MIN(size.width, size.height) / 20];
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UIImage *)cornerImageWithSize:(CGSize)size
|
||||||
|
{
|
||||||
|
return [UIImage imageWithSize:size fillColor:UIColor.redColor shapeBlock:^UIBezierPath *{
|
||||||
|
return [UIBezierPath bezierPathWithOvalInRect:(CGRect){ CGPointZero, size }];
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
@interface UserProfileSample ()
|
||||||
|
@property (nonatomic, strong) ASImageNode *badgeNode;
|
||||||
|
@property (nonatomic, strong) ASImageNode *avatarNode;
|
||||||
|
@property (nonatomic, strong) ASTextNode *usernameNode;
|
||||||
|
@property (nonatomic, strong) ASTextNode *subtitleNode;
|
||||||
|
@property (nonatomic, assign) CGFloat photoSizeValue;
|
||||||
|
@property (nonatomic, assign) CGFloat iconSizeValue;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation UserProfileSample
|
||||||
|
|
||||||
|
+ (NSString *)title
|
||||||
|
{
|
||||||
|
return @"Common user profile layout.";
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (NSString *)descriptionTitle
|
||||||
|
{
|
||||||
|
return @"For corner image layout and text truncation.";
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)init
|
||||||
|
{
|
||||||
|
self = [super init];
|
||||||
|
if (self) {
|
||||||
|
_photoSizeValue = 44;
|
||||||
|
_iconSizeValue = 15;
|
||||||
|
|
||||||
|
CGSize iconSize = CGSizeMake(_iconSizeValue, _iconSizeValue);
|
||||||
|
CGSize photoSize = CGSizeMake(_photoSizeValue, _photoSizeValue);
|
||||||
|
|
||||||
|
_badgeNode = [ASImageNode new];
|
||||||
|
_badgeNode.style.preferredSize = iconSize;
|
||||||
|
_badgeNode.image = [UIImage imageWithSize:iconSize fillColor:UIColor.redColor shapeBlock:^UIBezierPath *{
|
||||||
|
return [UIBezierPath bezierPathWithOvalInRect:(CGRect){ CGPointZero, iconSize }];
|
||||||
|
}];
|
||||||
|
|
||||||
|
_avatarNode = [ASImageNode new];
|
||||||
|
_avatarNode.style.preferredSize = photoSize;
|
||||||
|
_avatarNode.image = [UIImage imageWithSize:photoSize fillColor:UIColor.lightGrayColor shapeBlock:^UIBezierPath *{
|
||||||
|
return [UIBezierPath bezierPathWithOvalInRect:(CGRect){ CGPointZero, photoSize }];
|
||||||
|
}];
|
||||||
|
|
||||||
|
_usernameNode = [ASTextNode new];
|
||||||
|
_usernameNode.attributedText = [NSAttributedString attributedStringWithString:@"Hello World" fontSize:17 color:UIColor.blackColor];
|
||||||
|
_usernameNode.maximumNumberOfLines = 1;
|
||||||
|
|
||||||
|
_subtitleNode = [ASTextNode new];
|
||||||
|
_subtitleNode.attributedText = [NSAttributedString attributedStringWithString:@"This is a long long subtitle, with a long long appended string." fontSize:14 color:UIColor.lightGrayColor];
|
||||||
|
_subtitleNode.maximumNumberOfLines = 1;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
|
||||||
|
{
|
||||||
|
// Apply avatar with badge
|
||||||
|
// Normally, avatar's box size is the only photo size and it will not include the badge size.
|
||||||
|
// Otherwise, use includeCornerForSizeCalculation property to increase the box's size if needed.
|
||||||
|
ASCornerLayoutSpec *avatarBox = [ASCornerLayoutSpec new];
|
||||||
|
avatarBox.child = _avatarNode;
|
||||||
|
avatarBox.corner = _badgeNode;
|
||||||
|
avatarBox.cornerLocation = ASCornerLayoutLocationBottomRight;
|
||||||
|
avatarBox.offset = CGPointMake(-6, -6);
|
||||||
|
|
||||||
|
ASStackLayoutSpec *textBox = [ASStackLayoutSpec verticalStackLayoutSpec];
|
||||||
|
textBox.justifyContent = ASStackLayoutJustifyContentSpaceAround;
|
||||||
|
textBox.children = @[_usernameNode, _subtitleNode];
|
||||||
|
|
||||||
|
ASStackLayoutSpec *profileBox = [ASStackLayoutSpec horizontalStackLayoutSpec];
|
||||||
|
profileBox.spacing = 10;
|
||||||
|
profileBox.children = @[avatarBox, textBox];
|
||||||
|
|
||||||
|
// Apply text truncation.
|
||||||
|
NSArray *elems = @[_usernameNode, _subtitleNode, textBox, profileBox];
|
||||||
|
for (id <ASLayoutElement> elem in elems) {
|
||||||
|
elem.style.flexShrink = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASInsetLayoutSpec *profileInsetBox = [ASInsetLayoutSpec new];
|
||||||
|
profileInsetBox.insets = UIEdgeInsetsMake(120, 20, INFINITY, 20);
|
||||||
|
profileInsetBox.child = profileBox;
|
||||||
|
|
||||||
|
return profileInsetBox;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
@implementation LayoutExampleNode
|
@implementation LayoutExampleNode
|
||||||
|
|
||||||
+ (NSString *)title
|
+ (NSString *)title
|
||||||
|
|||||||
@@ -1,11 +1,18 @@
|
|||||||
//
|
//
|
||||||
// OverviewViewController.m
|
// OverviewViewController.m
|
||||||
// Sample
|
// Texture
|
||||||
//
|
//
|
||||||
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
|
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
|
||||||
// This source code is licensed under the BSD-style license found in the
|
// 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
|
// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional
|
||||||
// of patent rights can be found in the PATENTS file in the same directory.
|
// grant of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
//
|
||||||
|
// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present,
|
||||||
|
// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
//
|
//
|
||||||
|
|
||||||
#import "OverviewViewController.h"
|
#import "OverviewViewController.h"
|
||||||
@@ -37,7 +44,10 @@
|
|||||||
_layoutExamples = @[[HeaderWithRightAndLeftItems class],
|
_layoutExamples = @[[HeaderWithRightAndLeftItems class],
|
||||||
[PhotoWithInsetTextOverlay class],
|
[PhotoWithInsetTextOverlay class],
|
||||||
[PhotoWithOutsetIconOverlay class],
|
[PhotoWithOutsetIconOverlay class],
|
||||||
[FlexibleSeparatorSurroundingContent class]];
|
[FlexibleSeparatorSurroundingContent class],
|
||||||
|
[CornerLayoutExample class],
|
||||||
|
[UserProfileSample class]
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
|
|||||||
@@ -1,11 +1,18 @@
|
|||||||
//
|
//
|
||||||
// Utilities.h
|
// Utilities.h
|
||||||
// Sample
|
// Texture
|
||||||
//
|
//
|
||||||
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
|
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
|
||||||
// This source code is licensed under the BSD-style license found in the
|
// 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
|
// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional
|
||||||
// of patent rights can be found in the PATENTS file in the same directory.
|
// grant of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
//
|
||||||
|
// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present,
|
||||||
|
// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
//
|
//
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
@@ -18,6 +25,7 @@
|
|||||||
|
|
||||||
@interface UIImage (Additions)
|
@interface UIImage (Additions)
|
||||||
- (UIImage *)makeCircularImageWithSize:(CGSize)size withBorderWidth:(CGFloat)width;
|
- (UIImage *)makeCircularImageWithSize:(CGSize)size withBorderWidth:(CGFloat)width;
|
||||||
|
+ (UIImage *)imageWithSize:(CGSize)size fillColor:(UIColor *)fillColor shapeBlock:(UIBezierPath *(^)(void))shapeBlock;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface NSAttributedString (Additions)
|
@interface NSAttributedString (Additions)
|
||||||
|
|||||||
@@ -1,11 +1,18 @@
|
|||||||
//
|
//
|
||||||
// Utilities.m
|
// Utilities.m
|
||||||
// Sample
|
// Texture
|
||||||
//
|
//
|
||||||
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
|
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
|
||||||
// This source code is licensed under the BSD-style license found in the
|
// 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
|
// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional
|
||||||
// of patent rights can be found in the PATENTS file in the same directory.
|
// grant of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
//
|
||||||
|
// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present,
|
||||||
|
// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
//
|
//
|
||||||
|
|
||||||
#import "Utilities.h"
|
#import "Utilities.h"
|
||||||
@@ -64,6 +71,21 @@
|
|||||||
return roundedImage;
|
return roundedImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
+ (UIImage *)imageWithSize:(CGSize)size fillColor:(UIColor *)fillColor shapeBlock:(UIBezierPath *(^)(void))shapeBlock
|
||||||
|
{
|
||||||
|
UIGraphicsBeginImageContext(size);
|
||||||
|
[fillColor setFill];
|
||||||
|
|
||||||
|
UIBezierPath *path = shapeBlock();
|
||||||
|
[path addClip];
|
||||||
|
[path fill];
|
||||||
|
|
||||||
|
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
|
||||||
|
UIGraphicsEndImageContext();
|
||||||
|
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation NSAttributedString (Additions)
|
@implementation NSAttributedString (Additions)
|
||||||
|
|||||||
@@ -203,6 +203,20 @@ Featuring:
|
|||||||
- ASTableView
|
- ASTableView
|
||||||
- ASCellNode
|
- ASCellNode
|
||||||
|
|
||||||
|
### LayoutSpecExamples [ObjC]
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Featuring:
|
||||||
|
- ASStackLayoutSpec
|
||||||
|
- ASInsetLayoutSpec
|
||||||
|
- ASOverlayLayoutSpec
|
||||||
|
- ASAbsoluteLayoutSpec
|
||||||
|
- ASBackgroundLayoutSpec
|
||||||
|
- ASCornerLayoutSpec
|
||||||
|
|
||||||
|
There is an associated swift version app: LayoutSpecExamples-Swift with same logic implementation.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
This file provided by Facebook is for non-commercial testing and evaluation
|
This file provided by Facebook is for non-commercial testing and evaluation
|
||||||
@@ -214,3 +228,5 @@ Featuring:
|
|||||||
FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
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
|
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.
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||