diff --git a/Example/LWZComponents.xcodeproj/project.pbxproj b/Example/LWZComponents.xcodeproj/project.pbxproj index cc93780..a827bc1 100644 --- a/Example/LWZComponents.xcodeproj/project.pbxproj +++ b/Example/LWZComponents.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 0E92C3372A62BF6E88FBCE65 /* libPods-LWZComponents_Tests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 72EB7D5216733E35112A5649 /* libPods-LWZComponents_Tests.a */; }; 6003F58E195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; }; 6003F590195388D20070C39A /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58F195388D20070C39A /* CoreGraphics.framework */; }; 6003F592195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; @@ -21,9 +22,54 @@ 6003F5BA195388D20070C39A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6003F5B8195388D20070C39A /* InfoPlist.strings */; }; 6003F5BC195388D20070C39A /* Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F5BB195388D20070C39A /* Tests.m */; }; 71719F9F1E33DC2100824A3D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 71719F9D1E33DC2100824A3D /* LaunchScreen.storyboard */; }; - 82F8C69ADA01784CAC89AD83 /* Pods_LWZComponents_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AF6817D988FA55CA6140834 /* Pods_LWZComponents_Example.framework */; }; 873B8AEB1B1F5CCA007FD442 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */; }; - B537CAA4C80FFF3299559D15 /* Pods_LWZComponents_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A73FE01DB5DF3E5FCFE135A3 /* Pods_LWZComponents_Tests.framework */; }; + A66AFDEF277734170024BBC9 /* TLViewController1.m in Sources */ = {isa = PBXBuildFile; fileRef = A66AFD85277734160024BBC9 /* TLViewController1.m */; }; + A66AFDF0277734170024BBC9 /* WFLProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = A66AFD8A277734160024BBC9 /* WFLProvider.m */; }; + A66AFDF1277734170024BBC9 /* WFLCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = A66AFD8C277734160024BBC9 /* WFLCollectionViewCell.xib */; }; + A66AFDF2277734170024BBC9 /* WFLCollectionItem.m in Sources */ = {isa = PBXBuildFile; fileRef = A66AFD8E277734160024BBC9 /* WFLCollectionItem.m */; }; + A66AFDF3277734170024BBC9 /* WFLCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = A66AFD90277734160024BBC9 /* WFLCollectionViewCell.m */; }; + A66AFDF4277734170024BBC9 /* WFLModel.m in Sources */ = {isa = PBXBuildFile; fileRef = A66AFD92277734160024BBC9 /* WFLModel.m */; }; + A66AFDF5277734170024BBC9 /* WFLJsonData.json in Resources */ = {isa = PBXBuildFile; fileRef = A66AFD95277734160024BBC9 /* WFLJsonData.json */; }; + A66AFDF6277734170024BBC9 /* WFLModelProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = A66AFD96277734160024BBC9 /* WFLModelProvider.m */; }; + A66AFDF7277734170024BBC9 /* WFLViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A66AFD97277734160024BBC9 /* WFLViewController.m */; }; + A66AFDF8277734170024BBC9 /* RLProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = A66AFD9B277734160024BBC9 /* RLProvider.m */; }; + A66AFDF9277734170024BBC9 /* RLTextCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = A66AFD9D277734160024BBC9 /* RLTextCell.xib */; }; + A66AFDFA277734170024BBC9 /* RLTextItem.m in Sources */ = {isa = PBXBuildFile; fileRef = A66AFDA0277734160024BBC9 /* RLTextItem.m */; }; + A66AFDFB277734170024BBC9 /* RLTextCell.m in Sources */ = {isa = PBXBuildFile; fileRef = A66AFDA1277734160024BBC9 /* RLTextCell.m */; }; + A66AFDFC277734170024BBC9 /* RLModel.m in Sources */ = {isa = PBXBuildFile; fileRef = A66AFDA3277734160024BBC9 /* RLModel.m */; }; + A66AFDFD277734170024BBC9 /* RLJsonData.json in Resources */ = {isa = PBXBuildFile; fileRef = A66AFDA5277734160024BBC9 /* RLJsonData.json */; }; + A66AFDFE277734170024BBC9 /* RLModelProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = A66AFDA6277734160024BBC9 /* RLModelProvider.m */; }; + A66AFDFF277734170024BBC9 /* RLViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A66AFDA9277734160024BBC9 /* RLViewController.m */; }; + A66AFE00277734170024BBC9 /* CommonTextLayoutSize.m in Sources */ = {isa = PBXBuildFile; fileRef = A66AFDAC277734160024BBC9 /* CommonTextLayoutSize.m */; }; + A66AFE01277734170024BBC9 /* CommonImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = A66AFDAF277734160024BBC9 /* CommonImageView.m */; }; + A66AFE02277734170024BBC9 /* CommonLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = A66AFDB0277734160024BBC9 /* CommonLabel.m */; }; + A66AFE03277734170024BBC9 /* HLProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = A66AFDB5277734170024BBC9 /* HLProvider.m */; }; + A66AFE04277734170024BBC9 /* HLModelProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = A66AFDB9277734170024BBC9 /* HLModelProvider.m */; }; + A66AFE05277734170024BBC9 /* HLModel.m in Sources */ = {isa = PBXBuildFile; fileRef = A66AFDBA277734170024BBC9 /* HLModel.m */; }; + A66AFE06277734170024BBC9 /* HLViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A66AFDBC277734170024BBC9 /* HLViewController.m */; }; + A66AFE07277734170024BBC9 /* LLProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = A66AFDC1277734170024BBC9 /* LLProvider.m */; }; + A66AFE08277734170024BBC9 /* LLCollectionItem.m in Sources */ = {isa = PBXBuildFile; fileRef = A66AFDC3277734170024BBC9 /* LLCollectionItem.m */; }; + A66AFE09277734170024BBC9 /* LLViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A66AFDC5277734170024BBC9 /* LLViewController.m */; }; + A66AFE0A277734170024BBC9 /* CLItem.m in Sources */ = {isa = PBXBuildFile; fileRef = A66AFDCB277734170024BBC9 /* CLItem.m */; }; + A66AFE0B277734170024BBC9 /* CLProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = A66AFDCD277734170024BBC9 /* CLProvider.m */; }; + A66AFE0C277734170024BBC9 /* CLViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A66AFDCE277734170024BBC9 /* CLViewController.m */; }; + A66AFE0D277734170024BBC9 /* WLPostVideoLayoutCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = A66AFDD4277734170024BBC9 /* WLPostVideoLayoutCollectionViewCell.m */; }; + A66AFE0E277734170024BBC9 /* WLUserCollectionItem.m in Sources */ = {isa = PBXBuildFile; fileRef = A66AFDD5277734170024BBC9 /* WLUserCollectionItem.m */; }; + A66AFE0F277734170024BBC9 /* WLPostImageLayoutCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = A66AFDD6277734170024BBC9 /* WLPostImageLayoutCollectionViewCell.xib */; }; + A66AFE10277734170024BBC9 /* WLPostVideoLayoutCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = A66AFDD8277734170024BBC9 /* WLPostVideoLayoutCollectionViewCell.xib */; }; + A66AFE11277734170024BBC9 /* WLPostImageLayoutCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = A66AFDDD277734170024BBC9 /* WLPostImageLayoutCollectionViewCell.m */; }; + A66AFE12277734170024BBC9 /* WLUserCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = A66AFDDE277734170024BBC9 /* WLUserCollectionViewCell.xib */; }; + A66AFE13277734170024BBC9 /* WLUserCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = A66AFDDF277734170024BBC9 /* WLUserCollectionViewCell.m */; }; + A66AFE14277734170024BBC9 /* WLPostCollectionItem.m in Sources */ = {isa = PBXBuildFile; fileRef = A66AFDE0277734170024BBC9 /* WLPostCollectionItem.m */; }; + A66AFE15277734170024BBC9 /* WLProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = A66AFDE1277734170024BBC9 /* WLProvider.m */; }; + A66AFE16277734170024BBC9 /* WLJsonData.json in Resources */ = {isa = PBXBuildFile; fileRef = A66AFDE5277734170024BBC9 /* WLJsonData.json */; }; + A66AFE17277734170024BBC9 /* WLModelProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = A66AFDE6277734170024BBC9 /* WLModelProvider.m */; }; + A66AFE18277734170024BBC9 /* WLModel.m in Sources */ = {isa = PBXBuildFile; fileRef = A66AFDE7277734170024BBC9 /* WLModel.m */; }; + A66AFE19277734170024BBC9 /* WLCollectionSectionHeaderFooterView.m in Sources */ = {isa = PBXBuildFile; fileRef = A66AFDEA277734170024BBC9 /* WLCollectionSectionHeaderFooterView.m */; }; + A66AFE1A277734170024BBC9 /* WLCollectionSectionHeaderFooter.m in Sources */ = {isa = PBXBuildFile; fileRef = A66AFDEB277734170024BBC9 /* WLCollectionSectionHeaderFooter.m */; }; + A66AFE1B277734170024BBC9 /* WLCollectionSectionHeaderFooterView.xib in Resources */ = {isa = PBXBuildFile; fileRef = A66AFDED277734170024BBC9 /* WLCollectionSectionHeaderFooterView.xib */; }; + A66AFE1C277734170024BBC9 /* WLViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A66AFDEE277734170024BBC9 /* WLViewController.m */; }; + FD21647DBE6318D6EA355EE1 /* libPods-LWZComponents_Example.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4EE6FB63518A3F11D90DC843 /* libPods-LWZComponents_Example.a */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -40,8 +86,8 @@ 03654E61B4F2BE1A4EBA8EE6 /* Pods-LWZComponents_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LWZComponents_Example.release.xcconfig"; path = "Target Support Files/Pods-LWZComponents_Example/Pods-LWZComponents_Example.release.xcconfig"; sourceTree = ""; }; 111A7FA169DD6AA83FBFB94B /* Pods-LWZComponents_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LWZComponents_Tests.release.xcconfig"; path = "Target Support Files/Pods-LWZComponents_Tests/Pods-LWZComponents_Tests.release.xcconfig"; sourceTree = ""; }; 14A00C00CD2642970C780976 /* Pods-LWZComponents_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LWZComponents_Example.debug.xcconfig"; path = "Target Support Files/Pods-LWZComponents_Example/Pods-LWZComponents_Example.debug.xcconfig"; sourceTree = ""; }; - 1A04A43BC0AEB9C7DD727FEA /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; name = README.md; path = ../README.md; sourceTree = ""; }; - 3AF6817D988FA55CA6140834 /* Pods_LWZComponents_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_LWZComponents_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 1A04A43BC0AEB9C7DD727FEA /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; + 4EE6FB63518A3F11D90DC843 /* libPods-LWZComponents_Example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-LWZComponents_Example.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 6003F58A195388D20070C39A /* LWZComponents_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LWZComponents_Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 6003F58D195388D20070C39A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 6003F58F195388D20070C39A /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; @@ -62,10 +108,94 @@ 6003F5BB195388D20070C39A /* Tests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Tests.m; sourceTree = ""; }; 606FC2411953D9B200FFA9A0 /* Tests-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Tests-Prefix.pch"; sourceTree = ""; }; 71719F9E1E33DC2100824A3D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - 846E3DE8B24ADF6A8421B37C /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; + 72EB7D5216733E35112A5649 /* libPods-LWZComponents_Tests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-LWZComponents_Tests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 846E3DE8B24ADF6A8421B37C /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = Main.storyboard; path = Base.lproj/Main.storyboard; sourceTree = ""; }; - A73FE01DB5DF3E5FCFE135A3 /* Pods_LWZComponents_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_LWZComponents_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - F38AAD362329B4862453FA0F /* LWZComponents.podspec */ = {isa = PBXFileReference; includeInIndex = 1; name = LWZComponents.podspec; path = ../LWZComponents.podspec; sourceTree = ""; }; + A66AFD85277734160024BBC9 /* TLViewController1.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TLViewController1.m; sourceTree = ""; }; + A66AFD86277734160024BBC9 /* TLViewController1.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TLViewController1.h; sourceTree = ""; }; + A66AFD88277734160024BBC9 /* WFLProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WFLProvider.h; sourceTree = ""; }; + A66AFD89277734160024BBC9 /* WFLViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WFLViewController.h; sourceTree = ""; }; + A66AFD8A277734160024BBC9 /* WFLProvider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WFLProvider.m; sourceTree = ""; }; + A66AFD8C277734160024BBC9 /* WFLCollectionViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = WFLCollectionViewCell.xib; sourceTree = ""; }; + A66AFD8D277734160024BBC9 /* WFLCollectionViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WFLCollectionViewCell.h; sourceTree = ""; }; + A66AFD8E277734160024BBC9 /* WFLCollectionItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WFLCollectionItem.m; sourceTree = ""; }; + A66AFD8F277734160024BBC9 /* WFLCollectionItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WFLCollectionItem.h; sourceTree = ""; }; + A66AFD90277734160024BBC9 /* WFLCollectionViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WFLCollectionViewCell.m; sourceTree = ""; }; + A66AFD92277734160024BBC9 /* WFLModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WFLModel.m; sourceTree = ""; }; + A66AFD93277734160024BBC9 /* WFLModelProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WFLModelProvider.h; sourceTree = ""; }; + A66AFD94277734160024BBC9 /* WFLModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WFLModel.h; sourceTree = ""; }; + A66AFD95277734160024BBC9 /* WFLJsonData.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = WFLJsonData.json; sourceTree = ""; }; + A66AFD96277734160024BBC9 /* WFLModelProvider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WFLModelProvider.m; sourceTree = ""; }; + A66AFD97277734160024BBC9 /* WFLViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WFLViewController.m; sourceTree = ""; }; + A66AFD9A277734160024BBC9 /* RLProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RLProvider.h; sourceTree = ""; }; + A66AFD9B277734160024BBC9 /* RLProvider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RLProvider.m; sourceTree = ""; }; + A66AFD9D277734160024BBC9 /* RLTextCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = RLTextCell.xib; sourceTree = ""; }; + A66AFD9E277734160024BBC9 /* RLTextCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RLTextCell.h; sourceTree = ""; }; + A66AFD9F277734160024BBC9 /* RLTextItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RLTextItem.h; sourceTree = ""; }; + A66AFDA0277734160024BBC9 /* RLTextItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RLTextItem.m; sourceTree = ""; }; + A66AFDA1277734160024BBC9 /* RLTextCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RLTextCell.m; sourceTree = ""; }; + A66AFDA3277734160024BBC9 /* RLModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RLModel.m; sourceTree = ""; }; + A66AFDA4277734160024BBC9 /* RLModelProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RLModelProvider.h; sourceTree = ""; }; + A66AFDA5277734160024BBC9 /* RLJsonData.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = RLJsonData.json; sourceTree = ""; }; + A66AFDA6277734160024BBC9 /* RLModelProvider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RLModelProvider.m; sourceTree = ""; }; + A66AFDA7277734160024BBC9 /* RLModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RLModel.h; sourceTree = ""; }; + A66AFDA8277734160024BBC9 /* RLViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RLViewController.h; sourceTree = ""; }; + A66AFDA9277734160024BBC9 /* RLViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RLViewController.m; sourceTree = ""; }; + A66AFDAB277734160024BBC9 /* CommonImageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CommonImageView.h; sourceTree = ""; }; + A66AFDAC277734160024BBC9 /* CommonTextLayoutSize.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CommonTextLayoutSize.m; sourceTree = ""; }; + A66AFDAD277734160024BBC9 /* CommonLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CommonLabel.h; sourceTree = ""; }; + A66AFDAE277734160024BBC9 /* CommonDependencies.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CommonDependencies.h; sourceTree = ""; }; + A66AFDAF277734160024BBC9 /* CommonImageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CommonImageView.m; sourceTree = ""; }; + A66AFDB0277734160024BBC9 /* CommonLabel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CommonLabel.m; sourceTree = ""; }; + A66AFDB1277734160024BBC9 /* CommonTextLayoutSize.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CommonTextLayoutSize.h; sourceTree = ""; }; + A66AFDB3277734170024BBC9 /* HLViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HLViewController.h; sourceTree = ""; }; + A66AFDB5277734170024BBC9 /* HLProvider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HLProvider.m; sourceTree = ""; }; + A66AFDB6277734170024BBC9 /* HLProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HLProvider.h; sourceTree = ""; }; + A66AFDB8277734170024BBC9 /* HLModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HLModel.h; sourceTree = ""; }; + A66AFDB9277734170024BBC9 /* HLModelProvider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HLModelProvider.m; sourceTree = ""; }; + A66AFDBA277734170024BBC9 /* HLModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HLModel.m; sourceTree = ""; }; + A66AFDBB277734170024BBC9 /* HLModelProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HLModelProvider.h; sourceTree = ""; }; + A66AFDBC277734170024BBC9 /* HLViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HLViewController.m; sourceTree = ""; }; + A66AFDBE277734170024BBC9 /* LLViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LLViewController.h; sourceTree = ""; }; + A66AFDC0277734170024BBC9 /* LLProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LLProvider.h; sourceTree = ""; }; + A66AFDC1277734170024BBC9 /* LLProvider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LLProvider.m; sourceTree = ""; }; + A66AFDC3277734170024BBC9 /* LLCollectionItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LLCollectionItem.m; sourceTree = ""; }; + A66AFDC4277734170024BBC9 /* LLCollectionItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LLCollectionItem.h; sourceTree = ""; }; + A66AFDC5277734170024BBC9 /* LLViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LLViewController.m; sourceTree = ""; }; + A66AFDC7277734170024BBC9 /* CLViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CLViewController.h; sourceTree = ""; }; + A66AFDC9277734170024BBC9 /* CLProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CLProvider.h; sourceTree = ""; }; + A66AFDCB277734170024BBC9 /* CLItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CLItem.m; sourceTree = ""; }; + A66AFDCC277734170024BBC9 /* CLItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CLItem.h; sourceTree = ""; }; + A66AFDCD277734170024BBC9 /* CLProvider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CLProvider.m; sourceTree = ""; }; + A66AFDCE277734170024BBC9 /* CLViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CLViewController.m; sourceTree = ""; }; + A66AFDD0277734170024BBC9 /* WLViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WLViewController.h; sourceTree = ""; }; + A66AFDD2277734170024BBC9 /* WLProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WLProvider.h; sourceTree = ""; }; + A66AFDD4277734170024BBC9 /* WLPostVideoLayoutCollectionViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WLPostVideoLayoutCollectionViewCell.m; sourceTree = ""; }; + A66AFDD5277734170024BBC9 /* WLUserCollectionItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WLUserCollectionItem.m; sourceTree = ""; }; + A66AFDD6277734170024BBC9 /* WLPostImageLayoutCollectionViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = WLPostImageLayoutCollectionViewCell.xib; sourceTree = ""; }; + A66AFDD7277734170024BBC9 /* WLPostImageLayoutCollectionViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WLPostImageLayoutCollectionViewCell.h; sourceTree = ""; }; + A66AFDD8277734170024BBC9 /* WLPostVideoLayoutCollectionViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = WLPostVideoLayoutCollectionViewCell.xib; sourceTree = ""; }; + A66AFDD9277734170024BBC9 /* WLPostCollectionItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WLPostCollectionItem.h; sourceTree = ""; }; + A66AFDDA277734170024BBC9 /* WLUserCollectionViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WLUserCollectionViewCell.h; sourceTree = ""; }; + A66AFDDB277734170024BBC9 /* WLUserCollectionItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WLUserCollectionItem.h; sourceTree = ""; }; + A66AFDDC277734170024BBC9 /* WLPostVideoLayoutCollectionViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WLPostVideoLayoutCollectionViewCell.h; sourceTree = ""; }; + A66AFDDD277734170024BBC9 /* WLPostImageLayoutCollectionViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WLPostImageLayoutCollectionViewCell.m; sourceTree = ""; }; + A66AFDDE277734170024BBC9 /* WLUserCollectionViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = WLUserCollectionViewCell.xib; sourceTree = ""; }; + A66AFDDF277734170024BBC9 /* WLUserCollectionViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WLUserCollectionViewCell.m; sourceTree = ""; }; + A66AFDE0277734170024BBC9 /* WLPostCollectionItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WLPostCollectionItem.m; sourceTree = ""; }; + A66AFDE1277734170024BBC9 /* WLProvider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WLProvider.m; sourceTree = ""; }; + A66AFDE3277734170024BBC9 /* WLModelProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WLModelProvider.h; sourceTree = ""; }; + A66AFDE4277734170024BBC9 /* WLModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WLModel.h; sourceTree = ""; }; + A66AFDE5277734170024BBC9 /* WLJsonData.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = WLJsonData.json; sourceTree = ""; }; + A66AFDE6277734170024BBC9 /* WLModelProvider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WLModelProvider.m; sourceTree = ""; }; + A66AFDE7277734170024BBC9 /* WLModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WLModel.m; sourceTree = ""; }; + A66AFDE9277734170024BBC9 /* WLCollectionSectionHeaderFooter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WLCollectionSectionHeaderFooter.h; sourceTree = ""; }; + A66AFDEA277734170024BBC9 /* WLCollectionSectionHeaderFooterView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WLCollectionSectionHeaderFooterView.m; sourceTree = ""; }; + A66AFDEB277734170024BBC9 /* WLCollectionSectionHeaderFooter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WLCollectionSectionHeaderFooter.m; sourceTree = ""; }; + A66AFDEC277734170024BBC9 /* WLCollectionSectionHeaderFooterView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WLCollectionSectionHeaderFooterView.h; sourceTree = ""; }; + A66AFDED277734170024BBC9 /* WLCollectionSectionHeaderFooterView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = WLCollectionSectionHeaderFooterView.xib; sourceTree = ""; }; + A66AFDEE277734170024BBC9 /* WLViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WLViewController.m; sourceTree = ""; }; + F38AAD362329B4862453FA0F /* LWZComponents.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LWZComponents.podspec; path = ../LWZComponents.podspec; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; FC3B915387BE1F2E1D3F8404 /* Pods-LWZComponents_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LWZComponents_Tests.debug.xcconfig"; path = "Target Support Files/Pods-LWZComponents_Tests/Pods-LWZComponents_Tests.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -77,7 +207,7 @@ 6003F590195388D20070C39A /* CoreGraphics.framework in Frameworks */, 6003F592195388D20070C39A /* UIKit.framework in Frameworks */, 6003F58E195388D20070C39A /* Foundation.framework in Frameworks */, - 82F8C69ADA01784CAC89AD83 /* Pods_LWZComponents_Example.framework in Frameworks */, + FD21647DBE6318D6EA355EE1 /* libPods-LWZComponents_Example.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -88,7 +218,7 @@ 6003F5B0195388D20070C39A /* XCTest.framework in Frameworks */, 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */, 6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */, - B537CAA4C80FFF3299559D15 /* Pods_LWZComponents_Tests.framework in Frameworks */, + 0E92C3372A62BF6E88FBCE65 /* libPods-LWZComponents_Tests.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -103,7 +233,6 @@ FC3B915387BE1F2E1D3F8404 /* Pods-LWZComponents_Tests.debug.xcconfig */, 111A7FA169DD6AA83FBFB94B /* Pods-LWZComponents_Tests.release.xcconfig */, ); - name = Pods; path = Pods; sourceTree = ""; }; @@ -135,8 +264,8 @@ 6003F58F195388D20070C39A /* CoreGraphics.framework */, 6003F591195388D20070C39A /* UIKit.framework */, 6003F5AF195388D20070C39A /* XCTest.framework */, - 3AF6817D988FA55CA6140834 /* Pods_LWZComponents_Example.framework */, - A73FE01DB5DF3E5FCFE135A3 /* Pods_LWZComponents_Tests.framework */, + 4EE6FB63518A3F11D90DC843 /* libPods-LWZComponents_Example.a */, + 72EB7D5216733E35112A5649 /* libPods-LWZComponents_Tests.a */, ); name = Frameworks; sourceTree = ""; @@ -149,6 +278,7 @@ 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */, 6003F5A5195388D20070C39A /* LWZViewController.h */, 6003F5A6195388D20070C39A /* LWZViewController.m */, + A66AFD83277734040024BBC9 /* CollectionDemo */, 71719F9D1E33DC2100824A3D /* LaunchScreen.storyboard */, 6003F5A8195388D20070C39A /* Images.xcassets */, 6003F594195388D20070C39A /* Supporting Files */, @@ -197,6 +327,289 @@ name = "Podspec Metadata"; sourceTree = ""; }; + A66AFD83277734040024BBC9 /* CollectionDemo */ = { + isa = PBXGroup; + children = ( + A66AFDCF277734170024BBC9 /* WeightLayout */, + A66AFD87277734160024BBC9 /* WaterfallFlowLayout */, + A66AFDBD277734170024BBC9 /* ListLayout */, + A66AFD98277734160024BBC9 /* RestrictedLayout */, + A66AFD84277734160024BBC9 /* TempleteLayout */, + A66AFDC6277734170024BBC9 /* CompositionalLayout */, + A66AFDB2277734170024BBC9 /* HybridLayout(Mix) */, + A66AFDAA277734160024BBC9 /* Common */, + ); + name = CollectionDemo; + sourceTree = ""; + }; + A66AFD84277734160024BBC9 /* TempleteLayout */ = { + isa = PBXGroup; + children = ( + A66AFD86277734160024BBC9 /* TLViewController1.h */, + A66AFD85277734160024BBC9 /* TLViewController1.m */, + ); + path = TempleteLayout; + sourceTree = ""; + }; + A66AFD87277734160024BBC9 /* WaterfallFlowLayout */ = { + isa = PBXGroup; + children = ( + A66AFD89277734160024BBC9 /* WFLViewController.h */, + A66AFD97277734160024BBC9 /* WFLViewController.m */, + A66AFE1D277734660024BBC9 /* Provider */, + ); + path = WaterfallFlowLayout; + sourceTree = ""; + }; + A66AFD8B277734160024BBC9 /* Item */ = { + isa = PBXGroup; + children = ( + A66AFD8F277734160024BBC9 /* WFLCollectionItem.h */, + A66AFD8E277734160024BBC9 /* WFLCollectionItem.m */, + A66AFD8D277734160024BBC9 /* WFLCollectionViewCell.h */, + A66AFD90277734160024BBC9 /* WFLCollectionViewCell.m */, + A66AFD8C277734160024BBC9 /* WFLCollectionViewCell.xib */, + ); + path = Item; + sourceTree = ""; + }; + A66AFD91277734160024BBC9 /* Model */ = { + isa = PBXGroup; + children = ( + A66AFD93277734160024BBC9 /* WFLModelProvider.h */, + A66AFD96277734160024BBC9 /* WFLModelProvider.m */, + A66AFD95277734160024BBC9 /* WFLJsonData.json */, + A66AFD94277734160024BBC9 /* WFLModel.h */, + A66AFD92277734160024BBC9 /* WFLModel.m */, + ); + path = Model; + sourceTree = ""; + }; + A66AFD98277734160024BBC9 /* RestrictedLayout */ = { + isa = PBXGroup; + children = ( + A66AFDA8277734160024BBC9 /* RLViewController.h */, + A66AFDA9277734160024BBC9 /* RLViewController.m */, + A66AFD99277734160024BBC9 /* Provider */, + ); + path = RestrictedLayout; + sourceTree = ""; + }; + A66AFD99277734160024BBC9 /* Provider */ = { + isa = PBXGroup; + children = ( + A66AFD9A277734160024BBC9 /* RLProvider.h */, + A66AFD9B277734160024BBC9 /* RLProvider.m */, + A66AFD9C277734160024BBC9 /* Item */, + A66AFDA2277734160024BBC9 /* Model */, + ); + path = Provider; + sourceTree = ""; + }; + A66AFD9C277734160024BBC9 /* Item */ = { + isa = PBXGroup; + children = ( + A66AFD9E277734160024BBC9 /* RLTextCell.h */, + A66AFDA1277734160024BBC9 /* RLTextCell.m */, + A66AFD9D277734160024BBC9 /* RLTextCell.xib */, + A66AFD9F277734160024BBC9 /* RLTextItem.h */, + A66AFDA0277734160024BBC9 /* RLTextItem.m */, + ); + path = Item; + sourceTree = ""; + }; + A66AFDA2277734160024BBC9 /* Model */ = { + isa = PBXGroup; + children = ( + A66AFDA4277734160024BBC9 /* RLModelProvider.h */, + A66AFDA6277734160024BBC9 /* RLModelProvider.m */, + A66AFDA5277734160024BBC9 /* RLJsonData.json */, + A66AFDA7277734160024BBC9 /* RLModel.h */, + A66AFDA3277734160024BBC9 /* RLModel.m */, + ); + path = Model; + sourceTree = ""; + }; + A66AFDAA277734160024BBC9 /* Common */ = { + isa = PBXGroup; + children = ( + A66AFDAE277734160024BBC9 /* CommonDependencies.h */, + A66AFDAB277734160024BBC9 /* CommonImageView.h */, + A66AFDAF277734160024BBC9 /* CommonImageView.m */, + A66AFDAD277734160024BBC9 /* CommonLabel.h */, + A66AFDB0277734160024BBC9 /* CommonLabel.m */, + A66AFDB1277734160024BBC9 /* CommonTextLayoutSize.h */, + A66AFDAC277734160024BBC9 /* CommonTextLayoutSize.m */, + ); + path = Common; + sourceTree = ""; + }; + A66AFDB2277734170024BBC9 /* HybridLayout(Mix) */ = { + isa = PBXGroup; + children = ( + A66AFDB3277734170024BBC9 /* HLViewController.h */, + A66AFDBC277734170024BBC9 /* HLViewController.m */, + A66AFDB4277734170024BBC9 /* Pvovider */, + ); + path = "HybridLayout(Mix)"; + sourceTree = ""; + }; + A66AFDB4277734170024BBC9 /* Pvovider */ = { + isa = PBXGroup; + children = ( + A66AFDB6277734170024BBC9 /* HLProvider.h */, + A66AFDB5277734170024BBC9 /* HLProvider.m */, + A66AFDB7277734170024BBC9 /* Model */, + ); + path = Pvovider; + sourceTree = ""; + }; + A66AFDB7277734170024BBC9 /* Model */ = { + isa = PBXGroup; + children = ( + A66AFDBB277734170024BBC9 /* HLModelProvider.h */, + A66AFDB9277734170024BBC9 /* HLModelProvider.m */, + A66AFDB8277734170024BBC9 /* HLModel.h */, + A66AFDBA277734170024BBC9 /* HLModel.m */, + ); + path = Model; + sourceTree = ""; + }; + A66AFDBD277734170024BBC9 /* ListLayout */ = { + isa = PBXGroup; + children = ( + A66AFDBE277734170024BBC9 /* LLViewController.h */, + A66AFDC5277734170024BBC9 /* LLViewController.m */, + A66AFDBF277734170024BBC9 /* Provider */, + ); + path = ListLayout; + sourceTree = ""; + }; + A66AFDBF277734170024BBC9 /* Provider */ = { + isa = PBXGroup; + children = ( + A66AFDC0277734170024BBC9 /* LLProvider.h */, + A66AFDC1277734170024BBC9 /* LLProvider.m */, + A66AFDC2277734170024BBC9 /* Item */, + ); + path = Provider; + sourceTree = ""; + }; + A66AFDC2277734170024BBC9 /* Item */ = { + isa = PBXGroup; + children = ( + A66AFDC3277734170024BBC9 /* LLCollectionItem.m */, + A66AFDC4277734170024BBC9 /* LLCollectionItem.h */, + ); + path = Item; + sourceTree = ""; + }; + A66AFDC6277734170024BBC9 /* CompositionalLayout */ = { + isa = PBXGroup; + children = ( + A66AFDC7277734170024BBC9 /* CLViewController.h */, + A66AFDCE277734170024BBC9 /* CLViewController.m */, + A66AFDC8277734170024BBC9 /* Provider */, + ); + path = CompositionalLayout; + sourceTree = ""; + }; + A66AFDC8277734170024BBC9 /* Provider */ = { + isa = PBXGroup; + children = ( + A66AFDC9277734170024BBC9 /* CLProvider.h */, + A66AFDCD277734170024BBC9 /* CLProvider.m */, + A66AFDCA277734170024BBC9 /* Item */, + ); + path = Provider; + sourceTree = ""; + }; + A66AFDCA277734170024BBC9 /* Item */ = { + isa = PBXGroup; + children = ( + A66AFDCC277734170024BBC9 /* CLItem.h */, + A66AFDCB277734170024BBC9 /* CLItem.m */, + ); + path = Item; + sourceTree = ""; + }; + A66AFDCF277734170024BBC9 /* WeightLayout */ = { + isa = PBXGroup; + children = ( + A66AFDD0277734170024BBC9 /* WLViewController.h */, + A66AFDEE277734170024BBC9 /* WLViewController.m */, + A66AFDD1277734170024BBC9 /* Provider */, + ); + path = WeightLayout; + sourceTree = ""; + }; + A66AFDD1277734170024BBC9 /* Provider */ = { + isa = PBXGroup; + children = ( + A66AFDD2277734170024BBC9 /* WLProvider.h */, + A66AFDE1277734170024BBC9 /* WLProvider.m */, + A66AFDE8277734170024BBC9 /* Header */, + A66AFDD3277734170024BBC9 /* Item */, + A66AFDE2277734170024BBC9 /* Model */, + ); + path = Provider; + sourceTree = ""; + }; + A66AFDD3277734170024BBC9 /* Item */ = { + isa = PBXGroup; + children = ( + A66AFDDB277734170024BBC9 /* WLUserCollectionItem.h */, + A66AFDD5277734170024BBC9 /* WLUserCollectionItem.m */, + A66AFDDA277734170024BBC9 /* WLUserCollectionViewCell.h */, + A66AFDDF277734170024BBC9 /* WLUserCollectionViewCell.m */, + A66AFDDE277734170024BBC9 /* WLUserCollectionViewCell.xib */, + A66AFDD9277734170024BBC9 /* WLPostCollectionItem.h */, + A66AFDE0277734170024BBC9 /* WLPostCollectionItem.m */, + A66AFDD7277734170024BBC9 /* WLPostImageLayoutCollectionViewCell.h */, + A66AFDDD277734170024BBC9 /* WLPostImageLayoutCollectionViewCell.m */, + A66AFDD6277734170024BBC9 /* WLPostImageLayoutCollectionViewCell.xib */, + A66AFDDC277734170024BBC9 /* WLPostVideoLayoutCollectionViewCell.h */, + A66AFDD4277734170024BBC9 /* WLPostVideoLayoutCollectionViewCell.m */, + A66AFDD8277734170024BBC9 /* WLPostVideoLayoutCollectionViewCell.xib */, + ); + path = Item; + sourceTree = ""; + }; + A66AFDE2277734170024BBC9 /* Model */ = { + isa = PBXGroup; + children = ( + A66AFDE3277734170024BBC9 /* WLModelProvider.h */, + A66AFDE6277734170024BBC9 /* WLModelProvider.m */, + A66AFDE5277734170024BBC9 /* WLJsonData.json */, + A66AFDE4277734170024BBC9 /* WLModel.h */, + A66AFDE7277734170024BBC9 /* WLModel.m */, + ); + path = Model; + sourceTree = ""; + }; + A66AFDE8277734170024BBC9 /* Header */ = { + isa = PBXGroup; + children = ( + A66AFDE9277734170024BBC9 /* WLCollectionSectionHeaderFooter.h */, + A66AFDEB277734170024BBC9 /* WLCollectionSectionHeaderFooter.m */, + A66AFDEC277734170024BBC9 /* WLCollectionSectionHeaderFooterView.h */, + A66AFDEA277734170024BBC9 /* WLCollectionSectionHeaderFooterView.m */, + A66AFDED277734170024BBC9 /* WLCollectionSectionHeaderFooterView.xib */, + ); + path = Header; + sourceTree = ""; + }; + A66AFE1D277734660024BBC9 /* Provider */ = { + isa = PBXGroup; + children = ( + A66AFD88277734160024BBC9 /* WFLProvider.h */, + A66AFD8A277734160024BBC9 /* WFLProvider.m */, + A66AFD8B277734160024BBC9 /* Item */, + A66AFD91277734160024BBC9 /* Model */, + ); + path = Provider; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -208,7 +621,6 @@ 6003F586195388D20070C39A /* Sources */, 6003F587195388D20070C39A /* Frameworks */, 6003F588195388D20070C39A /* Resources */, - 944B424815F180578C3838F7 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -248,6 +660,9 @@ LastUpgradeCheck = 0720; ORGANIZATIONNAME = "changsanjiang@gmail.com"; TargetAttributes = { + 6003F589195388D20070C39A = { + DevelopmentTeam = DVCY355DY9; + }; 6003F5AD195388D20070C39A = { TestTargetID = 6003F589195388D20070C39A; }; @@ -258,6 +673,7 @@ developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( + English, en, Base, ); @@ -278,9 +694,18 @@ buildActionMask = 2147483647; files = ( 873B8AEB1B1F5CCA007FD442 /* Main.storyboard in Resources */, + A66AFE0F277734170024BBC9 /* WLPostImageLayoutCollectionViewCell.xib in Resources */, + A66AFDFD277734170024BBC9 /* RLJsonData.json in Resources */, + A66AFE10277734170024BBC9 /* WLPostVideoLayoutCollectionViewCell.xib in Resources */, 71719F9F1E33DC2100824A3D /* LaunchScreen.storyboard in Resources */, 6003F5A9195388D20070C39A /* Images.xcassets in Resources */, + A66AFE16277734170024BBC9 /* WLJsonData.json in Resources */, + A66AFDF5277734170024BBC9 /* WFLJsonData.json in Resources */, + A66AFE12277734170024BBC9 /* WLUserCollectionViewCell.xib in Resources */, 6003F598195388D20070C39A /* InfoPlist.strings in Resources */, + A66AFE1B277734170024BBC9 /* WLCollectionSectionHeaderFooterView.xib in Resources */, + A66AFDF9277734170024BBC9 /* RLTextCell.xib in Resources */, + A66AFDF1277734170024BBC9 /* WFLCollectionViewCell.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -317,24 +742,6 @@ 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; }; - 944B424815F180578C3838F7 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-LWZComponents_Example/Pods-LWZComponents_Example-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/LWZComponents/LWZComponents.framework", - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/LWZComponents.framework", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-LWZComponents_Example/Pods-LWZComponents_Example-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; BEBE6CC7A6ABDF648C475B64 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -364,9 +771,46 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + A66AFDF3277734170024BBC9 /* WFLCollectionViewCell.m in Sources */, + A66AFDF8277734170024BBC9 /* RLProvider.m in Sources */, + A66AFE07277734170024BBC9 /* LLProvider.m in Sources */, + A66AFE03277734170024BBC9 /* HLProvider.m in Sources */, + A66AFE08277734170024BBC9 /* LLCollectionItem.m in Sources */, + A66AFE0C277734170024BBC9 /* CLViewController.m in Sources */, 6003F59E195388D20070C39A /* LWZAppDelegate.m in Sources */, + A66AFDFB277734170024BBC9 /* RLTextCell.m in Sources */, + A66AFE0E277734170024BBC9 /* WLUserCollectionItem.m in Sources */, + A66AFDF0277734170024BBC9 /* WFLProvider.m in Sources */, + A66AFDF6277734170024BBC9 /* WFLModelProvider.m in Sources */, + A66AFE18277734170024BBC9 /* WLModel.m in Sources */, + A66AFE15277734170024BBC9 /* WLProvider.m in Sources */, + A66AFDFA277734170024BBC9 /* RLTextItem.m in Sources */, + A66AFE02277734170024BBC9 /* CommonLabel.m in Sources */, + A66AFE1C277734170024BBC9 /* WLViewController.m in Sources */, + A66AFE19277734170024BBC9 /* WLCollectionSectionHeaderFooterView.m in Sources */, + A66AFE05277734170024BBC9 /* HLModel.m in Sources */, + A66AFE0A277734170024BBC9 /* CLItem.m in Sources */, + A66AFE01277734170024BBC9 /* CommonImageView.m in Sources */, 6003F5A7195388D20070C39A /* LWZViewController.m in Sources */, + A66AFE11277734170024BBC9 /* WLPostImageLayoutCollectionViewCell.m in Sources */, + A66AFDEF277734170024BBC9 /* TLViewController1.m in Sources */, + A66AFE14277734170024BBC9 /* WLPostCollectionItem.m in Sources */, + A66AFDFC277734170024BBC9 /* RLModel.m in Sources */, + A66AFE00277734170024BBC9 /* CommonTextLayoutSize.m in Sources */, + A66AFE1A277734170024BBC9 /* WLCollectionSectionHeaderFooter.m in Sources */, + A66AFDFF277734170024BBC9 /* RLViewController.m in Sources */, + A66AFE09277734170024BBC9 /* LLViewController.m in Sources */, + A66AFE13277734170024BBC9 /* WLUserCollectionViewCell.m in Sources */, + A66AFE0D277734170024BBC9 /* WLPostVideoLayoutCollectionViewCell.m in Sources */, + A66AFE17277734170024BBC9 /* WLModelProvider.m in Sources */, + A66AFDF7277734170024BBC9 /* WFLViewController.m in Sources */, + A66AFE0B277734170024BBC9 /* CLProvider.m in Sources */, + A66AFE06277734170024BBC9 /* HLViewController.m in Sources */, + A66AFE04277734170024BBC9 /* HLModelProvider.m in Sources */, + A66AFDF2277734170024BBC9 /* WFLCollectionItem.m in Sources */, 6003F59A195388D20070C39A /* main.m in Sources */, + A66AFDFE277734170024BBC9 /* RLModelProvider.m in Sources */, + A66AFDF4277734170024BBC9 /* WFLModel.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -494,6 +938,7 @@ baseConfigurationReference = 14A00C00CD2642970C780976 /* Pods-LWZComponents_Example.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + DEVELOPMENT_TEAM = DVCY355DY9; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "LWZComponents/LWZComponents-Prefix.pch"; INFOPLIST_FILE = "LWZComponents/LWZComponents-Info.plist"; @@ -510,6 +955,7 @@ baseConfigurationReference = 03654E61B4F2BE1A4EBA8EE6 /* Pods-LWZComponents_Example.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + DEVELOPMENT_TEAM = DVCY355DY9; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "LWZComponents/LWZComponents-Prefix.pch"; INFOPLIST_FILE = "LWZComponents/LWZComponents-Info.plist"; diff --git a/Example/LWZComponents.xcworkspace/contents.xcworkspacedata b/Example/LWZComponents.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..c9ed477 --- /dev/null +++ b/Example/LWZComponents.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/Example/LWZComponents.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Example/LWZComponents.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/Example/LWZComponents.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Example/LWZComponents/Base.lproj/Main.storyboard b/Example/LWZComponents/Base.lproj/Main.storyboard index 2169803..cfb0209 100644 --- a/Example/LWZComponents/Base.lproj/Main.storyboard +++ b/Example/LWZComponents/Base.lproj/Main.storyboard @@ -1,11 +1,9 @@ - - - - + + - + @@ -20,12 +18,100 @@ + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + diff --git a/Example/LWZComponents/Common/CommonDependencies.h b/Example/LWZComponents/Common/CommonDependencies.h new file mode 100644 index 0000000..7bf24eb --- /dev/null +++ b/Example/LWZComponents/Common/CommonDependencies.h @@ -0,0 +1,19 @@ +// +// CommonDependencies.h +// LWZCollectionViewComponents +// +// Created by changsanjiang on 2021/11/25. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#ifndef CommonDependencies_h +#define CommonDependencies_h + + +#import +#import +#import + +#import + +#endif /* CommonDependencies_h */ diff --git a/Example/LWZComponents/Common/CommonImageView.h b/Example/LWZComponents/Common/CommonImageView.h new file mode 100644 index 0000000..340c648 --- /dev/null +++ b/Example/LWZComponents/Common/CommonImageView.h @@ -0,0 +1,19 @@ +// +// CommonImageView.h +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/11/26. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface CommonImageView : UIImageView + +@property (nonatomic, copy, nullable) void(^layoutSubviewsExeBlock)(__kindof CommonImageView *view); + +@end + +NS_ASSUME_NONNULL_END diff --git a/Example/LWZComponents/Common/CommonImageView.m b/Example/LWZComponents/Common/CommonImageView.m new file mode 100644 index 0000000..d8bef41 --- /dev/null +++ b/Example/LWZComponents/Common/CommonImageView.m @@ -0,0 +1,16 @@ +// +// CommonImageView.m +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/11/26. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "CommonImageView.h" + +@implementation CommonImageView +- (void)layoutSubviews { + [super layoutSubviews]; + if ( _layoutSubviewsExeBlock ) _layoutSubviewsExeBlock(self); +} +@end diff --git a/Example/LWZComponents/Common/CommonLabel.h b/Example/LWZComponents/Common/CommonLabel.h new file mode 100755 index 0000000..4992f83 --- /dev/null +++ b/Example/LWZComponents/Common/CommonLabel.h @@ -0,0 +1,29 @@ +// +// LWZAttributeLabel.h +// Pods +// +// Created by changsanjiang on 2020/2/23. +// + +#import +NS_ASSUME_NONNULL_BEGIN +@interface CommonLabel : UILabel +@property (nonatomic, copy, nullable) void(^layoutSubviewsExeBlock)(__kindof CommonLabel *label); + +/// +/// 配置文本区域inset +/// +/// 当设置了富文本垂直方向不起作用时, 请设置段落的lineBreakMode. +/// +@property (nonatomic) UIEdgeInsets contentInsets; +@end + +#if TARGET_INTERFACE_BUILDER +@interface CommonLabel (CommonIBSupported) +@property (nonatomic) IBInspectable CGFloat topInset; +@property (nonatomic) IBInspectable CGFloat leftInset; +@property (nonatomic) IBInspectable CGFloat bottomInset; +@property (nonatomic) IBInspectable CGFloat rightInset; +@end +#endif +NS_ASSUME_NONNULL_END diff --git a/Example/LWZComponents/Common/CommonLabel.m b/Example/LWZComponents/Common/CommonLabel.m new file mode 100755 index 0000000..eaecc83 --- /dev/null +++ b/Example/LWZComponents/Common/CommonLabel.m @@ -0,0 +1,93 @@ +// +// LWZAttributeLabel.m +// Pods +// +// Created by changsanjiang on 2020/2/23. +// + +#import "CommonLabel.h" + +@implementation CommonLabel +- (void)layoutSubviews { + [super layoutSubviews]; + if ( _layoutSubviewsExeBlock ) _layoutSubviewsExeBlock(self); +} + +#pragma mark - + +- (void)drawTextInRect:(CGRect)rect { + rect.origin.x += _contentInsets.left; + rect.size.width -= _contentInsets.left + _contentInsets.right; + rect.origin.y += _contentInsets.top; + rect.size.height -= _contentInsets.top + _contentInsets.bottom; + [super drawTextInRect:rect]; +} + +- (CGRect)textRectForBounds:(CGRect)bounds limitedToNumberOfLines:(NSInteger)numberOfLines { + if ( self.text.length != 0 ) { + bounds.size.width -= _contentInsets.left + _contentInsets.right; + bounds.size.height -= _contentInsets.top + _contentInsets.bottom; + } + + if ( bounds.size.width < 0 || bounds.size.height < 0 ) + return CGRectZero; + + CGRect rect = [super textRectForBounds:bounds limitedToNumberOfLines:numberOfLines]; + if ( self.text.length != 0 ) { + rect.size.width += _contentInsets.left + _contentInsets.right; + rect.size.height += _contentInsets.top + _contentInsets.bottom; + } + return rect; +} + +- (void)setContentInsets:(UIEdgeInsets)contentInsets { + if ( !UIEdgeInsetsEqualToEdgeInsets(contentInsets, _contentInsets) ) { + _contentInsets = contentInsets; + [self setNeedsDisplay]; + [self invalidateIntrinsicContentSize]; + } +} + +@end + +@implementation CommonLabel (CommonIBSupported) +- (void)setTopInset:(CGFloat)topInset { + if ( topInset != _contentInsets.top ) { + _contentInsets.top = topInset; + [self setNeedsDisplay]; + } +} +- (CGFloat)topInset { + return _contentInsets.top; +} + +- (void)setLeftInset:(CGFloat)leftInset { + if ( leftInset != _contentInsets.left ) { + _contentInsets.left = leftInset; + [self setNeedsDisplay]; + } +} +- (CGFloat)leftInset { + return _contentInsets.left; +} + +- (void)setBottomInset:(CGFloat)bottomInset { + if ( bottomInset != _contentInsets.bottom ) { + _contentInsets.bottom = bottomInset; + [self setNeedsDisplay]; + } +} +- (CGFloat)bottomInset { + return _contentInsets.bottom; +} + +- (void)setRightInset:(CGFloat)rightInset { + if ( rightInset != _contentInsets.right ) { + _contentInsets.right = rightInset; + [self setNeedsDisplay]; + } +} +- (CGFloat)rightInset { + return _contentInsets.right; +} +@end diff --git a/Example/LWZComponents/Common/CommonTextLayoutSize.h b/Example/LWZComponents/Common/CommonTextLayoutSize.h new file mode 100644 index 0000000..70debf4 --- /dev/null +++ b/Example/LWZComponents/Common/CommonTextLayoutSize.h @@ -0,0 +1,38 @@ +// +// CommonTextLayoutSize.h +// LWZAppComponents +// +// Created by changsanjiang on 2021/8/27. +// + +#import +@class LWZTextContainer; + +NS_ASSUME_NONNULL_BEGIN +@interface NSString (CommonTextLayoutSizeAdditions) +- (CGSize)lwz_layoutSizeThatFits:(CGSize)sizeLimit font:(UIFont *)font limitedToNumberOfLines:(NSInteger)limitedToNumberOfLines; +@end + +@interface NSAttributedString (CommonTextLayoutSizeAdditions) +- (CGSize)lwz_layoutSizeThatFits:(CGSize)sizeLimit limitedToNumberOfLines:(NSInteger)limitedToNumberOfLines; +- (LWZTextContainer *)lwz_textContainerWithLayoutSizeThatFits:(CGSize)size limitedToNumberOfLines:(NSInteger)limitedToNumberOfLines; +- (LWZTextContainer *)lwz_textContainerWithLayoutSizeThatFits:(CGSize)size limitedToNumberOfLines:(NSInteger)limitedToNumberOfLines fixesSingleLineSpacing:(BOOL)fixesSingleLineSpacing; +@end + +@interface LWZTextContainer : NSObject +@property (nonatomic, readonly) NSAttributedString *text; +/// text布局宽高. +/// +/// 注意: 单行时高度不包含 lineSpacing; 如果要在单行 UILabel 上显示, label的布局高度需要考虑加上 lineSpacing; +///\code +/// CGFloat lineSpacing = ...; +/// if ( container.numberOfLines == 1 ) { +/// CGSize layoutSize = container.layoutSize; +/// layoutSize.height += lineSpacing; +/// } +///\endcode +/// +@property (nonatomic, readonly) CGSize layoutSize; +@property (nonatomic, readonly) NSInteger numberOfLines; +@end +NS_ASSUME_NONNULL_END diff --git a/Example/LWZComponents/Common/CommonTextLayoutSize.m b/Example/LWZComponents/Common/CommonTextLayoutSize.m new file mode 100644 index 0000000..09b03de --- /dev/null +++ b/Example/LWZComponents/Common/CommonTextLayoutSize.m @@ -0,0 +1,156 @@ +// +// CommonTextLayoutSize.m +// LWZAppComponents +// +// Created by changsanjiang on 2021/8/27. +// + +#import "CommonTextLayoutSize.h" + +@implementation NSString (CommonTextLayoutSizeAdditions) +- (CGSize)lwz_layoutSizeThatFits:(CGSize)sizeLimit font:(UIFont *)font limitedToNumberOfLines:(NSInteger)limitedToNumberOfLines { + return [[NSAttributedString.alloc initWithString:self attributes:@{ + NSFontAttributeName : font + }] lwz_layoutSizeThatFits:sizeLimit limitedToNumberOfLines:limitedToNumberOfLines]; +} +@end + +@implementation LWZTextContainer +- (instancetype)initWithText:(NSAttributedString *)text layoutSize:(CGSize)size numberOfLines:(NSInteger)numberOfLines { + self = [super init]; + if ( self ) { + _text = text; + _layoutSize = size; + _numberOfLines = numberOfLines; + } + return self; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"LWZTextContainer:<%p> { text: %@, layoutSize: %@, numberOfLines: %ld}", self, _text, NSStringFromCGSize(_layoutSize), (long)_numberOfLines]; +} +@end + +typedef NSValue LWZRangeValueKey; +typedef NSNumber LWZLineBreakModeNumber; +static NSAttributedStringKey const LWZNSOriginalFontAttributeName = @"NSOriginalFont"; + +@implementation NSAttributedString (CommonTextLayoutSizeAdditions) +- (CGSize)lwz_layoutSizeThatFits:(CGSize)sizeLimit limitedToNumberOfLines:(NSInteger)limitedToNumberOfLines { + CGSize retv = CGSizeZero; + if ( self.length != 0 ) { + NSMutableAttributedString *layoutText = [NSMutableAttributedString.alloc initWithAttributedString:self]; + [layoutText enumerateAttribute:NSFontAttributeName inRange:(NSRange){0, self.length} options:NSAttributedStringEnumerationReverse usingBlock:^(UIFont *_Nullable value, NSRange range, BOOL * _Nonnull stop) { + if ( value == nil ) return; + [layoutText addAttribute:LWZNSOriginalFontAttributeName value:value range:range]; + }]; + + NSTextContainer *textContainer = [NSTextContainer.alloc initWithSize:sizeLimit]; + textContainer.lineFragmentPadding = 0; + textContainer.maximumNumberOfLines = limitedToNumberOfLines; + + NSLayoutManager *layoutManager = [NSLayoutManager.alloc init]; + [layoutManager addTextContainer:textContainer]; + + NSTextStorage *textStorage = [NSTextStorage.alloc initWithAttributedString:layoutText]; + [textStorage enumerateAttributesInRange:(NSRange){0, layoutText.length} options:NSAttributedStringEnumerationReverse usingBlock:^(NSDictionary * _Nonnull attrs, NSRange range, BOOL * _Nonnull stop) { + if ( range.length == 0 ) return; + NSParagraphStyle *style = attrs[NSParagraphStyleAttributeName]; + if ( style == nil ) return; + NSMutableParagraphStyle *m = [style mutableCopy]; + m.lineBreakMode = 0; + [textStorage removeAttribute:NSParagraphStyleAttributeName range:range]; + [textStorage addAttribute:NSParagraphStyleAttributeName value:m range:range]; + }]; + [textStorage addLayoutManager:layoutManager]; + + CGFloat scale = UIScreen.mainScreen.scale; + (void)[layoutManager glyphRangeForTextContainer:textContainer]; + retv = [layoutManager usedRectForTextContainer:textContainer].size; + retv.width = ceil(retv.width * scale) * (1.0 / scale); + retv.height = ceil(retv.height * scale) * (1.0 / scale); + } + return retv; +} + +- (LWZTextContainer *)lwz_textContainerWithLayoutSizeThatFits:(CGSize)size limitedToNumberOfLines:(NSInteger)limitedToNumberOfLines { + return [self lwz_textContainerWithLayoutSizeThatFits:size limitedToNumberOfLines:limitedToNumberOfLines fixesSingleLineSpacing:NO]; +} + + +- (LWZTextContainer *)lwz_textContainerWithLayoutSizeThatFits:(CGSize)size limitedToNumberOfLines:(NSInteger)limitedToNumberOfLines fixesSingleLineSpacing:(BOOL)fixesSingleLineSpacing { + NSTextContainer *container = [NSTextContainer.alloc initWithSize:size]; + container.lineFragmentPadding = 0; + container.maximumNumberOfLines = limitedToNumberOfLines; + container.lineBreakMode = NSLineBreakByTruncatingTail; + + NSMutableAttributedString *layoutText = [NSMutableAttributedString.alloc initWithAttributedString:self]; + [layoutText enumerateAttribute:NSFontAttributeName inRange:(NSRange){0, self.length} options:NSAttributedStringEnumerationReverse usingBlock:^(UIFont *_Nullable value, NSRange range, BOOL * _Nonnull stop) { + if ( value == nil ) return; + [layoutText addAttribute:LWZNSOriginalFontAttributeName value:value range:range]; + }]; + + NSMutableDictionary *lineBreakModes = NSMutableDictionary.dictionary; + __block BOOL hasCustomLineSpacing = NO; + [layoutText enumerateAttribute:NSParagraphStyleAttributeName inRange:(NSRange){0, self.length} options:NSAttributedStringEnumerationReverse usingBlock:^(NSParagraphStyle *_Nullable value, NSRange range, BOOL * _Nonnull stop) { + if ( value == nil ) return; + if ( value.lineSpacing != 0 ) { + hasCustomLineSpacing = YES; + } + + NSMutableParagraphStyle *style = [value mutableCopy]; + lineBreakModes[[LWZRangeValueKey valueWithRange:range]] = @(style.lineBreakMode); + style.lineBreakMode = 0; + [layoutText removeAttribute:NSParagraphStyleAttributeName range:range]; + [layoutText addAttribute:NSParagraphStyleAttributeName value:style range:range]; + }]; + + // 获取宽高 + NSTextStorage *storage = [NSTextStorage.alloc initWithAttributedString:layoutText]; + NSLayoutManager *manager = [NSLayoutManager.alloc init]; + [manager addTextContainer:container]; + [storage addLayoutManager:manager]; + CGSize layoutSize = CGSizeZero; + CGFloat scale = UIScreen.mainScreen.scale; + [manager glyphRangeForTextContainer:container]; + layoutSize = [manager usedRectForTextContainer:container].size; + layoutSize.width = ceil(layoutSize.width * scale) * (1.0 / scale); + layoutSize.height = ceil(layoutSize.height * scale) * (1.0 / scale); + + // 获取行数 + NSUInteger numberOfGlyphs = manager.numberOfGlyphs; + NSInteger numberOfLines = 0; + NSInteger index = 0; + NSRange lineRange; + + for ( numberOfLines = 0 , index = 0; index < numberOfGlyphs ; numberOfLines ++ ) { + [manager lineFragmentRectForGlyphAtIndex:index effectiveRange:&lineRange]; + index = NSMaxRange(lineRange); + } + + // 确定是否单行布局 + if ( fixesSingleLineSpacing && hasCustomLineSpacing && numberOfLines == 1 ) { + [layoutText enumerateAttribute:NSParagraphStyleAttributeName inRange:(NSRange){0, self.length} options:NSAttributedStringEnumerationReverse usingBlock:^(NSParagraphStyle *value, NSRange range, BOOL * _Nonnull stop) { + if ( value == nil ) return; + NSMutableParagraphStyle *style = [value mutableCopy]; + style.lineSpacing = 0; + [layoutText removeAttribute:NSParagraphStyleAttributeName range:range]; + [layoutText addAttribute:NSParagraphStyleAttributeName value:style range:range]; + }]; + } + + // 恢复`lineBreakMode` + [layoutText enumerateAttribute:NSParagraphStyleAttributeName inRange:(NSRange){0, self.length} options:NSAttributedStringEnumerationReverse usingBlock:^(NSParagraphStyle *value, NSRange range, BOOL * _Nonnull stop) { + if ( value == nil ) return; + NSMutableParagraphStyle *style = [value mutableCopy]; + LWZLineBreakModeNumber *lineBreakModeValue = lineBreakModes[[LWZRangeValueKey valueWithRange:range]]; + if ( lineBreakModeValue != nil ) { + style.lineBreakMode = [lineBreakModeValue integerValue]; + [layoutText removeAttribute:NSParagraphStyleAttributeName range:range]; + [layoutText addAttribute:NSParagraphStyleAttributeName value:style range:range]; + } + }]; + + return [LWZTextContainer.alloc initWithText:layoutText layoutSize:layoutSize numberOfLines:numberOfLines]; +} +@end diff --git a/Example/LWZComponents/CompositionalLayout/CLViewController.h b/Example/LWZComponents/CompositionalLayout/CLViewController.h new file mode 100644 index 0000000..9d0d2fc --- /dev/null +++ b/Example/LWZComponents/CompositionalLayout/CLViewController.h @@ -0,0 +1,17 @@ +// +// CLViewController.h +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/12/13. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface CLViewController : UIViewController + +@end + +NS_ASSUME_NONNULL_END diff --git a/Example/LWZComponents/CompositionalLayout/CLViewController.m b/Example/LWZComponents/CompositionalLayout/CLViewController.m new file mode 100644 index 0000000..a37168a --- /dev/null +++ b/Example/LWZComponents/CompositionalLayout/CLViewController.m @@ -0,0 +1,42 @@ +// +// CLViewController.m +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/12/13. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "CLViewController.h" +#import "CLProvider.h" +#import "CommonDependencies.h" + +@interface CLViewController () +@property (nonatomic, strong) LWZCollectionViewPresenter *presenter; +@property (nonatomic, strong) LWZCollectionView *collectionView; +@end + +@implementation CLViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + [self _setupViews]; + // Do any additional setup after loading the view. +} + +- (void)_setupViews { + self.edgesForExtendedLayout = UIRectEdgeNone; + self.title = NSStringFromClass(self.class); + self.view.backgroundColor = UIColor.whiteColor; + + _presenter = [LWZCollectionViewPresenter.alloc initWithProvider:CLProvider.alloc.init]; + + LWZCollectionCompositionalLayout *layout = [LWZCollectionCompositionalLayout.alloc initWithScrollDirection:UICollectionViewScrollDirectionVertical delegate:_presenter]; + _collectionView = [LWZCollectionView.alloc initWithFrame:CGRectZero collectionViewLayout:layout]; + _collectionView.dataSource = _presenter; + [self.view addSubview:_collectionView]; + [_collectionView mas_makeConstraints:^(MASConstraintMaker *make) { + make.edges.offset(0); + }]; +} + +@end diff --git a/Example/LWZComponents/CompositionalLayout/Provider/CLProvider.h b/Example/LWZComponents/CompositionalLayout/Provider/CLProvider.h new file mode 100644 index 0000000..c93632e --- /dev/null +++ b/Example/LWZComponents/CompositionalLayout/Provider/CLProvider.h @@ -0,0 +1,17 @@ +// +// CLProvider.h +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/12/13. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "LWZCollectionProvider.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface CLProvider : LWZCollectionProvider + +@end + +NS_ASSUME_NONNULL_END diff --git a/Example/LWZComponents/CompositionalLayout/Provider/CLProvider.m b/Example/LWZComponents/CompositionalLayout/Provider/CLProvider.m new file mode 100644 index 0000000..a1c381e --- /dev/null +++ b/Example/LWZComponents/CompositionalLayout/Provider/CLProvider.m @@ -0,0 +1,106 @@ +// +// CLProvider.m +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/12/13. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "CLProvider.h" +#import "CLItem.h" +#import "CommonDependencies.h" + +@implementation CLProvider +- (instancetype)init { + self = [super init]; + if ( self ) { + for ( NSInteger i = 0 ; i < 5 ; ++ i ) { + // 左右滑动 + [self addSectionWithBlock:^(__kindof LWZCollectionSection * _Nonnull make) { + make.layoutType = LWZCollectionLayoutTypeWeight; + make.minimumLineSpacing = 12; + make.contentInsets = UIEdgeInsetsMake(12, 12, 12, 12); + + make.orthogonalScrolling = YES; +// LWZCollectionLayoutContentOrthogonalScrollingBehaviorNormal, +// LWZCollectionLayoutContentOrthogonalScrollingBehaviorContinuousCentered, +// LWZCollectionLayoutContentOrthogonalScrollingBehaviorPaging, + make.orthogonalScrollingBehavior = LWZCollectionLayoutContentOrthogonalScrollingBehaviorPaging; + make.orthogonalContentLayoutSizeCalculator = ^CGSize(LWZCollectionSection * _Nonnull section, CGSize fittingSize, NSInteger index, UICollectionViewScrollDirection scrollDirection) { + return CGSizeMake(fittingSize.width - 24, (fittingSize.width - 24) * 9 / 16.0); + }; + + for ( NSInteger i = 0 ; i < 9 ; ++ i ) { + CLItem *item = CLItem.alloc.init; + LWZCollectionBackgroundDecoration *decoration = [LWZCollectionItemBackgroundDecoration.alloc initWithBackgroundColor:UIColor.greenColor]; + decoration.contentInsets = UIEdgeInsetsMake(-2, -2, -2, -2); + item.decoration = decoration; + [make addItem:item]; + } + }]; + + // 常规布局 + [self addSectionWithBlock:^(__kindof LWZCollectionSection * _Nonnull make) { + make.layoutType = LWZCollectionLayoutTypeWeight; + make.minimumLineSpacing = 12; + make.contentInsets = UIEdgeInsetsMake(12, 12, 12, 12); + + [make addItem:CLItem.alloc.init]; + }]; + + // 模板布局 + [self addSectionWithBlock:^(__kindof LWZCollectionSection * _Nonnull make) { + make.layoutType = LWZCollectionLayoutTypeTemplate; + make.minimumLineSpacing = 12; + make.minimumInteritemSpacing = 12; + make.contentInsets = UIEdgeInsetsMake(12, 12, 12, 12); + + for ( NSInteger i = 0 ; i < 6 ; ++ i ) { + [make addItem:CLItem.alloc.init]; + } + + make.layoutTemplateContainerGroups = [LWZCollectionLayoutTemplate build:^(LWZCollectionTemplateBuilder * _Nonnull make) { + // 添加1个容器组 + make.addGroup(^(LWZCollectionTemplateGroupBuilder * _Nonnull group) { + // 设置容器组的宽度等于父布局的宽度 + group.width.fractionalWidth(1.0); + // 设置容器组的高度等于父布局的宽度 + group.width.fractionalWidth(1.0); + + // 向组内添加容器1 + group.addContainer(^(LWZCollectionTemplateContainerBuilder * _Nonnull container) { + // 设置容器1的宽度等于父布局的宽度的一半 + container.width.fractionalWidth(0.5); + // 设置容器1的高度等于父布局的高度 + container.height.fractionalHeight(1.0); + + // 左侧安排1个item + container.addItem(^(LWZCollectionTemplateItemBuilder * _Nonnull item) { + // 设置item的宽度等于父布局的宽度 + item.width.fractionalWidth(1.0); + // 设置item的高度等于父布局的高度 + item.height.fractionalHeight(1.0); + }); + }); + + // 向组内添加容器2 + group.addContainer(^(LWZCollectionTemplateContainerBuilder * _Nonnull container) { + container.width.fractionalWidth(0.5); + container.height.fractionalHeight(1.0); + + // 右侧安排2个item + for ( NSInteger i = 0 ; i < 2 ; ++ i ) { + container.addItem(^(LWZCollectionTemplateItemBuilder * _Nonnull item) { + item.width.fractionalWidth(1.0); + item.height.fractionalHeight(0.5); + }); + } + }); + }); + }]; + }]; + } + } + return self; +} +@end diff --git a/Example/LWZComponents/CompositionalLayout/Provider/Item/CLItem.h b/Example/LWZComponents/CompositionalLayout/Provider/Item/CLItem.h new file mode 100644 index 0000000..e837620 --- /dev/null +++ b/Example/LWZComponents/CompositionalLayout/Provider/Item/CLItem.h @@ -0,0 +1,17 @@ +// +// CLItem.h +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/12/13. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "LWZCollectionItem.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface CLItem : LWZCollectionItem + +@end + +NS_ASSUME_NONNULL_END diff --git a/Example/LWZComponents/CompositionalLayout/Provider/Item/CLItem.m b/Example/LWZComponents/CompositionalLayout/Provider/Item/CLItem.m new file mode 100644 index 0000000..3b6a1db --- /dev/null +++ b/Example/LWZComponents/CompositionalLayout/Provider/Item/CLItem.m @@ -0,0 +1,44 @@ +// +// CLItem.m +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/12/13. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "CLItem.h" + +@interface CLItem () { + UIColor *_backgroundColor; +} +@end + +@implementation CLItem +- (instancetype)init { + self = [super init]; + if ( self ) { + _backgroundColor = [UIColor colorWithRed:arc4random() % 256 / 255.0 + green:arc4random() % 256 / 255.0 + blue:arc4random() % 256 / 255.0 + alpha:1.0]; + } + return self; +} + +- (Class)cellClass { + return UICollectionViewCell.class; +} + +- (CGSize)layoutSizeThatFits:(CGSize)size inSection:(LWZCollectionSection *)section atIndexPath:(NSIndexPath *)indexPath scrollDirection:(UICollectionViewScrollDirection)scrollDirection { + switch ( scrollDirection ) { + case UICollectionViewScrollDirectionVertical: + return CGSizeMake(size.width, size.width * 9 / 16.0); + case UICollectionViewScrollDirectionHorizontal: + return CGSizeMake(size.height * 16 / 9.0, size.height); + } +} + +- (void)bindCell:(__kindof UICollectionViewCell *)cell atIndexPath:(NSIndexPath *)indexPath { + cell.contentView.backgroundColor = _backgroundColor; +} +@end diff --git a/Example/LWZComponents/HybridLayout(Mix)/HLViewController.h b/Example/LWZComponents/HybridLayout(Mix)/HLViewController.h new file mode 100644 index 0000000..4bc899d --- /dev/null +++ b/Example/LWZComponents/HybridLayout(Mix)/HLViewController.h @@ -0,0 +1,17 @@ +// +// HLViewController.h +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/12/25. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface HLViewController : UIViewController + +@end + +NS_ASSUME_NONNULL_END diff --git a/Example/LWZComponents/HybridLayout(Mix)/HLViewController.m b/Example/LWZComponents/HybridLayout(Mix)/HLViewController.m new file mode 100644 index 0000000..6b69d89 --- /dev/null +++ b/Example/LWZComponents/HybridLayout(Mix)/HLViewController.m @@ -0,0 +1,97 @@ +// +// HLViewController.m +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/12/25. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "HLViewController.h" +#import "CommonDependencies.h" +#import "HLModelProvider.h" +#import "HLProvider.h" + +@interface HLViewController () +@property (nonatomic, strong) LWZCollectionView *collectionView; +@property (nonatomic, strong) LWZCollectionViewPresenter *presenter; +@property (nonatomic, strong) HLProvider *provider; +@end + +@implementation HLViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + [self _setupViews]; + // Do any additional setup after loading the view. +} + +- (void)_setupViews { + self.edgesForExtendedLayout = UIRectEdgeNone; + self.title = NSStringFromClass(self.class); + self.view.backgroundColor = UIColor.whiteColor; + + _presenter = [LWZCollectionViewPresenter.alloc init]; + + LWZCollectionViewLayout *layout = [LWZCollectionHybridLayout.alloc initWithScrollDirection:UICollectionViewScrollDirectionVertical delegate:_presenter]; + layout.sectionHeadersPinToVisibleBounds = YES; + _collectionView = [LWZCollectionView.alloc initWithFrame:CGRectZero collectionViewLayout:layout]; + _collectionView.dataSource = _presenter; + [self.view addSubview:_collectionView]; + [_collectionView mas_makeConstraints:^(MASConstraintMaker *make) { + make.edges.offset(0); + }]; + + __weak typeof(self) _self = self; + [HLModelProvider requestDataWithComplete:^(HLModel * _Nullable model, NSError * _Nullable error) { + __strong typeof(_self) self = _self; + if ( self == nil ) return; + if ( error != nil ) { + NSLog(@"(%d : %s) WLViewController.error: %@", __LINE__, sel_getName(_cmd), error); + return; + } + + self.provider = [HLProvider.alloc initWithModel:model]; + self.provider.userItemTapHandler = ^(NSInteger userId) { + __strong typeof(_self) self = _self; + if ( self == nil ) return; +#ifdef DEBUG + NSLog(@"%d : %s", __LINE__, sel_getName(_cmd)); +#endif + }; + + self.provider.commentItemTapHandler = ^(NSIndexPath * _Nonnull indexPath) { + __strong typeof(_self) self = _self; + if ( self == nil ) return; + NSLog(@"处理评论动作..."); + + UIViewController *vc = [UIViewController.alloc init]; + vc.view.backgroundColor = [UIColor colorWithRed:arc4random() % 256 / 255.0 + green:arc4random() % 256 / 255.0 + blue:arc4random() % 256 / 255.0 + alpha:1]; + [self.navigationController pushViewController:vc animated:YES]; + }; + + self.provider.likingItemTapHandler = ^(NSIndexPath * _Nonnull indexPath) { + __strong typeof(_self) self = _self; + if ( self == nil ) return; + NSLog(@"处理点赞动作..."); + + BOOL isLiked = [self.provider isPostLikedForItemAtIndexPath:indexPath]; + [self.provider setPostLiked:!isLiked forItemAtIndexPath:indexPath]; + }; + + self.provider.shareItemTapHandler = ^(NSIndexPath * _Nonnull indexPath) { + __strong typeof(_self) self = _self; + if ( self == nil ) return; + NSLog(@"处理分享动作..."); + + NSInteger count = [self.provider postShareCountForItemAtIndexPath:indexPath]; + [self.provider setPostShareCount:count + 1 forItemAtIndexPath:indexPath]; + }; + + self.presenter.provider = self.provider; + [self.collectionView reloadData]; + }]; +} +@end diff --git a/Example/LWZComponents/HybridLayout(Mix)/Pvovider/HLProvider.h b/Example/LWZComponents/HybridLayout(Mix)/Pvovider/HLProvider.h new file mode 100644 index 0000000..ee15c57 --- /dev/null +++ b/Example/LWZComponents/HybridLayout(Mix)/Pvovider/HLProvider.h @@ -0,0 +1,32 @@ +// +// HLProvider.h +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/12/25. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "LWZCollectionProvider.h" +#import "HLModel.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface HLProvider : LWZCollectionProvider + +- (instancetype)initWithModel:(HLModel *)model; + + +@property (nonatomic, copy, nullable) void(^userItemTapHandler)(NSInteger userId); + +@property (nonatomic, copy, nullable) void(^likingItemTapHandler)(NSIndexPath *indexPath); +@property (nonatomic, copy, nullable) void(^shareItemTapHandler)(NSIndexPath *indexPath); +@property (nonatomic, copy, nullable) void(^commentItemTapHandler)(NSIndexPath *indexPath); + +- (BOOL)isPostLikedForItemAtIndexPath:(NSIndexPath *)indexPath; +- (void)setPostLiked:(BOOL)isLiked forItemAtIndexPath:(NSIndexPath *)indexPath; + +- (NSInteger)postShareCountForItemAtIndexPath:(NSIndexPath *)indexPath; +- (void)setPostShareCount:(NSInteger)count forItemAtIndexPath:(NSIndexPath *)indexPath; +@end + +NS_ASSUME_NONNULL_END diff --git a/Example/LWZComponents/HybridLayout(Mix)/Pvovider/HLProvider.m b/Example/LWZComponents/HybridLayout(Mix)/Pvovider/HLProvider.m new file mode 100644 index 0000000..2100eb9 --- /dev/null +++ b/Example/LWZComponents/HybridLayout(Mix)/Pvovider/HLProvider.m @@ -0,0 +1,228 @@ +// +// HLProvider.m +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/12/25. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "HLProvider.h" +#import "CommonDependencies.h" +#import "WLUserCollectionItem.h" +#import "WLPostCollectionItem.h" +#import "WLCollectionSectionHeaderFooter.h" +#import "RLTextItem.h" +#import "WFLCollectionItem.h" +#import "CLItem.h" + +@implementation HLProvider +- (instancetype)initWithModel:(HLModel *)model { + self = [super init]; + if ( self ) { + [self _addRLSection:model.rl_List]; + [self _addWLSection:model.wl_Model]; + [self _addWFLSection:model.wfl_list]; + [self _addTLSection]; + } + return self; +} + +#pragma mark - WeightLayout + +- (void)_addWLSection:(WLDetailModel *)model { + __weak typeof(self) _self = self; + if ( model.users.count != 0 ) { + [self addSectionWithBlock:^(__kindof LWZCollectionSection * _Nonnull make) { + make.layoutType = LWZCollectionLayoutTypeWeight; + make.contentInsets = UIEdgeInsetsMake(12, 12, 12, 12); + make.minimumInteritemSpacing = 8; + make.minimumLineSpacing = 8; + + // 设置 sectionHeaderView + WLCollectionSectionHeaderFooter *header = [WLCollectionSectionHeaderFooter.alloc initWithTitle:[NSAttributedString sj_UIKitText:^(id _Nonnull make) { + make.append(@"设置比重: 每行显示三个item"); + make.font([UIFont boldSystemFontOfSize:18]); + }]]; + header.contentInsets = UIEdgeInsetsMake(12, 12, 0, 12); + make.header = header; + + // 添加 userItem + for ( WLUserModel *userModel in model.users ) { + WLUserCollectionItem *item = [WLUserCollectionItem.alloc initWithModel:userModel]; + // 每行显示三个; 设置比重占`1 / 3.0`; + item.weight = 1 / 3.0; + + // item 添加背景decoration + LWZCollectionItemBackgroundDecoration *decoration = [LWZCollectionItemBackgroundDecoration.alloc initWithBackgroundColor:[UIColor colorWithRed:245 / 255.0 green:245 / 255.0 blue:245 / 255.0 alpha:1]]; + decoration.cornerRadius = 5; + item.decoration = decoration; + + // 设置 userItem 点击事件 + item.tapHandler = ^(__kindof LWZCollectionItem * _Nonnull item, NSIndexPath * _Nonnull indexPath) { + __strong typeof(_self) self = _self; + if ( self == nil ) return; + if ( self.userItemTapHandler ) self.userItemTapHandler(userModel.id); + }; + + // 将 userItem 添加到 section 中 + [make addItem:item]; + } + }]; + } + + if ( model.posts.count != 0 ) { + [self addSectionWithBlock:^(__kindof LWZCollectionSection * _Nonnull make) { + make.layoutType = LWZCollectionLayoutTypeWeight; + make.contentInsets = UIEdgeInsetsMake(12, 12, 12, 12); + make.minimumLineSpacing = 24; + + // 设置 sectionHeaderView + WLCollectionSectionHeaderFooter *header = [WLCollectionSectionHeaderFooter.alloc initWithTitle:[NSAttributedString sj_UIKitText:^(id _Nonnull make) { + make.append(@"Header: 默认比重为 1.0, 即每行显示一个item"); + make.font([UIFont boldSystemFontOfSize:18]); + }]]; + header.contentInsets = UIEdgeInsetsMake(12, 12, 0, 12); + // 可以为 header 添加 decoration + LWZCollectionHeaderSeparatorDecoration *headerDecoration = [LWZCollectionHeaderSeparatorDecoration.alloc initWithColor:[UIColor redColor] height:2]; + headerDecoration.contentInsets = UIEdgeInsetsMake(0, 0, 0, 0); + header.decoration = headerDecoration; + make.header = header; + + // 添加 postItem + for ( WLPostModel *post in model.posts ) { + WLPostCollectionItem *item = [WLPostCollectionItem itemWithModel:post]; + item.likingItemTapHandler = ^(WLPostCollectionItem * _Nonnull item) { + __strong typeof(_self) self = _self; + if ( self == nil ) return; + if ( self.likingItemTapHandler != nil ) self.likingItemTapHandler([self indexPathOfItem:item]); + }; + item.shareItemTapHandler = ^(WLPostCollectionItem * _Nonnull item) { + __strong typeof(_self) self = _self; + if ( self == nil ) return; + if ( self.shareItemTapHandler != nil ) self.shareItemTapHandler([self indexPathOfItem:item]); + }; + item.commentItemTapHandler = ^(WLPostCollectionItem * _Nonnull item) { + __strong typeof(_self) self = _self; + if ( self == nil ) return; + if ( self.commentItemTapHandler != nil ) self.commentItemTapHandler([self indexPathOfItem:item]); + }; + + // 可以为 item 添加分割线decoration + LWZCollectionSeparatorDecoration *decoration = [LWZCollectionSeparatorDecoration.alloc initWithColor:[UIColor colorWithRed:230 / 255.0 green:230 / 255.0 blue:230 / 255.0 alpha:1] height:0.5]; + decoration.contentInsets = UIEdgeInsetsMake(0, 0, -12, 0); + item.decoration = decoration; + + [make addItem:item]; + } + }]; + } +} + +- (BOOL)isPostLikedForItemAtIndexPath:(NSIndexPath *)indexPath { + WLPostCollectionItem *item = [self itemAtIndexPath:indexPath]; + return item.isLiked; +} + +- (void)setPostLiked:(BOOL)isLiked forItemAtIndexPath:(NSIndexPath *)indexPath { + WLPostCollectionItem *item = [self itemAtIndexPath:indexPath]; + item.isLiked = isLiked; +} + +- (NSInteger)postShareCountForItemAtIndexPath:(NSIndexPath *)indexPath { + WLPostCollectionItem *item = [self itemAtIndexPath:indexPath]; + return item.shareCount; +} +- (void)setPostShareCount:(NSInteger)count forItemAtIndexPath:(NSIndexPath *)indexPath { + WLPostCollectionItem *item = [self itemAtIndexPath:indexPath]; + item.shareCount = count; +} + + +#pragma mark - RestrictedLayout + +- (void)_addRLSection:(NSArray *)list { + [self addSectionWithBlock:^(__kindof LWZCollectionSection * _Nonnull make) { + make.layoutType = LWZCollectionLayoutTypeRestrictedLayout; + make.contentInsets = UIEdgeInsetsMake(12, 12, 12, 12); + make.minimumLineSpacing = 5; + make.minimumInteritemSpacing = 5; + + for ( RLModel *tagModel in list ) { + RLTextItem *item = [RLTextItem.alloc initWithModel:tagModel]; + [make addItem:item]; + } + }]; +} + +#pragma mark - WaterfallFlowLayout + +- (void)_addWFLSection:(NSArray *)list { + [self addSectionWithBlock:^(__kindof LWZCollectionSection * _Nonnull make) { + make.layoutType = LWZCollectionLayoutTypeWaterfallFlow; + make.minimumInteritemSpacing = 8; + make.minimumLineSpacing = 8; + make.contentInsets = UIEdgeInsetsMake(12, 12, 12, 12); + make.numberOfArrangedItemsPerLine = 2; + + for ( WFLModel *model in list ) { + WFLCollectionItem *item = [WFLCollectionItem.alloc initWithModel:model]; + [make addItem:item]; + } + }]; +} + +#pragma mark - TemplateLayout + +- (void)_addTLSection { + [self addSectionWithBlock:^(__kindof LWZCollectionSection * _Nonnull make) { + make.layoutType = LWZCollectionLayoutTypeTemplate; + make.minimumLineSpacing = 12; + make.minimumInteritemSpacing = 12; + make.contentInsets = UIEdgeInsetsMake(12, 12, 12, 12); + + for ( NSInteger i = 0 ; i < 6 ; ++ i ) { + [make addItem:CLItem.alloc.init]; + } + + make.layoutTemplateContainerGroups = [LWZCollectionLayoutTemplate build:^(LWZCollectionTemplateBuilder * _Nonnull make) { + // 添加1个容器组 + make.addGroup(^(LWZCollectionTemplateGroupBuilder * _Nonnull group) { + // 设置容器组的宽度等于父布局的宽度 + group.width.fractionalWidth(1.0); + // 设置容器组的高度等于父布局的宽度 + group.width.fractionalWidth(1.0); + + // 向组内添加容器1 + group.addContainer(^(LWZCollectionTemplateContainerBuilder * _Nonnull container) { + // 设置容器1的宽度等于父布局的宽度的一半 + container.width.fractionalWidth(0.5); + // 设置容器1的高度等于父布局的高度 + container.height.fractionalHeight(1.0); + + // 左侧安排1个item + container.addItem(^(LWZCollectionTemplateItemBuilder * _Nonnull item) { + // 设置item的宽度等于父布局的宽度 + item.width.fractionalWidth(1.0); + // 设置item的高度等于父布局的高度 + item.height.fractionalHeight(1.0); + }); + }); + + // 向组内添加容器2 + group.addContainer(^(LWZCollectionTemplateContainerBuilder * _Nonnull container) { + container.width.fractionalWidth(0.5); + container.height.fractionalHeight(1.0); + + // 右侧安排2个item + for ( NSInteger i = 0 ; i < 2 ; ++ i ) { + container.addItem(^(LWZCollectionTemplateItemBuilder * _Nonnull item) { + item.width.fractionalWidth(1.0); + item.height.fractionalHeight(0.5); + }); + } + }); + }); + }]; + }]; +} +@end diff --git a/Example/LWZComponents/HybridLayout(Mix)/Pvovider/Model/HLModel.h b/Example/LWZComponents/HybridLayout(Mix)/Pvovider/Model/HLModel.h new file mode 100644 index 0000000..972926b --- /dev/null +++ b/Example/LWZComponents/HybridLayout(Mix)/Pvovider/Model/HLModel.h @@ -0,0 +1,19 @@ +// +// HLModel.h +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/12/25. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "WLModel.h" +#import "RLModel.h" +#import "WFLModel.h" + +NS_ASSUME_NONNULL_BEGIN +@interface HLModel : NSObject +@property (nonatomic, strong, nullable) WLDetailModel *wl_Model; +@property (nonatomic, strong, nullable) NSArray *rl_List; +@property (nonatomic, strong, nullable) NSArray *wfl_list; +@end +NS_ASSUME_NONNULL_END diff --git a/Example/LWZComponents/HybridLayout(Mix)/Pvovider/Model/HLModel.m b/Example/LWZComponents/HybridLayout(Mix)/Pvovider/Model/HLModel.m new file mode 100644 index 0000000..a81a456 --- /dev/null +++ b/Example/LWZComponents/HybridLayout(Mix)/Pvovider/Model/HLModel.m @@ -0,0 +1,13 @@ +// +// HLModel.m +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/12/25. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "HLModel.h" + +@implementation HLModel + +@end diff --git a/Example/LWZComponents/HybridLayout(Mix)/Pvovider/Model/HLModelProvider.h b/Example/LWZComponents/HybridLayout(Mix)/Pvovider/Model/HLModelProvider.h new file mode 100644 index 0000000..2bb553a --- /dev/null +++ b/Example/LWZComponents/HybridLayout(Mix)/Pvovider/Model/HLModelProvider.h @@ -0,0 +1,19 @@ +// +// HLModelProvider.h +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/12/25. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "HLModel.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface HLModelProvider : NSObject + ++ (void)requestDataWithComplete:(void(^)(HLModel *_Nullable model, NSError *_Nullable error))completionHandler; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Example/LWZComponents/HybridLayout(Mix)/Pvovider/Model/HLModelProvider.m b/Example/LWZComponents/HybridLayout(Mix)/Pvovider/Model/HLModelProvider.m new file mode 100644 index 0000000..6ed57cc --- /dev/null +++ b/Example/LWZComponents/HybridLayout(Mix)/Pvovider/Model/HLModelProvider.m @@ -0,0 +1,54 @@ +// +// HLModelProvider.m +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/12/25. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "HLModelProvider.h" +#import "WLModelProvider.h" +#import "RLModelProvider.h" +#import "WFLModelProvider.h" + +@implementation HLModelProvider ++ (void)requestDataWithComplete:(void(^)(HLModel *_Nullable model, NSError *_Nullable error))completionHandler { + __block WLDetailModel *wl_model; + __block NSArray *rl_list; + __block NSArray *wfl_list; + __block NSError *mError; + + dispatch_group_t group = dispatch_group_create(); + dispatch_group_enter(group); + [WLModelProvider requestDataWithComplete:^(WLDetailModel * _Nullable detail, NSError * _Nullable error) { + if ( mError == nil ) mError = error; + wl_model = detail; + dispatch_group_leave(group); + }]; + + dispatch_group_enter(group); + [RLModelProvider requestDataWithComplete:^(NSArray * _Nullable list, NSError * _Nullable error) { + if ( mError == nil ) mError = error; + rl_list = list; + dispatch_group_leave(group); + }]; + + dispatch_group_enter(group); + [WFLModelProvider requestDataWithComplete:^(NSArray * _Nullable list, NSError * _Nullable error) { + if ( mError == nil ) mError = error; + wfl_list = list; + dispatch_group_leave(group); + }]; + + dispatch_group_notify(group, dispatch_get_main_queue(), ^{ + HLModel *model = nil; + if ( mError == nil ) { + model = [HLModel new]; + model.wl_Model = wl_model; + model.rl_List = rl_list; + model.wfl_list = wfl_list; + } + if ( completionHandler != nil ) completionHandler(model, mError); + }); +} +@end diff --git a/Example/LWZComponents/Images.xcassets/Contents.json b/Example/LWZComponents/Images.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Example/LWZComponents/Images.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/LWZComponents/Images.xcassets/avatar_1.imageset/Contents.json b/Example/LWZComponents/Images.xcassets/avatar_1.imageset/Contents.json new file mode 100644 index 0000000..bec4c7e --- /dev/null +++ b/Example/LWZComponents/Images.xcassets/avatar_1.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "avatar_1.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/LWZComponents/Images.xcassets/avatar_1.imageset/avatar_1.png b/Example/LWZComponents/Images.xcassets/avatar_1.imageset/avatar_1.png new file mode 100644 index 0000000..5075011 Binary files /dev/null and b/Example/LWZComponents/Images.xcassets/avatar_1.imageset/avatar_1.png differ diff --git a/Example/LWZComponents/Images.xcassets/avatar_2.imageset/Contents.json b/Example/LWZComponents/Images.xcassets/avatar_2.imageset/Contents.json new file mode 100644 index 0000000..5904746 --- /dev/null +++ b/Example/LWZComponents/Images.xcassets/avatar_2.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "avatar_2.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/LWZComponents/Images.xcassets/avatar_2.imageset/avatar_2.png b/Example/LWZComponents/Images.xcassets/avatar_2.imageset/avatar_2.png new file mode 100644 index 0000000..869be57 Binary files /dev/null and b/Example/LWZComponents/Images.xcassets/avatar_2.imageset/avatar_2.png differ diff --git a/Example/LWZComponents/Images.xcassets/avatar_3.imageset/Contents.json b/Example/LWZComponents/Images.xcassets/avatar_3.imageset/Contents.json new file mode 100644 index 0000000..de55902 --- /dev/null +++ b/Example/LWZComponents/Images.xcassets/avatar_3.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "avatar_3.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/LWZComponents/Images.xcassets/avatar_3.imageset/avatar_3.png b/Example/LWZComponents/Images.xcassets/avatar_3.imageset/avatar_3.png new file mode 100644 index 0000000..12d5a51 Binary files /dev/null and b/Example/LWZComponents/Images.xcassets/avatar_3.imageset/avatar_3.png differ diff --git a/Example/LWZComponents/Images.xcassets/avatar_4.imageset/Contents.json b/Example/LWZComponents/Images.xcassets/avatar_4.imageset/Contents.json new file mode 100644 index 0000000..bf7f8fa --- /dev/null +++ b/Example/LWZComponents/Images.xcassets/avatar_4.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "avatar_4.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/LWZComponents/Images.xcassets/avatar_4.imageset/avatar_4.png b/Example/LWZComponents/Images.xcassets/avatar_4.imageset/avatar_4.png new file mode 100644 index 0000000..4ab6cbb Binary files /dev/null and b/Example/LWZComponents/Images.xcassets/avatar_4.imageset/avatar_4.png differ diff --git a/Example/LWZComponents/Images.xcassets/avatar_5.imageset/Contents.json b/Example/LWZComponents/Images.xcassets/avatar_5.imageset/Contents.json new file mode 100644 index 0000000..a177ac5 --- /dev/null +++ b/Example/LWZComponents/Images.xcassets/avatar_5.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "avatar_5.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/LWZComponents/Images.xcassets/avatar_5.imageset/avatar_5.png b/Example/LWZComponents/Images.xcassets/avatar_5.imageset/avatar_5.png new file mode 100644 index 0000000..5b481d9 Binary files /dev/null and b/Example/LWZComponents/Images.xcassets/avatar_5.imageset/avatar_5.png differ diff --git a/Example/LWZComponents/Images.xcassets/avatar_6.imageset/Contents.json b/Example/LWZComponents/Images.xcassets/avatar_6.imageset/Contents.json new file mode 100644 index 0000000..19435cc --- /dev/null +++ b/Example/LWZComponents/Images.xcassets/avatar_6.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "avatar_6.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/LWZComponents/Images.xcassets/avatar_6.imageset/avatar_6.png b/Example/LWZComponents/Images.xcassets/avatar_6.imageset/avatar_6.png new file mode 100644 index 0000000..8a06fcc Binary files /dev/null and b/Example/LWZComponents/Images.xcassets/avatar_6.imageset/avatar_6.png differ diff --git a/Example/LWZComponents/Images.xcassets/icon_comment.imageset/Contents.json b/Example/LWZComponents/Images.xcassets/icon_comment.imageset/Contents.json new file mode 100644 index 0000000..e9abd1a --- /dev/null +++ b/Example/LWZComponents/Images.xcassets/icon_comment.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "icon_comment.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/LWZComponents/Images.xcassets/icon_comment.imageset/icon_comment.png b/Example/LWZComponents/Images.xcassets/icon_comment.imageset/icon_comment.png new file mode 100644 index 0000000..5f81a89 Binary files /dev/null and b/Example/LWZComponents/Images.xcassets/icon_comment.imageset/icon_comment.png differ diff --git a/Example/LWZComponents/Images.xcassets/icon_liking_0.imageset/Contents.json b/Example/LWZComponents/Images.xcassets/icon_liking_0.imageset/Contents.json new file mode 100644 index 0000000..856a546 --- /dev/null +++ b/Example/LWZComponents/Images.xcassets/icon_liking_0.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "icon_liking_0.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/LWZComponents/Images.xcassets/icon_liking_0.imageset/icon_liking_0.png b/Example/LWZComponents/Images.xcassets/icon_liking_0.imageset/icon_liking_0.png new file mode 100644 index 0000000..f455f6b Binary files /dev/null and b/Example/LWZComponents/Images.xcassets/icon_liking_0.imageset/icon_liking_0.png differ diff --git a/Example/LWZComponents/Images.xcassets/icon_liking_1.imageset/Contents.json b/Example/LWZComponents/Images.xcassets/icon_liking_1.imageset/Contents.json new file mode 100644 index 0000000..90b125e --- /dev/null +++ b/Example/LWZComponents/Images.xcassets/icon_liking_1.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "icon_liking_1.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/LWZComponents/Images.xcassets/icon_liking_1.imageset/icon_liking_1.png b/Example/LWZComponents/Images.xcassets/icon_liking_1.imageset/icon_liking_1.png new file mode 100644 index 0000000..5d460cb Binary files /dev/null and b/Example/LWZComponents/Images.xcassets/icon_liking_1.imageset/icon_liking_1.png differ diff --git a/Example/LWZComponents/Images.xcassets/icon_play.imageset/Contents.json b/Example/LWZComponents/Images.xcassets/icon_play.imageset/Contents.json new file mode 100644 index 0000000..5f8e6a7 --- /dev/null +++ b/Example/LWZComponents/Images.xcassets/icon_play.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "icon_play.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/LWZComponents/Images.xcassets/icon_play.imageset/icon_play.png b/Example/LWZComponents/Images.xcassets/icon_play.imageset/icon_play.png new file mode 100644 index 0000000..46eaefa Binary files /dev/null and b/Example/LWZComponents/Images.xcassets/icon_play.imageset/icon_play.png differ diff --git a/Example/LWZComponents/Images.xcassets/icon_share.imageset/Contents.json b/Example/LWZComponents/Images.xcassets/icon_share.imageset/Contents.json new file mode 100644 index 0000000..3b8b6a6 --- /dev/null +++ b/Example/LWZComponents/Images.xcassets/icon_share.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "icon_share.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/LWZComponents/Images.xcassets/icon_share.imageset/icon_share.png b/Example/LWZComponents/Images.xcassets/icon_share.imageset/icon_share.png new file mode 100644 index 0000000..e53fda1 Binary files /dev/null and b/Example/LWZComponents/Images.xcassets/icon_share.imageset/icon_share.png differ diff --git a/Example/LWZComponents/Images.xcassets/kwi_1.imageset/Contents.json b/Example/LWZComponents/Images.xcassets/kwi_1.imageset/Contents.json new file mode 100644 index 0000000..3c450cb --- /dev/null +++ b/Example/LWZComponents/Images.xcassets/kwi_1.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "kwi_1.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/LWZComponents/Images.xcassets/kwi_1.imageset/kwi_1.png b/Example/LWZComponents/Images.xcassets/kwi_1.imageset/kwi_1.png new file mode 100644 index 0000000..00fe2dc Binary files /dev/null and b/Example/LWZComponents/Images.xcassets/kwi_1.imageset/kwi_1.png differ diff --git a/Example/LWZComponents/Images.xcassets/kwi_2.imageset/Contents.json b/Example/LWZComponents/Images.xcassets/kwi_2.imageset/Contents.json new file mode 100644 index 0000000..5d74ac2 --- /dev/null +++ b/Example/LWZComponents/Images.xcassets/kwi_2.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "kwi_2.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/LWZComponents/Images.xcassets/kwi_2.imageset/kwi_2.png b/Example/LWZComponents/Images.xcassets/kwi_2.imageset/kwi_2.png new file mode 100644 index 0000000..855882d Binary files /dev/null and b/Example/LWZComponents/Images.xcassets/kwi_2.imageset/kwi_2.png differ diff --git a/Example/LWZComponents/Images.xcassets/kwi_3.imageset/Contents.json b/Example/LWZComponents/Images.xcassets/kwi_3.imageset/Contents.json new file mode 100644 index 0000000..2a12f6e --- /dev/null +++ b/Example/LWZComponents/Images.xcassets/kwi_3.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "kwi_3.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/LWZComponents/Images.xcassets/kwi_3.imageset/kwi_3.png b/Example/LWZComponents/Images.xcassets/kwi_3.imageset/kwi_3.png new file mode 100644 index 0000000..2dc5361 Binary files /dev/null and b/Example/LWZComponents/Images.xcassets/kwi_3.imageset/kwi_3.png differ diff --git a/Example/LWZComponents/Images.xcassets/kwi_4.imageset/Contents.json b/Example/LWZComponents/Images.xcassets/kwi_4.imageset/Contents.json new file mode 100644 index 0000000..7a9c566 --- /dev/null +++ b/Example/LWZComponents/Images.xcassets/kwi_4.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "kwi_4.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/LWZComponents/Images.xcassets/kwi_4.imageset/kwi_4.png b/Example/LWZComponents/Images.xcassets/kwi_4.imageset/kwi_4.png new file mode 100644 index 0000000..f17281d Binary files /dev/null and b/Example/LWZComponents/Images.xcassets/kwi_4.imageset/kwi_4.png differ diff --git a/Example/LWZComponents/Images.xcassets/kwi_5.imageset/Contents.json b/Example/LWZComponents/Images.xcassets/kwi_5.imageset/Contents.json new file mode 100644 index 0000000..cd91c12 --- /dev/null +++ b/Example/LWZComponents/Images.xcassets/kwi_5.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "kwi_5.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/LWZComponents/Images.xcassets/kwi_5.imageset/kwi_5.png b/Example/LWZComponents/Images.xcassets/kwi_5.imageset/kwi_5.png new file mode 100644 index 0000000..384e7c6 Binary files /dev/null and b/Example/LWZComponents/Images.xcassets/kwi_5.imageset/kwi_5.png differ diff --git a/Example/LWZComponents/Images.xcassets/kwi_6.imageset/Contents.json b/Example/LWZComponents/Images.xcassets/kwi_6.imageset/Contents.json new file mode 100644 index 0000000..2d577ae --- /dev/null +++ b/Example/LWZComponents/Images.xcassets/kwi_6.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "kwi_6.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/LWZComponents/Images.xcassets/kwi_6.imageset/kwi_6.png b/Example/LWZComponents/Images.xcassets/kwi_6.imageset/kwi_6.png new file mode 100644 index 0000000..f17281d Binary files /dev/null and b/Example/LWZComponents/Images.xcassets/kwi_6.imageset/kwi_6.png differ diff --git a/Example/LWZComponents/Images.xcassets/kwi_7.imageset/Contents.json b/Example/LWZComponents/Images.xcassets/kwi_7.imageset/Contents.json new file mode 100644 index 0000000..5bcfbb7 --- /dev/null +++ b/Example/LWZComponents/Images.xcassets/kwi_7.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "kwi_7.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/LWZComponents/Images.xcassets/kwi_7.imageset/kwi_7.png b/Example/LWZComponents/Images.xcassets/kwi_7.imageset/kwi_7.png new file mode 100644 index 0000000..f4f5da2 Binary files /dev/null and b/Example/LWZComponents/Images.xcassets/kwi_7.imageset/kwi_7.png differ diff --git a/Example/LWZComponents/Images.xcassets/kwi_8.imageset/Contents.json b/Example/LWZComponents/Images.xcassets/kwi_8.imageset/Contents.json new file mode 100644 index 0000000..39d887f --- /dev/null +++ b/Example/LWZComponents/Images.xcassets/kwi_8.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "kwi_8.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/LWZComponents/Images.xcassets/kwi_8.imageset/kwi_8.png b/Example/LWZComponents/Images.xcassets/kwi_8.imageset/kwi_8.png new file mode 100644 index 0000000..2d25f8a Binary files /dev/null and b/Example/LWZComponents/Images.xcassets/kwi_8.imageset/kwi_8.png differ diff --git a/Example/LWZComponents/Images.xcassets/kwi_9.imageset/Contents.json b/Example/LWZComponents/Images.xcassets/kwi_9.imageset/Contents.json new file mode 100644 index 0000000..bad0dd2 --- /dev/null +++ b/Example/LWZComponents/Images.xcassets/kwi_9.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "kwi_9.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/LWZComponents/Images.xcassets/kwi_9.imageset/kwi_9.png b/Example/LWZComponents/Images.xcassets/kwi_9.imageset/kwi_9.png new file mode 100644 index 0000000..b42df01 Binary files /dev/null and b/Example/LWZComponents/Images.xcassets/kwi_9.imageset/kwi_9.png differ diff --git a/Example/LWZComponents/Images.xcassets/shici_1.imageset/Contents.json b/Example/LWZComponents/Images.xcassets/shici_1.imageset/Contents.json new file mode 100644 index 0000000..f0f32cc --- /dev/null +++ b/Example/LWZComponents/Images.xcassets/shici_1.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "shici_1.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/LWZComponents/Images.xcassets/shici_1.imageset/shici_1.png b/Example/LWZComponents/Images.xcassets/shici_1.imageset/shici_1.png new file mode 100644 index 0000000..028ff62 Binary files /dev/null and b/Example/LWZComponents/Images.xcassets/shici_1.imageset/shici_1.png differ diff --git a/Example/LWZComponents/Images.xcassets/video_1.imageset/Contents.json b/Example/LWZComponents/Images.xcassets/video_1.imageset/Contents.json new file mode 100644 index 0000000..a49b672 --- /dev/null +++ b/Example/LWZComponents/Images.xcassets/video_1.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "video_1.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/LWZComponents/Images.xcassets/video_1.imageset/video_1.png b/Example/LWZComponents/Images.xcassets/video_1.imageset/video_1.png new file mode 100644 index 0000000..c26c1d6 Binary files /dev/null and b/Example/LWZComponents/Images.xcassets/video_1.imageset/video_1.png differ diff --git a/Example/LWZComponents/LWZAppDelegate.m b/Example/LWZComponents/LWZAppDelegate.m index aaee4d6..541921d 100644 --- a/Example/LWZComponents/LWZAppDelegate.m +++ b/Example/LWZComponents/LWZAppDelegate.m @@ -12,6 +12,7 @@ @implementation LWZAppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + self.window.backgroundColor = UIColor.whiteColor; // Override point for customization after application launch. return YES; } diff --git a/Example/LWZComponents/LWZComponents-Info.plist b/Example/LWZComponents/LWZComponents-Info.plist index 7576a0d..d1ce75c 100644 --- a/Example/LWZComponents/LWZComponents-Info.plist +++ b/Example/LWZComponents/LWZComponents-Info.plist @@ -45,5 +45,10 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + diff --git a/Example/LWZComponents/LWZViewController.m b/Example/LWZComponents/LWZViewController.m index 31a2b52..366df90 100644 --- a/Example/LWZComponents/LWZViewController.m +++ b/Example/LWZComponents/LWZViewController.m @@ -7,6 +7,13 @@ // #import "LWZViewController.h" +#import "WLViewController.h" +#import "LLViewController.h" +#import "WFLViewController.h" +#import "TLViewController1.h" +#import "CLViewController.h" +#import "RLViewController.h" +#import "HLViewController.h" @interface LWZViewController () @@ -14,16 +21,40 @@ @interface LWZViewController () @implementation LWZViewController -- (void)viewDidLoad -{ +- (void)viewDidLoad { [super viewDidLoad]; - // Do any additional setup after loading the view, typically from a nib. + // Do any additional setup after loading the view, typically from a nib. } -- (void)didReceiveMemoryWarning -{ - [super didReceiveMemoryWarning]; - // Dispose of any resources that can be recreated. +- (IBAction)pushWeightLayout:(id)sender { + WLViewController *vc = [WLViewController.alloc init]; + [self.navigationController pushViewController:vc animated:YES]; } +- (IBAction)pushListLayout:(id)sender { + LLViewController *vc = [LLViewController.alloc init]; + [self.navigationController pushViewController:vc animated:YES]; +} +- (IBAction)pushRestrictedLayout:(id)sender { + RLViewController *vc = [RLViewController.alloc init]; + [self.navigationController pushViewController:vc animated:YES]; +} +- (IBAction)pushWaterfallFlowLayout:(id)sender { + WFLViewController *vc = [WFLViewController.alloc init]; + [self.navigationController pushViewController:vc animated:YES]; +} + +- (IBAction)pushTemplateLayout:(id)sender { + TLViewController1 *vc = [TLViewController1.alloc init]; + [self.navigationController pushViewController:vc animated:YES]; +} + +- (IBAction)pushCompositionalLayout:(id)sender { + CLViewController *vc = [CLViewController.alloc init]; + [self.navigationController pushViewController:vc animated:YES]; +} +- (IBAction)pushHybridLayout:(id)sender { + HLViewController *vc = [HLViewController.alloc init]; + [self.navigationController pushViewController:vc animated:YES]; +} @end diff --git a/Example/LWZComponents/ListLayout/LLViewController.h b/Example/LWZComponents/ListLayout/LLViewController.h new file mode 100644 index 0000000..ca85be8 --- /dev/null +++ b/Example/LWZComponents/ListLayout/LLViewController.h @@ -0,0 +1,17 @@ +// +// LLViewController.h +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/11/26. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface LLViewController : UIViewController + +@end + +NS_ASSUME_NONNULL_END diff --git a/Example/LWZComponents/ListLayout/LLViewController.m b/Example/LWZComponents/ListLayout/LLViewController.m new file mode 100644 index 0000000..36d2c70 --- /dev/null +++ b/Example/LWZComponents/ListLayout/LLViewController.m @@ -0,0 +1,106 @@ +// +// LLViewController.m +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/11/26. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "LLViewController.h" +#import "CommonDependencies.h" +#import "LLProvider.h" + +@interface LLViewController () +// horizontal scroll +@property (nonatomic, strong) LWZCollectionViewPresenter *presenter1; +@property (nonatomic, strong) LWZCollectionViewPresenter *presenter2; +@property (nonatomic, strong) LWZCollectionViewPresenter *presenter3; + +// vertical scroll +@property (nonatomic, strong) LWZCollectionViewPresenter *presenter4; +@property (nonatomic, strong) LWZCollectionViewPresenter *presenter5; +@property (nonatomic, strong) LWZCollectionViewPresenter *presenter6; +@end + +@implementation LLViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + + self.view.backgroundColor = UIColor.whiteColor; + self.edgesForExtendedLayout = UIRectEdgeNone; + self.title = NSStringFromClass(self.class); + +#pragma mark - horizontal scroll + + + CGRect frame = CGRectMake(12, 20, self.view.bounds.size.width - 12 * 2, 88); + // 与 collectionView 顶部对齐 + LWZCollectionViewPresenter *presenter = [LWZCollectionViewPresenter.alloc initWithProvider:[LLProvider.alloc initWithAlignment:LWZCollectionLayoutAlignmentStart]]; + LWZCollectionListLayout *collectionListLayout = [LWZCollectionListLayout.alloc initWithScrollDirection:UICollectionViewScrollDirectionHorizontal delegate:presenter]; + LWZCollectionView *collectionView = [LWZCollectionView.alloc initWithFrame:frame collectionViewLayout:collectionListLayout]; + collectionView.dataSource = presenter; + collectionView.backgroundColor = UIColor.blackColor; + [self.view addSubview:collectionView]; + _presenter1 = presenter; + + + frame.origin.y = CGRectGetMaxY(frame) + 20; + // 与 collectionView1 中心对齐 + presenter = [LWZCollectionViewPresenter.alloc initWithProvider:[LLProvider.alloc initWithAlignment:LWZCollectionLayoutAlignmentCenter]]; + collectionListLayout = [LWZCollectionListLayout.alloc initWithScrollDirection:UICollectionViewScrollDirectionHorizontal delegate:presenter]; + collectionView = [LWZCollectionView.alloc initWithFrame:frame collectionViewLayout:collectionListLayout]; + collectionView.dataSource = presenter; + collectionView.backgroundColor = UIColor.blackColor; + [self.view addSubview:collectionView]; + _presenter2 = presenter; + + + frame.origin.y = CGRectGetMaxY(frame) + 20; + // 与 collectionView 底部对齐 + presenter = [LWZCollectionViewPresenter.alloc initWithProvider:[LLProvider.alloc initWithAlignment:LWZCollectionLayoutAlignmentEnd]]; + collectionListLayout = [LWZCollectionListLayout.alloc initWithScrollDirection:UICollectionViewScrollDirectionHorizontal delegate:presenter]; + collectionView = [LWZCollectionView.alloc initWithFrame:frame collectionViewLayout:collectionListLayout]; + collectionView.dataSource = presenter; + collectionView.backgroundColor = UIColor.blackColor; + [self.view addSubview:collectionView]; + _presenter3 = presenter; + + +#pragma mark - vertical scroll + + + frame.origin.y = CGRectGetMaxY(frame) + 20; + // 与 collectionView 底部对齐 + presenter = [LWZCollectionViewPresenter.alloc initWithProvider:[LLProvider.alloc initWithAlignment:LWZCollectionLayoutAlignmentStart]]; + collectionListLayout = [LWZCollectionListLayout.alloc initWithScrollDirection:UICollectionViewScrollDirectionVertical delegate:presenter]; + collectionView = [LWZCollectionView.alloc initWithFrame:frame collectionViewLayout:collectionListLayout]; + collectionView.dataSource = presenter; + collectionView.backgroundColor = UIColor.blackColor; + [self.view addSubview:collectionView]; + _presenter4 = presenter; + + + frame.origin.y = CGRectGetMaxY(frame) + 20; + // 与 collectionView 底部对齐 + presenter = [LWZCollectionViewPresenter.alloc initWithProvider:[LLProvider.alloc initWithAlignment:LWZCollectionLayoutAlignmentCenter]]; + collectionListLayout = [LWZCollectionListLayout.alloc initWithScrollDirection:UICollectionViewScrollDirectionVertical delegate:presenter]; + collectionView = [LWZCollectionView.alloc initWithFrame:frame collectionViewLayout:collectionListLayout]; + collectionView.dataSource = presenter; + collectionView.backgroundColor = UIColor.blackColor; + [self.view addSubview:collectionView]; + _presenter5 = presenter; + + + frame.origin.y = CGRectGetMaxY(frame) + 20; + // 与 collectionView 底部对齐 + presenter = [LWZCollectionViewPresenter.alloc initWithProvider:[LLProvider.alloc initWithAlignment:LWZCollectionLayoutAlignmentEnd]]; + collectionListLayout = [LWZCollectionListLayout.alloc initWithScrollDirection:UICollectionViewScrollDirectionVertical delegate:presenter]; + collectionView = [LWZCollectionView.alloc initWithFrame:frame collectionViewLayout:collectionListLayout]; + collectionView.dataSource = presenter; + collectionView.backgroundColor = UIColor.blackColor; + [self.view addSubview:collectionView]; + _presenter6 = presenter; +} + +@end diff --git a/Example/LWZComponents/ListLayout/Provider/Item/LLCollectionItem.h b/Example/LWZComponents/ListLayout/Provider/Item/LLCollectionItem.h new file mode 100644 index 0000000..e6b1a62 --- /dev/null +++ b/Example/LWZComponents/ListLayout/Provider/Item/LLCollectionItem.h @@ -0,0 +1,17 @@ +// +// LLCollectionItem.h +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/11/26. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "LWZCollectionItem.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface LLCollectionItem : LWZCollectionItem + +@end + +NS_ASSUME_NONNULL_END diff --git a/Example/LWZComponents/ListLayout/Provider/Item/LLCollectionItem.m b/Example/LWZComponents/ListLayout/Provider/Item/LLCollectionItem.m new file mode 100644 index 0000000..bb82238 --- /dev/null +++ b/Example/LWZComponents/ListLayout/Provider/Item/LLCollectionItem.m @@ -0,0 +1,44 @@ +// +// LLCollectionItem.m +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/11/26. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "LLCollectionItem.h" + +@interface LLCollectionItem () { + UIColor *_backgroundColor; +} +@end + +@implementation LLCollectionItem +- (instancetype)init { + self = [super init]; + if ( self ) { + _backgroundColor = [UIColor colorWithRed:arc4random() % 256 / 255.0 + green:arc4random() % 256 / 255.0 + blue:arc4random() % 256 / 255.0 + alpha:1.0]; + } + return self; +} + +- (Class)cellClass { + return UICollectionViewCell.class; +} + +- (CGSize)layoutSizeThatFits:(CGSize)size inSection:(LWZCollectionSection *)section atIndexPath:(NSIndexPath *)indexPath scrollDirection:(UICollectionViewScrollDirection)scrollDirection { + switch ( scrollDirection ) { + case UICollectionViewScrollDirectionVertical: + return CGSizeMake(size.width * 0.5, 44); + case UICollectionViewScrollDirectionHorizontal: + return CGSizeMake(88, size.height * 0.5); + } +} + +- (void)bindCell:(__kindof UICollectionViewCell *)cell atIndexPath:(NSIndexPath *)indexPath { + cell.contentView.backgroundColor = _backgroundColor; +} +@end diff --git a/Example/LWZComponents/ListLayout/Provider/LLProvider.h b/Example/LWZComponents/ListLayout/Provider/LLProvider.h new file mode 100644 index 0000000..a1020b8 --- /dev/null +++ b/Example/LWZComponents/ListLayout/Provider/LLProvider.h @@ -0,0 +1,17 @@ +// +// LLProvider.h +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/11/26. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "LWZCollectionProvider.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface LLProvider : LWZCollectionProvider +- (instancetype)initWithAlignment:(LWZCollectionLayoutAlignment)alignment; +@end + +NS_ASSUME_NONNULL_END diff --git a/Example/LWZComponents/ListLayout/Provider/LLProvider.m b/Example/LWZComponents/ListLayout/Provider/LLProvider.m new file mode 100644 index 0000000..343f5d0 --- /dev/null +++ b/Example/LWZComponents/ListLayout/Provider/LLProvider.m @@ -0,0 +1,28 @@ +// +// LLProvider.m +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/11/26. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "LLProvider.h" +#import "LLCollectionItem.h" + +@implementation LLProvider +- (instancetype)initWithAlignment:(LWZCollectionLayoutAlignment)alignment { + self = [super init]; + if ( self ) { + [self addSectionWithBlock:^(__kindof LWZCollectionSection * _Nonnull make) { + make.minimumLineSpacing = 8; + + for ( NSInteger i = 0 ; i < 12 ; ++ i ) { + LLCollectionItem *item = [LLCollectionItem.alloc init]; + item.layoutAlignment = alignment; + [make addItem:item]; + } + }]; + } + return self; +} +@end diff --git a/Example/LWZComponents/RestrictedLayout/Provider/Item/RLTextCell.h b/Example/LWZComponents/RestrictedLayout/Provider/Item/RLTextCell.h new file mode 100644 index 0000000..f2e2a2f --- /dev/null +++ b/Example/LWZComponents/RestrictedLayout/Provider/Item/RLTextCell.h @@ -0,0 +1,22 @@ +// +// RLTextCell.h +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/12/25. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import +@protocol RLTextCellDataSource; + +NS_ASSUME_NONNULL_BEGIN +@interface RLTextCell : UICollectionViewCell +@property (nonatomic, weak, nullable) id dataSource; +@end + +@protocol RLTextCellDataSource +@property (nonatomic, strong, readonly, nullable) NSAttributedString *text; +@property (nonatomic, strong, readonly, nullable) UIColor *backgroundColor; +@property (nonatomic, readonly) UIEdgeInsets contentInset; +@end +NS_ASSUME_NONNULL_END diff --git a/Example/LWZComponents/RestrictedLayout/Provider/Item/RLTextCell.m b/Example/LWZComponents/RestrictedLayout/Provider/Item/RLTextCell.m new file mode 100644 index 0000000..ce76ac9 --- /dev/null +++ b/Example/LWZComponents/RestrictedLayout/Provider/Item/RLTextCell.m @@ -0,0 +1,38 @@ +// +// RLTextCell.m +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/12/25. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "RLTextCell.h" + +@interface RLTextCell() +@property (weak, nonatomic) IBOutlet UILabel *label; +@end + +@implementation RLTextCell + +- (void)awakeFromNib { + [super awakeFromNib]; + self.contentView.clipsToBounds = YES; + // Initialization code +} + +- (void)layoutSubviews { + [super layoutSubviews]; + CGSize size = self.bounds.size; + self.contentView.layer.cornerRadius = size.height < size.width ? size.height * 0.5 : size.width * 0.5; + _label.frame = UIEdgeInsetsInsetRect(self.bounds, _dataSource.contentInset); +} + +- (void)setDataSource:(id)dataSource { + if ( dataSource != _dataSource ) { + _dataSource = dataSource; + _label.attributedText = dataSource.text; + + self.contentView.backgroundColor = dataSource.backgroundColor; + } +} +@end diff --git a/Example/LWZComponents/RestrictedLayout/Provider/Item/RLTextCell.xib b/Example/LWZComponents/RestrictedLayout/Provider/Item/RLTextCell.xib new file mode 100644 index 0000000..55098b8 --- /dev/null +++ b/Example/LWZComponents/RestrictedLayout/Provider/Item/RLTextCell.xib @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Example/LWZComponents/RestrictedLayout/Provider/Item/RLTextItem.h b/Example/LWZComponents/RestrictedLayout/Provider/Item/RLTextItem.h new file mode 100644 index 0000000..825e3c2 --- /dev/null +++ b/Example/LWZComponents/RestrictedLayout/Provider/Item/RLTextItem.h @@ -0,0 +1,18 @@ +// +// RLTextItem.h +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/12/25. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "LWZCollectionItem.h" +#import "RLModel.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface RLTextItem : LWZCollectionItem +- (instancetype)initWithModel:(RLModel *)model; +@end + +NS_ASSUME_NONNULL_END diff --git a/Example/LWZComponents/RestrictedLayout/Provider/Item/RLTextItem.m b/Example/LWZComponents/RestrictedLayout/Provider/Item/RLTextItem.m new file mode 100644 index 0000000..3504f29 --- /dev/null +++ b/Example/LWZComponents/RestrictedLayout/Provider/Item/RLTextItem.m @@ -0,0 +1,67 @@ +// +// RLTextItem.m +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/12/25. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "RLTextItem.h" +#import "RLTextCell.h" +#import "CommonDependencies.h" +#import "CommonTextLayoutSize.h" + +@interface RLTextItem () +@property (nonatomic, strong, nullable) NSAttributedString *text; +@property (nonatomic, strong, nullable) UIColor *backgroundColor; +@property (nonatomic) UIEdgeInsets contentInset; +@end + +@implementation RLTextItem + +- (instancetype)initWithModel:(RLModel *)model { + self = [super init]; + if ( self ) { + _text = [NSAttributedString sj_UIKitText:^(id _Nonnull make) { + make.append(model.name); + make.textColor(UIColor.whiteColor); + make.font([UIFont systemFontOfSize:14]); + make.alignment(NSTextAlignmentCenter); + }]; + _backgroundColor = UIColor.magentaColor; + _contentInset = UIEdgeInsetsMake(8, 12, 8, 12); + } + return self; +} + +- (Class)cellClass { + return RLTextCell.class; +} + +- (NSBundle *)cellNibBundle { + return NSBundle.mainBundle; +} + +- (CGSize)layoutSizeThatFits:(CGSize)size inSection:(LWZCollectionSection *)section atIndexPath:(NSIndexPath *)indexPath scrollDirection:(UICollectionViewScrollDirection)scrollDirection { + CGSize textSize = CGSizeZero; + switch ( scrollDirection ) { + case UICollectionViewScrollDirectionVertical: { + textSize = [_text lwz_layoutSizeThatFits:CGSizeMake(size.width - _contentInset.left - _contentInset.right, size.height) + limitedToNumberOfLines:0]; + } + break; + case UICollectionViewScrollDirectionHorizontal: { + textSize = [_text lwz_layoutSizeThatFits:CGSizeMake(size.width, size.height - _contentInset.top - _contentInset.bottom) + limitedToNumberOfLines:0]; + } + break; + } + textSize.width += _contentInset.left + _contentInset.right; + textSize.height += _contentInset.top + _contentInset.bottom; + return textSize; +} + +- (void)bindCell:(RLTextCell *)cell atIndexPath:(NSIndexPath *)indexPath { + cell.dataSource = self; +} +@end diff --git a/Example/LWZComponents/RestrictedLayout/Provider/Model/RLJsonData.json b/Example/LWZComponents/RestrictedLayout/Provider/Model/RLJsonData.json new file mode 100644 index 0000000..05a1a79 --- /dev/null +++ b/Example/LWZComponents/RestrictedLayout/Provider/Model/RLJsonData.json @@ -0,0 +1,275 @@ +[ + { + "id" : 1, + "name" : "1QAZ" + }, + { + "id" : 2, + "name" : "2WSX" + }, + { + "id" : 3, + "name" : "3EDC" + }, + { + "id" : 4, + "name" : "4RFV5TGB" + }, + { + "id" : 5, + "name" : "6YHN7UJM" + }, + { + "id" : 6, + "name" : "8IK,9OL." + }, + { + "id" : 7, + "name" : "0P;/-['" + }, + { + "id" : 8, + "name" : "ZXCVBNM" + }, + { + "id" : 9, + "name" : "ASDFGHJKL" + }, + { + "id" : 10, + "name" : "QWERTYUIOP" + }, + { + "id" : 11, + "name" : "QAZ" + }, + { + "id" : 12, + "name" : "WSX" + }, + { + "id" : 13, + "name" : "EDC" + }, + { + "id" : 14, + "name" : "REWQASDF" + }, + { + "id" : 15, + "name" : "TGBYHNUJMIKLOP" + }, + { + "id" : 16, + "name" : "TREWQYUIOP" + }, + { + "id" : 17, + "name" : "GFDSWSXC" + }, + { + "id" : 18, + "name" : "TTVCSWXZ" + }, + { + "id" : 19, + "name" : "S" + }, + { + "id" : 20, + "name" : "A" + }, + { + "id" : 21, + "name" : "X" + }, + { + "id" : 22, + "name" : "XHYJH" + }, + { + "id" : 23, + "name" : "QWETYINFR4DCC" + }, + { + "id" : 24, + "name" : "76THY8I9OKH4EDQ322FDVXCZDSRFSTECVCX0OSDPSAOD0SAWQ2" + }, + { + "id" : 25, + "name" : "SS" + }, + { + "id" : 26, + "name" : "A" + }, + { + "id" : 27, + "name" : "89IUKMYTYTYTR" + }, + { + "id" : 28, + "name" : "GJHGTRET43D" + }, + { + "id" : 29, + "name" : "1" + } + , + { + "id" : 30, + "name" : "2" + }, + { + "id" : 1, + "name" : "1QAZ" + }, + { + "id" : 2, + "name" : "2WSX" + }, + { + "id" : 3, + "name" : "3EDC" + }, + { + "id" : 4, + "name" : "4RFV5TGB" + }, + { + "id" : 5, + "name" : "6YHN7UJM" + }, + { + "id" : 6, + "name" : "8IK,9OL." + }, + { + "id" : 7, + "name" : "0P;/-['" + }, + { + "id" : 8, + "name" : "ZXCVBNM" + }, + { + "id" : 9, + "name" : "ASDFGHJKL" + }, + { + "id" : 10, + "name" : "QWERTYUIOP" + }, + { + "id" : 11, + "name" : "QAZ" + }, + { + "id" : 12, + "name" : "WSX" + }, + { + "id" : 13, + "name" : "EDC" + }, + { + "id" : 14, + "name" : "REWQASDF" + }, + { + "id" : 15, + "name" : "TGBYHNUJMIKLOP" + }, + { + "id" : 16, + "name" : "TREWQYUIOP" + }, + { + "id" : 17, + "name" : "GFDSWSXC" + }, + { + "id" : 18, + "name" : "TTVCSWXZ" + }, + { + "id" : 19, + "name" : "S" + }, + { + "id" : 20, + "name" : "A" + }, + { + "id" : 21, + "name" : "X" + }, + { + "id" : 22, + "name" : "XHYJH" + }, + { + "id" : 23, + "name" : "QWETYINFR4DCC" + }, + { + "id" : 24, + "name" : "76THY8I9OKH4EDQ322FDVXCZDSRFSTECVCX0OSDPSAOD0SAWQ2" + }, + { + "id" : 25, + "name" : "SS" + }, + { + "id" : 26, + "name" : "A" + }, + { + "id" : 27, + "name" : "89IUKMYTYTYTR" + }, + { + "id" : 28, + "name" : "GJHGTRET43D" + }, + { + "id" : 29, + "name" : "1" + }, + { + "id" : 30, + "name" : "2" + }, + { + "id" : 30, + "name" : "3" + }, + { + "id" : 30, + "name" : "4" + }, + { + "id" : 30, + "name" : "5" + }, + { + "id" : 30, + "name" : "6" + }, + { + "id" : 30, + "name" : "7" + }, + { + "id" : 30, + "name" : "8" + }, + { + "id" : 30, + "name" : "9" + }, + { + "id" : 30, + "name" : "A" + } +] diff --git a/Example/LWZComponents/RestrictedLayout/Provider/Model/RLModel.h b/Example/LWZComponents/RestrictedLayout/Provider/Model/RLModel.h new file mode 100644 index 0000000..7778e86 --- /dev/null +++ b/Example/LWZComponents/RestrictedLayout/Provider/Model/RLModel.h @@ -0,0 +1,16 @@ +// +// RLModel.h +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/12/25. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN +@interface RLModel : NSObject +@property (nonatomic) NSInteger id; +@property (nonatomic, strong, nullable) NSString *name; +@end +NS_ASSUME_NONNULL_END diff --git a/Example/LWZComponents/RestrictedLayout/Provider/Model/RLModel.m b/Example/LWZComponents/RestrictedLayout/Provider/Model/RLModel.m new file mode 100644 index 0000000..414631d --- /dev/null +++ b/Example/LWZComponents/RestrictedLayout/Provider/Model/RLModel.m @@ -0,0 +1,13 @@ +// +// RLModel.m +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/12/25. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "RLModel.h" + +@implementation RLModel + +@end diff --git a/Example/LWZComponents/RestrictedLayout/Provider/Model/RLModelProvider.h b/Example/LWZComponents/RestrictedLayout/Provider/Model/RLModelProvider.h new file mode 100644 index 0000000..cbf578d --- /dev/null +++ b/Example/LWZComponents/RestrictedLayout/Provider/Model/RLModelProvider.h @@ -0,0 +1,19 @@ +// +// RLModelProvider.h +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/12/25. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "RLModel.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface RLModelProvider : NSObject + ++ (void)requestDataWithComplete:(void(^)(NSArray *_Nullable list, NSError *_Nullable error))completionHandler; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Example/LWZComponents/RestrictedLayout/Provider/Model/RLModelProvider.m b/Example/LWZComponents/RestrictedLayout/Provider/Model/RLModelProvider.m new file mode 100644 index 0000000..dda1a90 --- /dev/null +++ b/Example/LWZComponents/RestrictedLayout/Provider/Model/RLModelProvider.m @@ -0,0 +1,28 @@ +// +// RLModelProvider.m +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/12/25. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "RLModelProvider.h" +#import "CommonDependencies.h" + +@implementation RLModelProvider ++ (void)requestDataWithComplete:(void(^)(NSArray *_Nullable list, NSError *_Nullable error))completionHandler { + dispatch_async(dispatch_get_global_queue(0, 0), ^{ + NSString *filePath = [NSBundle.mainBundle pathForResource:@"RLJsonData" ofType:@"json"]; + NSData *data = [NSData dataWithContentsOfFile:filePath]; + NSError *error = nil; + id jsonObj = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error]; + NSArray *list = nil; + if ( error == nil ) { + list = [NSArray yy_modelArrayWithClass:RLModel.class json:jsonObj]; + } + dispatch_async(dispatch_get_main_queue(), ^{ + if ( completionHandler != nil ) completionHandler(list, error); + }); + }); +} +@end diff --git a/Example/LWZComponents/RestrictedLayout/Provider/RLProvider.h b/Example/LWZComponents/RestrictedLayout/Provider/RLProvider.h new file mode 100644 index 0000000..864f914 --- /dev/null +++ b/Example/LWZComponents/RestrictedLayout/Provider/RLProvider.h @@ -0,0 +1,18 @@ +// +// RLProvider.h +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/12/25. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "LWZCollectionProvider.h" +@class RLModel; + +NS_ASSUME_NONNULL_BEGIN + +@interface RLProvider : LWZCollectionProvider +- (instancetype)initWithList:(NSArray * _Nullable)list; +@end + +NS_ASSUME_NONNULL_END diff --git a/Example/LWZComponents/RestrictedLayout/Provider/RLProvider.m b/Example/LWZComponents/RestrictedLayout/Provider/RLProvider.m new file mode 100644 index 0000000..799a011 --- /dev/null +++ b/Example/LWZComponents/RestrictedLayout/Provider/RLProvider.m @@ -0,0 +1,31 @@ +// +// RLProvider.m +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/12/25. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "RLProvider.h" +#import "RLTextItem.h" + +@implementation RLProvider +- (instancetype)initWithList:(NSArray * _Nullable)list { + self = [super init]; + if ( self ) { + + [self addSectionWithBlock:^(__kindof LWZCollectionSection * _Nonnull make) { + make.contentInsets = UIEdgeInsetsMake(12, 12, 12, 12); + make.minimumLineSpacing = 5; + make.minimumInteritemSpacing = 5; + + for ( RLModel *tagModel in list ) { + RLTextItem *item = [RLTextItem.alloc initWithModel:tagModel]; + [make addItem:item]; + } + }]; + + } + return self; +} +@end diff --git a/Example/LWZComponents/RestrictedLayout/RLViewController.h b/Example/LWZComponents/RestrictedLayout/RLViewController.h new file mode 100644 index 0000000..6552049 --- /dev/null +++ b/Example/LWZComponents/RestrictedLayout/RLViewController.h @@ -0,0 +1,17 @@ +// +// RLViewController.h +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/12/25. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface RLViewController : UIViewController + +@end + +NS_ASSUME_NONNULL_END diff --git a/Example/LWZComponents/RestrictedLayout/RLViewController.m b/Example/LWZComponents/RestrictedLayout/RLViewController.m new file mode 100644 index 0000000..b9c7db4 --- /dev/null +++ b/Example/LWZComponents/RestrictedLayout/RLViewController.m @@ -0,0 +1,58 @@ +// +// RLViewController.m +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/12/25. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "RLViewController.h" +#import "RLModelProvider.h" +#import "RLProvider.h" +#import "CommonDependencies.h" + +@interface RLViewController () +@property (nonatomic, strong) LWZCollectionView *collectionView; +@property (nonatomic, strong) LWZCollectionViewPresenter *presenter; +@property (nonatomic, strong) RLProvider *provider; +@end + +@implementation RLViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + [self _setupViews]; + // Do any additional setup after loading the view. +} + +- (void)_setupViews { + self.edgesForExtendedLayout = UIRectEdgeNone; + self.title = NSStringFromClass(self.class); + self.view.backgroundColor = UIColor.whiteColor; + + _presenter = [LWZCollectionViewPresenter.alloc init]; + + LWZCollectionViewLayout *layout = [LWZCollectionRestrictedLayout.alloc initWithScrollDirection:UICollectionViewScrollDirectionVertical delegate:_presenter]; + layout.sectionHeadersPinToVisibleBounds = YES; + _collectionView = [LWZCollectionView.alloc initWithFrame:CGRectZero collectionViewLayout:layout]; + _collectionView.dataSource = _presenter; + [self.view addSubview:_collectionView]; + [_collectionView mas_makeConstraints:^(MASConstraintMaker *make) { + make.edges.offset(0); + }]; + + __weak typeof(self) _self = self; + [RLModelProvider requestDataWithComplete:^(NSArray * _Nullable list, NSError * _Nullable error) { + __strong typeof(_self) self = _self; + if ( self == nil ) return; + if ( error != nil ) { + NSLog(@"(%d : %s) WLViewController.error: %@", __LINE__, sel_getName(_cmd), error); + return; + } + + self.provider = [RLProvider.alloc initWithList:list]; + self.presenter.provider = self.provider; + [self.collectionView reloadData]; + }]; +} +@end diff --git a/Example/LWZComponents/TempleteLayout/TLViewController1.h b/Example/LWZComponents/TempleteLayout/TLViewController1.h new file mode 100644 index 0000000..44f52af --- /dev/null +++ b/Example/LWZComponents/TempleteLayout/TLViewController1.h @@ -0,0 +1,17 @@ +// +// TLViewController1.h +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/12/13. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TLViewController1 : UIViewController + +@end + +NS_ASSUME_NONNULL_END diff --git a/Example/LWZComponents/TempleteLayout/TLViewController1.m b/Example/LWZComponents/TempleteLayout/TLViewController1.m new file mode 100644 index 0000000..279a25e --- /dev/null +++ b/Example/LWZComponents/TempleteLayout/TLViewController1.m @@ -0,0 +1,293 @@ +// +// TLViewController1.m +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/12/13. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "TLViewController1.h" +#import "CommonDependencies.h" + +@interface TLCollectionViewCell1 : UICollectionViewCell +@property (nonatomic, strong, readonly) UILabel *label; +@end + +@implementation TLCollectionViewCell1 +- (instancetype)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if ( self ) { + _label = [UILabel.alloc initWithFrame:CGRectZero]; + _label.textAlignment = NSTextAlignmentCenter; + _label.textColor = UIColor.whiteColor; + _label.font = [UIFont boldSystemFontOfSize:40]; + [self.contentView addSubview:_label]; + [_label mas_makeConstraints:^(MASConstraintMaker *make) { + make.edges.offset(0); + }]; + } + return self; +} +@end + +@interface TLViewController1 () +@property (nonatomic, strong) LWZCollectionViewLayout *templateLayout; +@property (nonatomic, strong) UICollectionView *collectionView; +@property (nonatomic) CGFloat testMargin; +@end + +@implementation TLViewController1 + +static NSString *cellReusedId = @"A"; +static NSArray *colors; + +- (void)viewDidLoad { + [super viewDidLoad]; + self.view.backgroundColor = UIColor.whiteColor; + self.edgesForExtendedLayout = UIRectEdgeNone; + self.title = NSStringFromClass(self.class); + + LWZCollectionViewLayout *layout = [LWZCollectionTemplateLayout.alloc initWithScrollDirection:UICollectionViewScrollDirectionVertical delegate:self]; + _collectionView = [UICollectionView.alloc initWithFrame:CGRectZero collectionViewLayout:layout]; + _collectionView.backgroundColor = UIColor.whiteColor; + _collectionView.dataSource = self; + _collectionView.delegate = self; + [_collectionView registerClass:TLCollectionViewCell1.class forCellWithReuseIdentifier:cellReusedId]; + [self.view addSubview:_collectionView]; + [_collectionView mas_makeConstraints:^(MASConstraintMaker *make) { + make.edges.offset(0); + }]; + colors = @[ + UIColor.blackColor, + UIColor.darkGrayColor, + UIColor.grayColor, + UIColor.redColor, + UIColor.greenColor, + UIColor.blueColor, + UIColor.cyanColor, + UIColor.yellowColor, + UIColor.magentaColor, + UIColor.orangeColor, + UIColor.purpleColor, + UIColor.brownColor, + ]; + + UIBarButtonItem *rightItem = [UIBarButtonItem.alloc initWithTitle:@"Test" style:UIBarButtonItemStyleDone target:self action:@selector(test)]; + self.navigationItem.rightBarButtonItem = rightItem; + // Do any additional setup after loading the view. +} + +- (void)test { + _testMargin += 12; + [_collectionView mas_remakeConstraints:^(MASConstraintMaker *make) { + make.edges.mas_equalTo(UIEdgeInsetsMake(_testMargin, _testMargin, _testMargin, _testMargin)); + }]; +} + +#pragma mark - UICollectionViewDataSource, UICollectionViewDelegate + +- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { + return 1; +} + +- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { + return 99; +} + +- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { + return [collectionView dequeueReusableCellWithReuseIdentifier:cellReusedId forIndexPath:indexPath]; +} + +- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(TLCollectionViewCell1 *)cell forItemAtIndexPath:(NSIndexPath *)indexPath { + cell.label.text = [NSString stringWithFormat:@"%ld", (long)indexPath.item]; + cell.contentView.backgroundColor = colors[indexPath.item % colors.count]; +} + + +#pragma mark - LWZCollectionTemplateLayoutDelegate + +- (NSArray *)layout:(__kindof LWZCollectionViewLayout *)layout layoutTemplateContainerGroupsInSection:(NSInteger)section { + + return [LWZCollectionLayoutTemplate build:^(LWZCollectionTemplateBuilder * _Nonnull make) { + /** + Container 1 + | + +---|----------------------------------+ <-- Group + | v | + | +--------------+ +--------------+ <----- Container 2 + | | | | | | + | | +--------+ | | +--------+ <-------- Item + | | | | | | | | | | + | | | 1 | | | | | | | + | | +--------+ | | | | | | + | | | | | | | | + | | +--------+ | | | | | | + | | | | | | | | | | + | | | 2 | | | | 3 | | | + | | +--------+ | | +--------+ | | + | | | | | | + | +--------------+ +--------------+ | + | | + +--------------------------------------+ + */ + make.addGroup(^(LWZCollectionTemplateGroupBuilder * _Nonnull group) { + group.width.fractionalWidth(1.0); + group.height.fractionalWidth(1.0); + + // container 1 + group.addContainer(^(LWZCollectionTemplateContainerBuilder * _Nonnull container) { + container.width.fractionalWidth(0.5); + container.height.fractionalHeight(1.0); + + // items + for ( NSInteger i = 0 ; i < 2 ; ++ i ) { + container.addItem(^(LWZCollectionTemplateItemBuilder * _Nonnull item) { + item.width.fractionalWidth(1.0); + item.height.fractionalHeight(0.5); + }); + } + }); + + // container 2 + group.addContainer(^(LWZCollectionTemplateContainerBuilder * _Nonnull container) { + container.width.fractionalWidth(0.5); + container.height.fractionalHeight(1.0); + + container.addItem(^(LWZCollectionTemplateItemBuilder * _Nonnull item) { + item.width.fractionalWidth(1.0); + item.height.fractionalHeight(1.0); + }); + }); + }); + + /** + +--------------------------------------+ + | | + | +--------------+ +--------------+ | + | | | | | | + | | +--------+ | | +--------+ | | + | | | | | | | | | | + | | | 1 | | | | 3 | | | + | | +--------+ | | +--------+ | | + | | | | | | + | | +--------+ | | +--------+ | | + | | | | | | | | | | + | | | 2 | | | | 4 | | | + | | +--------+ | | +--------+ | | + | | | | | | + | +--------------+ +--------------+ | + | | + +--------------------------------------+ + */ + + make.addGroup(^(LWZCollectionTemplateGroupBuilder * _Nonnull group) { + group.width.fractionalWidth(1.0); + group.height.fractionalWidth(1.0); + + for ( NSInteger i = 0 ; i < 2 ; ++ i ) { + // container + group.addContainer(^(LWZCollectionTemplateContainerBuilder * _Nonnull container) { + container.width.fractionalWidth(0.5); + container.height.fractionalHeight(1.0); + + // items + for ( NSInteger i = 0 ; i < 2 ; ++ i ) { + container.addItem(^(LWZCollectionTemplateItemBuilder * _Nonnull item) { + item.width.fractionalWidth(1); + item.height.fractionalHeight(0.5); + }); + } + }); + } + }); + + /** + +--------------------+ + | | + | +--------------+ | + | | | | + | | +--------+ | | + | | | | | | + | | | 1 | | | + | | +--------+ | | + | | | | + | | +--------+ | | + | | | | | | + | | | 2 | | | + | | +--------+ | | + | | | | + | | +--------+ | | + | | | | | | + | | | 3 | | | + | | +--------+ | | + | | | | + | +--------------+ | + | | + +--------------------+ + */ + make.addGroup(^(LWZCollectionTemplateGroupBuilder * _Nonnull group) { + group.width.fractionalWidth(1.0); + group.height.fractionalWidth(1.0); + + group.addContainer(^(LWZCollectionTemplateContainerBuilder * _Nonnull container) { + container.width.fractionalWidth(1.0); + container.height.fractionalHeight(1.0); + + for ( NSInteger i = 0 ; i < 3 ; ++ i ) { + container.addItem(^(LWZCollectionTemplateItemBuilder * _Nonnull item) { + item.width.fractionalWidth(1); + item.height.fractionalHeight(1/3.0); + }); + } + }); + }); + }]; +} + +- (UIEdgeInsets)layout:(__kindof LWZCollectionViewLayout *)layout edgeSpacingsForSectionAtIndex:(NSInteger)section { + return UIEdgeInsetsZero; +} + +- (UIEdgeInsets)layout:(__kindof LWZCollectionViewLayout *)layout contentInsetsForSectionAtIndex:(NSInteger)section { + return UIEdgeInsetsMake(12, 12, 12, 12); +} + +//- (BOOL)layout:(__kindof LWZCollectionViewLayout *)layout canPinToVisibleBoundsForHeaderInSection:(NSInteger)section; +//- (UIEdgeInsets)layout:(__kindof LWZCollectionViewLayout *)layout adjustedPinnedInsetsForSectionAtIndex:(NSInteger)section; + +//- (CGSize)layout:(__kindof LWZCollectionViewLayout *)layout layoutSizeToFit:(CGSize)fittingSize forHeaderInSection:(NSInteger)section scrollDirection:(UICollectionViewScrollDirection)scrollDirection { +// +//} +//- (CGSize)layout:(__kindof LWZCollectionViewLayout *)layout layoutSizeToFit:(CGSize)fittingSize forItemAtIndexPath:(NSIndexPath *)indexPath scrollDirection:(UICollectionViewScrollDirection)scrollDirection; +//- (CGSize)layout:(__kindof LWZCollectionViewLayout *)layout layoutSizeToFit:(CGSize)fittingSize forFooterInSection:(NSInteger)section scrollDirection:(UICollectionViewScrollDirection)scrollDirection; + +- (CGFloat)layout:(__kindof LWZCollectionViewLayout *)layout minimumLineSpacingForSectionAtIndex:(NSInteger)section { + return 12; +} +- (CGFloat)layout:(__kindof LWZCollectionViewLayout *)layout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section { + return 12; +} +//- (nullable NSString *)layout:(__kindof LWZCollectionViewLayout *)layout elementKindForSectionDecorationAtIndexPath:(NSIndexPath *)indexPath; +//- (nullable NSString *)layout:(__kindof LWZCollectionViewLayout *)layout elementKindForHeaderDecorationAtIndexPath:(NSIndexPath *)indexPath; +//- (nullable NSString *)layout:(__kindof LWZCollectionViewLayout *)layout elementKindForItemDecorationAtIndexPath:(NSIndexPath *)indexPath; +//- (nullable NSString *)layout:(__kindof LWZCollectionViewLayout *)layout elementKindForFooterDecorationAtIndexPath:(NSIndexPath *)indexPath; +// +//- (nullable id)layout:(__kindof LWZCollectionViewLayout *)layout userInfoForSectionDecorationAtIndexPath:(NSIndexPath *)indexPath; +//- (nullable id)layout:(__kindof LWZCollectionViewLayout *)layout userInfoForHeaderDecorationAtIndexPath:(NSIndexPath *)indexPath; +//- (nullable id)layout:(__kindof LWZCollectionViewLayout *)layout userInfoForItemDecorationAtIndexPath:(NSIndexPath *)indexPath; +//- (nullable id)layout:(__kindof LWZCollectionViewLayout *)layout userInfoForFooterDecorationAtIndexPath:(NSIndexPath *)indexPath; +// +//- (CGRect)layout:(__kindof LWZCollectionViewLayout *)layout relativeRectToFit:(CGRect)rect forSectionDecorationAtIndexPath:(NSIndexPath *)indexPath; +//- (CGRect)layout:(__kindof LWZCollectionViewLayout *)layout relativeRectToFit:(CGRect)rect forHeaderDecorationAtIndexPath:(NSIndexPath *)indexPath; +//- (CGRect)layout:(__kindof LWZCollectionViewLayout *)layout relativeRectToFit:(CGRect)rect forItemDecorationAtIndexPath:(NSIndexPath *)indexPath; +//- (CGRect)layout:(__kindof LWZCollectionViewLayout *)layout relativeRectToFit:(CGRect)rect forFooterDecorationAtIndexPath:(NSIndexPath *)indexPath; +// +//- (NSInteger)layout:(__kindof LWZCollectionViewLayout *)layout zIndexForHeaderInSection:(NSInteger)section; +//- (NSInteger)layout:(__kindof LWZCollectionViewLayout *)layout zIndexForItemAtIndexPath:(NSIndexPath *)indexPath; +//- (NSInteger)layout:(__kindof LWZCollectionViewLayout *)layout zIndexForFooterInSection:(NSInteger)section; +// +//- (NSInteger)layout:(__kindof LWZCollectionViewLayout *)layout zIndexForSectionDecorationAtIndexPath:(NSIndexPath *)indexPath; +//- (NSInteger)layout:(__kindof LWZCollectionViewLayout *)layout zIndexForHeaderDecorationAtIndexPath:(NSIndexPath *)indexPath; +//- (NSInteger)layout:(__kindof LWZCollectionViewLayout *)layout zIndexForItemDecorationAtIndexPath:(NSIndexPath *)indexPath; +//- (NSInteger)layout:(__kindof LWZCollectionViewLayout *)layout zIndexForFooterDecorationAtIndexPath:(NSIndexPath *)indexPath; +@end diff --git a/Example/LWZComponents/WaterfallFlowLayout/Provider/Item/WFLCollectionItem.h b/Example/LWZComponents/WaterfallFlowLayout/Provider/Item/WFLCollectionItem.h new file mode 100644 index 0000000..02cbe02 --- /dev/null +++ b/Example/LWZComponents/WaterfallFlowLayout/Provider/Item/WFLCollectionItem.h @@ -0,0 +1,18 @@ +// +// WFLCollectionItem.h +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/11/26. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "LWZCollectionItem.h" +#import "WFLModel.h" + +NS_ASSUME_NONNULL_BEGIN +@interface WFLCollectionItem : LWZCollectionItem + +- (instancetype)initWithModel:(WFLModel *)model; + +@end +NS_ASSUME_NONNULL_END diff --git a/Example/LWZComponents/WaterfallFlowLayout/Provider/Item/WFLCollectionItem.m b/Example/LWZComponents/WaterfallFlowLayout/Provider/Item/WFLCollectionItem.m new file mode 100644 index 0000000..d1d2f07 --- /dev/null +++ b/Example/LWZComponents/WaterfallFlowLayout/Provider/Item/WFLCollectionItem.m @@ -0,0 +1,54 @@ +// +// WFLCollectionItem.m +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/11/26. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "WFLCollectionItem.h" +#import "WFLCollectionViewCell.h" +#import "CommonDependencies.h" +#import "CommonTextLayoutSize.h" + +@interface WFLCollectionItem () +@property (nonatomic, strong, nullable) UIImage *cover; +@property (nonatomic, strong, nullable) NSAttributedString *name; +@end + +@implementation WFLCollectionItem +- (instancetype)initWithModel:(WFLModel *)model { + self = [super init]; + if ( self ) { + _cover = [UIImage imageNamed:model.cover]; + _name = [NSAttributedString sj_UIKitText:^(id _Nonnull make) { + make.append(model.name); + make.textColor(UIColor.blackColor); + make.font([UIFont systemFontOfSize:14]); + }]; + } + return self; +} + +- (Class)cellClass { + return WFLCollectionViewCell.class; +} + +- (NSBundle *)cellNibBundle { + return NSBundle.mainBundle; +} + +- (CGSize)layoutSizeThatFits:(CGSize)size inSection:(LWZCollectionSection *)section atIndexPath:(NSIndexPath *)indexPath scrollDirection:(UICollectionViewScrollDirection)scrollDirection { + CGFloat w = size.width; + CGSize coverSize = _cover.size; + CGFloat coverH = w * coverSize.height / coverSize.height; + CGFloat nameH = 8 + [_name lwz_layoutSizeThatFits:CGSizeMake(size.width - 5 - 5, size.height) limitedToNumberOfLines:0].height + 12; + CGFloat h = coverH + nameH; + return CGSizeMake(w, h); +} + +- (void)bindCell:(WFLCollectionViewCell *)cell atIndexPath:(NSIndexPath *)indexPath { + cell.dataSource = self; +} + +@end diff --git a/Example/LWZComponents/WaterfallFlowLayout/Provider/Item/WFLCollectionViewCell.h b/Example/LWZComponents/WaterfallFlowLayout/Provider/Item/WFLCollectionViewCell.h new file mode 100644 index 0000000..75a6490 --- /dev/null +++ b/Example/LWZComponents/WaterfallFlowLayout/Provider/Item/WFLCollectionViewCell.h @@ -0,0 +1,21 @@ +// +// WFLCollectionViewCell.h +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/11/26. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import +@protocol WFLCollectionViewCellDataSource; + +NS_ASSUME_NONNULL_BEGIN +@interface WFLCollectionViewCell : UICollectionViewCell +@property (nonatomic, weak, nullable) id dataSource; +@end + +@protocol WFLCollectionViewCellDataSource +@property (nonatomic, strong, readonly, nullable) UIImage *cover; +@property (nonatomic, strong, readonly, nullable) NSAttributedString *name; +@end +NS_ASSUME_NONNULL_END diff --git a/Example/LWZComponents/WaterfallFlowLayout/Provider/Item/WFLCollectionViewCell.m b/Example/LWZComponents/WaterfallFlowLayout/Provider/Item/WFLCollectionViewCell.m new file mode 100644 index 0000000..98ae11f --- /dev/null +++ b/Example/LWZComponents/WaterfallFlowLayout/Provider/Item/WFLCollectionViewCell.m @@ -0,0 +1,36 @@ +// +// WFLCollectionViewCell.m +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/11/26. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "WFLCollectionViewCell.h" + +@interface WFLCollectionViewCell () +@property (weak, nonatomic) IBOutlet UIImageView *coverImageView; +@property (weak, nonatomic) IBOutlet UILabel *nameLabel; +@end + +@implementation WFLCollectionViewCell + +- (void)awakeFromNib { + [super awakeFromNib]; + + self.contentView.backgroundColor = [UIColor colorWithRed:245 / 255.0 green:245 / 255.0 blue:245 / 255.0 alpha:1]; + self.contentView.layer.cornerRadius = 5; + self.contentView.clipsToBounds = YES; + // Initialization code +} + + +- (void)setDataSource:(nullable id)dataSource { + if ( dataSource != _dataSource ) { + _dataSource = dataSource; + + _coverImageView.image = dataSource.cover; + _nameLabel.attributedText = dataSource.name; + } +} +@end diff --git a/Example/LWZComponents/WaterfallFlowLayout/Provider/Item/WFLCollectionViewCell.xib b/Example/LWZComponents/WaterfallFlowLayout/Provider/Item/WFLCollectionViewCell.xib new file mode 100644 index 0000000..76f880a --- /dev/null +++ b/Example/LWZComponents/WaterfallFlowLayout/Provider/Item/WFLCollectionViewCell.xib @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Example/LWZComponents/WaterfallFlowLayout/Provider/Model/WFLJsonData.json b/Example/LWZComponents/WaterfallFlowLayout/Provider/Model/WFLJsonData.json new file mode 100644 index 0000000..a32821b --- /dev/null +++ b/Example/LWZComponents/WaterfallFlowLayout/Provider/Model/WFLJsonData.json @@ -0,0 +1,132 @@ +[ + { + "name" : "没有一个冬天不可逾越,没有一个春天不会来临。——《南方周末》", + "cover" : "kwi_9" + }, + { + "name" : "有些鸟是注定不会被关在牢笼里的,它们的每一片羽毛都闪耀着自由的光辉。——《肖申克的救赎》", + "cover" : "kwi_8" + }, + { + "name" : "当我们爱脚下的泥泞时,说明我们已经拥抱了一种精神。——迟子建", + "cover" : "kwi_7" + }, + { + "name" : "大厦之成,非一木之材也;大海之润,非一流之归也。——《东周列国志》", + "cover" : "kwi_6" + }, + { + "name" : "履不必同,期于适足;治不必同,期于利民。——魏源", + "cover" : "kwi_5" + }, + { + "name" : "无穷的远方,无数的人们,都和我有关。——鲁迅", + "cover" : "kwi_4" + }, + { + "name" : "大鹏之动,非一羽之轻也;骐骥之速,非一足之力也。——《潜夫论·释难》", + "cover" : "kwi_3" + }, + { + "name" : "有时候仍不免呐喊几声,聊以慰藉那在寂寞里奔驰的猛士,使他不惮于前驱。——《呐喊》", + "cover" : "kwi_2" + }, + { + "name" : "唾手可得的果实往往并不甜美,稳操胜券的追求通常局限于眼前。", + "cover" : "kwi_1" + }, + { + "name" : "当华美的叶片落尽,生命的脉络才历历可见。——聂鲁达", + "cover" : "kwi_9" + }, + { + "name" : "历经万般红尘劫,犹如凉风轻拂面。——林清玄", + "cover" : "kwi_8" + }, + { + "name" : "枫叶经霜艳,梅花透雪香。——邓石如", + "cover" : "kwi_7" + }, + { + "name" : "历史是一堆灰烬,但灰烬深处有余温。——黑格尔《美学》", + "cover" : "kwi_6" + }, + { + "name" : "清谈可以饱,梦想接无由。——韩愈", + "cover" : "kwi_5" + }, + { + "name" : "长江悲已滞,万里念将归。——王勃《山中》", + "cover" : "kwi_4" + }, + { + "name" : "冀以尘雾之微补益山海,荧烛末光增辉日月。——曹植《求自试表》", + "cover" : "kwi_3" + }, + { + "name" : "没有一个冬天不可逾越,没有一个春天不会来临。——《南方周末》", + "cover" : "kwi_9" + }, + { + "name" : "有些鸟是注定不会被关在牢笼里的,它们的每一片羽毛都闪耀着自由的光辉。——《肖申克的救赎》", + "cover" : "kwi_8" + }, + { + "name" : "当我们爱脚下的泥泞时,说明我们已经拥抱了一种精神。——迟子建", + "cover" : "kwi_7" + }, + { + "name" : "大厦之成,非一木之材也;大海之润,非一流之归也。——《东周列国志》", + "cover" : "kwi_6" + }, + { + "name" : "履不必同,期于适足;治不必同,期于利民。——魏源", + "cover" : "kwi_5" + }, + { + "name" : "无穷的远方,无数的人们,都和我有关。——鲁迅", + "cover" : "kwi_4" + }, + { + "name" : "大鹏之动,非一羽之轻也;骐骥之速,非一足之力也。——《潜夫论·释难》", + "cover" : "kwi_3" + }, + { + "name" : "有时候仍不免呐喊几声,聊以慰藉那在寂寞里奔驰的猛士,使他不惮于前驱。——《呐喊》", + "cover" : "kwi_2" + }, + { + "name" : "唾手可得的果实往往并不甜美,稳操胜券的追求通常局限于眼前。", + "cover" : "kwi_1" + }, + { + "name" : "当华美的叶片落尽,生命的脉络才历历可见。——聂鲁达", + "cover" : "kwi_9" + }, + { + "name" : "历经万般红尘劫,犹如凉风轻拂面。——林清玄", + "cover" : "kwi_8" + }, + { + "name" : "枫叶经霜艳,梅花透雪香。——邓石如", + "cover" : "kwi_7" + }, + { + "name" : "历史是一堆灰烬,但灰烬深处有余温。——黑格尔《美学》", + "cover" : "kwi_6" + }, + { + "name" : "清谈可以饱,梦想接无由。——韩愈", + "cover" : "kwi_5" + }, + { + "name" : "长江悲已滞,万里念将归。——王勃《山中》", + "cover" : "kwi_4" + }, + { + "name" : "冀以尘雾之微补益山海,荧烛末光增辉日月。——曹植《求自试表》", + "cover" : "kwi_3" + } + +] + diff --git a/Example/LWZComponents/WaterfallFlowLayout/Provider/Model/WFLModel.h b/Example/LWZComponents/WaterfallFlowLayout/Provider/Model/WFLModel.h new file mode 100644 index 0000000..a11bc26 --- /dev/null +++ b/Example/LWZComponents/WaterfallFlowLayout/Provider/Model/WFLModel.h @@ -0,0 +1,16 @@ +// +// WFLModel.h +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/11/26. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN +@interface WFLModel : NSObject +@property (nonatomic, strong) NSString *name; +@property (nonatomic, strong) NSString *cover; +@end +NS_ASSUME_NONNULL_END diff --git a/Example/LWZComponents/WaterfallFlowLayout/Provider/Model/WFLModel.m b/Example/LWZComponents/WaterfallFlowLayout/Provider/Model/WFLModel.m new file mode 100644 index 0000000..95250ae --- /dev/null +++ b/Example/LWZComponents/WaterfallFlowLayout/Provider/Model/WFLModel.m @@ -0,0 +1,13 @@ +// +// WFLModel.m +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/11/26. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "WFLModel.h" + +@implementation WFLModel + +@end diff --git a/Example/LWZComponents/WaterfallFlowLayout/Provider/Model/WFLModelProvider.h b/Example/LWZComponents/WaterfallFlowLayout/Provider/Model/WFLModelProvider.h new file mode 100644 index 0000000..01cb491 --- /dev/null +++ b/Example/LWZComponents/WaterfallFlowLayout/Provider/Model/WFLModelProvider.h @@ -0,0 +1,18 @@ +// +// WFLModelProvider.h +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/11/26. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import +#import "WFLModel.h" + +NS_ASSUME_NONNULL_BEGIN +@interface WFLModelProvider : NSObject + ++ (void)requestDataWithComplete:(void(^)(NSArray *_Nullable list, NSError *_Nullable error))completionHandler; + +@end +NS_ASSUME_NONNULL_END diff --git a/Example/LWZComponents/WaterfallFlowLayout/Provider/Model/WFLModelProvider.m b/Example/LWZComponents/WaterfallFlowLayout/Provider/Model/WFLModelProvider.m new file mode 100644 index 0000000..ef53b4f --- /dev/null +++ b/Example/LWZComponents/WaterfallFlowLayout/Provider/Model/WFLModelProvider.m @@ -0,0 +1,30 @@ +// +// WFLModelProvider.m +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/11/26. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "WFLModelProvider.h" +#import "CommonDependencies.h" + +@implementation WFLModelProvider + ++ (void)requestDataWithComplete:(void(^)(NSArray *_Nullable list, NSError *_Nullable error))completionHandler { + dispatch_async(dispatch_get_global_queue(0, 0), ^{ + NSString *filePath = [NSBundle.mainBundle pathForResource:@"WFLJsonData" ofType:@"json"]; + NSData *data = [NSData dataWithContentsOfFile:filePath]; + NSError *error = nil; + id jsonObj = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error]; + NSArray *list = nil; + if ( error == nil ) { + list = [NSArray yy_modelArrayWithClass:WFLModel.class json:jsonObj]; + } + dispatch_async(dispatch_get_main_queue(), ^{ + if ( completionHandler != nil ) completionHandler(list, error); + }); + }); +} + +@end diff --git a/Example/LWZComponents/WaterfallFlowLayout/Provider/WFLProvider.h b/Example/LWZComponents/WaterfallFlowLayout/Provider/WFLProvider.h new file mode 100644 index 0000000..1b5cf32 --- /dev/null +++ b/Example/LWZComponents/WaterfallFlowLayout/Provider/WFLProvider.h @@ -0,0 +1,18 @@ +// +// WFLProvider.h +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/11/29. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "LWZCollectionProvider.h" +#import "WFLModel.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface WFLProvider : LWZCollectionProvider +- (instancetype)initWithList:(NSArray *)list; +@end + +NS_ASSUME_NONNULL_END diff --git a/Example/LWZComponents/WaterfallFlowLayout/Provider/WFLProvider.m b/Example/LWZComponents/WaterfallFlowLayout/Provider/WFLProvider.m new file mode 100644 index 0000000..5c396e4 --- /dev/null +++ b/Example/LWZComponents/WaterfallFlowLayout/Provider/WFLProvider.m @@ -0,0 +1,30 @@ +// +// WFLProvider.m +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/11/29. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "WFLProvider.h" +#import "WFLCollectionItem.h" + +@implementation WFLProvider +- (instancetype)initWithList:(NSArray *)list { + self = [super init]; + if ( self ) { + [self addSectionWithBlock:^(__kindof LWZCollectionSection * _Nonnull make) { + make.minimumInteritemSpacing = 8; + make.minimumLineSpacing = 8; + make.contentInsets = UIEdgeInsetsMake(12, 12, 12, 12); + make.numberOfArrangedItemsPerLine = 2; + + for ( WFLModel *model in list ) { + WFLCollectionItem *item = [WFLCollectionItem.alloc initWithModel:model]; + [make addItem:item]; + } + }]; + } + return self; +} +@end diff --git a/Example/LWZComponents/WaterfallFlowLayout/WFLViewController.h b/Example/LWZComponents/WaterfallFlowLayout/WFLViewController.h new file mode 100644 index 0000000..0d58022 --- /dev/null +++ b/Example/LWZComponents/WaterfallFlowLayout/WFLViewController.h @@ -0,0 +1,17 @@ +// +// WFLViewController.h +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/11/26. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface WFLViewController : UIViewController + +@end + +NS_ASSUME_NONNULL_END diff --git a/Example/LWZComponents/WaterfallFlowLayout/WFLViewController.m b/Example/LWZComponents/WaterfallFlowLayout/WFLViewController.m new file mode 100644 index 0000000..19a9a5c --- /dev/null +++ b/Example/LWZComponents/WaterfallFlowLayout/WFLViewController.m @@ -0,0 +1,59 @@ +// +// WFLViewController.m +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/11/26. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "WFLViewController.h" +#import "WFLProvider.h" +#import "WFLModelProvider.h" +#import "CommonDependencies.h" + +@interface WFLViewController () +@property (nonatomic, strong) LWZCollectionView *collectionView; +@property (nonatomic, strong) LWZCollectionViewPresenter *presenter; +@property (nonatomic, strong) WFLProvider *provider; +@end + +@implementation WFLViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + [self _setupViews]; + // Do any additional setup after loading the view. +} + +- (void)_setupViews { + self.edgesForExtendedLayout = UIRectEdgeNone; + self.title = NSStringFromClass(self.class); + self.view.backgroundColor = UIColor.whiteColor; + + _presenter = [LWZCollectionViewPresenter.alloc init]; + + LWZCollectionWaterfallFlowLayout *layout = [LWZCollectionWaterfallFlowLayout.alloc initWithScrollDirection:UICollectionViewScrollDirectionVertical delegate:_presenter]; + _collectionView = [LWZCollectionView.alloc initWithFrame:CGRectZero collectionViewLayout:layout]; + _collectionView.dataSource = _presenter; + [self.view addSubview:_collectionView]; + [_collectionView mas_makeConstraints:^(MASConstraintMaker *make) { + make.edges.offset(0); + }]; + + __weak typeof(self) _self = self; + [WFLModelProvider requestDataWithComplete:^(NSArray * _Nullable list, NSError * _Nullable error) { + __strong typeof(_self) self = _self; + if ( self == nil ) return; + if ( error != nil ) { + NSLog(@"(%d : %s) WFLViewController.error: %@", __LINE__, sel_getName(_cmd), error); + return; + } + + self.provider = [WFLProvider.alloc initWithList:list]; + self.presenter.provider = self.provider; + [self.collectionView reloadData]; + }]; + +} + +@end diff --git a/Example/LWZComponents/WeightLayout/Provider/Header/WLCollectionSectionHeaderFooter.h b/Example/LWZComponents/WeightLayout/Provider/Header/WLCollectionSectionHeaderFooter.h new file mode 100644 index 0000000..5cb6a7a --- /dev/null +++ b/Example/LWZComponents/WeightLayout/Provider/Header/WLCollectionSectionHeaderFooter.h @@ -0,0 +1,18 @@ +// +// WLCollectionSectionHeaderFooter.h +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/11/26. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "LWZCollectionSectionHeaderFooter.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface WLCollectionSectionHeaderFooter : LWZCollectionSectionHeaderFooter +- (instancetype)initWithTitle:(NSAttributedString *)title; +@property (nonatomic) UIEdgeInsets contentInsets; +@end + +NS_ASSUME_NONNULL_END diff --git a/Example/LWZComponents/WeightLayout/Provider/Header/WLCollectionSectionHeaderFooter.m b/Example/LWZComponents/WeightLayout/Provider/Header/WLCollectionSectionHeaderFooter.m new file mode 100644 index 0000000..b865cfe --- /dev/null +++ b/Example/LWZComponents/WeightLayout/Provider/Header/WLCollectionSectionHeaderFooter.m @@ -0,0 +1,41 @@ +// +// WLCollectionSectionHeaderFooter.m +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/11/26. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "WLCollectionSectionHeaderFooter.h" +#import "WLCollectionSectionHeaderFooterView.h" +#import "CommonTextLayoutSize.h" + +@interface WLCollectionSectionHeaderFooter () +@property (nonatomic, strong, nullable) NSAttributedString *title; +@end + +@implementation WLCollectionSectionHeaderFooter +- (instancetype)initWithTitle:(NSAttributedString *)title { + self = [super init]; + if ( self ) { + _title = title; + } + return self; +} + +- (Class)viewClass { + return WLCollectionSectionHeaderFooterView.class; +} + +- (NSBundle *)viewNibBundle { + return NSBundle.mainBundle; +} + +- (CGSize)layoutSizeThatFits:(CGSize)size inSection:(__kindof LWZCollectionSection *)section atIndex:(NSInteger)index scrollDirection:(UICollectionViewScrollDirection)scrollDirection { + return CGSizeMake(size.width, _contentInsets.top + [_title lwz_textContainerWithLayoutSizeThatFits:size limitedToNumberOfLines:0 fixesSingleLineSpacing:YES].layoutSize.height + _contentInsets.bottom); +} + +- (void)bindView:(WLCollectionSectionHeaderFooterView *)view inSection:(NSInteger)section { + view.dataSource = self; +} +@end diff --git a/Example/LWZComponents/WeightLayout/Provider/Header/WLCollectionSectionHeaderFooterView.h b/Example/LWZComponents/WeightLayout/Provider/Header/WLCollectionSectionHeaderFooterView.h new file mode 100644 index 0000000..5343af3 --- /dev/null +++ b/Example/LWZComponents/WeightLayout/Provider/Header/WLCollectionSectionHeaderFooterView.h @@ -0,0 +1,21 @@ +// +// WLCollectionSectionHeaderFooterView.h +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/11/26. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import +@protocol WLCollectionSectionHeaderFooterViewDataSource; + +NS_ASSUME_NONNULL_BEGIN +@interface WLCollectionSectionHeaderFooterView : UICollectionReusableView +@property (nonatomic, weak, nullable) id dataSource; +@end + +@protocol WLCollectionSectionHeaderFooterViewDataSource +@property (nonatomic, strong, readonly, nullable) NSAttributedString *title; +@property (nonatomic, readonly) UIEdgeInsets contentInsets; +@end +NS_ASSUME_NONNULL_END diff --git a/Example/LWZComponents/WeightLayout/Provider/Header/WLCollectionSectionHeaderFooterView.m b/Example/LWZComponents/WeightLayout/Provider/Header/WLCollectionSectionHeaderFooterView.m new file mode 100644 index 0000000..dfe899e --- /dev/null +++ b/Example/LWZComponents/WeightLayout/Provider/Header/WLCollectionSectionHeaderFooterView.m @@ -0,0 +1,34 @@ +// +// WLCollectionSectionHeaderFooterView.m +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/11/26. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "WLCollectionSectionHeaderFooterView.h" + +@interface WLCollectionSectionHeaderFooterView () +@property (weak, nonatomic) IBOutlet UILabel *titleLabel; +@end + +@implementation WLCollectionSectionHeaderFooterView + +- (void)layoutSubviews { + [super layoutSubviews]; + + [self updateTitleLabelLayout]; +} + +- (void)updateTitleLabelLayout { + _titleLabel.frame = UIEdgeInsetsInsetRect(self.bounds, _dataSource.contentInsets); +} + +- (void)setDataSource:(nullable id)dataSource { + if ( _dataSource != dataSource ) { + _dataSource = dataSource; + _titleLabel.attributedText = dataSource.title; + [self updateTitleLabelLayout]; + } +} +@end diff --git a/Example/LWZComponents/WeightLayout/Provider/Header/WLCollectionSectionHeaderFooterView.xib b/Example/LWZComponents/WeightLayout/Provider/Header/WLCollectionSectionHeaderFooterView.xib new file mode 100644 index 0000000..70b3375 --- /dev/null +++ b/Example/LWZComponents/WeightLayout/Provider/Header/WLCollectionSectionHeaderFooterView.xib @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Example/LWZComponents/WeightLayout/Provider/Item/WLPostCollectionItem.h b/Example/LWZComponents/WeightLayout/Provider/Item/WLPostCollectionItem.h new file mode 100644 index 0000000..a1e9a41 --- /dev/null +++ b/Example/LWZComponents/WeightLayout/Provider/Item/WLPostCollectionItem.h @@ -0,0 +1,24 @@ +// +// WLPostCollectionItem.h +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/11/26. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "LWZCollectionItem.h" +#import "WLModel.h" + +NS_ASSUME_NONNULL_BEGIN +@interface WLPostCollectionItem : LWZCollectionItem + ++ (instancetype)itemWithModel:(WLPostModel *)post; + +@property (nonatomic) BOOL isLiked; +@property (nonatomic) NSInteger shareCount; + +@property (nonatomic, copy, nullable) void(^likingItemTapHandler)(WLPostCollectionItem *item); +@property (nonatomic, copy, nullable) void(^shareItemTapHandler)(WLPostCollectionItem *item); +@property (nonatomic, copy, nullable) void(^commentItemTapHandler)(WLPostCollectionItem *item); +@end +NS_ASSUME_NONNULL_END diff --git a/Example/LWZComponents/WeightLayout/Provider/Item/WLPostCollectionItem.m b/Example/LWZComponents/WeightLayout/Provider/Item/WLPostCollectionItem.m new file mode 100644 index 0000000..94ccc4f --- /dev/null +++ b/Example/LWZComponents/WeightLayout/Provider/Item/WLPostCollectionItem.m @@ -0,0 +1,299 @@ +// +// WLPostCollectionItem.m +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/11/26. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "WLPostCollectionItem.h" +#import "CommonDependencies.h" +#import "WLPostImageLayoutCollectionViewCell.h" +#import "WLPostVideoLayoutCollectionViewCell.h" +#import "CommonTextLayoutSize.h" + +@interface WLPostVideoLayoutCollectionItem : WLPostCollectionItem +@end + + +@interface WLPostImageLayoutCollectionItem : WLPostCollectionItem +@end + +@interface WLPostCollectionItem () +@property (nonatomic, strong, nullable) UIImage *avatar; +@property (nonatomic, strong, nullable) NSAttributedString *name; +@property (nonatomic, strong, nullable) NSAttributedString *content; +@property (nonatomic, strong, nullable) NSAttributedString *likingNum; +@property (nonatomic, strong, nullable) NSAttributedString *commentNum; +@property (nonatomic, strong, nullable) NSAttributedString *shareNum; +- (instancetype)initWithModel:(WLPostModel *)post; +- (void)reloadLikingNum; +- (void)reloadShareNum; +@end + +@implementation WLPostCollectionItem { + NSInteger _likingCount; +} ++ (instancetype)itemWithModel:(WLPostModel *)post { + return post.video != nil ? [WLPostVideoLayoutCollectionItem.alloc initWithModel:post] : [WLPostImageLayoutCollectionItem.alloc initWithModel:post]; +} + +- (instancetype)initWithModel:(WLPostModel *)post { + self = [super init]; + if ( self ) { + _shareCount = post.shareCount; + _likingCount = post.likingCount; + _isLiked = post.isLiked; + _avatar = [UIImage imageNamed:post.user.avatar]; + _name = [NSAttributedString sj_UIKitText:^(id _Nonnull make) { + make.append(post.user.name); + make.font([UIFont systemFontOfSize:16]); + }]; + + if ( post.content != nil ) { + _content = [NSAttributedString sj_UIKitText:^(id _Nonnull make) { + make.append(post.content); + make.font([UIFont systemFontOfSize:14]); + make.textColor(UIColor.darkTextColor); + make.lineBreakMode(NSLineBreakByTruncatingTail); + make.lineSpacing(5); + }]; + } + + [self reloadLikingNum]; + _commentNum = [self textWithNum:post.commentCount iconName:@"icon_comment"]; + [self reloadShareNum]; + } + return self; +} + +- (void)setIsLiked:(BOOL)isLiked { + if ( isLiked != _isLiked ) { + _isLiked = isLiked; + _likingCount += isLiked ? 1 : -1; + [self reloadLikingNum]; + } +} + +- (void)setShareCount:(NSInteger)shareCount { + if ( _shareCount != shareCount ) { + _shareCount = shareCount; + [self reloadShareNum]; + } +} + +- (void)reloadLikingNum { + _likingNum = [self textWithNum:_likingCount iconName:[NSString stringWithFormat:@"icon_liking_%d", _isLiked]]; +} + +- (void)reloadShareNum { + _shareNum = [self textWithNum:_shareCount iconName:@"icon_share"]; +} + +- (NSAttributedString *)textWithNum:(NSInteger)num iconName:(NSString *)iconName { + return [NSAttributedString sj_UIKitText:^(id _Nonnull make) { + make.appendImage(^(id _Nonnull make) { + make.image = [UIImage imageNamed:iconName]; + make.alignment = SJUTVerticalAlignmentCenter; + }); + make.append([NSString stringWithFormat:@" %ld", (long)num]); + make.font([UIFont systemFontOfSize:12]); + make.textColor(UIColor.darkGrayColor); + }]; +} +@end + +#pragma mark - <#mark#> + + +@interface WLPostVideoLayoutCollectionItem () +@property (nonatomic, strong, nullable) UIImage *cover; +@end + +@implementation WLPostVideoLayoutCollectionItem { + WLPostVideoLayoutCollectionViewCell *_cell; +} + +- (instancetype)initWithModel:(WLPostModel *)post { + self = [super initWithModel:post]; + if ( self ) { + _cover = [UIImage imageNamed:post.video.cover]; + } + return self; +} + +- (Class)cellClass { + return WLPostVideoLayoutCollectionViewCell.class; +} + +- (NSBundle *)cellNibBundle { + return NSBundle.mainBundle; +} + +- (CGSize)layoutSizeThatFits:(CGSize)size inSection:(LWZCollectionSection *)section atIndexPath:(NSIndexPath *)indexPath scrollDirection:(UICollectionViewScrollDirection)scrollDirection { + CGFloat w = size.width; + CGFloat avatarH = 44; + CGFloat contentH = self.content != nil ? (12 + [self.content lwz_textContainerWithLayoutSizeThatFits:CGSizeMake(w, 0x1000) limitedToNumberOfLines:0 fixesSingleLineSpacing:YES].layoutSize.height) : 0; + CGFloat coverH = 12 + w * 9 / 16.0; + CGFloat bottomBarH = 8 + 25; + CGFloat h = avatarH + contentH + coverH + bottomBarH; + return CGSizeMake(w, h); +} + +- (void)bindCell:(WLPostVideoLayoutCollectionViewCell *)cell atIndexPath:(NSIndexPath *)indexPath { + _cell = cell; + cell.dataSource = self; + cell.delegate = self; + if ( self.needsLayout ) [cell reloadDataSource]; +} + +- (void)unbindCell:(__kindof UICollectionViewCell *)cell atIndexPath:(NSIndexPath *)indexPath { + _cell = nil; +} + +- (void)reloadLikingNum { + [super reloadLikingNum]; + [_cell reloadLikingNum]; +} + +- (void)reloadShareNum { + [super reloadShareNum]; + [_cell reloadShareNum]; +} + +#pragma mark - WLPostVideoLayoutCollectionViewCellDelegate + +- (void)likingItemWasTappedInCell:(WLPostVideoLayoutCollectionViewCell *)cell { + if ( self.likingItemTapHandler != nil ) self.likingItemTapHandler(self); +} + +- (void)shareItemWasTappedInCell:(WLPostVideoLayoutCollectionViewCell *)cell { + if ( self.shareItemTapHandler != nil ) self.shareItemTapHandler(self); +} + +- (void)commentItemWasTappedInCell:(WLPostVideoLayoutCollectionViewCell *)cell { + if ( self.commentItemTapHandler != nil ) self.commentItemTapHandler(self); +} +@end + +#pragma mark - <#mark#> + +@interface WLPostCoverItem : NSObject +@property (nonatomic, strong, nullable) UIImage *image; +@property (nonatomic) CGRect frame; +@end + +@implementation WLPostCoverItem +- (instancetype)initWithImage:(UIImage *)image { + self = [super init]; + if ( self ) { + _image = image; + } + return self; +} +@end + +@interface WLPostImageLayoutCollectionItem () +@property (nonatomic, strong, nullable) NSMutableArray> *covers; +@end + +@implementation WLPostImageLayoutCollectionItem { + WLPostImageLayoutCollectionViewCell *_cell; +} + +- (instancetype)initWithModel:(WLPostModel *)post { + self = [super initWithModel:post]; + if ( self ) { + NSArray *covers = post.covers; + if ( covers.count != 0 ) { + _covers = [NSMutableArray array]; + for ( NSString *imageName in covers ) { + [_covers addObject:[WLPostCoverItem.alloc initWithImage:[UIImage imageNamed:imageName]]]; + } + } + } + return self; +} + +- (Class)cellClass { + return WLPostImageLayoutCollectionViewCell.class; +} + +- (NSBundle *)cellNibBundle { + return NSBundle.mainBundle; +} + +- (CGSize)layoutSizeThatFits:(CGSize)size inSection:(LWZCollectionSection *)section atIndexPath:(NSIndexPath *)indexPath scrollDirection:(UICollectionViewScrollDirection)scrollDirection { + NSInteger count = _covers.count; + if ( count != 0 ) { + // 根据布局宽度, 设置每个封面item的frame + CGFloat boundary = size.width; + NSInteger columns = count == 1 ? 1 : 3; + CGFloat interitemSpacing = 6; + CGFloat lineSpacing = 6; + CGFloat itemWidth = floor((boundary - interitemSpacing * (columns - 1)) / columns); + CGFloat itemHeight = floor(itemWidth); + CGRect frame = {0, 0, itemWidth, itemHeight}; + WLPostCoverItem *previous = nil; + for ( NSInteger i = 0 ; i < count ; ++ i ) { + if ( previous != nil ) { + frame.origin.x = CGRectGetMaxX(previous.frame) + interitemSpacing; + frame.origin.y = CGRectGetMinY(previous.frame); + } + BOOL isNewLine = CGRectGetMaxX(frame) > boundary; + if ( isNewLine ) { + frame.origin.x = 0; + frame.origin.y = CGRectGetMaxY(previous.frame) + lineSpacing; + } + + WLPostCoverItem *item = _covers[i]; + item.frame = frame; + previous = item; + } + } + + CGFloat w = size.width; + CGFloat avatarH = 44; + CGFloat contentH = self.content != nil ? (12 + [self.content lwz_textContainerWithLayoutSizeThatFits:CGSizeMake(w, 0x1000) limitedToNumberOfLines:0 fixesSingleLineSpacing:YES].layoutSize.height) : 0; + CGFloat imagesH = count != 0 ? (12 + CGRectGetMaxY(_covers.lastObject.frame)) : 0; + CGFloat bottomBarH = 8 + 25; + CGFloat h = avatarH + contentH + imagesH + bottomBarH; + return CGSizeMake(w, h); +} + +- (void)bindCell:(WLPostImageLayoutCollectionViewCell *)cell atIndexPath:(NSIndexPath *)indexPath { + _cell = cell; + cell.dataSource = self; + cell.delegate = self; + if ( self.needsLayout ) [cell reloadDataSource]; +} + +- (void)unbindCell:(__kindof UICollectionViewCell *)cell atIndexPath:(NSIndexPath *)indexPath { + _cell = nil; +} + +- (void)reloadLikingNum { + [super reloadLikingNum]; + [_cell reloadLikingNum]; +} + +- (void)reloadShareNum { + [super reloadShareNum]; + [_cell reloadShareNum]; +} + +#pragma mark - WLPostImageLayoutCollectionViewCellDelegate + +- (void)likingItemWasTappedInCell:(WLPostImageLayoutCollectionViewCell *)cell { + if ( self.likingItemTapHandler != nil ) self.likingItemTapHandler(self); +} + +- (void)shareItemWasTappedInCell:(WLPostImageLayoutCollectionViewCell *)cell { + if ( self.shareItemTapHandler != nil ) self.shareItemTapHandler(self); +} + +- (void)commentItemWasTappedInCell:(WLPostImageLayoutCollectionViewCell *)cell { + if ( self.commentItemTapHandler != nil ) self.commentItemTapHandler(self); +} + +@end diff --git a/Example/LWZComponents/WeightLayout/Provider/Item/WLPostImageLayoutCollectionViewCell.h b/Example/LWZComponents/WeightLayout/Provider/Item/WLPostImageLayoutCollectionViewCell.h new file mode 100644 index 0000000..d87d803 --- /dev/null +++ b/Example/LWZComponents/WeightLayout/Provider/Item/WLPostImageLayoutCollectionViewCell.h @@ -0,0 +1,42 @@ +// +// WLPostImageLayoutCollectionViewCell.h +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/11/26. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import +@protocol WLPostImageLayoutCollectionViewCellDataSource, WLPostImageLayoutCollectionViewCellDelegate; + +NS_ASSUME_NONNULL_BEGIN +@interface WLPostImageLayoutCollectionViewCell : UICollectionViewCell +@property (nonatomic, weak, nullable) id dataSource; +@property (nonatomic, weak, nullable) id delegate; + +- (void)reloadLikingNum; +- (void)reloadShareNum; +- (void)reloadDataSource; +@end + +@protocol WLPostCoverItem +@property (nonatomic, strong, readonly, nullable) UIImage *image; +@property (nonatomic, readonly) CGRect frame; +@end + +@protocol WLPostImageLayoutCollectionViewCellDataSource +@property (nonatomic, strong, readonly, nullable) UIImage *avatar; +@property (nonatomic, strong, readonly, nullable) NSAttributedString *name; +@property (nonatomic, strong, readonly, nullable) NSAttributedString *content; +@property (nonatomic, strong, readonly, nullable) NSArray> *covers; +@property (nonatomic, strong, readonly, nullable) NSAttributedString *likingNum; +@property (nonatomic, strong, readonly, nullable) NSAttributedString *commentNum; +@property (nonatomic, strong, readonly, nullable) NSAttributedString *shareNum; +@end + +@protocol WLPostImageLayoutCollectionViewCellDelegate +- (void)likingItemWasTappedInCell:(WLPostImageLayoutCollectionViewCell *)cell; +- (void)shareItemWasTappedInCell:(WLPostImageLayoutCollectionViewCell *)cell; +- (void)commentItemWasTappedInCell:(WLPostImageLayoutCollectionViewCell *)cell; +@end +NS_ASSUME_NONNULL_END diff --git a/Example/LWZComponents/WeightLayout/Provider/Item/WLPostImageLayoutCollectionViewCell.m b/Example/LWZComponents/WeightLayout/Provider/Item/WLPostImageLayoutCollectionViewCell.m new file mode 100644 index 0000000..34382b2 --- /dev/null +++ b/Example/LWZComponents/WeightLayout/Provider/Item/WLPostImageLayoutCollectionViewCell.m @@ -0,0 +1,111 @@ +// +// WLPostImageLayoutCollectionViewCell.m +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/11/26. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "WLPostImageLayoutCollectionViewCell.h" +#import "CommonLabel.h" + +@interface WLPostImageLayoutCollectionViewCell () +@property (weak, nonatomic) IBOutlet UIImageView *avatarImageView; +@property (weak, nonatomic) IBOutlet UILabel *nameLabel; +@property (weak, nonatomic) IBOutlet CommonLabel *contentLabel; +@property (weak, nonatomic) IBOutlet UIView *containerView; +@property (weak, nonatomic) IBOutlet UIButton *likingButton; +@property (weak, nonatomic) IBOutlet UIButton *commentButton; +@property (weak, nonatomic) IBOutlet UIButton *shareButton; + +@property (weak, nonatomic) IBOutlet NSLayoutConstraint *containerTopSpacing; +@property (nonatomic, strong, nullable) NSMutableArray *coverImageViews; +@end + +@implementation WLPostImageLayoutCollectionViewCell + +- (void)awakeFromNib { + [super awakeFromNib]; + + _avatarImageView.layer.cornerRadius = 22; + _avatarImageView.clipsToBounds = YES; + + // Initialization code +} + +- (void)setDataSource:(nullable id)dataSource { + if ( dataSource != _dataSource ) { + _dataSource = dataSource; + [self reloadDataSource]; + } +} + +- (void)reloadLikingNum { + [_likingButton setAttributedTitle:_dataSource.likingNum forState:UIControlStateNormal]; +} + +- (void)reloadShareNum { + [_shareButton setAttributedTitle:_dataSource.shareNum forState:UIControlStateNormal]; +} + +- (void)reloadCovers { + if ( _coverImageViews == nil ) _coverImageViews = [NSMutableArray array]; + __auto_type items = _dataSource.covers; + // 补充新增的视图 + if ( _coverImageViews.count < items.count ) { + for ( NSInteger i = _coverImageViews.count ; i < items.count ; ++ i ) { + UIImageView *view = [UIImageView.alloc initWithFrame:CGRectZero]; + view.contentMode = UIViewContentModeScaleAspectFill; + view.clipsToBounds = YES; + view.layer.cornerRadius = 5; + view.backgroundColor = UIColor.lightGrayColor; + [_coverImageViews addObject:view]; + [self.containerView addSubview:view]; + } + } + + NSArray> *covers = self.dataSource.covers; + NSInteger count = covers.count; + if ( count != 0 ) { + _containerView.hidden = NO; + _containerTopSpacing.constant = 12; + [_coverImageViews enumerateObjectsUsingBlock:^(UIImageView * _Nonnull imageView, NSUInteger idx, BOOL * _Nonnull stop) { + imageView.hidden = idx >= count; + // 旧的多余的视图将设置隐藏 + if ( imageView.isHidden ) + return; + + id item = covers[idx]; + imageView.frame = item.frame; + imageView.image = item.image; + }]; + } + else { + _containerView.hidden = YES; + _containerTopSpacing.constant = 0; + } +} + +- (void)reloadDataSource { + _avatarImageView.image = _dataSource.avatar; + _nameLabel.attributedText = _dataSource.name; + _contentLabel.attributedText = _dataSource.content; + + [self reloadCovers]; + [_commentButton setAttributedTitle:_dataSource.commentNum forState:UIControlStateNormal]; + [self reloadLikingNum]; + [self reloadShareNum]; +} + +- (IBAction)handleTapAction:(id)sender { + if ( sender == _likingButton ) { + [_delegate likingItemWasTappedInCell:self]; + } + else if ( sender == _commentButton ) { + [_delegate commentItemWasTappedInCell:self]; + } + else if ( sender == _shareButton ) { + [_delegate shareItemWasTappedInCell:self]; + } +} +@end diff --git a/Example/LWZComponents/WeightLayout/Provider/Item/WLPostImageLayoutCollectionViewCell.xib b/Example/LWZComponents/WeightLayout/Provider/Item/WLPostImageLayoutCollectionViewCell.xib new file mode 100644 index 0000000..9431278 --- /dev/null +++ b/Example/LWZComponents/WeightLayout/Provider/Item/WLPostImageLayoutCollectionViewCell.xib @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Example/LWZComponents/WeightLayout/Provider/Item/WLPostVideoLayoutCollectionViewCell.h b/Example/LWZComponents/WeightLayout/Provider/Item/WLPostVideoLayoutCollectionViewCell.h new file mode 100644 index 0000000..6fa9266 --- /dev/null +++ b/Example/LWZComponents/WeightLayout/Provider/Item/WLPostVideoLayoutCollectionViewCell.h @@ -0,0 +1,37 @@ +// +// WLPostVideoLayoutCollectionViewCell.h +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/11/26. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import +@protocol WLPostVideoLayoutCollectionViewCellDataSource, WLPostVideoLayoutCollectionViewCellDelegate; + +NS_ASSUME_NONNULL_BEGIN +@interface WLPostVideoLayoutCollectionViewCell : UICollectionViewCell +@property (nonatomic, weak, nullable) id dataSource; +@property (nonatomic, weak, nullable) id delegate; + +- (void)reloadLikingNum; +- (void)reloadShareNum; +- (void)reloadDataSource; +@end + +@protocol WLPostVideoLayoutCollectionViewCellDataSource +@property (nonatomic, strong, readonly, nullable) UIImage *avatar; +@property (nonatomic, strong, readonly, nullable) NSAttributedString *name; +@property (nonatomic, strong, readonly, nullable) NSAttributedString *content; +@property (nonatomic, strong, readonly, nullable) UIImage *cover; +@property (nonatomic, strong, readonly, nullable) NSAttributedString *likingNum; +@property (nonatomic, strong, readonly, nullable) NSAttributedString *commentNum; +@property (nonatomic, strong, readonly, nullable) NSAttributedString *shareNum; +@end + +@protocol WLPostVideoLayoutCollectionViewCellDelegate +- (void)likingItemWasTappedInCell:(WLPostVideoLayoutCollectionViewCell *)cell; +- (void)shareItemWasTappedInCell:(WLPostVideoLayoutCollectionViewCell *)cell; +- (void)commentItemWasTappedInCell:(WLPostVideoLayoutCollectionViewCell *)cell; +@end +NS_ASSUME_NONNULL_END diff --git a/Example/LWZComponents/WeightLayout/Provider/Item/WLPostVideoLayoutCollectionViewCell.m b/Example/LWZComponents/WeightLayout/Provider/Item/WLPostVideoLayoutCollectionViewCell.m new file mode 100644 index 0000000..97414af --- /dev/null +++ b/Example/LWZComponents/WeightLayout/Provider/Item/WLPostVideoLayoutCollectionViewCell.m @@ -0,0 +1,73 @@ +// +// WLPostVideoLayoutCollectionViewCell.m +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/11/26. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "WLPostVideoLayoutCollectionViewCell.h" +#import "CommonLabel.h" + +@interface WLPostVideoLayoutCollectionViewCell () +@property (weak, nonatomic) IBOutlet UIImageView *avatarImageView; +@property (weak, nonatomic) IBOutlet UILabel *nameLabel; +@property (weak, nonatomic) IBOutlet CommonLabel *contentLabel; +@property (weak, nonatomic) IBOutlet UIImageView *coverImageView; +@property (weak, nonatomic) IBOutlet UIImageView *playImageView; +@property (weak, nonatomic) IBOutlet UIButton *likingButton; +@property (weak, nonatomic) IBOutlet UIButton *commentButton; +@property (weak, nonatomic) IBOutlet UIButton *shareButton; +@end + +@implementation WLPostVideoLayoutCollectionViewCell + +- (void)awakeFromNib { + [super awakeFromNib]; + _avatarImageView.layer.cornerRadius = 22; + _avatarImageView.clipsToBounds = YES; + + _coverImageView.layer.cornerRadius = 5; + _coverImageView.clipsToBounds = YES; + _playImageView.image = [UIImage imageNamed:@"icon_play"]; + // Initialization code +} + +- (void)setDataSource:(nullable id)dataSource { + if ( dataSource != _dataSource ) { + _dataSource = dataSource; + [self reloadDataSource]; + } +} + +- (void)reloadLikingNum { + [_likingButton setAttributedTitle:_dataSource.likingNum forState:UIControlStateNormal]; +} + +- (void)reloadShareNum { + [_shareButton setAttributedTitle:_dataSource.shareNum forState:UIControlStateNormal]; +} + +- (void)reloadDataSource { + _avatarImageView.image = _dataSource.avatar; + _nameLabel.attributedText = _dataSource.name; + _contentLabel.attributedText = _dataSource.content; + _coverImageView.image = _dataSource.cover; + [_commentButton setAttributedTitle:_dataSource.commentNum forState:UIControlStateNormal]; + [self reloadLikingNum]; + [self reloadShareNum]; +} + +- (IBAction)handleTapAction:(id)sender { + if ( sender == _likingButton ) { + [_delegate likingItemWasTappedInCell:self]; + } + else if ( sender == _commentButton ) { + [_delegate commentItemWasTappedInCell:self]; + } + else if ( sender == _shareButton ) { + [_delegate shareItemWasTappedInCell:self]; + } +} + +@end diff --git a/Example/LWZComponents/WeightLayout/Provider/Item/WLPostVideoLayoutCollectionViewCell.xib b/Example/LWZComponents/WeightLayout/Provider/Item/WLPostVideoLayoutCollectionViewCell.xib new file mode 100644 index 0000000..c8c13d3 --- /dev/null +++ b/Example/LWZComponents/WeightLayout/Provider/Item/WLPostVideoLayoutCollectionViewCell.xib @@ -0,0 +1,142 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Example/LWZComponents/WeightLayout/Provider/Item/WLUserCollectionItem.h b/Example/LWZComponents/WeightLayout/Provider/Item/WLUserCollectionItem.h new file mode 100644 index 0000000..5c4ef8e --- /dev/null +++ b/Example/LWZComponents/WeightLayout/Provider/Item/WLUserCollectionItem.h @@ -0,0 +1,18 @@ +// +// WLUserCollectionItem.h +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/11/25. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "LWZCollectionItem.h" +#import "WLModel.h" + +NS_ASSUME_NONNULL_BEGIN +@interface WLUserCollectionItem : LWZCollectionItem +- (instancetype)initWithModel:(WLUserModel *)model; + +@property (nonatomic, readonly) NSInteger userId; +@end +NS_ASSUME_NONNULL_END diff --git a/Example/LWZComponents/WeightLayout/Provider/Item/WLUserCollectionItem.m b/Example/LWZComponents/WeightLayout/Provider/Item/WLUserCollectionItem.m new file mode 100644 index 0000000..a5f5cf2 --- /dev/null +++ b/Example/LWZComponents/WeightLayout/Provider/Item/WLUserCollectionItem.m @@ -0,0 +1,64 @@ +// +// WLUserCollectionItem.m +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/11/25. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "WLUserCollectionItem.h" +#import "WLUserCollectionViewCell.h" + +@interface WLUserCollectionItem () +@property (nonatomic, strong, nullable) UIImage *avatar; +@property (nonatomic, strong, nullable) NSString *name; +@end + +@implementation WLUserCollectionItem +- (instancetype)initWithModel:(WLUserModel *)model { + self = [super init]; + if ( self ) { + _avatar = [UIImage imageNamed:model.avatar]; + _name = model.name; + _userId = model.id; + } + return self; +} + +// 将要绑定的 cell 类 +- (Class)cellClass { + return WLUserCollectionViewCell.class; +} + +// 如果 cell 需要从 xib 创建, 则实现这个方法, 并返回对应的 cell.xib 所在的 bundle +- (NSBundle *)cellNibBundle { + return NSBundle.mainBundle; +} + +// 计算 cell 将要显示的 size +- (CGSize)layoutSizeThatFits:(CGSize)size inSection:(LWZCollectionSection *)section atIndexPath:(NSIndexPath *)indexPath scrollDirection:(UICollectionViewScrollDirection)scrollDirection { + CGFloat nameH = 15; + switch ( scrollDirection ) { + case UICollectionViewScrollDirectionVertical: { + CGFloat w = size.width; + CGFloat coverH = w - 12 * 2; + CGFloat h = 12 + coverH + 8 + nameH + 12; + return CGSizeMake(w, h); + } + break; + case UICollectionViewScrollDirectionHorizontal: { + CGFloat h = size.height; + CGFloat coverW = h - 12 - nameH - 8; + CGFloat w = 12 + coverW + 12; + return CGSizeMake(w, h); + } + break; + } +} + +// 绑定到 cell +// 一般在此处将数据配置到 cell 上 +- (void)bindCell:(WLUserCollectionViewCell *)cell atIndexPath:(NSIndexPath *)indexPath { + cell.dataSource = self; +} +@end diff --git a/Example/LWZComponents/WeightLayout/Provider/Item/WLUserCollectionViewCell.h b/Example/LWZComponents/WeightLayout/Provider/Item/WLUserCollectionViewCell.h new file mode 100644 index 0000000..b09ce81 --- /dev/null +++ b/Example/LWZComponents/WeightLayout/Provider/Item/WLUserCollectionViewCell.h @@ -0,0 +1,21 @@ +// +// WLUserCollectionViewCell.h +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/11/25. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import +@protocol WLUserCollectionViewCellDataSource; + +NS_ASSUME_NONNULL_BEGIN +@interface WLUserCollectionViewCell : UICollectionViewCell +@property (nonatomic, weak, nullable) id dataSource; +@end + +@protocol WLUserCollectionViewCellDataSource +@property (nonatomic, strong, readonly, nullable) UIImage *avatar; +@property (nonatomic, strong, readonly, nullable) NSString *name; +@end +NS_ASSUME_NONNULL_END diff --git a/Example/LWZComponents/WeightLayout/Provider/Item/WLUserCollectionViewCell.m b/Example/LWZComponents/WeightLayout/Provider/Item/WLUserCollectionViewCell.m new file mode 100644 index 0000000..0bc7f2a --- /dev/null +++ b/Example/LWZComponents/WeightLayout/Provider/Item/WLUserCollectionViewCell.m @@ -0,0 +1,33 @@ +// +// WLUserCollectionViewCell.m +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/11/25. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "WLUserCollectionViewCell.h" +#import "CommonImageView.h" + +@interface WLUserCollectionViewCell () +@property (weak, nonatomic) IBOutlet CommonImageView *imageView; +@property (weak, nonatomic) IBOutlet UILabel *nameLabel; +@end + +@implementation WLUserCollectionViewCell + +- (void)awakeFromNib { + [super awakeFromNib]; + [_imageView setLayoutSubviewsExeBlock:^(__kindof CommonImageView * _Nonnull view) { + view.layer.cornerRadius = view.bounds.size.height * 0.5; + }]; +} + +- (void)setDataSource:(nullable id)dataSource { + if ( dataSource != _dataSource ) { + _dataSource = dataSource; + _imageView.image = dataSource.avatar; + _nameLabel.text = dataSource.name; + } +} +@end diff --git a/Example/LWZComponents/WeightLayout/Provider/Item/WLUserCollectionViewCell.xib b/Example/LWZComponents/WeightLayout/Provider/Item/WLUserCollectionViewCell.xib new file mode 100644 index 0000000..9ea9cc9 --- /dev/null +++ b/Example/LWZComponents/WeightLayout/Provider/Item/WLUserCollectionViewCell.xib @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Example/LWZComponents/WeightLayout/Provider/Model/WLJsonData.json b/Example/LWZComponents/WeightLayout/Provider/Model/WLJsonData.json new file mode 100644 index 0000000..5220b88 --- /dev/null +++ b/Example/LWZComponents/WeightLayout/Provider/Model/WLJsonData.json @@ -0,0 +1,184 @@ +{ + "users" : [ + { + "id" : 1, + "name" : "平立面", + "avatar" : "avatar_1" + }, + { + "id" : 2, + "name" : "欧锦", + "avatar" : "avatar_2" + }, + { + "id" : 3, + "name" : "额度", + "avatar" : "avatar_3" + }, + { + "id" : 4, + "name" : "二电厂", + "avatar" : "avatar_4" + }, + { + "id" : 5, + "name" : "全文若", + "avatar" : "avatar_5" + }, + { + "id" : 6, + "name" : "不定时", + "avatar" : "avatar_6" + } + ], + "posts" : [ + { + "id" : 1, + "user" : { + "id" : 1, + "name" : "水龙吟·赠赵晦之吹笛侍儿", + "avatar" : "avatar_1" + }, + "isLiked" : false, + "likingCount" : 12, + "commentCount" : 20, + "shareCount" : 33, + "content" : "楚山修竹如云,异材秀出千林表。\n龙须半翦,凤膺微涨,玉肌匀绕。木落淮南,雨晴云梦,月明风袅。\n自中郎不见,桓伊去后,知孤负、秋多少。\n闻道岭南太守,后堂深、绿珠娇小。\n绮窗学弄,梁州初遍,霓裳未了。\n嚼徵含宫,泛商流羽,一声云杪。\n为使君洗尽,蛮风瘴雨,作霜天晓。", + "video" : null, + "covers" : [ + "shici_1" + ] + }, + { + "id" : 2, + "user" : { + "id" : 2, + "name" : "星空纪元", + "avatar" : "avatar_2" + }, + "commentCount" : 993, + "shareCount" : 96, + "content" : "最新播报:近日郭达·斯坦森成功点燃星火,成为北美区第七位星空级战神。", + "video" : { + "id" : 1, + "cover" : "video_1" + }, + "covers" : null + }, + { + "id" : 4, + "user" : { + "id" : 4, + "name" : "孤单狗", + "avatar" : "avatar_4" + }, + "isLiked" : false, + "likingCount" : 44, + "commentCount" : 3, + "shareCount" : 12, + "content" : "亲爱的朋友,你是世界上最善良、最可爱的,我一辈子都会喜欢你。", + "videos" : null, + "covers" : null + }, + { + "id" : 5, + "user" : { + "id" : 5, + "name" : "犀利搜索显示", + "avatar" : "avatar_5" + }, + "isLiked" : false, + "likingCount" : 55, + "commentCount" : 11, + "shareCount" : 222, + "content" : null, + "video" : null, + "covers" : [ + "kwi_1", + "kwi_2" + ] + }, + { + "id" : 6, + "user" : { + "id" : 6, + "name" : "犀利搜索显示", + "avatar" : "avatar_5" + }, + "isLiked" : true, + "likingCount" : 322, + "commentCount" : 11, + "shareCount" : 222, + "content" : null, + "video" : null, + "covers" : [ + "kwi_3" + ] + }, + { + "id" : 5, + "user" : { + "id" : 5, + "name" : "犀利搜索显示", + "avatar" : "avatar_5" + }, + "isLiked" : false, + "likingCount" : 66, + "commentCount" : 11, + "shareCount" : 222, + "content" : null, + "video" : null, + "covers" : [ + "kwi_7", + "kwi_3", + "kwi_8" + ] + }, + { + "id" : 5, + "user" : { + "id" : 5, + "name" : "犀利搜索显示", + "avatar" : "avatar_5" + }, + "isLiked" : false, + "likingCount" : 77, + "commentCount" : 11, + "shareCount" : 222, + "content" : "我的老家, 就在这个村, 我是这里土生土长的人. 很久以前这里有座灵剑山, 山里住着有好多神仙. 每月初一十五, 就会有很多神仙下山来.", + "video" : null, + "covers" : [ + "kwi_5", + "kwi_6", + "kwi_7", + "kwi_8", + "kwi_9" + ] + }, + { + "id" : 3, + "user" : { + "id" : 3, + "name" : "抖腿舞冠军😄", + "avatar" : "avatar_3" + }, + "isLiked" : true, + "likingCount" : 33, + "commentCount" : 11, + "shareCount" : 222, + "content" : null, + "video" : null, + "covers" : [ + "kwi_1", + "kwi_2", + "kwi_3", + "kwi_4", + "kwi_5", + "kwi_6", + "kwi_7", + "kwi_8", + "kwi_9" + ] + } + ] +} diff --git a/Example/LWZComponents/WeightLayout/Provider/Model/WLModel.h b/Example/LWZComponents/WeightLayout/Provider/Model/WLModel.h new file mode 100644 index 0000000..4191736 --- /dev/null +++ b/Example/LWZComponents/WeightLayout/Provider/Model/WLModel.h @@ -0,0 +1,39 @@ +// +// WLModel.h +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/11/25. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN +@interface WLUserModel : NSObject +@property (nonatomic) NSInteger id; +@property (nonatomic, strong, nullable) NSString *avatar; +@property (nonatomic, strong, nullable) NSString *name; +@end + +@interface WLVideoModel : NSObject +@property (nonatomic) NSInteger id; +@property (nonatomic, strong, nullable) NSString *cover; +@end + +@interface WLPostModel : NSObject +@property (nonatomic) NSInteger id; +@property (nonatomic, strong, nullable) WLUserModel *user; +@property (nonatomic) BOOL isLiked; +@property (nonatomic) NSInteger likingCount; +@property (nonatomic) NSInteger commentCount; +@property (nonatomic) NSInteger shareCount; +@property (nonatomic, strong, nullable) NSString *content; +@property (nonatomic, strong, nullable) NSArray *covers; +@property (nonatomic, strong, nullable) WLVideoModel *video; +@end + +@interface WLDetailModel : NSObject +@property (nonatomic, strong, nullable) NSArray *users; +@property (nonatomic, strong, nullable) NSArray *posts; +@end +NS_ASSUME_NONNULL_END diff --git a/Example/LWZComponents/WeightLayout/Provider/Model/WLModel.m b/Example/LWZComponents/WeightLayout/Provider/Model/WLModel.m new file mode 100644 index 0000000..7e8590c --- /dev/null +++ b/Example/LWZComponents/WeightLayout/Provider/Model/WLModel.m @@ -0,0 +1,34 @@ +// +// WLModel.m +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/11/25. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "WLModel.h" + +@implementation WLUserModel + +@end + +@implementation WLVideoModel + +@end + +@implementation WLPostModel ++ (nullable NSDictionary *)modelContainerPropertyGenericClass { + return @{ + @"covers" : NSString.class + }; +} +@end + +@implementation WLDetailModel ++ (nullable NSDictionary *)modelContainerPropertyGenericClass { + return @{ + @"users" : WLUserModel.class, + @"posts" : WLPostModel.class + }; +} +@end diff --git a/Example/LWZComponents/WeightLayout/Provider/Model/WLModelProvider.h b/Example/LWZComponents/WeightLayout/Provider/Model/WLModelProvider.h new file mode 100644 index 0000000..d234ae1 --- /dev/null +++ b/Example/LWZComponents/WeightLayout/Provider/Model/WLModelProvider.h @@ -0,0 +1,17 @@ +// +// WLModelProvider.h +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/11/25. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "WLModel.h" + +NS_ASSUME_NONNULL_BEGIN +@interface WLModelProvider : NSObject + ++ (void)requestDataWithComplete:(void(^)(WLDetailModel *_Nullable detail, NSError *_Nullable error))completionHandler; + +@end +NS_ASSUME_NONNULL_END diff --git a/Example/LWZComponents/WeightLayout/Provider/Model/WLModelProvider.m b/Example/LWZComponents/WeightLayout/Provider/Model/WLModelProvider.m new file mode 100644 index 0000000..fc3f058 --- /dev/null +++ b/Example/LWZComponents/WeightLayout/Provider/Model/WLModelProvider.m @@ -0,0 +1,30 @@ +// +// WLModelProvider.m +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/11/25. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "WLModelProvider.h" +#import "CommonDependencies.h" + +@implementation WLModelProvider + ++ (void)requestDataWithComplete:(void(^)(WLDetailModel *_Nullable detail, NSError *_Nullable error))completionHandler { + dispatch_async(dispatch_get_global_queue(0, 0), ^{ + NSString *filePath = [NSBundle.mainBundle pathForResource:@"WLJsonData" ofType:@"json"]; + NSData *data = [NSData dataWithContentsOfFile:filePath]; + NSError *error = nil; + id jsonObj = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error]; + WLDetailModel *model = nil; + if ( error == nil ) { + model = [WLDetailModel yy_modelWithJSON:jsonObj]; + } + dispatch_async(dispatch_get_main_queue(), ^{ + if ( completionHandler != nil ) completionHandler(model, error); + }); + }); +} + +@end diff --git a/Example/LWZComponents/WeightLayout/Provider/WLProvider.h b/Example/LWZComponents/WeightLayout/Provider/WLProvider.h new file mode 100644 index 0000000..258694b --- /dev/null +++ b/Example/LWZComponents/WeightLayout/Provider/WLProvider.h @@ -0,0 +1,30 @@ +// +// WLProvider.h +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/11/25. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "LWZCollectionProvider.h" +#import "WLModel.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface WLProvider : LWZCollectionProvider +- (instancetype)initWithModel:(WLDetailModel *)model; + +@property (nonatomic, copy, nullable) void(^userItemTapHandler)(NSInteger userId); + +@property (nonatomic, copy, nullable) void(^likingItemTapHandler)(NSIndexPath *indexPath); +@property (nonatomic, copy, nullable) void(^shareItemTapHandler)(NSIndexPath *indexPath); +@property (nonatomic, copy, nullable) void(^commentItemTapHandler)(NSIndexPath *indexPath); + +- (BOOL)isPostLikedForItemAtIndexPath:(NSIndexPath *)indexPath; +- (void)setPostLiked:(BOOL)isLiked forItemAtIndexPath:(NSIndexPath *)indexPath; + +- (NSInteger)postShareCountForItemAtIndexPath:(NSIndexPath *)indexPath; +- (void)setPostShareCount:(NSInteger)count forItemAtIndexPath:(NSIndexPath *)indexPath; +@end + +NS_ASSUME_NONNULL_END diff --git a/Example/LWZComponents/WeightLayout/Provider/WLProvider.m b/Example/LWZComponents/WeightLayout/Provider/WLProvider.m new file mode 100644 index 0000000..9166c2a --- /dev/null +++ b/Example/LWZComponents/WeightLayout/Provider/WLProvider.m @@ -0,0 +1,125 @@ +// +// WLProvider.m +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/11/25. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "WLProvider.h" +#import "WLUserCollectionItem.h" +#import "WLPostCollectionItem.h" +#import "WLCollectionSectionHeaderFooter.h" +#import "CommonDependencies.h" + +@implementation WLProvider +- (instancetype)initWithModel:(WLDetailModel *)model { + self = [super init]; + if ( self ) { + __weak typeof(self) _self = self; + if ( model.users.count != 0 ) { + [self addSectionWithBlock:^(__kindof LWZCollectionSection * _Nonnull make) { + make.contentInsets = UIEdgeInsetsMake(12, 12, 12, 12); + make.minimumInteritemSpacing = 8; + make.minimumLineSpacing = 8; + + // 设置 sectionHeaderView + WLCollectionSectionHeaderFooter *header = [WLCollectionSectionHeaderFooter.alloc initWithTitle:[NSAttributedString sj_UIKitText:^(id _Nonnull make) { + make.append(@"设置比重: 每行显示三个item"); + make.font([UIFont boldSystemFontOfSize:18]); + }]]; + header.contentInsets = UIEdgeInsetsMake(12, 12, 0, 12); + make.header = header; + + // 添加 userItem + for ( WLUserModel *userModel in model.users ) { + WLUserCollectionItem *item = [WLUserCollectionItem.alloc initWithModel:userModel]; + // 每行显示三个; 设置比重占`1 / 3.0`; + item.weight = 1 / 3.0; + + // item 添加背景decoration + LWZCollectionItemBackgroundDecoration *decoration = [LWZCollectionItemBackgroundDecoration.alloc initWithBackgroundColor:[UIColor colorWithRed:245 / 255.0 green:245 / 255.0 blue:245 / 255.0 alpha:1]]; + decoration.cornerRadius = 5; + item.decoration = decoration; + + // 设置 userItem 点击事件 + item.tapHandler = ^(__kindof LWZCollectionItem * _Nonnull item, NSIndexPath * _Nonnull indexPath) { + __strong typeof(_self) self = _self; + if ( self == nil ) return; + if ( self.userItemTapHandler ) self.userItemTapHandler(userModel.id); + }; + + // 将 userItem 添加到 section 中 + [make addItem:item]; + } + }]; + } + + if ( model.posts.count != 0 ) { + [self addSectionWithBlock:^(__kindof LWZCollectionSection * _Nonnull make) { + make.contentInsets = UIEdgeInsetsMake(12, 12, 12, 12); + make.minimumLineSpacing = 24; + + // 设置 sectionHeaderView + WLCollectionSectionHeaderFooter *header = [WLCollectionSectionHeaderFooter.alloc initWithTitle:[NSAttributedString sj_UIKitText:^(id _Nonnull make) { + make.append(@"Header: 默认比重为 1.0, 即每行显示一个item"); + make.font([UIFont boldSystemFontOfSize:18]); + }]]; + header.contentInsets = UIEdgeInsetsMake(12, 12, 0, 12); + // 可以为 header 添加 decoration + LWZCollectionHeaderSeparatorDecoration *headerDecoration = [LWZCollectionHeaderSeparatorDecoration.alloc initWithColor:[UIColor redColor] height:2]; + headerDecoration.contentInsets = UIEdgeInsetsMake(0, 0, 0, 0); + header.decoration = headerDecoration; + make.header = header; + + // 添加 postItem + for ( WLPostModel *post in model.posts ) { + WLPostCollectionItem *item = [WLPostCollectionItem itemWithModel:post]; + item.likingItemTapHandler = ^(WLPostCollectionItem * _Nonnull item) { + __strong typeof(_self) self = _self; + if ( self == nil ) return; + if ( self.likingItemTapHandler != nil ) self.likingItemTapHandler([self indexPathOfItem:item]); + }; + item.shareItemTapHandler = ^(WLPostCollectionItem * _Nonnull item) { + __strong typeof(_self) self = _self; + if ( self == nil ) return; + if ( self.shareItemTapHandler != nil ) self.shareItemTapHandler([self indexPathOfItem:item]); + }; + item.commentItemTapHandler = ^(WLPostCollectionItem * _Nonnull item) { + __strong typeof(_self) self = _self; + if ( self == nil ) return; + if ( self.commentItemTapHandler != nil ) self.commentItemTapHandler([self indexPathOfItem:item]); + }; + + // 可以为 item 添加分割线decoration + LWZCollectionSeparatorDecoration *decoration = [LWZCollectionSeparatorDecoration.alloc initWithColor:[UIColor colorWithRed:230 / 255.0 green:230 / 255.0 blue:230 / 255.0 alpha:1] height:0.5]; + decoration.contentInsets = UIEdgeInsetsMake(0, 0, -12, 0); + item.decoration = decoration; + + [make addItem:item]; + } + }]; + } + } + return self; +} + +- (BOOL)isPostLikedForItemAtIndexPath:(NSIndexPath *)indexPath { + WLPostCollectionItem *item = [self itemAtIndexPath:indexPath]; + return item.isLiked; +} + +- (void)setPostLiked:(BOOL)isLiked forItemAtIndexPath:(NSIndexPath *)indexPath { + WLPostCollectionItem *item = [self itemAtIndexPath:indexPath]; + item.isLiked = isLiked; +} + +- (NSInteger)postShareCountForItemAtIndexPath:(NSIndexPath *)indexPath { + WLPostCollectionItem *item = [self itemAtIndexPath:indexPath]; + return item.shareCount; +} +- (void)setPostShareCount:(NSInteger)count forItemAtIndexPath:(NSIndexPath *)indexPath { + WLPostCollectionItem *item = [self itemAtIndexPath:indexPath]; + item.shareCount = count; +} +@end diff --git a/Example/LWZComponents/WeightLayout/WLViewController.h b/Example/LWZComponents/WeightLayout/WLViewController.h new file mode 100644 index 0000000..8829d16 --- /dev/null +++ b/Example/LWZComponents/WeightLayout/WLViewController.h @@ -0,0 +1,17 @@ +// +// WLViewController.h +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/11/25. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface WLViewController : UIViewController + +@end + +NS_ASSUME_NONNULL_END diff --git a/Example/LWZComponents/WeightLayout/WLViewController.m b/Example/LWZComponents/WeightLayout/WLViewController.m new file mode 100644 index 0000000..295ec0b --- /dev/null +++ b/Example/LWZComponents/WeightLayout/WLViewController.m @@ -0,0 +1,97 @@ +// +// WLViewController.m +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2021/11/25. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "WLViewController.h" +#import "WLModelProvider.h" +#import "CommonDependencies.h" +#import "WLProvider.h" + +@interface WLViewController () +@property (nonatomic, strong) LWZCollectionView *collectionView; +@property (nonatomic, strong) LWZCollectionViewPresenter *presenter; +@property (nonatomic, strong) WLProvider *provider; +@end + +@implementation WLViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + [self _setupViews]; + // Do any additional setup after loading the view. +} + +- (void)_setupViews { + self.edgesForExtendedLayout = UIRectEdgeNone; + self.title = NSStringFromClass(self.class); + self.view.backgroundColor = UIColor.whiteColor; + + _presenter = [LWZCollectionViewPresenter.alloc init]; + + LWZCollectionWeightLayout *layout = [LWZCollectionWeightLayout.alloc initWithScrollDirection:UICollectionViewScrollDirectionVertical delegate:_presenter]; + layout.sectionHeadersPinToVisibleBounds = YES; + _collectionView = [LWZCollectionView.alloc initWithFrame:CGRectZero collectionViewLayout:layout]; + _collectionView.dataSource = _presenter; + [self.view addSubview:_collectionView]; + [_collectionView mas_makeConstraints:^(MASConstraintMaker *make) { + make.edges.offset(0); + }]; + + __weak typeof(self) _self = self; + [WLModelProvider requestDataWithComplete:^(WLDetailModel * _Nullable detail, NSError * _Nullable error) { + __strong typeof(_self) self = _self; + if ( self == nil ) return; + if ( error != nil ) { + NSLog(@"(%d : %s) WLViewController.error: %@", __LINE__, sel_getName(_cmd), error); + return; + } + + self.provider = [WLProvider.alloc initWithModel:detail]; + self.provider.userItemTapHandler = ^(NSInteger userId) { + __strong typeof(_self) self = _self; + if ( self == nil ) return; +#ifdef DEBUG + NSLog(@"%d : %s", __LINE__, sel_getName(_cmd)); +#endif + }; + + self.provider.commentItemTapHandler = ^(NSIndexPath * _Nonnull indexPath) { + __strong typeof(_self) self = _self; + if ( self == nil ) return; + NSLog(@"处理评论动作..."); + + UIViewController *vc = [UIViewController.alloc init]; + vc.view.backgroundColor = [UIColor colorWithRed:arc4random() % 256 / 255.0 + green:arc4random() % 256 / 255.0 + blue:arc4random() % 256 / 255.0 + alpha:1]; + [self.navigationController pushViewController:vc animated:YES]; + }; + + self.provider.likingItemTapHandler = ^(NSIndexPath * _Nonnull indexPath) { + __strong typeof(_self) self = _self; + if ( self == nil ) return; + NSLog(@"处理点赞动作..."); + + BOOL isLiked = [self.provider isPostLikedForItemAtIndexPath:indexPath]; + [self.provider setPostLiked:!isLiked forItemAtIndexPath:indexPath]; + }; + + self.provider.shareItemTapHandler = ^(NSIndexPath * _Nonnull indexPath) { + __strong typeof(_self) self = _self; + if ( self == nil ) return; + NSLog(@"处理分享动作..."); + + NSInteger count = [self.provider postShareCountForItemAtIndexPath:indexPath]; + [self.provider setPostShareCount:count + 1 forItemAtIndexPath:indexPath]; + }; + + self.presenter.provider = self.provider; + [self.collectionView reloadData]; + }]; +} +@end diff --git a/Example/Podfile b/Example/Podfile index ef5b1f1..fa180d0 100644 --- a/Example/Podfile +++ b/Example/Podfile @@ -1,9 +1,22 @@ -use_frameworks! +#use_frameworks! platform :ios, '9.0' +post_install do |installer| + installer.pods_project.targets.each do |target| + target.build_configurations.each do |config| + if config.name == 'Debug' + config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] = '$(inherited) LWZ_DEBUG=1' + end + end + end +end + target 'LWZComponents_Example' do pod 'LWZComponents', :path => '../' + pod 'YYModel' + pod 'Masonry' + pod 'SJUIKit/AttributesFactory' target 'LWZComponents_Tests' do inherit! :search_paths diff --git a/Example/Podfile.lock b/Example/Podfile.lock new file mode 100644 index 0000000..f700e7b --- /dev/null +++ b/Example/Podfile.lock @@ -0,0 +1,37 @@ +PODS: + - LWZComponents (1.0.0): + - LWZComponents/LWZCollectionViewComponents (= 1.0.0) + - LWZComponents/LWZCollectionViewComponents (1.0.0) + - Masonry (1.1.0) + - SJUIKit/AttributesFactory (0.0.0.58): + - SJUIKit/AttributesFactory/Deprecated (= 0.0.0.58) + - SJUIKit/AttributesFactory/UIKitText (= 0.0.0.58) + - SJUIKit/AttributesFactory/Deprecated (0.0.0.58) + - SJUIKit/AttributesFactory/UIKitText (0.0.0.58) + - YYModel (1.0.4) + +DEPENDENCIES: + - LWZComponents (from `../`) + - Masonry + - SJUIKit/AttributesFactory + - YYModel + +SPEC REPOS: + trunk: + - Masonry + - SJUIKit + - YYModel + +EXTERNAL SOURCES: + LWZComponents: + :path: "../" + +SPEC CHECKSUMS: + LWZComponents: f19371ecd3240e340582c6ad248873b25df38e4b + Masonry: 678fab65091a9290e40e2832a55e7ab731aad201 + SJUIKit: 86d5b16f8b8b17274ef8805acf965e4014629e15 + YYModel: 2a7fdd96aaa4b86a824e26d0c517de8928c04b30 + +PODFILE CHECKSUM: 23e50482f501cceba50da17046f8db03e5254e4b + +COCOAPODS: 1.11.2 diff --git a/Example/Pods/Headers/Private/LWZComponents/LWZCollectionDecoration.h b/Example/Pods/Headers/Private/LWZComponents/LWZCollectionDecoration.h new file mode 120000 index 0000000..f3d5e35 --- /dev/null +++ b/Example/Pods/Headers/Private/LWZComponents/LWZCollectionDecoration.h @@ -0,0 +1 @@ +../../../../../LWZComponents/LWZCollectionViewComponents/Provider/LWZCollectionDecoration.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/LWZComponents/LWZCollectionDefines.h b/Example/Pods/Headers/Private/LWZComponents/LWZCollectionDefines.h new file mode 120000 index 0000000..c82b230 --- /dev/null +++ b/Example/Pods/Headers/Private/LWZComponents/LWZCollectionDefines.h @@ -0,0 +1 @@ +../../../../../LWZComponents/LWZCollectionViewComponents/Defines/LWZCollectionDefines.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/LWZComponents/LWZCollectionInternals.h b/Example/Pods/Headers/Private/LWZComponents/LWZCollectionInternals.h new file mode 120000 index 0000000..f2c69f0 --- /dev/null +++ b/Example/Pods/Headers/Private/LWZComponents/LWZCollectionInternals.h @@ -0,0 +1 @@ +../../../../../LWZComponents/LWZCollectionViewComponents/Provider/LWZCollectionInternals.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/LWZComponents/LWZCollectionItem.h b/Example/Pods/Headers/Private/LWZComponents/LWZCollectionItem.h new file mode 120000 index 0000000..9787cf0 --- /dev/null +++ b/Example/Pods/Headers/Private/LWZComponents/LWZCollectionItem.h @@ -0,0 +1 @@ +../../../../../LWZComponents/LWZCollectionViewComponents/Provider/LWZCollectionItem.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/LWZComponents/LWZCollectionLayoutContentContainer.h b/Example/Pods/Headers/Private/LWZComponents/LWZCollectionLayoutContentContainer.h new file mode 120000 index 0000000..dbef4a1 --- /dev/null +++ b/Example/Pods/Headers/Private/LWZComponents/LWZCollectionLayoutContentContainer.h @@ -0,0 +1 @@ +../../../../../LWZComponents/LWZCollectionViewComponents/Layout/LWZCollectionLayoutContentContainer.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/LWZComponents/LWZCollectionLayoutFittingSize.h b/Example/Pods/Headers/Private/LWZComponents/LWZCollectionLayoutFittingSize.h new file mode 120000 index 0000000..a52729e --- /dev/null +++ b/Example/Pods/Headers/Private/LWZComponents/LWZCollectionLayoutFittingSize.h @@ -0,0 +1 @@ +../../../../../LWZComponents/LWZCollectionViewComponents/Layout/LWZCollectionLayoutFittingSize.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/LWZComponents/LWZCollectionProvider.h b/Example/Pods/Headers/Private/LWZComponents/LWZCollectionProvider.h new file mode 120000 index 0000000..5911b8a --- /dev/null +++ b/Example/Pods/Headers/Private/LWZComponents/LWZCollectionProvider.h @@ -0,0 +1 @@ +../../../../../LWZComponents/LWZCollectionViewComponents/Provider/LWZCollectionProvider.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/LWZComponents/LWZCollectionSection.h b/Example/Pods/Headers/Private/LWZComponents/LWZCollectionSection.h new file mode 120000 index 0000000..6eb5c65 --- /dev/null +++ b/Example/Pods/Headers/Private/LWZComponents/LWZCollectionSection.h @@ -0,0 +1 @@ +../../../../../LWZComponents/LWZCollectionViewComponents/Provider/LWZCollectionSection.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/LWZComponents/LWZCollectionSectionHeaderFooter.h b/Example/Pods/Headers/Private/LWZComponents/LWZCollectionSectionHeaderFooter.h new file mode 120000 index 0000000..1f7b97f --- /dev/null +++ b/Example/Pods/Headers/Private/LWZComponents/LWZCollectionSectionHeaderFooter.h @@ -0,0 +1 @@ +../../../../../LWZComponents/LWZCollectionViewComponents/Provider/LWZCollectionSectionHeaderFooter.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/LWZComponents/LWZCollectionView.h b/Example/Pods/Headers/Private/LWZComponents/LWZCollectionView.h new file mode 120000 index 0000000..040c4b3 --- /dev/null +++ b/Example/Pods/Headers/Private/LWZComponents/LWZCollectionView.h @@ -0,0 +1 @@ +../../../../../LWZComponents/LWZCollectionViewComponents/View/LWZCollectionView.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/LWZComponents/LWZCollectionViewComponents.h b/Example/Pods/Headers/Private/LWZComponents/LWZCollectionViewComponents.h new file mode 120000 index 0000000..9e450f1 --- /dev/null +++ b/Example/Pods/Headers/Private/LWZComponents/LWZCollectionViewComponents.h @@ -0,0 +1 @@ +../../../../../LWZComponents/LWZCollectionViewComponents/LWZCollectionViewComponents.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/LWZComponents/LWZCollectionViewDelegateProxy.h b/Example/Pods/Headers/Private/LWZComponents/LWZCollectionViewDelegateProxy.h new file mode 120000 index 0000000..a5542e1 --- /dev/null +++ b/Example/Pods/Headers/Private/LWZComponents/LWZCollectionViewDelegateProxy.h @@ -0,0 +1 @@ +../../../../../LWZComponents/LWZCollectionViewComponents/View/LWZCollectionViewDelegateProxy.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/LWZComponents/LWZCollectionViewLayout.h b/Example/Pods/Headers/Private/LWZComponents/LWZCollectionViewLayout.h new file mode 120000 index 0000000..e36d716 --- /dev/null +++ b/Example/Pods/Headers/Private/LWZComponents/LWZCollectionViewLayout.h @@ -0,0 +1 @@ +../../../../../LWZComponents/LWZCollectionViewComponents/Layout/LWZCollectionViewLayout.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/LWZComponents/LWZCollectionViewLayoutAttributes.h b/Example/Pods/Headers/Private/LWZComponents/LWZCollectionViewLayoutAttributes.h new file mode 120000 index 0000000..71075c4 --- /dev/null +++ b/Example/Pods/Headers/Private/LWZComponents/LWZCollectionViewLayoutAttributes.h @@ -0,0 +1 @@ +../../../../../LWZComponents/LWZCollectionViewComponents/Layout/LWZCollectionViewLayoutAttributes.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/LWZComponents/LWZCollectionViewLayoutSubclass.h b/Example/Pods/Headers/Private/LWZComponents/LWZCollectionViewLayoutSubclass.h new file mode 120000 index 0000000..7116443 --- /dev/null +++ b/Example/Pods/Headers/Private/LWZComponents/LWZCollectionViewLayoutSubclass.h @@ -0,0 +1 @@ +../../../../../LWZComponents/LWZCollectionViewComponents/Layout/LWZCollectionViewLayoutSubclass.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/LWZComponents/LWZCollectionViewPresenter.h b/Example/Pods/Headers/Private/LWZComponents/LWZCollectionViewPresenter.h new file mode 120000 index 0000000..d0c559c --- /dev/null +++ b/Example/Pods/Headers/Private/LWZComponents/LWZCollectionViewPresenter.h @@ -0,0 +1 @@ +../../../../../LWZComponents/LWZCollectionViewComponents/Presenter/LWZCollectionViewPresenter.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/LWZComponents/LWZCollectionViewRegister.h b/Example/Pods/Headers/Private/LWZComponents/LWZCollectionViewRegister.h new file mode 120000 index 0000000..19f54df --- /dev/null +++ b/Example/Pods/Headers/Private/LWZComponents/LWZCollectionViewRegister.h @@ -0,0 +1 @@ +../../../../../LWZComponents/LWZCollectionViewComponents/Presenter/LWZCollectionViewRegister.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/LWZComponents/LWZComponents.h b/Example/Pods/Headers/Private/LWZComponents/LWZComponents.h new file mode 120000 index 0000000..79d40db --- /dev/null +++ b/Example/Pods/Headers/Private/LWZComponents/LWZComponents.h @@ -0,0 +1 @@ +../../../../../LWZComponents/LWZComponents.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/Masonry/MASCompositeConstraint.h b/Example/Pods/Headers/Private/Masonry/MASCompositeConstraint.h new file mode 120000 index 0000000..b1b3410 --- /dev/null +++ b/Example/Pods/Headers/Private/Masonry/MASCompositeConstraint.h @@ -0,0 +1 @@ +../../../Masonry/Masonry/MASCompositeConstraint.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/Masonry/MASConstraint+Private.h b/Example/Pods/Headers/Private/Masonry/MASConstraint+Private.h new file mode 120000 index 0000000..ed9aa9f --- /dev/null +++ b/Example/Pods/Headers/Private/Masonry/MASConstraint+Private.h @@ -0,0 +1 @@ +../../../Masonry/Masonry/MASConstraint+Private.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/Masonry/MASConstraint.h b/Example/Pods/Headers/Private/Masonry/MASConstraint.h new file mode 120000 index 0000000..76e9e73 --- /dev/null +++ b/Example/Pods/Headers/Private/Masonry/MASConstraint.h @@ -0,0 +1 @@ +../../../Masonry/Masonry/MASConstraint.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/Masonry/MASConstraintMaker.h b/Example/Pods/Headers/Private/Masonry/MASConstraintMaker.h new file mode 120000 index 0000000..b6d6027 --- /dev/null +++ b/Example/Pods/Headers/Private/Masonry/MASConstraintMaker.h @@ -0,0 +1 @@ +../../../Masonry/Masonry/MASConstraintMaker.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/Masonry/MASLayoutConstraint.h b/Example/Pods/Headers/Private/Masonry/MASLayoutConstraint.h new file mode 120000 index 0000000..73e8796 --- /dev/null +++ b/Example/Pods/Headers/Private/Masonry/MASLayoutConstraint.h @@ -0,0 +1 @@ +../../../Masonry/Masonry/MASLayoutConstraint.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/Masonry/MASUtilities.h b/Example/Pods/Headers/Private/Masonry/MASUtilities.h new file mode 120000 index 0000000..aae5e32 --- /dev/null +++ b/Example/Pods/Headers/Private/Masonry/MASUtilities.h @@ -0,0 +1 @@ +../../../Masonry/Masonry/MASUtilities.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/Masonry/MASViewAttribute.h b/Example/Pods/Headers/Private/Masonry/MASViewAttribute.h new file mode 120000 index 0000000..5399f64 --- /dev/null +++ b/Example/Pods/Headers/Private/Masonry/MASViewAttribute.h @@ -0,0 +1 @@ +../../../Masonry/Masonry/MASViewAttribute.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/Masonry/MASViewConstraint.h b/Example/Pods/Headers/Private/Masonry/MASViewConstraint.h new file mode 120000 index 0000000..382c419 --- /dev/null +++ b/Example/Pods/Headers/Private/Masonry/MASViewConstraint.h @@ -0,0 +1 @@ +../../../Masonry/Masonry/MASViewConstraint.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/Masonry/Masonry.h b/Example/Pods/Headers/Private/Masonry/Masonry.h new file mode 120000 index 0000000..f509303 --- /dev/null +++ b/Example/Pods/Headers/Private/Masonry/Masonry.h @@ -0,0 +1 @@ +../../../Masonry/Masonry/Masonry.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/Masonry/NSArray+MASAdditions.h b/Example/Pods/Headers/Private/Masonry/NSArray+MASAdditions.h new file mode 120000 index 0000000..db1d0ea --- /dev/null +++ b/Example/Pods/Headers/Private/Masonry/NSArray+MASAdditions.h @@ -0,0 +1 @@ +../../../Masonry/Masonry/NSArray+MASAdditions.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/Masonry/NSArray+MASShorthandAdditions.h b/Example/Pods/Headers/Private/Masonry/NSArray+MASShorthandAdditions.h new file mode 120000 index 0000000..2169edc --- /dev/null +++ b/Example/Pods/Headers/Private/Masonry/NSArray+MASShorthandAdditions.h @@ -0,0 +1 @@ +../../../Masonry/Masonry/NSArray+MASShorthandAdditions.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/Masonry/NSLayoutConstraint+MASDebugAdditions.h b/Example/Pods/Headers/Private/Masonry/NSLayoutConstraint+MASDebugAdditions.h new file mode 120000 index 0000000..72f5943 --- /dev/null +++ b/Example/Pods/Headers/Private/Masonry/NSLayoutConstraint+MASDebugAdditions.h @@ -0,0 +1 @@ +../../../Masonry/Masonry/NSLayoutConstraint+MASDebugAdditions.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/Masonry/View+MASAdditions.h b/Example/Pods/Headers/Private/Masonry/View+MASAdditions.h new file mode 120000 index 0000000..9060451 --- /dev/null +++ b/Example/Pods/Headers/Private/Masonry/View+MASAdditions.h @@ -0,0 +1 @@ +../../../Masonry/Masonry/View+MASAdditions.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/Masonry/View+MASShorthandAdditions.h b/Example/Pods/Headers/Private/Masonry/View+MASShorthandAdditions.h new file mode 120000 index 0000000..91ef93f --- /dev/null +++ b/Example/Pods/Headers/Private/Masonry/View+MASShorthandAdditions.h @@ -0,0 +1 @@ +../../../Masonry/Masonry/View+MASShorthandAdditions.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/Masonry/ViewController+MASAdditions.h b/Example/Pods/Headers/Private/Masonry/ViewController+MASAdditions.h new file mode 120000 index 0000000..12320e3 --- /dev/null +++ b/Example/Pods/Headers/Private/Masonry/ViewController+MASAdditions.h @@ -0,0 +1 @@ +../../../Masonry/Masonry/ViewController+MASAdditions.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/SJUIKit/NSAttributedString+SJMake.h b/Example/Pods/Headers/Private/SJUIKit/NSAttributedString+SJMake.h new file mode 120000 index 0000000..b6b8df6 --- /dev/null +++ b/Example/Pods/Headers/Private/SJUIKit/NSAttributedString+SJMake.h @@ -0,0 +1 @@ +../../../SJUIKit/SJUIKit/AttributesFactory/NSAttributedString+SJMake.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/SJUIKit/SJAttributeWorker.h b/Example/Pods/Headers/Private/SJUIKit/SJAttributeWorker.h new file mode 120000 index 0000000..64ab872 --- /dev/null +++ b/Example/Pods/Headers/Private/SJUIKit/SJAttributeWorker.h @@ -0,0 +1 @@ +../../../SJUIKit/SJUIKit/AttributesFactory/Deprecated/SJAttributeWorker.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/SJUIKit/SJAttributesFactory.h b/Example/Pods/Headers/Private/SJUIKit/SJAttributesFactory.h new file mode 120000 index 0000000..e38a326 --- /dev/null +++ b/Example/Pods/Headers/Private/SJUIKit/SJAttributesFactory.h @@ -0,0 +1 @@ +../../../SJUIKit/SJUIKit/AttributesFactory/SJAttributesFactory.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/SJUIKit/SJAttributesRecorder.h b/Example/Pods/Headers/Private/SJUIKit/SJAttributesRecorder.h new file mode 120000 index 0000000..05d2617 --- /dev/null +++ b/Example/Pods/Headers/Private/SJUIKit/SJAttributesRecorder.h @@ -0,0 +1 @@ +../../../SJUIKit/SJUIKit/AttributesFactory/Deprecated/SJAttributesRecorder.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/SJUIKit/SJUIKitAttributesDefines.h b/Example/Pods/Headers/Private/SJUIKit/SJUIKitAttributesDefines.h new file mode 120000 index 0000000..d039f24 --- /dev/null +++ b/Example/Pods/Headers/Private/SJUIKit/SJUIKitAttributesDefines.h @@ -0,0 +1 @@ +../../../SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUIKitAttributesDefines.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/SJUIKit/SJUIKitTextMaker.h b/Example/Pods/Headers/Private/SJUIKit/SJUIKitTextMaker.h new file mode 120000 index 0000000..a5de327 --- /dev/null +++ b/Example/Pods/Headers/Private/SJUIKit/SJUIKitTextMaker.h @@ -0,0 +1 @@ +../../../SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUIKitTextMaker.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/SJUIKit/SJUTAttributes.h b/Example/Pods/Headers/Private/SJUIKit/SJUTAttributes.h new file mode 120000 index 0000000..5a14f6a --- /dev/null +++ b/Example/Pods/Headers/Private/SJUIKit/SJUTAttributes.h @@ -0,0 +1 @@ +../../../SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUTAttributes.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/SJUIKit/SJUTRangeHandler.h b/Example/Pods/Headers/Private/SJUIKit/SJUTRangeHandler.h new file mode 120000 index 0000000..6db67bc --- /dev/null +++ b/Example/Pods/Headers/Private/SJUIKit/SJUTRangeHandler.h @@ -0,0 +1 @@ +../../../SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUTRangeHandler.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/SJUIKit/SJUTRecorder.h b/Example/Pods/Headers/Private/SJUIKit/SJUTRecorder.h new file mode 120000 index 0000000..20a1efd --- /dev/null +++ b/Example/Pods/Headers/Private/SJUIKit/SJUTRecorder.h @@ -0,0 +1 @@ +../../../SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUTRecorder.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/SJUIKit/SJUTRegexHandler.h b/Example/Pods/Headers/Private/SJUIKit/SJUTRegexHandler.h new file mode 120000 index 0000000..2add68c --- /dev/null +++ b/Example/Pods/Headers/Private/SJUIKit/SJUTRegexHandler.h @@ -0,0 +1 @@ +../../../SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUTRegexHandler.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/SJUIKit/SJUTUtils.h b/Example/Pods/Headers/Private/SJUIKit/SJUTUtils.h new file mode 120000 index 0000000..6ef7b1a --- /dev/null +++ b/Example/Pods/Headers/Private/SJUIKit/SJUTUtils.h @@ -0,0 +1 @@ +../../../SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUTUtils.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/YYModel/NSObject+YYModel.h b/Example/Pods/Headers/Private/YYModel/NSObject+YYModel.h new file mode 120000 index 0000000..bce0afa --- /dev/null +++ b/Example/Pods/Headers/Private/YYModel/NSObject+YYModel.h @@ -0,0 +1 @@ +../../../YYModel/YYModel/NSObject+YYModel.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/YYModel/YYClassInfo.h b/Example/Pods/Headers/Private/YYModel/YYClassInfo.h new file mode 120000 index 0000000..5d2dd13 --- /dev/null +++ b/Example/Pods/Headers/Private/YYModel/YYClassInfo.h @@ -0,0 +1 @@ +../../../YYModel/YYModel/YYClassInfo.h \ No newline at end of file diff --git a/Example/Pods/Headers/Private/YYModel/YYModel.h b/Example/Pods/Headers/Private/YYModel/YYModel.h new file mode 120000 index 0000000..572e923 --- /dev/null +++ b/Example/Pods/Headers/Private/YYModel/YYModel.h @@ -0,0 +1 @@ +../../../YYModel/YYModel/YYModel.h \ No newline at end of file diff --git a/Example/Pods/Headers/Public/LWZComponents/LWZCollectionDecoration.h b/Example/Pods/Headers/Public/LWZComponents/LWZCollectionDecoration.h new file mode 120000 index 0000000..f3d5e35 --- /dev/null +++ b/Example/Pods/Headers/Public/LWZComponents/LWZCollectionDecoration.h @@ -0,0 +1 @@ +../../../../../LWZComponents/LWZCollectionViewComponents/Provider/LWZCollectionDecoration.h \ No newline at end of file diff --git a/Example/Pods/Headers/Public/LWZComponents/LWZCollectionDefines.h b/Example/Pods/Headers/Public/LWZComponents/LWZCollectionDefines.h new file mode 120000 index 0000000..c82b230 --- /dev/null +++ b/Example/Pods/Headers/Public/LWZComponents/LWZCollectionDefines.h @@ -0,0 +1 @@ +../../../../../LWZComponents/LWZCollectionViewComponents/Defines/LWZCollectionDefines.h \ No newline at end of file diff --git a/Example/Pods/Headers/Public/LWZComponents/LWZCollectionInternals.h b/Example/Pods/Headers/Public/LWZComponents/LWZCollectionInternals.h new file mode 120000 index 0000000..f2c69f0 --- /dev/null +++ b/Example/Pods/Headers/Public/LWZComponents/LWZCollectionInternals.h @@ -0,0 +1 @@ +../../../../../LWZComponents/LWZCollectionViewComponents/Provider/LWZCollectionInternals.h \ No newline at end of file diff --git a/Example/Pods/Headers/Public/LWZComponents/LWZCollectionItem.h b/Example/Pods/Headers/Public/LWZComponents/LWZCollectionItem.h new file mode 120000 index 0000000..9787cf0 --- /dev/null +++ b/Example/Pods/Headers/Public/LWZComponents/LWZCollectionItem.h @@ -0,0 +1 @@ +../../../../../LWZComponents/LWZCollectionViewComponents/Provider/LWZCollectionItem.h \ No newline at end of file diff --git a/Example/Pods/Headers/Public/LWZComponents/LWZCollectionLayoutContentContainer.h b/Example/Pods/Headers/Public/LWZComponents/LWZCollectionLayoutContentContainer.h new file mode 120000 index 0000000..dbef4a1 --- /dev/null +++ b/Example/Pods/Headers/Public/LWZComponents/LWZCollectionLayoutContentContainer.h @@ -0,0 +1 @@ +../../../../../LWZComponents/LWZCollectionViewComponents/Layout/LWZCollectionLayoutContentContainer.h \ No newline at end of file diff --git a/Example/Pods/Headers/Public/LWZComponents/LWZCollectionLayoutFittingSize.h b/Example/Pods/Headers/Public/LWZComponents/LWZCollectionLayoutFittingSize.h new file mode 120000 index 0000000..a52729e --- /dev/null +++ b/Example/Pods/Headers/Public/LWZComponents/LWZCollectionLayoutFittingSize.h @@ -0,0 +1 @@ +../../../../../LWZComponents/LWZCollectionViewComponents/Layout/LWZCollectionLayoutFittingSize.h \ No newline at end of file diff --git a/Example/Pods/Headers/Public/LWZComponents/LWZCollectionProvider.h b/Example/Pods/Headers/Public/LWZComponents/LWZCollectionProvider.h new file mode 120000 index 0000000..5911b8a --- /dev/null +++ b/Example/Pods/Headers/Public/LWZComponents/LWZCollectionProvider.h @@ -0,0 +1 @@ +../../../../../LWZComponents/LWZCollectionViewComponents/Provider/LWZCollectionProvider.h \ No newline at end of file diff --git a/Example/Pods/Headers/Public/LWZComponents/LWZCollectionSection.h b/Example/Pods/Headers/Public/LWZComponents/LWZCollectionSection.h new file mode 120000 index 0000000..6eb5c65 --- /dev/null +++ b/Example/Pods/Headers/Public/LWZComponents/LWZCollectionSection.h @@ -0,0 +1 @@ +../../../../../LWZComponents/LWZCollectionViewComponents/Provider/LWZCollectionSection.h \ No newline at end of file diff --git a/Example/Pods/Headers/Public/LWZComponents/LWZCollectionSectionHeaderFooter.h b/Example/Pods/Headers/Public/LWZComponents/LWZCollectionSectionHeaderFooter.h new file mode 120000 index 0000000..1f7b97f --- /dev/null +++ b/Example/Pods/Headers/Public/LWZComponents/LWZCollectionSectionHeaderFooter.h @@ -0,0 +1 @@ +../../../../../LWZComponents/LWZCollectionViewComponents/Provider/LWZCollectionSectionHeaderFooter.h \ No newline at end of file diff --git a/Example/Pods/Headers/Public/LWZComponents/LWZCollectionView.h b/Example/Pods/Headers/Public/LWZComponents/LWZCollectionView.h new file mode 120000 index 0000000..040c4b3 --- /dev/null +++ b/Example/Pods/Headers/Public/LWZComponents/LWZCollectionView.h @@ -0,0 +1 @@ +../../../../../LWZComponents/LWZCollectionViewComponents/View/LWZCollectionView.h \ No newline at end of file diff --git a/Example/Pods/Headers/Public/LWZComponents/LWZCollectionViewComponents.h b/Example/Pods/Headers/Public/LWZComponents/LWZCollectionViewComponents.h new file mode 120000 index 0000000..9e450f1 --- /dev/null +++ b/Example/Pods/Headers/Public/LWZComponents/LWZCollectionViewComponents.h @@ -0,0 +1 @@ +../../../../../LWZComponents/LWZCollectionViewComponents/LWZCollectionViewComponents.h \ No newline at end of file diff --git a/Example/Pods/Headers/Public/LWZComponents/LWZCollectionViewDelegateProxy.h b/Example/Pods/Headers/Public/LWZComponents/LWZCollectionViewDelegateProxy.h new file mode 120000 index 0000000..a5542e1 --- /dev/null +++ b/Example/Pods/Headers/Public/LWZComponents/LWZCollectionViewDelegateProxy.h @@ -0,0 +1 @@ +../../../../../LWZComponents/LWZCollectionViewComponents/View/LWZCollectionViewDelegateProxy.h \ No newline at end of file diff --git a/Example/Pods/Headers/Public/LWZComponents/LWZCollectionViewLayout.h b/Example/Pods/Headers/Public/LWZComponents/LWZCollectionViewLayout.h new file mode 120000 index 0000000..e36d716 --- /dev/null +++ b/Example/Pods/Headers/Public/LWZComponents/LWZCollectionViewLayout.h @@ -0,0 +1 @@ +../../../../../LWZComponents/LWZCollectionViewComponents/Layout/LWZCollectionViewLayout.h \ No newline at end of file diff --git a/Example/Pods/Headers/Public/LWZComponents/LWZCollectionViewLayoutAttributes.h b/Example/Pods/Headers/Public/LWZComponents/LWZCollectionViewLayoutAttributes.h new file mode 120000 index 0000000..71075c4 --- /dev/null +++ b/Example/Pods/Headers/Public/LWZComponents/LWZCollectionViewLayoutAttributes.h @@ -0,0 +1 @@ +../../../../../LWZComponents/LWZCollectionViewComponents/Layout/LWZCollectionViewLayoutAttributes.h \ No newline at end of file diff --git a/Example/Pods/Headers/Public/LWZComponents/LWZCollectionViewLayoutSubclass.h b/Example/Pods/Headers/Public/LWZComponents/LWZCollectionViewLayoutSubclass.h new file mode 120000 index 0000000..7116443 --- /dev/null +++ b/Example/Pods/Headers/Public/LWZComponents/LWZCollectionViewLayoutSubclass.h @@ -0,0 +1 @@ +../../../../../LWZComponents/LWZCollectionViewComponents/Layout/LWZCollectionViewLayoutSubclass.h \ No newline at end of file diff --git a/Example/Pods/Headers/Public/LWZComponents/LWZCollectionViewPresenter.h b/Example/Pods/Headers/Public/LWZComponents/LWZCollectionViewPresenter.h new file mode 120000 index 0000000..d0c559c --- /dev/null +++ b/Example/Pods/Headers/Public/LWZComponents/LWZCollectionViewPresenter.h @@ -0,0 +1 @@ +../../../../../LWZComponents/LWZCollectionViewComponents/Presenter/LWZCollectionViewPresenter.h \ No newline at end of file diff --git a/Example/Pods/Headers/Public/LWZComponents/LWZCollectionViewRegister.h b/Example/Pods/Headers/Public/LWZComponents/LWZCollectionViewRegister.h new file mode 120000 index 0000000..19f54df --- /dev/null +++ b/Example/Pods/Headers/Public/LWZComponents/LWZCollectionViewRegister.h @@ -0,0 +1 @@ +../../../../../LWZComponents/LWZCollectionViewComponents/Presenter/LWZCollectionViewRegister.h \ No newline at end of file diff --git a/Example/Pods/Headers/Public/LWZComponents/LWZComponents.h b/Example/Pods/Headers/Public/LWZComponents/LWZComponents.h new file mode 120000 index 0000000..79d40db --- /dev/null +++ b/Example/Pods/Headers/Public/LWZComponents/LWZComponents.h @@ -0,0 +1 @@ +../../../../../LWZComponents/LWZComponents.h \ No newline at end of file diff --git a/Example/Pods/Headers/Public/Masonry/MASCompositeConstraint.h b/Example/Pods/Headers/Public/Masonry/MASCompositeConstraint.h new file mode 120000 index 0000000..b1b3410 --- /dev/null +++ b/Example/Pods/Headers/Public/Masonry/MASCompositeConstraint.h @@ -0,0 +1 @@ +../../../Masonry/Masonry/MASCompositeConstraint.h \ No newline at end of file diff --git a/Example/Pods/Headers/Public/Masonry/MASConstraint+Private.h b/Example/Pods/Headers/Public/Masonry/MASConstraint+Private.h new file mode 120000 index 0000000..ed9aa9f --- /dev/null +++ b/Example/Pods/Headers/Public/Masonry/MASConstraint+Private.h @@ -0,0 +1 @@ +../../../Masonry/Masonry/MASConstraint+Private.h \ No newline at end of file diff --git a/Example/Pods/Headers/Public/Masonry/MASConstraint.h b/Example/Pods/Headers/Public/Masonry/MASConstraint.h new file mode 120000 index 0000000..76e9e73 --- /dev/null +++ b/Example/Pods/Headers/Public/Masonry/MASConstraint.h @@ -0,0 +1 @@ +../../../Masonry/Masonry/MASConstraint.h \ No newline at end of file diff --git a/Example/Pods/Headers/Public/Masonry/MASConstraintMaker.h b/Example/Pods/Headers/Public/Masonry/MASConstraintMaker.h new file mode 120000 index 0000000..b6d6027 --- /dev/null +++ b/Example/Pods/Headers/Public/Masonry/MASConstraintMaker.h @@ -0,0 +1 @@ +../../../Masonry/Masonry/MASConstraintMaker.h \ No newline at end of file diff --git a/Example/Pods/Headers/Public/Masonry/MASLayoutConstraint.h b/Example/Pods/Headers/Public/Masonry/MASLayoutConstraint.h new file mode 120000 index 0000000..73e8796 --- /dev/null +++ b/Example/Pods/Headers/Public/Masonry/MASLayoutConstraint.h @@ -0,0 +1 @@ +../../../Masonry/Masonry/MASLayoutConstraint.h \ No newline at end of file diff --git a/Example/Pods/Headers/Public/Masonry/MASUtilities.h b/Example/Pods/Headers/Public/Masonry/MASUtilities.h new file mode 120000 index 0000000..aae5e32 --- /dev/null +++ b/Example/Pods/Headers/Public/Masonry/MASUtilities.h @@ -0,0 +1 @@ +../../../Masonry/Masonry/MASUtilities.h \ No newline at end of file diff --git a/Example/Pods/Headers/Public/Masonry/MASViewAttribute.h b/Example/Pods/Headers/Public/Masonry/MASViewAttribute.h new file mode 120000 index 0000000..5399f64 --- /dev/null +++ b/Example/Pods/Headers/Public/Masonry/MASViewAttribute.h @@ -0,0 +1 @@ +../../../Masonry/Masonry/MASViewAttribute.h \ No newline at end of file diff --git a/Example/Pods/Headers/Public/Masonry/MASViewConstraint.h b/Example/Pods/Headers/Public/Masonry/MASViewConstraint.h new file mode 120000 index 0000000..382c419 --- /dev/null +++ b/Example/Pods/Headers/Public/Masonry/MASViewConstraint.h @@ -0,0 +1 @@ +../../../Masonry/Masonry/MASViewConstraint.h \ No newline at end of file diff --git a/Example/Pods/Headers/Public/Masonry/Masonry.h b/Example/Pods/Headers/Public/Masonry/Masonry.h new file mode 120000 index 0000000..f509303 --- /dev/null +++ b/Example/Pods/Headers/Public/Masonry/Masonry.h @@ -0,0 +1 @@ +../../../Masonry/Masonry/Masonry.h \ No newline at end of file diff --git a/Example/Pods/Headers/Public/Masonry/NSArray+MASAdditions.h b/Example/Pods/Headers/Public/Masonry/NSArray+MASAdditions.h new file mode 120000 index 0000000..db1d0ea --- /dev/null +++ b/Example/Pods/Headers/Public/Masonry/NSArray+MASAdditions.h @@ -0,0 +1 @@ +../../../Masonry/Masonry/NSArray+MASAdditions.h \ No newline at end of file diff --git a/Example/Pods/Headers/Public/Masonry/NSArray+MASShorthandAdditions.h b/Example/Pods/Headers/Public/Masonry/NSArray+MASShorthandAdditions.h new file mode 120000 index 0000000..2169edc --- /dev/null +++ b/Example/Pods/Headers/Public/Masonry/NSArray+MASShorthandAdditions.h @@ -0,0 +1 @@ +../../../Masonry/Masonry/NSArray+MASShorthandAdditions.h \ No newline at end of file diff --git a/Example/Pods/Headers/Public/Masonry/NSLayoutConstraint+MASDebugAdditions.h b/Example/Pods/Headers/Public/Masonry/NSLayoutConstraint+MASDebugAdditions.h new file mode 120000 index 0000000..72f5943 --- /dev/null +++ b/Example/Pods/Headers/Public/Masonry/NSLayoutConstraint+MASDebugAdditions.h @@ -0,0 +1 @@ +../../../Masonry/Masonry/NSLayoutConstraint+MASDebugAdditions.h \ No newline at end of file diff --git a/Example/Pods/Headers/Public/Masonry/View+MASAdditions.h b/Example/Pods/Headers/Public/Masonry/View+MASAdditions.h new file mode 120000 index 0000000..9060451 --- /dev/null +++ b/Example/Pods/Headers/Public/Masonry/View+MASAdditions.h @@ -0,0 +1 @@ +../../../Masonry/Masonry/View+MASAdditions.h \ No newline at end of file diff --git a/Example/Pods/Headers/Public/Masonry/View+MASShorthandAdditions.h b/Example/Pods/Headers/Public/Masonry/View+MASShorthandAdditions.h new file mode 120000 index 0000000..91ef93f --- /dev/null +++ b/Example/Pods/Headers/Public/Masonry/View+MASShorthandAdditions.h @@ -0,0 +1 @@ +../../../Masonry/Masonry/View+MASShorthandAdditions.h \ No newline at end of file diff --git a/Example/Pods/Headers/Public/Masonry/ViewController+MASAdditions.h b/Example/Pods/Headers/Public/Masonry/ViewController+MASAdditions.h new file mode 120000 index 0000000..12320e3 --- /dev/null +++ b/Example/Pods/Headers/Public/Masonry/ViewController+MASAdditions.h @@ -0,0 +1 @@ +../../../Masonry/Masonry/ViewController+MASAdditions.h \ No newline at end of file diff --git a/Example/Pods/Headers/Public/SJUIKit/NSAttributedString+SJMake.h b/Example/Pods/Headers/Public/SJUIKit/NSAttributedString+SJMake.h new file mode 120000 index 0000000..b6b8df6 --- /dev/null +++ b/Example/Pods/Headers/Public/SJUIKit/NSAttributedString+SJMake.h @@ -0,0 +1 @@ +../../../SJUIKit/SJUIKit/AttributesFactory/NSAttributedString+SJMake.h \ No newline at end of file diff --git a/Example/Pods/Headers/Public/SJUIKit/SJAttributeWorker.h b/Example/Pods/Headers/Public/SJUIKit/SJAttributeWorker.h new file mode 120000 index 0000000..64ab872 --- /dev/null +++ b/Example/Pods/Headers/Public/SJUIKit/SJAttributeWorker.h @@ -0,0 +1 @@ +../../../SJUIKit/SJUIKit/AttributesFactory/Deprecated/SJAttributeWorker.h \ No newline at end of file diff --git a/Example/Pods/Headers/Public/SJUIKit/SJAttributesFactory.h b/Example/Pods/Headers/Public/SJUIKit/SJAttributesFactory.h new file mode 120000 index 0000000..e38a326 --- /dev/null +++ b/Example/Pods/Headers/Public/SJUIKit/SJAttributesFactory.h @@ -0,0 +1 @@ +../../../SJUIKit/SJUIKit/AttributesFactory/SJAttributesFactory.h \ No newline at end of file diff --git a/Example/Pods/Headers/Public/SJUIKit/SJAttributesRecorder.h b/Example/Pods/Headers/Public/SJUIKit/SJAttributesRecorder.h new file mode 120000 index 0000000..05d2617 --- /dev/null +++ b/Example/Pods/Headers/Public/SJUIKit/SJAttributesRecorder.h @@ -0,0 +1 @@ +../../../SJUIKit/SJUIKit/AttributesFactory/Deprecated/SJAttributesRecorder.h \ No newline at end of file diff --git a/Example/Pods/Headers/Public/SJUIKit/SJUIKitAttributesDefines.h b/Example/Pods/Headers/Public/SJUIKit/SJUIKitAttributesDefines.h new file mode 120000 index 0000000..d039f24 --- /dev/null +++ b/Example/Pods/Headers/Public/SJUIKit/SJUIKitAttributesDefines.h @@ -0,0 +1 @@ +../../../SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUIKitAttributesDefines.h \ No newline at end of file diff --git a/Example/Pods/Headers/Public/SJUIKit/SJUIKitTextMaker.h b/Example/Pods/Headers/Public/SJUIKit/SJUIKitTextMaker.h new file mode 120000 index 0000000..a5de327 --- /dev/null +++ b/Example/Pods/Headers/Public/SJUIKit/SJUIKitTextMaker.h @@ -0,0 +1 @@ +../../../SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUIKitTextMaker.h \ No newline at end of file diff --git a/Example/Pods/Headers/Public/SJUIKit/SJUTAttributes.h b/Example/Pods/Headers/Public/SJUIKit/SJUTAttributes.h new file mode 120000 index 0000000..5a14f6a --- /dev/null +++ b/Example/Pods/Headers/Public/SJUIKit/SJUTAttributes.h @@ -0,0 +1 @@ +../../../SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUTAttributes.h \ No newline at end of file diff --git a/Example/Pods/Headers/Public/SJUIKit/SJUTRangeHandler.h b/Example/Pods/Headers/Public/SJUIKit/SJUTRangeHandler.h new file mode 120000 index 0000000..6db67bc --- /dev/null +++ b/Example/Pods/Headers/Public/SJUIKit/SJUTRangeHandler.h @@ -0,0 +1 @@ +../../../SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUTRangeHandler.h \ No newline at end of file diff --git a/Example/Pods/Headers/Public/SJUIKit/SJUTRecorder.h b/Example/Pods/Headers/Public/SJUIKit/SJUTRecorder.h new file mode 120000 index 0000000..20a1efd --- /dev/null +++ b/Example/Pods/Headers/Public/SJUIKit/SJUTRecorder.h @@ -0,0 +1 @@ +../../../SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUTRecorder.h \ No newline at end of file diff --git a/Example/Pods/Headers/Public/SJUIKit/SJUTRegexHandler.h b/Example/Pods/Headers/Public/SJUIKit/SJUTRegexHandler.h new file mode 120000 index 0000000..2add68c --- /dev/null +++ b/Example/Pods/Headers/Public/SJUIKit/SJUTRegexHandler.h @@ -0,0 +1 @@ +../../../SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUTRegexHandler.h \ No newline at end of file diff --git a/Example/Pods/Headers/Public/SJUIKit/SJUTUtils.h b/Example/Pods/Headers/Public/SJUIKit/SJUTUtils.h new file mode 120000 index 0000000..6ef7b1a --- /dev/null +++ b/Example/Pods/Headers/Public/SJUIKit/SJUTUtils.h @@ -0,0 +1 @@ +../../../SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUTUtils.h \ No newline at end of file diff --git a/Example/Pods/Headers/Public/YYModel/NSObject+YYModel.h b/Example/Pods/Headers/Public/YYModel/NSObject+YYModel.h new file mode 120000 index 0000000..bce0afa --- /dev/null +++ b/Example/Pods/Headers/Public/YYModel/NSObject+YYModel.h @@ -0,0 +1 @@ +../../../YYModel/YYModel/NSObject+YYModel.h \ No newline at end of file diff --git a/Example/Pods/Headers/Public/YYModel/YYClassInfo.h b/Example/Pods/Headers/Public/YYModel/YYClassInfo.h new file mode 120000 index 0000000..5d2dd13 --- /dev/null +++ b/Example/Pods/Headers/Public/YYModel/YYClassInfo.h @@ -0,0 +1 @@ +../../../YYModel/YYModel/YYClassInfo.h \ No newline at end of file diff --git a/Example/Pods/Headers/Public/YYModel/YYModel.h b/Example/Pods/Headers/Public/YYModel/YYModel.h new file mode 120000 index 0000000..572e923 --- /dev/null +++ b/Example/Pods/Headers/Public/YYModel/YYModel.h @@ -0,0 +1 @@ +../../../YYModel/YYModel/YYModel.h \ No newline at end of file diff --git a/Example/Pods/Local Podspecs/LWZComponents.podspec.json b/Example/Pods/Local Podspecs/LWZComponents.podspec.json new file mode 100644 index 0000000..4783295 --- /dev/null +++ b/Example/Pods/Local Podspecs/LWZComponents.podspec.json @@ -0,0 +1,28 @@ +{ + "name": "LWZComponents", + "version": "1.0.0", + "summary": "一些组件库.", + "description": "https://github.com/changsanjiang/LWZComponents/blob/master/README.md", + "homepage": "https://github.com/changsanjiang/LWZComponents", + "license": { + "type": "MIT", + "file": "LICENSE" + }, + "authors": { + "changsanjiang": "changsanjiang@gmail.com" + }, + "source": { + "git": "https://github.com/changsanjiang/LWZComponents.git", + "tag": "1.0.0" + }, + "platforms": { + "ios": "9.0" + }, + "source_files": "LWZComponents/*.{h,m}", + "subspecs": [ + { + "name": "LWZCollectionViewComponents", + "source_files": "LWZComponents/LWZCollectionViewComponents/**/*.{h,m}" + } + ] +} diff --git a/Example/Pods/Manifest.lock b/Example/Pods/Manifest.lock new file mode 100644 index 0000000..f700e7b --- /dev/null +++ b/Example/Pods/Manifest.lock @@ -0,0 +1,37 @@ +PODS: + - LWZComponents (1.0.0): + - LWZComponents/LWZCollectionViewComponents (= 1.0.0) + - LWZComponents/LWZCollectionViewComponents (1.0.0) + - Masonry (1.1.0) + - SJUIKit/AttributesFactory (0.0.0.58): + - SJUIKit/AttributesFactory/Deprecated (= 0.0.0.58) + - SJUIKit/AttributesFactory/UIKitText (= 0.0.0.58) + - SJUIKit/AttributesFactory/Deprecated (0.0.0.58) + - SJUIKit/AttributesFactory/UIKitText (0.0.0.58) + - YYModel (1.0.4) + +DEPENDENCIES: + - LWZComponents (from `../`) + - Masonry + - SJUIKit/AttributesFactory + - YYModel + +SPEC REPOS: + trunk: + - Masonry + - SJUIKit + - YYModel + +EXTERNAL SOURCES: + LWZComponents: + :path: "../" + +SPEC CHECKSUMS: + LWZComponents: f19371ecd3240e340582c6ad248873b25df38e4b + Masonry: 678fab65091a9290e40e2832a55e7ab731aad201 + SJUIKit: 86d5b16f8b8b17274ef8805acf965e4014629e15 + YYModel: 2a7fdd96aaa4b86a824e26d0c517de8928c04b30 + +PODFILE CHECKSUM: 23e50482f501cceba50da17046f8db03e5254e4b + +COCOAPODS: 1.11.2 diff --git a/Example/Pods/Masonry/LICENSE b/Example/Pods/Masonry/LICENSE new file mode 100644 index 0000000..a843c00 --- /dev/null +++ b/Example/Pods/Masonry/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2011-2012 Masonry Team - https://github.com/Masonry + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +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 THE +AUTHORS OR COPYRIGHT HOLDERS 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. \ No newline at end of file diff --git a/Example/Pods/Masonry/Masonry/MASCompositeConstraint.h b/Example/Pods/Masonry/Masonry/MASCompositeConstraint.h new file mode 100644 index 0000000..934c6f1 --- /dev/null +++ b/Example/Pods/Masonry/Masonry/MASCompositeConstraint.h @@ -0,0 +1,26 @@ +// +// MASCompositeConstraint.h +// Masonry +// +// Created by Jonas Budelmann on 21/07/13. +// Copyright (c) 2013 cloudling. All rights reserved. +// + +#import "MASConstraint.h" +#import "MASUtilities.h" + +/** + * A group of MASConstraint objects + */ +@interface MASCompositeConstraint : MASConstraint + +/** + * Creates a composite with a predefined array of children + * + * @param children child MASConstraints + * + * @return a composite constraint + */ +- (id)initWithChildren:(NSArray *)children; + +@end diff --git a/Example/Pods/Masonry/Masonry/MASCompositeConstraint.m b/Example/Pods/Masonry/Masonry/MASCompositeConstraint.m new file mode 100644 index 0000000..2002a40 --- /dev/null +++ b/Example/Pods/Masonry/Masonry/MASCompositeConstraint.m @@ -0,0 +1,183 @@ +// +// MASCompositeConstraint.m +// Masonry +// +// Created by Jonas Budelmann on 21/07/13. +// Copyright (c) 2013 cloudling. All rights reserved. +// + +#import "MASCompositeConstraint.h" +#import "MASConstraint+Private.h" + +@interface MASCompositeConstraint () + +@property (nonatomic, strong) id mas_key; +@property (nonatomic, strong) NSMutableArray *childConstraints; + +@end + +@implementation MASCompositeConstraint + +- (id)initWithChildren:(NSArray *)children { + self = [super init]; + if (!self) return nil; + + _childConstraints = [children mutableCopy]; + for (MASConstraint *constraint in _childConstraints) { + constraint.delegate = self; + } + + return self; +} + +#pragma mark - MASConstraintDelegate + +- (void)constraint:(MASConstraint *)constraint shouldBeReplacedWithConstraint:(MASConstraint *)replacementConstraint { + NSUInteger index = [self.childConstraints indexOfObject:constraint]; + NSAssert(index != NSNotFound, @"Could not find constraint %@", constraint); + [self.childConstraints replaceObjectAtIndex:index withObject:replacementConstraint]; +} + +- (MASConstraint *)constraint:(MASConstraint __unused *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute { + id strongDelegate = self.delegate; + MASConstraint *newConstraint = [strongDelegate constraint:self addConstraintWithLayoutAttribute:layoutAttribute]; + newConstraint.delegate = self; + [self.childConstraints addObject:newConstraint]; + return newConstraint; +} + +#pragma mark - NSLayoutConstraint multiplier proxies + +- (MASConstraint * (^)(CGFloat))multipliedBy { + return ^id(CGFloat multiplier) { + for (MASConstraint *constraint in self.childConstraints) { + constraint.multipliedBy(multiplier); + } + return self; + }; +} + +- (MASConstraint * (^)(CGFloat))dividedBy { + return ^id(CGFloat divider) { + for (MASConstraint *constraint in self.childConstraints) { + constraint.dividedBy(divider); + } + return self; + }; +} + +#pragma mark - MASLayoutPriority proxy + +- (MASConstraint * (^)(MASLayoutPriority))priority { + return ^id(MASLayoutPriority priority) { + for (MASConstraint *constraint in self.childConstraints) { + constraint.priority(priority); + } + return self; + }; +} + +#pragma mark - NSLayoutRelation proxy + +- (MASConstraint * (^)(id, NSLayoutRelation))equalToWithRelation { + return ^id(id attr, NSLayoutRelation relation) { + for (MASConstraint *constraint in self.childConstraints.copy) { + constraint.equalToWithRelation(attr, relation); + } + return self; + }; +} + +#pragma mark - attribute chaining + +- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute { + [self constraint:self addConstraintWithLayoutAttribute:layoutAttribute]; + return self; +} + +#pragma mark - Animator proxy + +#if TARGET_OS_MAC && !(TARGET_OS_IPHONE || TARGET_OS_TV) + +- (MASConstraint *)animator { + for (MASConstraint *constraint in self.childConstraints) { + [constraint animator]; + } + return self; +} + +#endif + +#pragma mark - debug helpers + +- (MASConstraint * (^)(id))key { + return ^id(id key) { + self.mas_key = key; + int i = 0; + for (MASConstraint *constraint in self.childConstraints) { + constraint.key([NSString stringWithFormat:@"%@[%d]", key, i++]); + } + return self; + }; +} + +#pragma mark - NSLayoutConstraint constant setters + +- (void)setInsets:(MASEdgeInsets)insets { + for (MASConstraint *constraint in self.childConstraints) { + constraint.insets = insets; + } +} + +- (void)setInset:(CGFloat)inset { + for (MASConstraint *constraint in self.childConstraints) { + constraint.inset = inset; + } +} + +- (void)setOffset:(CGFloat)offset { + for (MASConstraint *constraint in self.childConstraints) { + constraint.offset = offset; + } +} + +- (void)setSizeOffset:(CGSize)sizeOffset { + for (MASConstraint *constraint in self.childConstraints) { + constraint.sizeOffset = sizeOffset; + } +} + +- (void)setCenterOffset:(CGPoint)centerOffset { + for (MASConstraint *constraint in self.childConstraints) { + constraint.centerOffset = centerOffset; + } +} + +#pragma mark - MASConstraint + +- (void)activate { + for (MASConstraint *constraint in self.childConstraints) { + [constraint activate]; + } +} + +- (void)deactivate { + for (MASConstraint *constraint in self.childConstraints) { + [constraint deactivate]; + } +} + +- (void)install { + for (MASConstraint *constraint in self.childConstraints) { + constraint.updateExisting = self.updateExisting; + [constraint install]; + } +} + +- (void)uninstall { + for (MASConstraint *constraint in self.childConstraints) { + [constraint uninstall]; + } +} + +@end diff --git a/Example/Pods/Masonry/Masonry/MASConstraint+Private.h b/Example/Pods/Masonry/Masonry/MASConstraint+Private.h new file mode 100644 index 0000000..ee0fd96 --- /dev/null +++ b/Example/Pods/Masonry/Masonry/MASConstraint+Private.h @@ -0,0 +1,66 @@ +// +// MASConstraint+Private.h +// Masonry +// +// Created by Nick Tymchenko on 29/04/14. +// Copyright (c) 2014 cloudling. All rights reserved. +// + +#import "MASConstraint.h" + +@protocol MASConstraintDelegate; + + +@interface MASConstraint () + +/** + * Whether or not to check for an existing constraint instead of adding constraint + */ +@property (nonatomic, assign) BOOL updateExisting; + +/** + * Usually MASConstraintMaker but could be a parent MASConstraint + */ +@property (nonatomic, weak) id delegate; + +/** + * Based on a provided value type, is equal to calling: + * NSNumber - setOffset: + * NSValue with CGPoint - setPointOffset: + * NSValue with CGSize - setSizeOffset: + * NSValue with MASEdgeInsets - setInsets: + */ +- (void)setLayoutConstantWithValue:(NSValue *)value; + +@end + + +@interface MASConstraint (Abstract) + +/** + * Sets the constraint relation to given NSLayoutRelation + * returns a block which accepts one of the following: + * MASViewAttribute, UIView, NSValue, NSArray + * see readme for more details. + */ +- (MASConstraint * (^)(id, NSLayoutRelation))equalToWithRelation; + +/** + * Override to set a custom chaining behaviour + */ +- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute; + +@end + + +@protocol MASConstraintDelegate + +/** + * Notifies the delegate when the constraint needs to be replaced with another constraint. For example + * A MASViewConstraint may turn into a MASCompositeConstraint when an array is passed to one of the equality blocks + */ +- (void)constraint:(MASConstraint *)constraint shouldBeReplacedWithConstraint:(MASConstraint *)replacementConstraint; + +- (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute; + +@end diff --git a/Example/Pods/Masonry/Masonry/MASConstraint.h b/Example/Pods/Masonry/Masonry/MASConstraint.h new file mode 100644 index 0000000..3eaa8a1 --- /dev/null +++ b/Example/Pods/Masonry/Masonry/MASConstraint.h @@ -0,0 +1,272 @@ +// +// MASConstraint.h +// Masonry +// +// Created by Jonas Budelmann on 22/07/13. +// Copyright (c) 2013 cloudling. All rights reserved. +// + +#import "MASUtilities.h" + +/** + * Enables Constraints to be created with chainable syntax + * Constraint can represent single NSLayoutConstraint (MASViewConstraint) + * or a group of NSLayoutConstraints (MASComposisteConstraint) + */ +@interface MASConstraint : NSObject + +// Chaining Support + +/** + * Modifies the NSLayoutConstraint constant, + * only affects MASConstraints in which the first item's NSLayoutAttribute is one of the following + * NSLayoutAttributeTop, NSLayoutAttributeLeft, NSLayoutAttributeBottom, NSLayoutAttributeRight + */ +- (MASConstraint * (^)(MASEdgeInsets insets))insets; + +/** + * Modifies the NSLayoutConstraint constant, + * only affects MASConstraints in which the first item's NSLayoutAttribute is one of the following + * NSLayoutAttributeTop, NSLayoutAttributeLeft, NSLayoutAttributeBottom, NSLayoutAttributeRight + */ +- (MASConstraint * (^)(CGFloat inset))inset; + +/** + * Modifies the NSLayoutConstraint constant, + * only affects MASConstraints in which the first item's NSLayoutAttribute is one of the following + * NSLayoutAttributeWidth, NSLayoutAttributeHeight + */ +- (MASConstraint * (^)(CGSize offset))sizeOffset; + +/** + * Modifies the NSLayoutConstraint constant, + * only affects MASConstraints in which the first item's NSLayoutAttribute is one of the following + * NSLayoutAttributeCenterX, NSLayoutAttributeCenterY + */ +- (MASConstraint * (^)(CGPoint offset))centerOffset; + +/** + * Modifies the NSLayoutConstraint constant + */ +- (MASConstraint * (^)(CGFloat offset))offset; + +/** + * Modifies the NSLayoutConstraint constant based on a value type + */ +- (MASConstraint * (^)(NSValue *value))valueOffset; + +/** + * Sets the NSLayoutConstraint multiplier property + */ +- (MASConstraint * (^)(CGFloat multiplier))multipliedBy; + +/** + * Sets the NSLayoutConstraint multiplier to 1.0/dividedBy + */ +- (MASConstraint * (^)(CGFloat divider))dividedBy; + +/** + * Sets the NSLayoutConstraint priority to a float or MASLayoutPriority + */ +- (MASConstraint * (^)(MASLayoutPriority priority))priority; + +/** + * Sets the NSLayoutConstraint priority to MASLayoutPriorityLow + */ +- (MASConstraint * (^)(void))priorityLow; + +/** + * Sets the NSLayoutConstraint priority to MASLayoutPriorityMedium + */ +- (MASConstraint * (^)(void))priorityMedium; + +/** + * Sets the NSLayoutConstraint priority to MASLayoutPriorityHigh + */ +- (MASConstraint * (^)(void))priorityHigh; + +/** + * Sets the constraint relation to NSLayoutRelationEqual + * returns a block which accepts one of the following: + * MASViewAttribute, UIView, NSValue, NSArray + * see readme for more details. + */ +- (MASConstraint * (^)(id attr))equalTo; + +/** + * Sets the constraint relation to NSLayoutRelationGreaterThanOrEqual + * returns a block which accepts one of the following: + * MASViewAttribute, UIView, NSValue, NSArray + * see readme for more details. + */ +- (MASConstraint * (^)(id attr))greaterThanOrEqualTo; + +/** + * Sets the constraint relation to NSLayoutRelationLessThanOrEqual + * returns a block which accepts one of the following: + * MASViewAttribute, UIView, NSValue, NSArray + * see readme for more details. + */ +- (MASConstraint * (^)(id attr))lessThanOrEqualTo; + +/** + * Optional semantic property which has no effect but improves the readability of constraint + */ +- (MASConstraint *)with; + +/** + * Optional semantic property which has no effect but improves the readability of constraint + */ +- (MASConstraint *)and; + +/** + * Creates a new MASCompositeConstraint with the called attribute and reciever + */ +- (MASConstraint *)left; +- (MASConstraint *)top; +- (MASConstraint *)right; +- (MASConstraint *)bottom; +- (MASConstraint *)leading; +- (MASConstraint *)trailing; +- (MASConstraint *)width; +- (MASConstraint *)height; +- (MASConstraint *)centerX; +- (MASConstraint *)centerY; +- (MASConstraint *)baseline; + +#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) + +- (MASConstraint *)firstBaseline; +- (MASConstraint *)lastBaseline; + +#endif + +#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) + +- (MASConstraint *)leftMargin; +- (MASConstraint *)rightMargin; +- (MASConstraint *)topMargin; +- (MASConstraint *)bottomMargin; +- (MASConstraint *)leadingMargin; +- (MASConstraint *)trailingMargin; +- (MASConstraint *)centerXWithinMargins; +- (MASConstraint *)centerYWithinMargins; + +#endif + + +/** + * Sets the constraint debug name + */ +- (MASConstraint * (^)(id key))key; + +// NSLayoutConstraint constant Setters +// for use outside of mas_updateConstraints/mas_makeConstraints blocks + +/** + * Modifies the NSLayoutConstraint constant, + * only affects MASConstraints in which the first item's NSLayoutAttribute is one of the following + * NSLayoutAttributeTop, NSLayoutAttributeLeft, NSLayoutAttributeBottom, NSLayoutAttributeRight + */ +- (void)setInsets:(MASEdgeInsets)insets; + +/** + * Modifies the NSLayoutConstraint constant, + * only affects MASConstraints in which the first item's NSLayoutAttribute is one of the following + * NSLayoutAttributeTop, NSLayoutAttributeLeft, NSLayoutAttributeBottom, NSLayoutAttributeRight + */ +- (void)setInset:(CGFloat)inset; + +/** + * Modifies the NSLayoutConstraint constant, + * only affects MASConstraints in which the first item's NSLayoutAttribute is one of the following + * NSLayoutAttributeWidth, NSLayoutAttributeHeight + */ +- (void)setSizeOffset:(CGSize)sizeOffset; + +/** + * Modifies the NSLayoutConstraint constant, + * only affects MASConstraints in which the first item's NSLayoutAttribute is one of the following + * NSLayoutAttributeCenterX, NSLayoutAttributeCenterY + */ +- (void)setCenterOffset:(CGPoint)centerOffset; + +/** + * Modifies the NSLayoutConstraint constant + */ +- (void)setOffset:(CGFloat)offset; + + +// NSLayoutConstraint Installation support + +#if TARGET_OS_MAC && !(TARGET_OS_IPHONE || TARGET_OS_TV) +/** + * Whether or not to go through the animator proxy when modifying the constraint + */ +@property (nonatomic, copy, readonly) MASConstraint *animator; +#endif + +/** + * Activates an NSLayoutConstraint if it's supported by an OS. + * Invokes install otherwise. + */ +- (void)activate; + +/** + * Deactivates previously installed/activated NSLayoutConstraint. + */ +- (void)deactivate; + +/** + * Creates a NSLayoutConstraint and adds it to the appropriate view. + */ +- (void)install; + +/** + * Removes previously installed NSLayoutConstraint + */ +- (void)uninstall; + +@end + + +/** + * Convenience auto-boxing macros for MASConstraint methods. + * + * Defining MAS_SHORTHAND_GLOBALS will turn on auto-boxing for default syntax. + * A potential drawback of this is that the unprefixed macros will appear in global scope. + */ +#define mas_equalTo(...) equalTo(MASBoxValue((__VA_ARGS__))) +#define mas_greaterThanOrEqualTo(...) greaterThanOrEqualTo(MASBoxValue((__VA_ARGS__))) +#define mas_lessThanOrEqualTo(...) lessThanOrEqualTo(MASBoxValue((__VA_ARGS__))) + +#define mas_offset(...) valueOffset(MASBoxValue((__VA_ARGS__))) + + +#ifdef MAS_SHORTHAND_GLOBALS + +#define equalTo(...) mas_equalTo(__VA_ARGS__) +#define greaterThanOrEqualTo(...) mas_greaterThanOrEqualTo(__VA_ARGS__) +#define lessThanOrEqualTo(...) mas_lessThanOrEqualTo(__VA_ARGS__) + +#define offset(...) mas_offset(__VA_ARGS__) + +#endif + + +@interface MASConstraint (AutoboxingSupport) + +/** + * Aliases to corresponding relation methods (for shorthand macros) + * Also needed to aid autocompletion + */ +- (MASConstraint * (^)(id attr))mas_equalTo; +- (MASConstraint * (^)(id attr))mas_greaterThanOrEqualTo; +- (MASConstraint * (^)(id attr))mas_lessThanOrEqualTo; + +/** + * A dummy method to aid autocompletion + */ +- (MASConstraint * (^)(id offset))mas_offset; + +@end diff --git a/Example/Pods/Masonry/Masonry/MASConstraint.m b/Example/Pods/Masonry/Masonry/MASConstraint.m new file mode 100644 index 0000000..52de590 --- /dev/null +++ b/Example/Pods/Masonry/Masonry/MASConstraint.m @@ -0,0 +1,301 @@ +// +// MASConstraint.m +// Masonry +// +// Created by Nick Tymchenko on 1/20/14. +// + +#import "MASConstraint.h" +#import "MASConstraint+Private.h" + +#define MASMethodNotImplemented() \ + @throw [NSException exceptionWithName:NSInternalInconsistencyException \ + reason:[NSString stringWithFormat:@"You must override %@ in a subclass.", NSStringFromSelector(_cmd)] \ + userInfo:nil] + +@implementation MASConstraint + +#pragma mark - Init + +- (id)init { + NSAssert(![self isMemberOfClass:[MASConstraint class]], @"MASConstraint is an abstract class, you should not instantiate it directly."); + return [super init]; +} + +#pragma mark - NSLayoutRelation proxies + +- (MASConstraint * (^)(id))equalTo { + return ^id(id attribute) { + return self.equalToWithRelation(attribute, NSLayoutRelationEqual); + }; +} + +- (MASConstraint * (^)(id))mas_equalTo { + return ^id(id attribute) { + return self.equalToWithRelation(attribute, NSLayoutRelationEqual); + }; +} + +- (MASConstraint * (^)(id))greaterThanOrEqualTo { + return ^id(id attribute) { + return self.equalToWithRelation(attribute, NSLayoutRelationGreaterThanOrEqual); + }; +} + +- (MASConstraint * (^)(id))mas_greaterThanOrEqualTo { + return ^id(id attribute) { + return self.equalToWithRelation(attribute, NSLayoutRelationGreaterThanOrEqual); + }; +} + +- (MASConstraint * (^)(id))lessThanOrEqualTo { + return ^id(id attribute) { + return self.equalToWithRelation(attribute, NSLayoutRelationLessThanOrEqual); + }; +} + +- (MASConstraint * (^)(id))mas_lessThanOrEqualTo { + return ^id(id attribute) { + return self.equalToWithRelation(attribute, NSLayoutRelationLessThanOrEqual); + }; +} + +#pragma mark - MASLayoutPriority proxies + +- (MASConstraint * (^)(void))priorityLow { + return ^id{ + self.priority(MASLayoutPriorityDefaultLow); + return self; + }; +} + +- (MASConstraint * (^)(void))priorityMedium { + return ^id{ + self.priority(MASLayoutPriorityDefaultMedium); + return self; + }; +} + +- (MASConstraint * (^)(void))priorityHigh { + return ^id{ + self.priority(MASLayoutPriorityDefaultHigh); + return self; + }; +} + +#pragma mark - NSLayoutConstraint constant proxies + +- (MASConstraint * (^)(MASEdgeInsets))insets { + return ^id(MASEdgeInsets insets){ + self.insets = insets; + return self; + }; +} + +- (MASConstraint * (^)(CGFloat))inset { + return ^id(CGFloat inset){ + self.inset = inset; + return self; + }; +} + +- (MASConstraint * (^)(CGSize))sizeOffset { + return ^id(CGSize offset) { + self.sizeOffset = offset; + return self; + }; +} + +- (MASConstraint * (^)(CGPoint))centerOffset { + return ^id(CGPoint offset) { + self.centerOffset = offset; + return self; + }; +} + +- (MASConstraint * (^)(CGFloat))offset { + return ^id(CGFloat offset){ + self.offset = offset; + return self; + }; +} + +- (MASConstraint * (^)(NSValue *value))valueOffset { + return ^id(NSValue *offset) { + NSAssert([offset isKindOfClass:NSValue.class], @"expected an NSValue offset, got: %@", offset); + [self setLayoutConstantWithValue:offset]; + return self; + }; +} + +- (MASConstraint * (^)(id offset))mas_offset { + // Will never be called due to macro + return nil; +} + +#pragma mark - NSLayoutConstraint constant setter + +- (void)setLayoutConstantWithValue:(NSValue *)value { + if ([value isKindOfClass:NSNumber.class]) { + self.offset = [(NSNumber *)value doubleValue]; + } else if (strcmp(value.objCType, @encode(CGPoint)) == 0) { + CGPoint point; + [value getValue:&point]; + self.centerOffset = point; + } else if (strcmp(value.objCType, @encode(CGSize)) == 0) { + CGSize size; + [value getValue:&size]; + self.sizeOffset = size; + } else if (strcmp(value.objCType, @encode(MASEdgeInsets)) == 0) { + MASEdgeInsets insets; + [value getValue:&insets]; + self.insets = insets; + } else { + NSAssert(NO, @"attempting to set layout constant with unsupported value: %@", value); + } +} + +#pragma mark - Semantic properties + +- (MASConstraint *)with { + return self; +} + +- (MASConstraint *)and { + return self; +} + +#pragma mark - Chaining + +- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute __unused)layoutAttribute { + MASMethodNotImplemented(); +} + +- (MASConstraint *)left { + return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeft]; +} + +- (MASConstraint *)top { + return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTop]; +} + +- (MASConstraint *)right { + return [self addConstraintWithLayoutAttribute:NSLayoutAttributeRight]; +} + +- (MASConstraint *)bottom { + return [self addConstraintWithLayoutAttribute:NSLayoutAttributeBottom]; +} + +- (MASConstraint *)leading { + return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeading]; +} + +- (MASConstraint *)trailing { + return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTrailing]; +} + +- (MASConstraint *)width { + return [self addConstraintWithLayoutAttribute:NSLayoutAttributeWidth]; +} + +- (MASConstraint *)height { + return [self addConstraintWithLayoutAttribute:NSLayoutAttributeHeight]; +} + +- (MASConstraint *)centerX { + return [self addConstraintWithLayoutAttribute:NSLayoutAttributeCenterX]; +} + +- (MASConstraint *)centerY { + return [self addConstraintWithLayoutAttribute:NSLayoutAttributeCenterY]; +} + +- (MASConstraint *)baseline { + return [self addConstraintWithLayoutAttribute:NSLayoutAttributeBaseline]; +} + +#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) + +- (MASConstraint *)firstBaseline { + return [self addConstraintWithLayoutAttribute:NSLayoutAttributeFirstBaseline]; +} +- (MASConstraint *)lastBaseline { + return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLastBaseline]; +} + +#endif + +#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) + +- (MASConstraint *)leftMargin { + return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeftMargin]; +} + +- (MASConstraint *)rightMargin { + return [self addConstraintWithLayoutAttribute:NSLayoutAttributeRightMargin]; +} + +- (MASConstraint *)topMargin { + return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTopMargin]; +} + +- (MASConstraint *)bottomMargin { + return [self addConstraintWithLayoutAttribute:NSLayoutAttributeBottomMargin]; +} + +- (MASConstraint *)leadingMargin { + return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeadingMargin]; +} + +- (MASConstraint *)trailingMargin { + return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTrailingMargin]; +} + +- (MASConstraint *)centerXWithinMargins { + return [self addConstraintWithLayoutAttribute:NSLayoutAttributeCenterXWithinMargins]; +} + +- (MASConstraint *)centerYWithinMargins { + return [self addConstraintWithLayoutAttribute:NSLayoutAttributeCenterYWithinMargins]; +} + +#endif + +#pragma mark - Abstract + +- (MASConstraint * (^)(CGFloat multiplier))multipliedBy { MASMethodNotImplemented(); } + +- (MASConstraint * (^)(CGFloat divider))dividedBy { MASMethodNotImplemented(); } + +- (MASConstraint * (^)(MASLayoutPriority priority))priority { MASMethodNotImplemented(); } + +- (MASConstraint * (^)(id, NSLayoutRelation))equalToWithRelation { MASMethodNotImplemented(); } + +- (MASConstraint * (^)(id key))key { MASMethodNotImplemented(); } + +- (void)setInsets:(MASEdgeInsets __unused)insets { MASMethodNotImplemented(); } + +- (void)setInset:(CGFloat __unused)inset { MASMethodNotImplemented(); } + +- (void)setSizeOffset:(CGSize __unused)sizeOffset { MASMethodNotImplemented(); } + +- (void)setCenterOffset:(CGPoint __unused)centerOffset { MASMethodNotImplemented(); } + +- (void)setOffset:(CGFloat __unused)offset { MASMethodNotImplemented(); } + +#if TARGET_OS_MAC && !(TARGET_OS_IPHONE || TARGET_OS_TV) + +- (MASConstraint *)animator { MASMethodNotImplemented(); } + +#endif + +- (void)activate { MASMethodNotImplemented(); } + +- (void)deactivate { MASMethodNotImplemented(); } + +- (void)install { MASMethodNotImplemented(); } + +- (void)uninstall { MASMethodNotImplemented(); } + +@end diff --git a/Example/Pods/Masonry/Masonry/MASConstraintMaker.h b/Example/Pods/Masonry/Masonry/MASConstraintMaker.h new file mode 100644 index 0000000..d9b58f4 --- /dev/null +++ b/Example/Pods/Masonry/Masonry/MASConstraintMaker.h @@ -0,0 +1,146 @@ +// +// MASConstraintMaker.h +// Masonry +// +// Created by Jonas Budelmann on 20/07/13. +// Copyright (c) 2013 cloudling. All rights reserved. +// + +#import "MASConstraint.h" +#import "MASUtilities.h" + +typedef NS_OPTIONS(NSInteger, MASAttribute) { + MASAttributeLeft = 1 << NSLayoutAttributeLeft, + MASAttributeRight = 1 << NSLayoutAttributeRight, + MASAttributeTop = 1 << NSLayoutAttributeTop, + MASAttributeBottom = 1 << NSLayoutAttributeBottom, + MASAttributeLeading = 1 << NSLayoutAttributeLeading, + MASAttributeTrailing = 1 << NSLayoutAttributeTrailing, + MASAttributeWidth = 1 << NSLayoutAttributeWidth, + MASAttributeHeight = 1 << NSLayoutAttributeHeight, + MASAttributeCenterX = 1 << NSLayoutAttributeCenterX, + MASAttributeCenterY = 1 << NSLayoutAttributeCenterY, + MASAttributeBaseline = 1 << NSLayoutAttributeBaseline, + +#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) + + MASAttributeFirstBaseline = 1 << NSLayoutAttributeFirstBaseline, + MASAttributeLastBaseline = 1 << NSLayoutAttributeLastBaseline, + +#endif + +#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) + + MASAttributeLeftMargin = 1 << NSLayoutAttributeLeftMargin, + MASAttributeRightMargin = 1 << NSLayoutAttributeRightMargin, + MASAttributeTopMargin = 1 << NSLayoutAttributeTopMargin, + MASAttributeBottomMargin = 1 << NSLayoutAttributeBottomMargin, + MASAttributeLeadingMargin = 1 << NSLayoutAttributeLeadingMargin, + MASAttributeTrailingMargin = 1 << NSLayoutAttributeTrailingMargin, + MASAttributeCenterXWithinMargins = 1 << NSLayoutAttributeCenterXWithinMargins, + MASAttributeCenterYWithinMargins = 1 << NSLayoutAttributeCenterYWithinMargins, + +#endif + +}; + +/** + * Provides factory methods for creating MASConstraints. + * Constraints are collected until they are ready to be installed + * + */ +@interface MASConstraintMaker : NSObject + +/** + * The following properties return a new MASViewConstraint + * with the first item set to the makers associated view and the appropriate MASViewAttribute + */ +@property (nonatomic, strong, readonly) MASConstraint *left; +@property (nonatomic, strong, readonly) MASConstraint *top; +@property (nonatomic, strong, readonly) MASConstraint *right; +@property (nonatomic, strong, readonly) MASConstraint *bottom; +@property (nonatomic, strong, readonly) MASConstraint *leading; +@property (nonatomic, strong, readonly) MASConstraint *trailing; +@property (nonatomic, strong, readonly) MASConstraint *width; +@property (nonatomic, strong, readonly) MASConstraint *height; +@property (nonatomic, strong, readonly) MASConstraint *centerX; +@property (nonatomic, strong, readonly) MASConstraint *centerY; +@property (nonatomic, strong, readonly) MASConstraint *baseline; + +#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) + +@property (nonatomic, strong, readonly) MASConstraint *firstBaseline; +@property (nonatomic, strong, readonly) MASConstraint *lastBaseline; + +#endif + +#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) + +@property (nonatomic, strong, readonly) MASConstraint *leftMargin; +@property (nonatomic, strong, readonly) MASConstraint *rightMargin; +@property (nonatomic, strong, readonly) MASConstraint *topMargin; +@property (nonatomic, strong, readonly) MASConstraint *bottomMargin; +@property (nonatomic, strong, readonly) MASConstraint *leadingMargin; +@property (nonatomic, strong, readonly) MASConstraint *trailingMargin; +@property (nonatomic, strong, readonly) MASConstraint *centerXWithinMargins; +@property (nonatomic, strong, readonly) MASConstraint *centerYWithinMargins; + +#endif + +/** + * Returns a block which creates a new MASCompositeConstraint with the first item set + * to the makers associated view and children corresponding to the set bits in the + * MASAttribute parameter. Combine multiple attributes via binary-or. + */ +@property (nonatomic, strong, readonly) MASConstraint *(^attributes)(MASAttribute attrs); + +/** + * Creates a MASCompositeConstraint with type MASCompositeConstraintTypeEdges + * which generates the appropriate MASViewConstraint children (top, left, bottom, right) + * with the first item set to the makers associated view + */ +@property (nonatomic, strong, readonly) MASConstraint *edges; + +/** + * Creates a MASCompositeConstraint with type MASCompositeConstraintTypeSize + * which generates the appropriate MASViewConstraint children (width, height) + * with the first item set to the makers associated view + */ +@property (nonatomic, strong, readonly) MASConstraint *size; + +/** + * Creates a MASCompositeConstraint with type MASCompositeConstraintTypeCenter + * which generates the appropriate MASViewConstraint children (centerX, centerY) + * with the first item set to the makers associated view + */ +@property (nonatomic, strong, readonly) MASConstraint *center; + +/** + * Whether or not to check for an existing constraint instead of adding constraint + */ +@property (nonatomic, assign) BOOL updateExisting; + +/** + * Whether or not to remove existing constraints prior to installing + */ +@property (nonatomic, assign) BOOL removeExisting; + +/** + * initialises the maker with a default view + * + * @param view any MASConstraint are created with this view as the first item + * + * @return a new MASConstraintMaker + */ +- (id)initWithView:(MAS_VIEW *)view; + +/** + * Calls install method on any MASConstraints which have been created by this maker + * + * @return an array of all the installed MASConstraints + */ +- (NSArray *)install; + +- (MASConstraint * (^)(dispatch_block_t))group; + +@end diff --git a/Example/Pods/Masonry/Masonry/MASConstraintMaker.m b/Example/Pods/Masonry/Masonry/MASConstraintMaker.m new file mode 100644 index 0000000..f11492a --- /dev/null +++ b/Example/Pods/Masonry/Masonry/MASConstraintMaker.m @@ -0,0 +1,273 @@ +// +// MASConstraintMaker.m +// Masonry +// +// Created by Jonas Budelmann on 20/07/13. +// Copyright (c) 2013 cloudling. All rights reserved. +// + +#import "MASConstraintMaker.h" +#import "MASViewConstraint.h" +#import "MASCompositeConstraint.h" +#import "MASConstraint+Private.h" +#import "MASViewAttribute.h" +#import "View+MASAdditions.h" + +@interface MASConstraintMaker () + +@property (nonatomic, weak) MAS_VIEW *view; +@property (nonatomic, strong) NSMutableArray *constraints; + +@end + +@implementation MASConstraintMaker + +- (id)initWithView:(MAS_VIEW *)view { + self = [super init]; + if (!self) return nil; + + self.view = view; + self.constraints = NSMutableArray.new; + + return self; +} + +- (NSArray *)install { + if (self.removeExisting) { + NSArray *installedConstraints = [MASViewConstraint installedConstraintsForView:self.view]; + for (MASConstraint *constraint in installedConstraints) { + [constraint uninstall]; + } + } + NSArray *constraints = self.constraints.copy; + for (MASConstraint *constraint in constraints) { + constraint.updateExisting = self.updateExisting; + [constraint install]; + } + [self.constraints removeAllObjects]; + return constraints; +} + +#pragma mark - MASConstraintDelegate + +- (void)constraint:(MASConstraint *)constraint shouldBeReplacedWithConstraint:(MASConstraint *)replacementConstraint { + NSUInteger index = [self.constraints indexOfObject:constraint]; + NSAssert(index != NSNotFound, @"Could not find constraint %@", constraint); + [self.constraints replaceObjectAtIndex:index withObject:replacementConstraint]; +} + +- (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute { + MASViewAttribute *viewAttribute = [[MASViewAttribute alloc] initWithView:self.view layoutAttribute:layoutAttribute]; + MASViewConstraint *newConstraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:viewAttribute]; + if ([constraint isKindOfClass:MASViewConstraint.class]) { + //replace with composite constraint + NSArray *children = @[constraint, newConstraint]; + MASCompositeConstraint *compositeConstraint = [[MASCompositeConstraint alloc] initWithChildren:children]; + compositeConstraint.delegate = self; + [self constraint:constraint shouldBeReplacedWithConstraint:compositeConstraint]; + return compositeConstraint; + } + if (!constraint) { + newConstraint.delegate = self; + [self.constraints addObject:newConstraint]; + } + return newConstraint; +} + +- (MASConstraint *)addConstraintWithAttributes:(MASAttribute)attrs { + __unused MASAttribute anyAttribute = (MASAttributeLeft | MASAttributeRight | MASAttributeTop | MASAttributeBottom | MASAttributeLeading + | MASAttributeTrailing | MASAttributeWidth | MASAttributeHeight | MASAttributeCenterX + | MASAttributeCenterY | MASAttributeBaseline +#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) + | MASAttributeFirstBaseline | MASAttributeLastBaseline +#endif +#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) + | MASAttributeLeftMargin | MASAttributeRightMargin | MASAttributeTopMargin | MASAttributeBottomMargin + | MASAttributeLeadingMargin | MASAttributeTrailingMargin | MASAttributeCenterXWithinMargins + | MASAttributeCenterYWithinMargins +#endif + ); + + NSAssert((attrs & anyAttribute) != 0, @"You didn't pass any attribute to make.attributes(...)"); + + NSMutableArray *attributes = [NSMutableArray array]; + + if (attrs & MASAttributeLeft) [attributes addObject:self.view.mas_left]; + if (attrs & MASAttributeRight) [attributes addObject:self.view.mas_right]; + if (attrs & MASAttributeTop) [attributes addObject:self.view.mas_top]; + if (attrs & MASAttributeBottom) [attributes addObject:self.view.mas_bottom]; + if (attrs & MASAttributeLeading) [attributes addObject:self.view.mas_leading]; + if (attrs & MASAttributeTrailing) [attributes addObject:self.view.mas_trailing]; + if (attrs & MASAttributeWidth) [attributes addObject:self.view.mas_width]; + if (attrs & MASAttributeHeight) [attributes addObject:self.view.mas_height]; + if (attrs & MASAttributeCenterX) [attributes addObject:self.view.mas_centerX]; + if (attrs & MASAttributeCenterY) [attributes addObject:self.view.mas_centerY]; + if (attrs & MASAttributeBaseline) [attributes addObject:self.view.mas_baseline]; + +#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) + + if (attrs & MASAttributeFirstBaseline) [attributes addObject:self.view.mas_firstBaseline]; + if (attrs & MASAttributeLastBaseline) [attributes addObject:self.view.mas_lastBaseline]; + +#endif + +#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) + + if (attrs & MASAttributeLeftMargin) [attributes addObject:self.view.mas_leftMargin]; + if (attrs & MASAttributeRightMargin) [attributes addObject:self.view.mas_rightMargin]; + if (attrs & MASAttributeTopMargin) [attributes addObject:self.view.mas_topMargin]; + if (attrs & MASAttributeBottomMargin) [attributes addObject:self.view.mas_bottomMargin]; + if (attrs & MASAttributeLeadingMargin) [attributes addObject:self.view.mas_leadingMargin]; + if (attrs & MASAttributeTrailingMargin) [attributes addObject:self.view.mas_trailingMargin]; + if (attrs & MASAttributeCenterXWithinMargins) [attributes addObject:self.view.mas_centerXWithinMargins]; + if (attrs & MASAttributeCenterYWithinMargins) [attributes addObject:self.view.mas_centerYWithinMargins]; + +#endif + + NSMutableArray *children = [NSMutableArray arrayWithCapacity:attributes.count]; + + for (MASViewAttribute *a in attributes) { + [children addObject:[[MASViewConstraint alloc] initWithFirstViewAttribute:a]]; + } + + MASCompositeConstraint *constraint = [[MASCompositeConstraint alloc] initWithChildren:children]; + constraint.delegate = self; + [self.constraints addObject:constraint]; + return constraint; +} + +#pragma mark - standard Attributes + +- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute { + return [self constraint:nil addConstraintWithLayoutAttribute:layoutAttribute]; +} + +- (MASConstraint *)left { + return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeft]; +} + +- (MASConstraint *)top { + return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTop]; +} + +- (MASConstraint *)right { + return [self addConstraintWithLayoutAttribute:NSLayoutAttributeRight]; +} + +- (MASConstraint *)bottom { + return [self addConstraintWithLayoutAttribute:NSLayoutAttributeBottom]; +} + +- (MASConstraint *)leading { + return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeading]; +} + +- (MASConstraint *)trailing { + return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTrailing]; +} + +- (MASConstraint *)width { + return [self addConstraintWithLayoutAttribute:NSLayoutAttributeWidth]; +} + +- (MASConstraint *)height { + return [self addConstraintWithLayoutAttribute:NSLayoutAttributeHeight]; +} + +- (MASConstraint *)centerX { + return [self addConstraintWithLayoutAttribute:NSLayoutAttributeCenterX]; +} + +- (MASConstraint *)centerY { + return [self addConstraintWithLayoutAttribute:NSLayoutAttributeCenterY]; +} + +- (MASConstraint *)baseline { + return [self addConstraintWithLayoutAttribute:NSLayoutAttributeBaseline]; +} + +- (MASConstraint *(^)(MASAttribute))attributes { + return ^(MASAttribute attrs){ + return [self addConstraintWithAttributes:attrs]; + }; +} + +#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) + +- (MASConstraint *)firstBaseline { + return [self addConstraintWithLayoutAttribute:NSLayoutAttributeFirstBaseline]; +} + +- (MASConstraint *)lastBaseline { + return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLastBaseline]; +} + +#endif + + +#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) + +- (MASConstraint *)leftMargin { + return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeftMargin]; +} + +- (MASConstraint *)rightMargin { + return [self addConstraintWithLayoutAttribute:NSLayoutAttributeRightMargin]; +} + +- (MASConstraint *)topMargin { + return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTopMargin]; +} + +- (MASConstraint *)bottomMargin { + return [self addConstraintWithLayoutAttribute:NSLayoutAttributeBottomMargin]; +} + +- (MASConstraint *)leadingMargin { + return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeadingMargin]; +} + +- (MASConstraint *)trailingMargin { + return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTrailingMargin]; +} + +- (MASConstraint *)centerXWithinMargins { + return [self addConstraintWithLayoutAttribute:NSLayoutAttributeCenterXWithinMargins]; +} + +- (MASConstraint *)centerYWithinMargins { + return [self addConstraintWithLayoutAttribute:NSLayoutAttributeCenterYWithinMargins]; +} + +#endif + + +#pragma mark - composite Attributes + +- (MASConstraint *)edges { + return [self addConstraintWithAttributes:MASAttributeTop | MASAttributeLeft | MASAttributeRight | MASAttributeBottom]; +} + +- (MASConstraint *)size { + return [self addConstraintWithAttributes:MASAttributeWidth | MASAttributeHeight]; +} + +- (MASConstraint *)center { + return [self addConstraintWithAttributes:MASAttributeCenterX | MASAttributeCenterY]; +} + +#pragma mark - grouping + +- (MASConstraint *(^)(dispatch_block_t group))group { + return ^id(dispatch_block_t group) { + NSInteger previousCount = self.constraints.count; + group(); + + NSArray *children = [self.constraints subarrayWithRange:NSMakeRange(previousCount, self.constraints.count - previousCount)]; + MASCompositeConstraint *constraint = [[MASCompositeConstraint alloc] initWithChildren:children]; + constraint.delegate = self; + return constraint; + }; +} + +@end diff --git a/Example/Pods/Masonry/Masonry/MASLayoutConstraint.h b/Example/Pods/Masonry/Masonry/MASLayoutConstraint.h new file mode 100644 index 0000000..699041c --- /dev/null +++ b/Example/Pods/Masonry/Masonry/MASLayoutConstraint.h @@ -0,0 +1,22 @@ +// +// MASLayoutConstraint.h +// Masonry +// +// Created by Jonas Budelmann on 3/08/13. +// Copyright (c) 2013 Jonas Budelmann. All rights reserved. +// + +#import "MASUtilities.h" + +/** + * When you are debugging or printing the constraints attached to a view this subclass + * makes it easier to identify which constraints have been created via Masonry + */ +@interface MASLayoutConstraint : NSLayoutConstraint + +/** + * a key to associate with this constraint + */ +@property (nonatomic, strong) id mas_key; + +@end diff --git a/Example/Pods/Masonry/Masonry/MASLayoutConstraint.m b/Example/Pods/Masonry/Masonry/MASLayoutConstraint.m new file mode 100644 index 0000000..3483f02 --- /dev/null +++ b/Example/Pods/Masonry/Masonry/MASLayoutConstraint.m @@ -0,0 +1,13 @@ +// +// MASLayoutConstraint.m +// Masonry +// +// Created by Jonas Budelmann on 3/08/13. +// Copyright (c) 2013 Jonas Budelmann. All rights reserved. +// + +#import "MASLayoutConstraint.h" + +@implementation MASLayoutConstraint + +@end diff --git a/Example/Pods/Masonry/Masonry/MASUtilities.h b/Example/Pods/Masonry/Masonry/MASUtilities.h new file mode 100644 index 0000000..1dbfd93 --- /dev/null +++ b/Example/Pods/Masonry/Masonry/MASUtilities.h @@ -0,0 +1,136 @@ +// +// MASUtilities.h +// Masonry +// +// Created by Jonas Budelmann on 19/08/13. +// Copyright (c) 2013 Jonas Budelmann. All rights reserved. +// + +#import + + + +#if TARGET_OS_IPHONE || TARGET_OS_TV + + #import + #define MAS_VIEW UIView + #define MAS_VIEW_CONTROLLER UIViewController + #define MASEdgeInsets UIEdgeInsets + + typedef UILayoutPriority MASLayoutPriority; + static const MASLayoutPriority MASLayoutPriorityRequired = UILayoutPriorityRequired; + static const MASLayoutPriority MASLayoutPriorityDefaultHigh = UILayoutPriorityDefaultHigh; + static const MASLayoutPriority MASLayoutPriorityDefaultMedium = 500; + static const MASLayoutPriority MASLayoutPriorityDefaultLow = UILayoutPriorityDefaultLow; + static const MASLayoutPriority MASLayoutPriorityFittingSizeLevel = UILayoutPriorityFittingSizeLevel; + +#elif TARGET_OS_MAC + + #import + #define MAS_VIEW NSView + #define MASEdgeInsets NSEdgeInsets + + typedef NSLayoutPriority MASLayoutPriority; + static const MASLayoutPriority MASLayoutPriorityRequired = NSLayoutPriorityRequired; + static const MASLayoutPriority MASLayoutPriorityDefaultHigh = NSLayoutPriorityDefaultHigh; + static const MASLayoutPriority MASLayoutPriorityDragThatCanResizeWindow = NSLayoutPriorityDragThatCanResizeWindow; + static const MASLayoutPriority MASLayoutPriorityDefaultMedium = 501; + static const MASLayoutPriority MASLayoutPriorityWindowSizeStayPut = NSLayoutPriorityWindowSizeStayPut; + static const MASLayoutPriority MASLayoutPriorityDragThatCannotResizeWindow = NSLayoutPriorityDragThatCannotResizeWindow; + static const MASLayoutPriority MASLayoutPriorityDefaultLow = NSLayoutPriorityDefaultLow; + static const MASLayoutPriority MASLayoutPriorityFittingSizeCompression = NSLayoutPriorityFittingSizeCompression; + +#endif + +/** + * Allows you to attach keys to objects matching the variable names passed. + * + * view1.mas_key = @"view1", view2.mas_key = @"view2"; + * + * is equivalent to: + * + * MASAttachKeys(view1, view2); + */ +#define MASAttachKeys(...) \ + { \ + NSDictionary *keyPairs = NSDictionaryOfVariableBindings(__VA_ARGS__); \ + for (id key in keyPairs.allKeys) { \ + id obj = keyPairs[key]; \ + NSAssert([obj respondsToSelector:@selector(setMas_key:)], \ + @"Cannot attach mas_key to %@", obj); \ + [obj setMas_key:key]; \ + } \ + } + +/** + * Used to create object hashes + * Based on http://www.mikeash.com/pyblog/friday-qa-2010-06-18-implementing-equality-and-hashing.html + */ +#define MAS_NSUINT_BIT (CHAR_BIT * sizeof(NSUInteger)) +#define MAS_NSUINTROTATE(val, howmuch) ((((NSUInteger)val) << howmuch) | (((NSUInteger)val) >> (MAS_NSUINT_BIT - howmuch))) + +/** + * Given a scalar or struct value, wraps it in NSValue + * Based on EXPObjectify: https://github.com/specta/expecta + */ +static inline id _MASBoxValue(const char *type, ...) { + va_list v; + va_start(v, type); + id obj = nil; + if (strcmp(type, @encode(id)) == 0) { + id actual = va_arg(v, id); + obj = actual; + } else if (strcmp(type, @encode(CGPoint)) == 0) { + CGPoint actual = (CGPoint)va_arg(v, CGPoint); + obj = [NSValue value:&actual withObjCType:type]; + } else if (strcmp(type, @encode(CGSize)) == 0) { + CGSize actual = (CGSize)va_arg(v, CGSize); + obj = [NSValue value:&actual withObjCType:type]; + } else if (strcmp(type, @encode(MASEdgeInsets)) == 0) { + MASEdgeInsets actual = (MASEdgeInsets)va_arg(v, MASEdgeInsets); + obj = [NSValue value:&actual withObjCType:type]; + } else if (strcmp(type, @encode(double)) == 0) { + double actual = (double)va_arg(v, double); + obj = [NSNumber numberWithDouble:actual]; + } else if (strcmp(type, @encode(float)) == 0) { + float actual = (float)va_arg(v, double); + obj = [NSNumber numberWithFloat:actual]; + } else if (strcmp(type, @encode(int)) == 0) { + int actual = (int)va_arg(v, int); + obj = [NSNumber numberWithInt:actual]; + } else if (strcmp(type, @encode(long)) == 0) { + long actual = (long)va_arg(v, long); + obj = [NSNumber numberWithLong:actual]; + } else if (strcmp(type, @encode(long long)) == 0) { + long long actual = (long long)va_arg(v, long long); + obj = [NSNumber numberWithLongLong:actual]; + } else if (strcmp(type, @encode(short)) == 0) { + short actual = (short)va_arg(v, int); + obj = [NSNumber numberWithShort:actual]; + } else if (strcmp(type, @encode(char)) == 0) { + char actual = (char)va_arg(v, int); + obj = [NSNumber numberWithChar:actual]; + } else if (strcmp(type, @encode(bool)) == 0) { + bool actual = (bool)va_arg(v, int); + obj = [NSNumber numberWithBool:actual]; + } else if (strcmp(type, @encode(unsigned char)) == 0) { + unsigned char actual = (unsigned char)va_arg(v, unsigned int); + obj = [NSNumber numberWithUnsignedChar:actual]; + } else if (strcmp(type, @encode(unsigned int)) == 0) { + unsigned int actual = (unsigned int)va_arg(v, unsigned int); + obj = [NSNumber numberWithUnsignedInt:actual]; + } else if (strcmp(type, @encode(unsigned long)) == 0) { + unsigned long actual = (unsigned long)va_arg(v, unsigned long); + obj = [NSNumber numberWithUnsignedLong:actual]; + } else if (strcmp(type, @encode(unsigned long long)) == 0) { + unsigned long long actual = (unsigned long long)va_arg(v, unsigned long long); + obj = [NSNumber numberWithUnsignedLongLong:actual]; + } else if (strcmp(type, @encode(unsigned short)) == 0) { + unsigned short actual = (unsigned short)va_arg(v, unsigned int); + obj = [NSNumber numberWithUnsignedShort:actual]; + } + va_end(v); + return obj; +} + +#define MASBoxValue(value) _MASBoxValue(@encode(__typeof__((value))), (value)) diff --git a/Example/Pods/Masonry/Masonry/MASViewAttribute.h b/Example/Pods/Masonry/Masonry/MASViewAttribute.h new file mode 100644 index 0000000..601c25d --- /dev/null +++ b/Example/Pods/Masonry/Masonry/MASViewAttribute.h @@ -0,0 +1,49 @@ +// +// MASViewAttribute.h +// Masonry +// +// Created by Jonas Budelmann on 21/07/13. +// Copyright (c) 2013 cloudling. All rights reserved. +// + +#import "MASUtilities.h" + +/** + * An immutable tuple which stores the view and the related NSLayoutAttribute. + * Describes part of either the left or right hand side of a constraint equation + */ +@interface MASViewAttribute : NSObject + +/** + * The view which the reciever relates to. Can be nil if item is not a view. + */ +@property (nonatomic, weak, readonly) MAS_VIEW *view; + +/** + * The item which the reciever relates to. + */ +@property (nonatomic, weak, readonly) id item; + +/** + * The attribute which the reciever relates to + */ +@property (nonatomic, assign, readonly) NSLayoutAttribute layoutAttribute; + +/** + * Convenience initializer. + */ +- (id)initWithView:(MAS_VIEW *)view layoutAttribute:(NSLayoutAttribute)layoutAttribute; + +/** + * The designated initializer. + */ +- (id)initWithView:(MAS_VIEW *)view item:(id)item layoutAttribute:(NSLayoutAttribute)layoutAttribute; + +/** + * Determine whether the layoutAttribute is a size attribute + * + * @return YES if layoutAttribute is equal to NSLayoutAttributeWidth or NSLayoutAttributeHeight + */ +- (BOOL)isSizeAttribute; + +@end diff --git a/Example/Pods/Masonry/Masonry/MASViewAttribute.m b/Example/Pods/Masonry/Masonry/MASViewAttribute.m new file mode 100644 index 0000000..e573e8b --- /dev/null +++ b/Example/Pods/Masonry/Masonry/MASViewAttribute.m @@ -0,0 +1,46 @@ +// +// MASViewAttribute.m +// Masonry +// +// Created by Jonas Budelmann on 21/07/13. +// Copyright (c) 2013 cloudling. All rights reserved. +// + +#import "MASViewAttribute.h" + +@implementation MASViewAttribute + +- (id)initWithView:(MAS_VIEW *)view layoutAttribute:(NSLayoutAttribute)layoutAttribute { + self = [self initWithView:view item:view layoutAttribute:layoutAttribute]; + return self; +} + +- (id)initWithView:(MAS_VIEW *)view item:(id)item layoutAttribute:(NSLayoutAttribute)layoutAttribute { + self = [super init]; + if (!self) return nil; + + _view = view; + _item = item; + _layoutAttribute = layoutAttribute; + + return self; +} + +- (BOOL)isSizeAttribute { + return self.layoutAttribute == NSLayoutAttributeWidth + || self.layoutAttribute == NSLayoutAttributeHeight; +} + +- (BOOL)isEqual:(MASViewAttribute *)viewAttribute { + if ([viewAttribute isKindOfClass:self.class]) { + return self.view == viewAttribute.view + && self.layoutAttribute == viewAttribute.layoutAttribute; + } + return [super isEqual:viewAttribute]; +} + +- (NSUInteger)hash { + return MAS_NSUINTROTATE([self.view hash], MAS_NSUINT_BIT / 2) ^ self.layoutAttribute; +} + +@end diff --git a/Example/Pods/Masonry/Masonry/MASViewConstraint.h b/Example/Pods/Masonry/Masonry/MASViewConstraint.h new file mode 100644 index 0000000..ec390d1 --- /dev/null +++ b/Example/Pods/Masonry/Masonry/MASViewConstraint.h @@ -0,0 +1,48 @@ +// +// MASViewConstraint.h +// Masonry +// +// Created by Jonas Budelmann on 20/07/13. +// Copyright (c) 2013 cloudling. All rights reserved. +// + +#import "MASViewAttribute.h" +#import "MASConstraint.h" +#import "MASLayoutConstraint.h" +#import "MASUtilities.h" + +/** + * A single constraint. + * Contains the attributes neccessary for creating a NSLayoutConstraint and adding it to the appropriate view + */ +@interface MASViewConstraint : MASConstraint + +/** + * First item/view and first attribute of the NSLayoutConstraint + */ +@property (nonatomic, strong, readonly) MASViewAttribute *firstViewAttribute; + +/** + * Second item/view and second attribute of the NSLayoutConstraint + */ +@property (nonatomic, strong, readonly) MASViewAttribute *secondViewAttribute; + +/** + * initialises the MASViewConstraint with the first part of the equation + * + * @param firstViewAttribute view.mas_left, view.mas_width etc. + * + * @return a new view constraint + */ +- (id)initWithFirstViewAttribute:(MASViewAttribute *)firstViewAttribute; + +/** + * Returns all MASViewConstraints installed with this view as a first item. + * + * @param view A view to retrieve constraints for. + * + * @return An array of MASViewConstraints. + */ ++ (NSArray *)installedConstraintsForView:(MAS_VIEW *)view; + +@end diff --git a/Example/Pods/Masonry/Masonry/MASViewConstraint.m b/Example/Pods/Masonry/Masonry/MASViewConstraint.m new file mode 100644 index 0000000..173eec1 --- /dev/null +++ b/Example/Pods/Masonry/Masonry/MASViewConstraint.m @@ -0,0 +1,401 @@ +// +// MASViewConstraint.m +// Masonry +// +// Created by Jonas Budelmann on 20/07/13. +// Copyright (c) 2013 cloudling. All rights reserved. +// + +#import "MASViewConstraint.h" +#import "MASConstraint+Private.h" +#import "MASCompositeConstraint.h" +#import "MASLayoutConstraint.h" +#import "View+MASAdditions.h" +#import + +@interface MAS_VIEW (MASConstraints) + +@property (nonatomic, readonly) NSMutableSet *mas_installedConstraints; + +@end + +@implementation MAS_VIEW (MASConstraints) + +static char kInstalledConstraintsKey; + +- (NSMutableSet *)mas_installedConstraints { + NSMutableSet *constraints = objc_getAssociatedObject(self, &kInstalledConstraintsKey); + if (!constraints) { + constraints = [NSMutableSet set]; + objc_setAssociatedObject(self, &kInstalledConstraintsKey, constraints, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + } + return constraints; +} + +@end + + +@interface MASViewConstraint () + +@property (nonatomic, strong, readwrite) MASViewAttribute *secondViewAttribute; +@property (nonatomic, weak) MAS_VIEW *installedView; +@property (nonatomic, weak) MASLayoutConstraint *layoutConstraint; +@property (nonatomic, assign) NSLayoutRelation layoutRelation; +@property (nonatomic, assign) MASLayoutPriority layoutPriority; +@property (nonatomic, assign) CGFloat layoutMultiplier; +@property (nonatomic, assign) CGFloat layoutConstant; +@property (nonatomic, assign) BOOL hasLayoutRelation; +@property (nonatomic, strong) id mas_key; +@property (nonatomic, assign) BOOL useAnimator; + +@end + +@implementation MASViewConstraint + +- (id)initWithFirstViewAttribute:(MASViewAttribute *)firstViewAttribute { + self = [super init]; + if (!self) return nil; + + _firstViewAttribute = firstViewAttribute; + self.layoutPriority = MASLayoutPriorityRequired; + self.layoutMultiplier = 1; + + return self; +} + +#pragma mark - NSCoping + +- (id)copyWithZone:(NSZone __unused *)zone { + MASViewConstraint *constraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:self.firstViewAttribute]; + constraint.layoutConstant = self.layoutConstant; + constraint.layoutRelation = self.layoutRelation; + constraint.layoutPriority = self.layoutPriority; + constraint.layoutMultiplier = self.layoutMultiplier; + constraint.delegate = self.delegate; + return constraint; +} + +#pragma mark - Public + ++ (NSArray *)installedConstraintsForView:(MAS_VIEW *)view { + return [view.mas_installedConstraints allObjects]; +} + +#pragma mark - Private + +- (void)setLayoutConstant:(CGFloat)layoutConstant { + _layoutConstant = layoutConstant; + +#if TARGET_OS_MAC && !(TARGET_OS_IPHONE || TARGET_OS_TV) + if (self.useAnimator) { + [self.layoutConstraint.animator setConstant:layoutConstant]; + } else { + self.layoutConstraint.constant = layoutConstant; + } +#else + self.layoutConstraint.constant = layoutConstant; +#endif +} + +- (void)setLayoutRelation:(NSLayoutRelation)layoutRelation { + _layoutRelation = layoutRelation; + self.hasLayoutRelation = YES; +} + +- (BOOL)supportsActiveProperty { + return [self.layoutConstraint respondsToSelector:@selector(isActive)]; +} + +- (BOOL)isActive { + BOOL active = YES; + if ([self supportsActiveProperty]) { + active = [self.layoutConstraint isActive]; + } + + return active; +} + +- (BOOL)hasBeenInstalled { + return (self.layoutConstraint != nil) && [self isActive]; +} + +- (void)setSecondViewAttribute:(id)secondViewAttribute { + if ([secondViewAttribute isKindOfClass:NSValue.class]) { + [self setLayoutConstantWithValue:secondViewAttribute]; + } else if ([secondViewAttribute isKindOfClass:MAS_VIEW.class]) { + _secondViewAttribute = [[MASViewAttribute alloc] initWithView:secondViewAttribute layoutAttribute:self.firstViewAttribute.layoutAttribute]; + } else if ([secondViewAttribute isKindOfClass:MASViewAttribute.class]) { + _secondViewAttribute = secondViewAttribute; + } else { + NSAssert(NO, @"attempting to add unsupported attribute: %@", secondViewAttribute); + } +} + +#pragma mark - NSLayoutConstraint multiplier proxies + +- (MASConstraint * (^)(CGFloat))multipliedBy { + return ^id(CGFloat multiplier) { + NSAssert(!self.hasBeenInstalled, + @"Cannot modify constraint multiplier after it has been installed"); + + self.layoutMultiplier = multiplier; + return self; + }; +} + + +- (MASConstraint * (^)(CGFloat))dividedBy { + return ^id(CGFloat divider) { + NSAssert(!self.hasBeenInstalled, + @"Cannot modify constraint multiplier after it has been installed"); + + self.layoutMultiplier = 1.0/divider; + return self; + }; +} + +#pragma mark - MASLayoutPriority proxy + +- (MASConstraint * (^)(MASLayoutPriority))priority { + return ^id(MASLayoutPriority priority) { + NSAssert(!self.hasBeenInstalled, + @"Cannot modify constraint priority after it has been installed"); + + self.layoutPriority = priority; + return self; + }; +} + +#pragma mark - NSLayoutRelation proxy + +- (MASConstraint * (^)(id, NSLayoutRelation))equalToWithRelation { + return ^id(id attribute, NSLayoutRelation relation) { + if ([attribute isKindOfClass:NSArray.class]) { + NSAssert(!self.hasLayoutRelation, @"Redefinition of constraint relation"); + NSMutableArray *children = NSMutableArray.new; + for (id attr in attribute) { + MASViewConstraint *viewConstraint = [self copy]; + viewConstraint.layoutRelation = relation; + viewConstraint.secondViewAttribute = attr; + [children addObject:viewConstraint]; + } + MASCompositeConstraint *compositeConstraint = [[MASCompositeConstraint alloc] initWithChildren:children]; + compositeConstraint.delegate = self.delegate; + [self.delegate constraint:self shouldBeReplacedWithConstraint:compositeConstraint]; + return compositeConstraint; + } else { + NSAssert(!self.hasLayoutRelation || self.layoutRelation == relation && [attribute isKindOfClass:NSValue.class], @"Redefinition of constraint relation"); + self.layoutRelation = relation; + self.secondViewAttribute = attribute; + return self; + } + }; +} + +#pragma mark - Semantic properties + +- (MASConstraint *)with { + return self; +} + +- (MASConstraint *)and { + return self; +} + +#pragma mark - attribute chaining + +- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute { + NSAssert(!self.hasLayoutRelation, @"Attributes should be chained before defining the constraint relation"); + + return [self.delegate constraint:self addConstraintWithLayoutAttribute:layoutAttribute]; +} + +#pragma mark - Animator proxy + +#if TARGET_OS_MAC && !(TARGET_OS_IPHONE || TARGET_OS_TV) + +- (MASConstraint *)animator { + self.useAnimator = YES; + return self; +} + +#endif + +#pragma mark - debug helpers + +- (MASConstraint * (^)(id))key { + return ^id(id key) { + self.mas_key = key; + return self; + }; +} + +#pragma mark - NSLayoutConstraint constant setters + +- (void)setInsets:(MASEdgeInsets)insets { + NSLayoutAttribute layoutAttribute = self.firstViewAttribute.layoutAttribute; + switch (layoutAttribute) { + case NSLayoutAttributeLeft: + case NSLayoutAttributeLeading: + self.layoutConstant = insets.left; + break; + case NSLayoutAttributeTop: + self.layoutConstant = insets.top; + break; + case NSLayoutAttributeBottom: + self.layoutConstant = -insets.bottom; + break; + case NSLayoutAttributeRight: + case NSLayoutAttributeTrailing: + self.layoutConstant = -insets.right; + break; + default: + break; + } +} + +- (void)setInset:(CGFloat)inset { + [self setInsets:(MASEdgeInsets){.top = inset, .left = inset, .bottom = inset, .right = inset}]; +} + +- (void)setOffset:(CGFloat)offset { + self.layoutConstant = offset; +} + +- (void)setSizeOffset:(CGSize)sizeOffset { + NSLayoutAttribute layoutAttribute = self.firstViewAttribute.layoutAttribute; + switch (layoutAttribute) { + case NSLayoutAttributeWidth: + self.layoutConstant = sizeOffset.width; + break; + case NSLayoutAttributeHeight: + self.layoutConstant = sizeOffset.height; + break; + default: + break; + } +} + +- (void)setCenterOffset:(CGPoint)centerOffset { + NSLayoutAttribute layoutAttribute = self.firstViewAttribute.layoutAttribute; + switch (layoutAttribute) { + case NSLayoutAttributeCenterX: + self.layoutConstant = centerOffset.x; + break; + case NSLayoutAttributeCenterY: + self.layoutConstant = centerOffset.y; + break; + default: + break; + } +} + +#pragma mark - MASConstraint + +- (void)activate { + [self install]; +} + +- (void)deactivate { + [self uninstall]; +} + +- (void)install { + if (self.hasBeenInstalled) { + return; + } + + if ([self supportsActiveProperty] && self.layoutConstraint) { + self.layoutConstraint.active = YES; + [self.firstViewAttribute.view.mas_installedConstraints addObject:self]; + return; + } + + MAS_VIEW *firstLayoutItem = self.firstViewAttribute.item; + NSLayoutAttribute firstLayoutAttribute = self.firstViewAttribute.layoutAttribute; + MAS_VIEW *secondLayoutItem = self.secondViewAttribute.item; + NSLayoutAttribute secondLayoutAttribute = self.secondViewAttribute.layoutAttribute; + + // alignment attributes must have a secondViewAttribute + // therefore we assume that is refering to superview + // eg make.left.equalTo(@10) + if (!self.firstViewAttribute.isSizeAttribute && !self.secondViewAttribute) { + secondLayoutItem = self.firstViewAttribute.view.superview; + secondLayoutAttribute = firstLayoutAttribute; + } + + MASLayoutConstraint *layoutConstraint + = [MASLayoutConstraint constraintWithItem:firstLayoutItem + attribute:firstLayoutAttribute + relatedBy:self.layoutRelation + toItem:secondLayoutItem + attribute:secondLayoutAttribute + multiplier:self.layoutMultiplier + constant:self.layoutConstant]; + + layoutConstraint.priority = self.layoutPriority; + layoutConstraint.mas_key = self.mas_key; + + if (self.secondViewAttribute.view) { + MAS_VIEW *closestCommonSuperview = [self.firstViewAttribute.view mas_closestCommonSuperview:self.secondViewAttribute.view]; + NSAssert(closestCommonSuperview, + @"couldn't find a common superview for %@ and %@", + self.firstViewAttribute.view, self.secondViewAttribute.view); + self.installedView = closestCommonSuperview; + } else if (self.firstViewAttribute.isSizeAttribute) { + self.installedView = self.firstViewAttribute.view; + } else { + self.installedView = self.firstViewAttribute.view.superview; + } + + + MASLayoutConstraint *existingConstraint = nil; + if (self.updateExisting) { + existingConstraint = [self layoutConstraintSimilarTo:layoutConstraint]; + } + if (existingConstraint) { + // just update the constant + existingConstraint.constant = layoutConstraint.constant; + self.layoutConstraint = existingConstraint; + } else { + [self.installedView addConstraint:layoutConstraint]; + self.layoutConstraint = layoutConstraint; + [firstLayoutItem.mas_installedConstraints addObject:self]; + } +} + +- (MASLayoutConstraint *)layoutConstraintSimilarTo:(MASLayoutConstraint *)layoutConstraint { + // check if any constraints are the same apart from the only mutable property constant + + // go through constraints in reverse as we do not want to match auto-resizing or interface builder constraints + // and they are likely to be added first. + for (NSLayoutConstraint *existingConstraint in self.installedView.constraints.reverseObjectEnumerator) { + if (![existingConstraint isKindOfClass:MASLayoutConstraint.class]) continue; + if (existingConstraint.firstItem != layoutConstraint.firstItem) continue; + if (existingConstraint.secondItem != layoutConstraint.secondItem) continue; + if (existingConstraint.firstAttribute != layoutConstraint.firstAttribute) continue; + if (existingConstraint.secondAttribute != layoutConstraint.secondAttribute) continue; + if (existingConstraint.relation != layoutConstraint.relation) continue; + if (existingConstraint.multiplier != layoutConstraint.multiplier) continue; + if (existingConstraint.priority != layoutConstraint.priority) continue; + + return (id)existingConstraint; + } + return nil; +} + +- (void)uninstall { + if ([self supportsActiveProperty]) { + self.layoutConstraint.active = NO; + [self.firstViewAttribute.view.mas_installedConstraints removeObject:self]; + return; + } + + [self.installedView removeConstraint:self.layoutConstraint]; + self.layoutConstraint = nil; + self.installedView = nil; + + [self.firstViewAttribute.view.mas_installedConstraints removeObject:self]; +} + +@end diff --git a/Example/Pods/Masonry/Masonry/Masonry.h b/Example/Pods/Masonry/Masonry/Masonry.h new file mode 100644 index 0000000..d1bd579 --- /dev/null +++ b/Example/Pods/Masonry/Masonry/Masonry.h @@ -0,0 +1,29 @@ +// +// Masonry.h +// Masonry +// +// Created by Jonas Budelmann on 20/07/13. +// Copyright (c) 2013 cloudling. All rights reserved. +// + +#import + +//! Project version number for Masonry. +FOUNDATION_EXPORT double MasonryVersionNumber; + +//! Project version string for Masonry. +FOUNDATION_EXPORT const unsigned char MasonryVersionString[]; + +#import "MASUtilities.h" +#import "View+MASAdditions.h" +#import "View+MASShorthandAdditions.h" +#import "ViewController+MASAdditions.h" +#import "NSArray+MASAdditions.h" +#import "NSArray+MASShorthandAdditions.h" +#import "MASConstraint.h" +#import "MASCompositeConstraint.h" +#import "MASViewAttribute.h" +#import "MASViewConstraint.h" +#import "MASConstraintMaker.h" +#import "MASLayoutConstraint.h" +#import "NSLayoutConstraint+MASDebugAdditions.h" diff --git a/Example/Pods/Masonry/Masonry/NSArray+MASAdditions.h b/Example/Pods/Masonry/Masonry/NSArray+MASAdditions.h new file mode 100644 index 0000000..587618d --- /dev/null +++ b/Example/Pods/Masonry/Masonry/NSArray+MASAdditions.h @@ -0,0 +1,72 @@ +// +// NSArray+MASAdditions.h +// +// +// Created by Daniel Hammond on 11/26/13. +// +// + +#import "MASUtilities.h" +#import "MASConstraintMaker.h" +#import "MASViewAttribute.h" + +typedef NS_ENUM(NSUInteger, MASAxisType) { + MASAxisTypeHorizontal, + MASAxisTypeVertical +}; + +@interface NSArray (MASAdditions) + +/** + * Creates a MASConstraintMaker with each view in the callee. + * Any constraints defined are added to the view or the appropriate superview once the block has finished executing on each view + * + * @param block scope within which you can build up the constraints which you wish to apply to each view. + * + * @return Array of created MASConstraints + */ +- (NSArray *)mas_makeConstraints:(void (NS_NOESCAPE ^)(MASConstraintMaker *make))block; + +/** + * Creates a MASConstraintMaker with each view in the callee. + * Any constraints defined are added to each view or the appropriate superview once the block has finished executing on each view. + * If an existing constraint exists then it will be updated instead. + * + * @param block scope within which you can build up the constraints which you wish to apply to each view. + * + * @return Array of created/updated MASConstraints + */ +- (NSArray *)mas_updateConstraints:(void (NS_NOESCAPE ^)(MASConstraintMaker *make))block; + +/** + * Creates a MASConstraintMaker with each view in the callee. + * Any constraints defined are added to each view or the appropriate superview once the block has finished executing on each view. + * All constraints previously installed for the views will be removed. + * + * @param block scope within which you can build up the constraints which you wish to apply to each view. + * + * @return Array of created/updated MASConstraints + */ +- (NSArray *)mas_remakeConstraints:(void (NS_NOESCAPE ^)(MASConstraintMaker *make))block; + +/** + * distribute with fixed spacing + * + * @param axisType which axis to distribute items along + * @param fixedSpacing the spacing between each item + * @param leadSpacing the spacing before the first item and the container + * @param tailSpacing the spacing after the last item and the container + */ +- (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType withFixedSpacing:(CGFloat)fixedSpacing leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing; + +/** + * distribute with fixed item size + * + * @param axisType which axis to distribute items along + * @param fixedItemLength the fixed length of each item + * @param leadSpacing the spacing before the first item and the container + * @param tailSpacing the spacing after the last item and the container + */ +- (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType withFixedItemLength:(CGFloat)fixedItemLength leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing; + +@end diff --git a/Example/Pods/Masonry/Masonry/NSArray+MASAdditions.m b/Example/Pods/Masonry/Masonry/NSArray+MASAdditions.m new file mode 100644 index 0000000..831d8cd --- /dev/null +++ b/Example/Pods/Masonry/Masonry/NSArray+MASAdditions.m @@ -0,0 +1,162 @@ +// +// NSArray+MASAdditions.m +// +// +// Created by Daniel Hammond on 11/26/13. +// +// + +#import "NSArray+MASAdditions.h" +#import "View+MASAdditions.h" + +@implementation NSArray (MASAdditions) + +- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *make))block { + NSMutableArray *constraints = [NSMutableArray array]; + for (MAS_VIEW *view in self) { + NSAssert([view isKindOfClass:[MAS_VIEW class]], @"All objects in the array must be views"); + [constraints addObjectsFromArray:[view mas_makeConstraints:block]]; + } + return constraints; +} + +- (NSArray *)mas_updateConstraints:(void(^)(MASConstraintMaker *make))block { + NSMutableArray *constraints = [NSMutableArray array]; + for (MAS_VIEW *view in self) { + NSAssert([view isKindOfClass:[MAS_VIEW class]], @"All objects in the array must be views"); + [constraints addObjectsFromArray:[view mas_updateConstraints:block]]; + } + return constraints; +} + +- (NSArray *)mas_remakeConstraints:(void(^)(MASConstraintMaker *make))block { + NSMutableArray *constraints = [NSMutableArray array]; + for (MAS_VIEW *view in self) { + NSAssert([view isKindOfClass:[MAS_VIEW class]], @"All objects in the array must be views"); + [constraints addObjectsFromArray:[view mas_remakeConstraints:block]]; + } + return constraints; +} + +- (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType withFixedSpacing:(CGFloat)fixedSpacing leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing { + if (self.count < 2) { + NSAssert(self.count>1,@"views to distribute need to bigger than one"); + return; + } + + MAS_VIEW *tempSuperView = [self mas_commonSuperviewOfViews]; + if (axisType == MASAxisTypeHorizontal) { + MAS_VIEW *prev; + for (int i = 0; i < self.count; i++) { + MAS_VIEW *v = self[i]; + [v mas_makeConstraints:^(MASConstraintMaker *make) { + if (prev) { + make.width.equalTo(prev); + make.left.equalTo(prev.mas_right).offset(fixedSpacing); + if (i == self.count - 1) {//last one + make.right.equalTo(tempSuperView).offset(-tailSpacing); + } + } + else {//first one + make.left.equalTo(tempSuperView).offset(leadSpacing); + } + + }]; + prev = v; + } + } + else { + MAS_VIEW *prev; + for (int i = 0; i < self.count; i++) { + MAS_VIEW *v = self[i]; + [v mas_makeConstraints:^(MASConstraintMaker *make) { + if (prev) { + make.height.equalTo(prev); + make.top.equalTo(prev.mas_bottom).offset(fixedSpacing); + if (i == self.count - 1) {//last one + make.bottom.equalTo(tempSuperView).offset(-tailSpacing); + } + } + else {//first one + make.top.equalTo(tempSuperView).offset(leadSpacing); + } + + }]; + prev = v; + } + } +} + +- (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType withFixedItemLength:(CGFloat)fixedItemLength leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing { + if (self.count < 2) { + NSAssert(self.count>1,@"views to distribute need to bigger than one"); + return; + } + + MAS_VIEW *tempSuperView = [self mas_commonSuperviewOfViews]; + if (axisType == MASAxisTypeHorizontal) { + MAS_VIEW *prev; + for (int i = 0; i < self.count; i++) { + MAS_VIEW *v = self[i]; + [v mas_makeConstraints:^(MASConstraintMaker *make) { + make.width.equalTo(@(fixedItemLength)); + if (prev) { + if (i == self.count - 1) {//last one + make.right.equalTo(tempSuperView).offset(-tailSpacing); + } + else { + CGFloat offset = (1-(i/((CGFloat)self.count-1)))*(fixedItemLength+leadSpacing)-i*tailSpacing/(((CGFloat)self.count-1)); + make.right.equalTo(tempSuperView).multipliedBy(i/((CGFloat)self.count-1)).with.offset(offset); + } + } + else {//first one + make.left.equalTo(tempSuperView).offset(leadSpacing); + } + }]; + prev = v; + } + } + else { + MAS_VIEW *prev; + for (int i = 0; i < self.count; i++) { + MAS_VIEW *v = self[i]; + [v mas_makeConstraints:^(MASConstraintMaker *make) { + make.height.equalTo(@(fixedItemLength)); + if (prev) { + if (i == self.count - 1) {//last one + make.bottom.equalTo(tempSuperView).offset(-tailSpacing); + } + else { + CGFloat offset = (1-(i/((CGFloat)self.count-1)))*(fixedItemLength+leadSpacing)-i*tailSpacing/(((CGFloat)self.count-1)); + make.bottom.equalTo(tempSuperView).multipliedBy(i/((CGFloat)self.count-1)).with.offset(offset); + } + } + else {//first one + make.top.equalTo(tempSuperView).offset(leadSpacing); + } + }]; + prev = v; + } + } +} + +- (MAS_VIEW *)mas_commonSuperviewOfViews +{ + MAS_VIEW *commonSuperview = nil; + MAS_VIEW *previousView = nil; + for (id object in self) { + if ([object isKindOfClass:[MAS_VIEW class]]) { + MAS_VIEW *view = (MAS_VIEW *)object; + if (previousView) { + commonSuperview = [view mas_closestCommonSuperview:commonSuperview]; + } else { + commonSuperview = view; + } + previousView = view; + } + } + NSAssert(commonSuperview, @"Can't constrain views that do not share a common superview. Make sure that all the views in this array have been added into the same view hierarchy."); + return commonSuperview; +} + +@end diff --git a/Example/Pods/Masonry/Masonry/NSArray+MASShorthandAdditions.h b/Example/Pods/Masonry/Masonry/NSArray+MASShorthandAdditions.h new file mode 100644 index 0000000..8b47369 --- /dev/null +++ b/Example/Pods/Masonry/Masonry/NSArray+MASShorthandAdditions.h @@ -0,0 +1,41 @@ +// +// NSArray+MASShorthandAdditions.h +// Masonry +// +// Created by Jonas Budelmann on 22/07/13. +// Copyright (c) 2013 Jonas Budelmann. All rights reserved. +// + +#import "NSArray+MASAdditions.h" + +#ifdef MAS_SHORTHAND + +/** + * Shorthand array additions without the 'mas_' prefixes, + * only enabled if MAS_SHORTHAND is defined + */ +@interface NSArray (MASShorthandAdditions) + +- (NSArray *)makeConstraints:(void(^)(MASConstraintMaker *make))block; +- (NSArray *)updateConstraints:(void(^)(MASConstraintMaker *make))block; +- (NSArray *)remakeConstraints:(void(^)(MASConstraintMaker *make))block; + +@end + +@implementation NSArray (MASShorthandAdditions) + +- (NSArray *)makeConstraints:(void(^)(MASConstraintMaker *))block { + return [self mas_makeConstraints:block]; +} + +- (NSArray *)updateConstraints:(void(^)(MASConstraintMaker *))block { + return [self mas_updateConstraints:block]; +} + +- (NSArray *)remakeConstraints:(void(^)(MASConstraintMaker *))block { + return [self mas_remakeConstraints:block]; +} + +@end + +#endif diff --git a/Example/Pods/Masonry/Masonry/NSLayoutConstraint+MASDebugAdditions.h b/Example/Pods/Masonry/Masonry/NSLayoutConstraint+MASDebugAdditions.h new file mode 100644 index 0000000..1279b4f --- /dev/null +++ b/Example/Pods/Masonry/Masonry/NSLayoutConstraint+MASDebugAdditions.h @@ -0,0 +1,16 @@ +// +// NSLayoutConstraint+MASDebugAdditions.h +// Masonry +// +// Created by Jonas Budelmann on 3/08/13. +// Copyright (c) 2013 Jonas Budelmann. All rights reserved. +// + +#import "MASUtilities.h" + +/** + * makes debug and log output of NSLayoutConstraints more readable + */ +@interface NSLayoutConstraint (MASDebugAdditions) + +@end diff --git a/Example/Pods/Masonry/Masonry/NSLayoutConstraint+MASDebugAdditions.m b/Example/Pods/Masonry/Masonry/NSLayoutConstraint+MASDebugAdditions.m new file mode 100644 index 0000000..ab539a2 --- /dev/null +++ b/Example/Pods/Masonry/Masonry/NSLayoutConstraint+MASDebugAdditions.m @@ -0,0 +1,146 @@ +// +// NSLayoutConstraint+MASDebugAdditions.m +// Masonry +// +// Created by Jonas Budelmann on 3/08/13. +// Copyright (c) 2013 Jonas Budelmann. All rights reserved. +// + +#import "NSLayoutConstraint+MASDebugAdditions.h" +#import "MASConstraint.h" +#import "MASLayoutConstraint.h" + +@implementation NSLayoutConstraint (MASDebugAdditions) + +#pragma mark - description maps + ++ (NSDictionary *)layoutRelationDescriptionsByValue { + static dispatch_once_t once; + static NSDictionary *descriptionMap; + dispatch_once(&once, ^{ + descriptionMap = @{ + @(NSLayoutRelationEqual) : @"==", + @(NSLayoutRelationGreaterThanOrEqual) : @">=", + @(NSLayoutRelationLessThanOrEqual) : @"<=", + }; + }); + return descriptionMap; +} + ++ (NSDictionary *)layoutAttributeDescriptionsByValue { + static dispatch_once_t once; + static NSDictionary *descriptionMap; + dispatch_once(&once, ^{ + descriptionMap = @{ + @(NSLayoutAttributeTop) : @"top", + @(NSLayoutAttributeLeft) : @"left", + @(NSLayoutAttributeBottom) : @"bottom", + @(NSLayoutAttributeRight) : @"right", + @(NSLayoutAttributeLeading) : @"leading", + @(NSLayoutAttributeTrailing) : @"trailing", + @(NSLayoutAttributeWidth) : @"width", + @(NSLayoutAttributeHeight) : @"height", + @(NSLayoutAttributeCenterX) : @"centerX", + @(NSLayoutAttributeCenterY) : @"centerY", + @(NSLayoutAttributeBaseline) : @"baseline", + +#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) + @(NSLayoutAttributeFirstBaseline) : @"firstBaseline", + @(NSLayoutAttributeLastBaseline) : @"lastBaseline", +#endif + +#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) + @(NSLayoutAttributeLeftMargin) : @"leftMargin", + @(NSLayoutAttributeRightMargin) : @"rightMargin", + @(NSLayoutAttributeTopMargin) : @"topMargin", + @(NSLayoutAttributeBottomMargin) : @"bottomMargin", + @(NSLayoutAttributeLeadingMargin) : @"leadingMargin", + @(NSLayoutAttributeTrailingMargin) : @"trailingMargin", + @(NSLayoutAttributeCenterXWithinMargins) : @"centerXWithinMargins", + @(NSLayoutAttributeCenterYWithinMargins) : @"centerYWithinMargins", +#endif + + }; + + }); + return descriptionMap; +} + + ++ (NSDictionary *)layoutPriorityDescriptionsByValue { + static dispatch_once_t once; + static NSDictionary *descriptionMap; + dispatch_once(&once, ^{ +#if TARGET_OS_IPHONE || TARGET_OS_TV + descriptionMap = @{ + @(MASLayoutPriorityDefaultHigh) : @"high", + @(MASLayoutPriorityDefaultLow) : @"low", + @(MASLayoutPriorityDefaultMedium) : @"medium", + @(MASLayoutPriorityRequired) : @"required", + @(MASLayoutPriorityFittingSizeLevel) : @"fitting size", + }; +#elif TARGET_OS_MAC + descriptionMap = @{ + @(MASLayoutPriorityDefaultHigh) : @"high", + @(MASLayoutPriorityDragThatCanResizeWindow) : @"drag can resize window", + @(MASLayoutPriorityDefaultMedium) : @"medium", + @(MASLayoutPriorityWindowSizeStayPut) : @"window size stay put", + @(MASLayoutPriorityDragThatCannotResizeWindow) : @"drag cannot resize window", + @(MASLayoutPriorityDefaultLow) : @"low", + @(MASLayoutPriorityFittingSizeCompression) : @"fitting size", + @(MASLayoutPriorityRequired) : @"required", + }; +#endif + }); + return descriptionMap; +} + +#pragma mark - description override + ++ (NSString *)descriptionForObject:(id)obj { + if ([obj respondsToSelector:@selector(mas_key)] && [obj mas_key]) { + return [NSString stringWithFormat:@"%@:%@", [obj class], [obj mas_key]]; + } + return [NSString stringWithFormat:@"%@:%p", [obj class], obj]; +} + +- (NSString *)description { + NSMutableString *description = [[NSMutableString alloc] initWithString:@"<"]; + + [description appendString:[self.class descriptionForObject:self]]; + + [description appendFormat:@" %@", [self.class descriptionForObject:self.firstItem]]; + if (self.firstAttribute != NSLayoutAttributeNotAnAttribute) { + [description appendFormat:@".%@", self.class.layoutAttributeDescriptionsByValue[@(self.firstAttribute)]]; + } + + [description appendFormat:@" %@", self.class.layoutRelationDescriptionsByValue[@(self.relation)]]; + + if (self.secondItem) { + [description appendFormat:@" %@", [self.class descriptionForObject:self.secondItem]]; + } + if (self.secondAttribute != NSLayoutAttributeNotAnAttribute) { + [description appendFormat:@".%@", self.class.layoutAttributeDescriptionsByValue[@(self.secondAttribute)]]; + } + + if (self.multiplier != 1) { + [description appendFormat:@" * %g", self.multiplier]; + } + + if (self.secondAttribute == NSLayoutAttributeNotAnAttribute) { + [description appendFormat:@" %g", self.constant]; + } else { + if (self.constant) { + [description appendFormat:@" %@ %g", (self.constant < 0 ? @"-" : @"+"), ABS(self.constant)]; + } + } + + if (self.priority != MASLayoutPriorityRequired) { + [description appendFormat:@" ^%@", self.class.layoutPriorityDescriptionsByValue[@(self.priority)] ?: [NSNumber numberWithDouble:self.priority]]; + } + + [description appendString:@">"]; + return description; +} + +@end diff --git a/Example/Pods/Masonry/Masonry/View+MASAdditions.h b/Example/Pods/Masonry/Masonry/View+MASAdditions.h new file mode 100644 index 0000000..f7343d2 --- /dev/null +++ b/Example/Pods/Masonry/Masonry/View+MASAdditions.h @@ -0,0 +1,111 @@ +// +// UIView+MASAdditions.h +// Masonry +// +// Created by Jonas Budelmann on 20/07/13. +// Copyright (c) 2013 cloudling. All rights reserved. +// + +#import "MASUtilities.h" +#import "MASConstraintMaker.h" +#import "MASViewAttribute.h" + +/** + * Provides constraint maker block + * and convience methods for creating MASViewAttribute which are view + NSLayoutAttribute pairs + */ +@interface MAS_VIEW (MASAdditions) + +/** + * following properties return a new MASViewAttribute with current view and appropriate NSLayoutAttribute + */ +@property (nonatomic, strong, readonly) MASViewAttribute *mas_left; +@property (nonatomic, strong, readonly) MASViewAttribute *mas_top; +@property (nonatomic, strong, readonly) MASViewAttribute *mas_right; +@property (nonatomic, strong, readonly) MASViewAttribute *mas_bottom; +@property (nonatomic, strong, readonly) MASViewAttribute *mas_leading; +@property (nonatomic, strong, readonly) MASViewAttribute *mas_trailing; +@property (nonatomic, strong, readonly) MASViewAttribute *mas_width; +@property (nonatomic, strong, readonly) MASViewAttribute *mas_height; +@property (nonatomic, strong, readonly) MASViewAttribute *mas_centerX; +@property (nonatomic, strong, readonly) MASViewAttribute *mas_centerY; +@property (nonatomic, strong, readonly) MASViewAttribute *mas_baseline; +@property (nonatomic, strong, readonly) MASViewAttribute *(^mas_attribute)(NSLayoutAttribute attr); + +#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) + +@property (nonatomic, strong, readonly) MASViewAttribute *mas_firstBaseline; +@property (nonatomic, strong, readonly) MASViewAttribute *mas_lastBaseline; + +#endif + +#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) + +@property (nonatomic, strong, readonly) MASViewAttribute *mas_leftMargin; +@property (nonatomic, strong, readonly) MASViewAttribute *mas_rightMargin; +@property (nonatomic, strong, readonly) MASViewAttribute *mas_topMargin; +@property (nonatomic, strong, readonly) MASViewAttribute *mas_bottomMargin; +@property (nonatomic, strong, readonly) MASViewAttribute *mas_leadingMargin; +@property (nonatomic, strong, readonly) MASViewAttribute *mas_trailingMargin; +@property (nonatomic, strong, readonly) MASViewAttribute *mas_centerXWithinMargins; +@property (nonatomic, strong, readonly) MASViewAttribute *mas_centerYWithinMargins; + +#endif + +#if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 110000) || (__TV_OS_VERSION_MAX_ALLOWED >= 110000) + +@property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuide API_AVAILABLE(ios(11.0),tvos(11.0)); +@property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuideTop API_AVAILABLE(ios(11.0),tvos(11.0)); +@property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuideBottom API_AVAILABLE(ios(11.0),tvos(11.0)); +@property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuideLeft API_AVAILABLE(ios(11.0),tvos(11.0)); +@property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuideRight API_AVAILABLE(ios(11.0),tvos(11.0)); + +#endif + +/** + * a key to associate with this view + */ +@property (nonatomic, strong) id mas_key; + +/** + * Finds the closest common superview between this view and another view + * + * @param view other view + * + * @return returns nil if common superview could not be found + */ +- (instancetype)mas_closestCommonSuperview:(MAS_VIEW *)view; + +/** + * Creates a MASConstraintMaker with the callee view. + * Any constraints defined are added to the view or the appropriate superview once the block has finished executing + * + * @param block scope within which you can build up the constraints which you wish to apply to the view. + * + * @return Array of created MASConstraints + */ +- (NSArray *)mas_makeConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block; + +/** + * Creates a MASConstraintMaker with the callee view. + * Any constraints defined are added to the view or the appropriate superview once the block has finished executing. + * If an existing constraint exists then it will be updated instead. + * + * @param block scope within which you can build up the constraints which you wish to apply to the view. + * + * @return Array of created/updated MASConstraints + */ +- (NSArray *)mas_updateConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block; + +/** + * Creates a MASConstraintMaker with the callee view. + * Any constraints defined are added to the view or the appropriate superview once the block has finished executing. + * All constraints previously installed for the view will be removed. + * + * @param block scope within which you can build up the constraints which you wish to apply to the view. + * + * @return Array of created/updated MASConstraints + */ +- (NSArray *)mas_remakeConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block; + +@end diff --git a/Example/Pods/Masonry/Masonry/View+MASAdditions.m b/Example/Pods/Masonry/Masonry/View+MASAdditions.m new file mode 100644 index 0000000..4fa07b4 --- /dev/null +++ b/Example/Pods/Masonry/Masonry/View+MASAdditions.m @@ -0,0 +1,186 @@ +// +// UIView+MASAdditions.m +// Masonry +// +// Created by Jonas Budelmann on 20/07/13. +// Copyright (c) 2013 cloudling. All rights reserved. +// + +#import "View+MASAdditions.h" +#import + +@implementation MAS_VIEW (MASAdditions) + +- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block { + self.translatesAutoresizingMaskIntoConstraints = NO; + MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self]; + block(constraintMaker); + return [constraintMaker install]; +} + +- (NSArray *)mas_updateConstraints:(void(^)(MASConstraintMaker *))block { + self.translatesAutoresizingMaskIntoConstraints = NO; + MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self]; + constraintMaker.updateExisting = YES; + block(constraintMaker); + return [constraintMaker install]; +} + +- (NSArray *)mas_remakeConstraints:(void(^)(MASConstraintMaker *make))block { + self.translatesAutoresizingMaskIntoConstraints = NO; + MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self]; + constraintMaker.removeExisting = YES; + block(constraintMaker); + return [constraintMaker install]; +} + +#pragma mark - NSLayoutAttribute properties + +- (MASViewAttribute *)mas_left { + return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeLeft]; +} + +- (MASViewAttribute *)mas_top { + return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeTop]; +} + +- (MASViewAttribute *)mas_right { + return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeRight]; +} + +- (MASViewAttribute *)mas_bottom { + return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeBottom]; +} + +- (MASViewAttribute *)mas_leading { + return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeLeading]; +} + +- (MASViewAttribute *)mas_trailing { + return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeTrailing]; +} + +- (MASViewAttribute *)mas_width { + return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeWidth]; +} + +- (MASViewAttribute *)mas_height { + return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeHeight]; +} + +- (MASViewAttribute *)mas_centerX { + return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeCenterX]; +} + +- (MASViewAttribute *)mas_centerY { + return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeCenterY]; +} + +- (MASViewAttribute *)mas_baseline { + return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeBaseline]; +} + +- (MASViewAttribute *(^)(NSLayoutAttribute))mas_attribute +{ + return ^(NSLayoutAttribute attr) { + return [[MASViewAttribute alloc] initWithView:self layoutAttribute:attr]; + }; +} + +#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) + +- (MASViewAttribute *)mas_firstBaseline { + return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeFirstBaseline]; +} +- (MASViewAttribute *)mas_lastBaseline { + return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeLastBaseline]; +} + +#endif + +#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) + +- (MASViewAttribute *)mas_leftMargin { + return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeLeftMargin]; +} + +- (MASViewAttribute *)mas_rightMargin { + return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeRightMargin]; +} + +- (MASViewAttribute *)mas_topMargin { + return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeTopMargin]; +} + +- (MASViewAttribute *)mas_bottomMargin { + return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeBottomMargin]; +} + +- (MASViewAttribute *)mas_leadingMargin { + return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeLeadingMargin]; +} + +- (MASViewAttribute *)mas_trailingMargin { + return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeTrailingMargin]; +} + +- (MASViewAttribute *)mas_centerXWithinMargins { + return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeCenterXWithinMargins]; +} + +- (MASViewAttribute *)mas_centerYWithinMargins { + return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeCenterYWithinMargins]; +} + +#endif + +#if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 110000) || (__TV_OS_VERSION_MAX_ALLOWED >= 110000) + +- (MASViewAttribute *)mas_safeAreaLayoutGuide { + return [[MASViewAttribute alloc] initWithView:self item:self.safeAreaLayoutGuide layoutAttribute:NSLayoutAttributeBottom]; +} +- (MASViewAttribute *)mas_safeAreaLayoutGuideTop { + return [[MASViewAttribute alloc] initWithView:self item:self.safeAreaLayoutGuide layoutAttribute:NSLayoutAttributeTop]; +} +- (MASViewAttribute *)mas_safeAreaLayoutGuideBottom { + return [[MASViewAttribute alloc] initWithView:self item:self.safeAreaLayoutGuide layoutAttribute:NSLayoutAttributeBottom]; +} +- (MASViewAttribute *)mas_safeAreaLayoutGuideLeft { + return [[MASViewAttribute alloc] initWithView:self item:self.safeAreaLayoutGuide layoutAttribute:NSLayoutAttributeLeft]; +} +- (MASViewAttribute *)mas_safeAreaLayoutGuideRight { + return [[MASViewAttribute alloc] initWithView:self item:self.safeAreaLayoutGuide layoutAttribute:NSLayoutAttributeRight]; +} + +#endif + +#pragma mark - associated properties + +- (id)mas_key { + return objc_getAssociatedObject(self, @selector(mas_key)); +} + +- (void)setMas_key:(id)key { + objc_setAssociatedObject(self, @selector(mas_key), key, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +#pragma mark - heirachy + +- (instancetype)mas_closestCommonSuperview:(MAS_VIEW *)view { + MAS_VIEW *closestCommonSuperview = nil; + + MAS_VIEW *secondViewSuperview = view; + while (!closestCommonSuperview && secondViewSuperview) { + MAS_VIEW *firstViewSuperview = self; + while (!closestCommonSuperview && firstViewSuperview) { + if (secondViewSuperview == firstViewSuperview) { + closestCommonSuperview = secondViewSuperview; + } + firstViewSuperview = firstViewSuperview.superview; + } + secondViewSuperview = secondViewSuperview.superview; + } + return closestCommonSuperview; +} + +@end diff --git a/Example/Pods/Masonry/Masonry/View+MASShorthandAdditions.h b/Example/Pods/Masonry/Masonry/View+MASShorthandAdditions.h new file mode 100644 index 0000000..1c19a94 --- /dev/null +++ b/Example/Pods/Masonry/Masonry/View+MASShorthandAdditions.h @@ -0,0 +1,133 @@ +// +// UIView+MASShorthandAdditions.h +// Masonry +// +// Created by Jonas Budelmann on 22/07/13. +// Copyright (c) 2013 Jonas Budelmann. All rights reserved. +// + +#import "View+MASAdditions.h" + +#ifdef MAS_SHORTHAND + +/** + * Shorthand view additions without the 'mas_' prefixes, + * only enabled if MAS_SHORTHAND is defined + */ +@interface MAS_VIEW (MASShorthandAdditions) + +@property (nonatomic, strong, readonly) MASViewAttribute *left; +@property (nonatomic, strong, readonly) MASViewAttribute *top; +@property (nonatomic, strong, readonly) MASViewAttribute *right; +@property (nonatomic, strong, readonly) MASViewAttribute *bottom; +@property (nonatomic, strong, readonly) MASViewAttribute *leading; +@property (nonatomic, strong, readonly) MASViewAttribute *trailing; +@property (nonatomic, strong, readonly) MASViewAttribute *width; +@property (nonatomic, strong, readonly) MASViewAttribute *height; +@property (nonatomic, strong, readonly) MASViewAttribute *centerX; +@property (nonatomic, strong, readonly) MASViewAttribute *centerY; +@property (nonatomic, strong, readonly) MASViewAttribute *baseline; +@property (nonatomic, strong, readonly) MASViewAttribute *(^attribute)(NSLayoutAttribute attr); + +#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) + +@property (nonatomic, strong, readonly) MASViewAttribute *firstBaseline; +@property (nonatomic, strong, readonly) MASViewAttribute *lastBaseline; + +#endif + +#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) + +@property (nonatomic, strong, readonly) MASViewAttribute *leftMargin; +@property (nonatomic, strong, readonly) MASViewAttribute *rightMargin; +@property (nonatomic, strong, readonly) MASViewAttribute *topMargin; +@property (nonatomic, strong, readonly) MASViewAttribute *bottomMargin; +@property (nonatomic, strong, readonly) MASViewAttribute *leadingMargin; +@property (nonatomic, strong, readonly) MASViewAttribute *trailingMargin; +@property (nonatomic, strong, readonly) MASViewAttribute *centerXWithinMargins; +@property (nonatomic, strong, readonly) MASViewAttribute *centerYWithinMargins; + +#endif + +#if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 110000) || (__TV_OS_VERSION_MAX_ALLOWED >= 110000) + +@property (nonatomic, strong, readonly) MASViewAttribute *safeAreaLayoutGuideTop API_AVAILABLE(ios(11.0),tvos(11.0)); +@property (nonatomic, strong, readonly) MASViewAttribute *safeAreaLayoutGuideBottom API_AVAILABLE(ios(11.0),tvos(11.0)); +@property (nonatomic, strong, readonly) MASViewAttribute *safeAreaLayoutGuideLeft API_AVAILABLE(ios(11.0),tvos(11.0)); +@property (nonatomic, strong, readonly) MASViewAttribute *safeAreaLayoutGuideRight API_AVAILABLE(ios(11.0),tvos(11.0)); + +#endif + +- (NSArray *)makeConstraints:(void(^)(MASConstraintMaker *make))block; +- (NSArray *)updateConstraints:(void(^)(MASConstraintMaker *make))block; +- (NSArray *)remakeConstraints:(void(^)(MASConstraintMaker *make))block; + +@end + +#define MAS_ATTR_FORWARD(attr) \ +- (MASViewAttribute *)attr { \ + return [self mas_##attr]; \ +} + +@implementation MAS_VIEW (MASShorthandAdditions) + +MAS_ATTR_FORWARD(top); +MAS_ATTR_FORWARD(left); +MAS_ATTR_FORWARD(bottom); +MAS_ATTR_FORWARD(right); +MAS_ATTR_FORWARD(leading); +MAS_ATTR_FORWARD(trailing); +MAS_ATTR_FORWARD(width); +MAS_ATTR_FORWARD(height); +MAS_ATTR_FORWARD(centerX); +MAS_ATTR_FORWARD(centerY); +MAS_ATTR_FORWARD(baseline); + +#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) + +MAS_ATTR_FORWARD(firstBaseline); +MAS_ATTR_FORWARD(lastBaseline); + +#endif + +#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) + +MAS_ATTR_FORWARD(leftMargin); +MAS_ATTR_FORWARD(rightMargin); +MAS_ATTR_FORWARD(topMargin); +MAS_ATTR_FORWARD(bottomMargin); +MAS_ATTR_FORWARD(leadingMargin); +MAS_ATTR_FORWARD(trailingMargin); +MAS_ATTR_FORWARD(centerXWithinMargins); +MAS_ATTR_FORWARD(centerYWithinMargins); + +#endif + +#if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 110000) || (__TV_OS_VERSION_MAX_ALLOWED >= 110000) + +MAS_ATTR_FORWARD(safeAreaLayoutGuideTop); +MAS_ATTR_FORWARD(safeAreaLayoutGuideBottom); +MAS_ATTR_FORWARD(safeAreaLayoutGuideLeft); +MAS_ATTR_FORWARD(safeAreaLayoutGuideRight); + +#endif + +- (MASViewAttribute *(^)(NSLayoutAttribute))attribute { + return [self mas_attribute]; +} + +- (NSArray *)makeConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *))block { + return [self mas_makeConstraints:block]; +} + +- (NSArray *)updateConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *))block { + return [self mas_updateConstraints:block]; +} + +- (NSArray *)remakeConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *))block { + return [self mas_remakeConstraints:block]; +} + +@end + +#endif diff --git a/Example/Pods/Masonry/Masonry/ViewController+MASAdditions.h b/Example/Pods/Masonry/Masonry/ViewController+MASAdditions.h new file mode 100644 index 0000000..79fd1fa --- /dev/null +++ b/Example/Pods/Masonry/Masonry/ViewController+MASAdditions.h @@ -0,0 +1,30 @@ +// +// UIViewController+MASAdditions.h +// Masonry +// +// Created by Craig Siemens on 2015-06-23. +// +// + +#import "MASUtilities.h" +#import "MASConstraintMaker.h" +#import "MASViewAttribute.h" + +#ifdef MAS_VIEW_CONTROLLER + +@interface MAS_VIEW_CONTROLLER (MASAdditions) + +/** + * following properties return a new MASViewAttribute with appropriate UILayoutGuide and NSLayoutAttribute + */ +@property (nonatomic, strong, readonly) MASViewAttribute *mas_topLayoutGuide; +@property (nonatomic, strong, readonly) MASViewAttribute *mas_bottomLayoutGuide; +@property (nonatomic, strong, readonly) MASViewAttribute *mas_topLayoutGuideTop; +@property (nonatomic, strong, readonly) MASViewAttribute *mas_topLayoutGuideBottom; +@property (nonatomic, strong, readonly) MASViewAttribute *mas_bottomLayoutGuideTop; +@property (nonatomic, strong, readonly) MASViewAttribute *mas_bottomLayoutGuideBottom; + + +@end + +#endif diff --git a/Example/Pods/Masonry/Masonry/ViewController+MASAdditions.m b/Example/Pods/Masonry/Masonry/ViewController+MASAdditions.m new file mode 100644 index 0000000..2f5139f --- /dev/null +++ b/Example/Pods/Masonry/Masonry/ViewController+MASAdditions.m @@ -0,0 +1,39 @@ +// +// UIViewController+MASAdditions.m +// Masonry +// +// Created by Craig Siemens on 2015-06-23. +// +// + +#import "ViewController+MASAdditions.h" + +#ifdef MAS_VIEW_CONTROLLER + +@implementation MAS_VIEW_CONTROLLER (MASAdditions) + +- (MASViewAttribute *)mas_topLayoutGuide { + return [[MASViewAttribute alloc] initWithView:self.view item:self.topLayoutGuide layoutAttribute:NSLayoutAttributeBottom]; +} +- (MASViewAttribute *)mas_topLayoutGuideTop { + return [[MASViewAttribute alloc] initWithView:self.view item:self.topLayoutGuide layoutAttribute:NSLayoutAttributeTop]; +} +- (MASViewAttribute *)mas_topLayoutGuideBottom { + return [[MASViewAttribute alloc] initWithView:self.view item:self.topLayoutGuide layoutAttribute:NSLayoutAttributeBottom]; +} + +- (MASViewAttribute *)mas_bottomLayoutGuide { + return [[MASViewAttribute alloc] initWithView:self.view item:self.bottomLayoutGuide layoutAttribute:NSLayoutAttributeTop]; +} +- (MASViewAttribute *)mas_bottomLayoutGuideTop { + return [[MASViewAttribute alloc] initWithView:self.view item:self.bottomLayoutGuide layoutAttribute:NSLayoutAttributeTop]; +} +- (MASViewAttribute *)mas_bottomLayoutGuideBottom { + return [[MASViewAttribute alloc] initWithView:self.view item:self.bottomLayoutGuide layoutAttribute:NSLayoutAttributeBottom]; +} + + + +@end + +#endif diff --git a/Example/Pods/Masonry/README.md b/Example/Pods/Masonry/README.md new file mode 100644 index 0000000..d428657 --- /dev/null +++ b/Example/Pods/Masonry/README.md @@ -0,0 +1,415 @@ +# Masonry [![Build Status](https://travis-ci.org/SnapKit/Masonry.svg?branch=master)](https://travis-ci.org/SnapKit/Masonry) [![Coverage Status](https://img.shields.io/coveralls/SnapKit/Masonry.svg?style=flat-square)](https://coveralls.io/r/SnapKit/Masonry) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) ![Pod Version](https://img.shields.io/cocoapods/v/Masonry.svg?style=flat) + +**Masonry is still actively maintained, we are committed to fixing bugs and merging good quality PRs from the wider community. However if you're using Swift in your project, we recommend using [SnapKit](https://github.com/SnapKit/SnapKit) as it provides better type safety with a simpler API.** + +Masonry is a light-weight layout framework which wraps AutoLayout with a nicer syntax. Masonry has its own layout DSL which provides a chainable way of describing your NSLayoutConstraints which results in layout code that is more concise and readable. +Masonry supports iOS and Mac OS X. + +For examples take a look at the **Masonry iOS Examples** project in the Masonry workspace. You will need to run `pod install` after downloading. + +## What's wrong with NSLayoutConstraints? + +Under the hood Auto Layout is a powerful and flexible way of organising and laying out your views. However creating constraints from code is verbose and not very descriptive. +Imagine a simple example in which you want to have a view fill its superview but inset by 10 pixels on every side +```obj-c +UIView *superview = self.view; + +UIView *view1 = [[UIView alloc] init]; +view1.translatesAutoresizingMaskIntoConstraints = NO; +view1.backgroundColor = [UIColor greenColor]; +[superview addSubview:view1]; + +UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10); + +[superview addConstraints:@[ + + //view1 constraints + [NSLayoutConstraint constraintWithItem:view1 + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationEqual + toItem:superview + attribute:NSLayoutAttributeTop + multiplier:1.0 + constant:padding.top], + + [NSLayoutConstraint constraintWithItem:view1 + attribute:NSLayoutAttributeLeft + relatedBy:NSLayoutRelationEqual + toItem:superview + attribute:NSLayoutAttributeLeft + multiplier:1.0 + constant:padding.left], + + [NSLayoutConstraint constraintWithItem:view1 + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationEqual + toItem:superview + attribute:NSLayoutAttributeBottom + multiplier:1.0 + constant:-padding.bottom], + + [NSLayoutConstraint constraintWithItem:view1 + attribute:NSLayoutAttributeRight + relatedBy:NSLayoutRelationEqual + toItem:superview + attribute:NSLayoutAttributeRight + multiplier:1 + constant:-padding.right], + + ]]; +``` +Even with such a simple example the code needed is quite verbose and quickly becomes unreadable when you have more than 2 or 3 views. +Another option is to use Visual Format Language (VFL), which is a bit less long winded. +However the ASCII type syntax has its own pitfalls and its also a bit harder to animate as `NSLayoutConstraint constraintsWithVisualFormat:` returns an array. + +## Prepare to meet your Maker! + +Heres the same constraints created using MASConstraintMaker + +```obj-c +UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10); + +[view1 mas_makeConstraints:^(MASConstraintMaker *make) { + make.top.equalTo(superview.mas_top).with.offset(padding.top); //with is an optional semantic filler + make.left.equalTo(superview.mas_left).with.offset(padding.left); + make.bottom.equalTo(superview.mas_bottom).with.offset(-padding.bottom); + make.right.equalTo(superview.mas_right).with.offset(-padding.right); +}]; +``` +Or even shorter + +```obj-c +[view1 mas_makeConstraints:^(MASConstraintMaker *make) { + make.edges.equalTo(superview).with.insets(padding); +}]; +``` + +Also note in the first example we had to add the constraints to the superview `[superview addConstraints:...`. +Masonry however will automagically add constraints to the appropriate view. + +Masonry will also call `view1.translatesAutoresizingMaskIntoConstraints = NO;` for you. + +## Not all things are created equal + +> `.equalTo` equivalent to **NSLayoutRelationEqual** + +> `.lessThanOrEqualTo` equivalent to **NSLayoutRelationLessThanOrEqual** + +> `.greaterThanOrEqualTo` equivalent to **NSLayoutRelationGreaterThanOrEqual** + +These three equality constraints accept one argument which can be any of the following: + +#### 1. MASViewAttribute + +```obj-c +make.centerX.lessThanOrEqualTo(view2.mas_left); +``` + +MASViewAttribute | NSLayoutAttribute +------------------------- | -------------------------- +view.mas_left | NSLayoutAttributeLeft +view.mas_right | NSLayoutAttributeRight +view.mas_top | NSLayoutAttributeTop +view.mas_bottom | NSLayoutAttributeBottom +view.mas_leading | NSLayoutAttributeLeading +view.mas_trailing | NSLayoutAttributeTrailing +view.mas_width | NSLayoutAttributeWidth +view.mas_height | NSLayoutAttributeHeight +view.mas_centerX | NSLayoutAttributeCenterX +view.mas_centerY | NSLayoutAttributeCenterY +view.mas_baseline | NSLayoutAttributeBaseline + +#### 2. UIView/NSView + +if you want view.left to be greater than or equal to label.left : +```obj-c +//these two constraints are exactly the same +make.left.greaterThanOrEqualTo(label); +make.left.greaterThanOrEqualTo(label.mas_left); +``` + +#### 3. NSNumber + +Auto Layout allows width and height to be set to constant values. +if you want to set view to have a minimum and maximum width you could pass a number to the equality blocks: +```obj-c +//width >= 200 && width <= 400 +make.width.greaterThanOrEqualTo(@200); +make.width.lessThanOrEqualTo(@400) +``` + +However Auto Layout does not allow alignment attributes such as left, right, centerY etc to be set to constant values. +So if you pass a NSNumber for these attributes Masonry will turn these into constraints relative to the view’s superview ie: +```obj-c +//creates view.left = view.superview.left + 10 +make.left.lessThanOrEqualTo(@10) +``` + +Instead of using NSNumber, you can use primitives and structs to build your constraints, like so: +```obj-c +make.top.mas_equalTo(42); +make.height.mas_equalTo(20); +make.size.mas_equalTo(CGSizeMake(50, 100)); +make.edges.mas_equalTo(UIEdgeInsetsMake(10, 0, 10, 0)); +make.left.mas_equalTo(view).mas_offset(UIEdgeInsetsMake(10, 0, 10, 0)); +``` + +By default, macros which support [autoboxing](https://en.wikipedia.org/wiki/Autoboxing#Autoboxing) are prefixed with `mas_`. Unprefixed versions are available by defining `MAS_SHORTHAND_GLOBALS` before importing Masonry. + +#### 4. NSArray + +An array of a mixture of any of the previous types +```obj-c +make.height.equalTo(@[view1.mas_height, view2.mas_height]); +make.height.equalTo(@[view1, view2]); +make.left.equalTo(@[view1, @100, view3.right]); +```` + +## Learn to prioritize + +> `.priority` allows you to specify an exact priority + +> `.priorityHigh` equivalent to **UILayoutPriorityDefaultHigh** + +> `.priorityMedium` is half way between high and low + +> `.priorityLow` equivalent to **UILayoutPriorityDefaultLow** + +Priorities are can be tacked on to the end of a constraint chain like so: +```obj-c +make.left.greaterThanOrEqualTo(label.mas_left).with.priorityLow(); + +make.top.equalTo(label.mas_top).with.priority(600); +``` + +## Composition, composition, composition + +Masonry also gives you a few convenience methods which create multiple constraints at the same time. These are called MASCompositeConstraints + +#### edges + +```obj-c +// make top, left, bottom, right equal view2 +make.edges.equalTo(view2); + +// make top = superview.top + 5, left = superview.left + 10, +// bottom = superview.bottom - 15, right = superview.right - 20 +make.edges.equalTo(superview).insets(UIEdgeInsetsMake(5, 10, 15, 20)) +``` + +#### size + +```obj-c +// make width and height greater than or equal to titleLabel +make.size.greaterThanOrEqualTo(titleLabel) + +// make width = superview.width + 100, height = superview.height - 50 +make.size.equalTo(superview).sizeOffset(CGSizeMake(100, -50)) +``` + +#### center +```obj-c +// make centerX and centerY = button1 +make.center.equalTo(button1) + +// make centerX = superview.centerX - 5, centerY = superview.centerY + 10 +make.center.equalTo(superview).centerOffset(CGPointMake(-5, 10)) +``` + +You can chain view attributes for increased readability: + +```obj-c +// All edges but the top should equal those of the superview +make.left.right.and.bottom.equalTo(superview); +make.top.equalTo(otherView); +``` + +## Hold on for dear life + +Sometimes you need modify existing constraints in order to animate or remove/replace constraints. +In Masonry there are a few different approaches to updating constraints. + +#### 1. References +You can hold on to a reference of a particular constraint by assigning the result of a constraint make expression to a local variable or a class property. +You could also reference multiple constraints by storing them away in an array. + +```obj-c +// in public/private interface +@property (nonatomic, strong) MASConstraint *topConstraint; + +... + +// when making constraints +[view1 mas_makeConstraints:^(MASConstraintMaker *make) { + self.topConstraint = make.top.equalTo(superview.mas_top).with.offset(padding.top); + make.left.equalTo(superview.mas_left).with.offset(padding.left); +}]; + +... +// then later you can call +[self.topConstraint uninstall]; +``` + +#### 2. mas_updateConstraints +Alternatively if you are only updating the constant value of the constraint you can use the convience method `mas_updateConstraints` instead of `mas_makeConstraints` + +```obj-c +// this is Apple's recommended place for adding/updating constraints +// this method can get called multiple times in response to setNeedsUpdateConstraints +// which can be called by UIKit internally or in your code if you need to trigger an update to your constraints +- (void)updateConstraints { + [self.growingButton mas_updateConstraints:^(MASConstraintMaker *make) { + make.center.equalTo(self); + make.width.equalTo(@(self.buttonSize.width)).priorityLow(); + make.height.equalTo(@(self.buttonSize.height)).priorityLow(); + make.width.lessThanOrEqualTo(self); + make.height.lessThanOrEqualTo(self); + }]; + + //according to apple super should be called at end of method + [super updateConstraints]; +} +``` + +### 3. mas_remakeConstraints +`mas_updateConstraints` is useful for updating a set of constraints, but doing anything beyond updating constant values can get exhausting. That's where `mas_remakeConstraints` comes in. + +`mas_remakeConstraints` is similar to `mas_updateConstraints`, but instead of updating constant values, it will remove all of its constraints before installing them again. This lets you provide different constraints without having to keep around references to ones which you want to remove. + +```obj-c +- (void)changeButtonPosition { + [self.button mas_remakeConstraints:^(MASConstraintMaker *make) { + make.size.equalTo(self.buttonSize); + + if (topLeft) { + make.top.and.left.offset(10); + } else { + make.bottom.and.right.offset(-10); + } + }]; +} +``` + +You can find more detailed examples of all three approaches in the **Masonry iOS Examples** project. + +## When the ^&*!@ hits the fan! + +Laying out your views doesn't always goto plan. So when things literally go pear shaped, you don't want to be looking at console output like this: + +```obj-c +Unable to simultaneously satisfy constraints.....blah blah blah.... +( + "=5000)]>", + "", + "", + "" +) + +Will attempt to recover by breaking constraint +=5000)]> +``` + +Masonry adds a category to NSLayoutConstraint which overrides the default implementation of `- (NSString *)description`. +Now you can give meaningful names to views and constraints, and also easily pick out the constraints created by Masonry. + +which means your console output can now look like this: + +```obj-c +Unable to simultaneously satisfy constraints......blah blah blah.... +( + "", + "= 5000>", + "", + "" +) + +Will attempt to recover by breaking constraint += 5000> +``` + +For an example of how to set this up take a look at the **Masonry iOS Examples** project in the Masonry workspace. + +## Where should I create my constraints? + +```objc +@implementation DIYCustomView + +- (id)init { + self = [super init]; + if (!self) return nil; + + // --- Create your views here --- + self.button = [[UIButton alloc] init]; + + return self; +} + +// tell UIKit that you are using AutoLayout ++ (BOOL)requiresConstraintBasedLayout { + return YES; +} + +// this is Apple's recommended place for adding/updating constraints +- (void)updateConstraints { + + // --- remake/update constraints here + [self.button remakeConstraints:^(MASConstraintMaker *make) { + make.width.equalTo(@(self.buttonSize.width)); + make.height.equalTo(@(self.buttonSize.height)); + }]; + + //according to apple super should be called at end of method + [super updateConstraints]; +} + +- (void)didTapButton:(UIButton *)button { + // --- Do your changes ie change variables that affect your layout etc --- + self.buttonSize = CGSize(200, 200); + + // tell constraints they need updating + [self setNeedsUpdateConstraints]; +} + +@end +``` + +## Installation +Use the [orsome](http://www.youtube.com/watch?v=YaIZF8uUTtk) [CocoaPods](http://github.com/CocoaPods/CocoaPods). + +In your Podfile +>`pod 'Masonry'` + +If you want to use masonry without all those pesky 'mas_' prefixes. Add #define MAS_SHORTHAND to your prefix.pch before importing Masonry +>`#define MAS_SHORTHAND` + +Get busy Masoning +>`#import "Masonry.h"` + +## Code Snippets + +Copy the included code snippets to ``~/Library/Developer/Xcode/UserData/CodeSnippets`` to write your masonry blocks at lightning speed! + +`mas_make` -> ` [<#view#> mas_makeConstraints:^(MASConstraintMaker *make) { + <#code#> + }];` + +`mas_update` -> ` [<#view#> mas_updateConstraints:^(MASConstraintMaker *make) { + <#code#> + }];` + +`mas_remake` -> ` [<#view#> mas_remakeConstraints:^(MASConstraintMaker *make) { + <#code#> + }];` + +## Features +* Not limited to subset of Auto Layout. Anything NSLayoutConstraint can do, Masonry can do too! +* Great debug support, give your views and constraints meaningful names. +* Constraints read like sentences. +* No crazy macro magic. Masonry won't pollute the global namespace with macros. +* Not string or dictionary based and hence you get compile time checking. + +## TODO +* Eye candy +* Mac example project +* More tests and examples + diff --git a/Example/Pods/Pods.xcodeproj/project.pbxproj b/Example/Pods/Pods.xcodeproj/project.pbxproj new file mode 100644 index 0000000..aacc8c6 --- /dev/null +++ b/Example/Pods/Pods.xcodeproj/project.pbxproj @@ -0,0 +1,1489 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 011DEAEC47C5802504A51B75A1AC15D9 /* MASViewConstraint.m in Sources */ = {isa = PBXBuildFile; fileRef = CE64B6C00807A34C54274710C494F424 /* MASViewConstraint.m */; }; + 01337680A3F6A694CB318554492D3958 /* LWZCollectionDecoration.h in Headers */ = {isa = PBXBuildFile; fileRef = 04224F8A8B277293862FC8BE2F7DDCD1 /* LWZCollectionDecoration.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 01D5FBC350E24AD4BF493C0AF742E47E /* MASLayoutConstraint.h in Headers */ = {isa = PBXBuildFile; fileRef = 95C377583286BDAA89983F17FDE855A1 /* MASLayoutConstraint.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 04284103476CBD782B3F0131944C7828 /* SJUTRangeHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 877D35E683F5B16821D47AAD7BD01A50 /* SJUTRangeHandler.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 04FB00A0608BB8472342FB38DAF2B5DE /* LWZCollectionDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C45598171F83D1C7051387FE2224FCD /* LWZCollectionDefines.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 064E87F1C465D5F44DD89D96B011C4AE /* NSObject+YYModel.m in Sources */ = {isa = PBXBuildFile; fileRef = CC7E6A965C764F1DAC4CFC660D7F56E5 /* NSObject+YYModel.m */; }; + 092DA04AF177E74D9C68CF25D73F725B /* SJUTRegexHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 391DBE8F46B7E8F62504992921799BBC /* SJUTRegexHandler.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 1AE43C529000CD4C288A06B13EF8CA0C /* LWZCollectionViewLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = D1E894E0D4B6F447C01DA0AB768E5A69 /* LWZCollectionViewLayout.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 1E62EE14D4E286FC91276863872B5963 /* SJUTRegexHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = A47BD0E60A44800D869D847839FEAAE8 /* SJUTRegexHandler.m */; }; + 216EDBA2722CB21C07C8798E79FBB6ED /* LWZCollectionViewRegister.m in Sources */ = {isa = PBXBuildFile; fileRef = 408C5AA4A8A89A29D9D5974BDAD09407 /* LWZCollectionViewRegister.m */; }; + 26B07D6380F2ADBBE886C33B6E13827A /* SJUIKitTextMaker.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CB4CAB3E861358EC1FFDDA3C5B04E83 /* SJUIKitTextMaker.m */; }; + 26E8B1A2A8178B34E03D58A93F9F0F54 /* SJUIKit-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D8354AEE2E1E862FCFB7F8105D04C0F /* SJUIKit-dummy.m */; }; + 2773A6BCB8CA4308AD23AB54F00C5085 /* LWZCollectionDefines.m in Sources */ = {isa = PBXBuildFile; fileRef = F467CB9BDEEB5168823F7F2EBCE50109 /* LWZCollectionDefines.m */; }; + 2A5FD5F8E43D704474602A2AC2CDC340 /* NSObject+YYModel.h in Headers */ = {isa = PBXBuildFile; fileRef = 7F45DAE7A5CE78624F34982144EE660B /* NSObject+YYModel.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 2D396745A85D7D9181FB4CE2F5D8E6FC /* LWZCollectionSectionHeaderFooter.m in Sources */ = {isa = PBXBuildFile; fileRef = 2491CAF63AAA2DEC67456689FB60E5E4 /* LWZCollectionSectionHeaderFooter.m */; }; + 2E00EFB627E61E85DA97CD1BE920BB2E /* SJUTUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 065C7AEE9AA469AFA2509F78ADFE464C /* SJUTUtils.m */; }; + 379902484CC10B58ACBD21D949702DC1 /* View+MASAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 261D19AD743D74BF081AE2ED8C4BACA9 /* View+MASAdditions.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 387523916AB680AC722992079BB01F67 /* MASLayoutConstraint.m in Sources */ = {isa = PBXBuildFile; fileRef = 0A0F23C6C82799A5BC8ED7D20DC5EC6F /* MASLayoutConstraint.m */; }; + 3AF45D1B033D1A4B64453248E928BC36 /* ViewController+MASAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 6862777F355280CFA2CBF258AFDEE5E8 /* ViewController+MASAdditions.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 3B749B0DA006A8EC75B9F9D0765EBE6F /* NSLayoutConstraint+MASDebugAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 78ED14447625C02E046A09AE4154C688 /* NSLayoutConstraint+MASDebugAdditions.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 42E543C6C23AF4792D82D23CAC723C12 /* MASConstraint+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = EA6EA2532BDE2069BDFB7664C0A50D17 /* MASConstraint+Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 42FE6C58DFEB078169BB1176572C6C9E /* LWZCollectionDecoration.m in Sources */ = {isa = PBXBuildFile; fileRef = C021EC3142A5919661B0268472C0FAC9 /* LWZCollectionDecoration.m */; }; + 457516E4EEE536D0D40BD90D79E5ADDD /* MASConstraintMaker.m in Sources */ = {isa = PBXBuildFile; fileRef = 1BEC500A7F9CDC69815C95C3B57F971B /* MASConstraintMaker.m */; }; + 4CF80DC5638027542DDBAEDA4A3A0D65 /* YYClassInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 9DCA23ACB94AFE9E857CB674FFC60814 /* YYClassInfo.m */; }; + 4DED4222581677326F0EEA9264D3649B /* LWZCollectionLayoutFittingSize.h in Headers */ = {isa = PBXBuildFile; fileRef = E3121C7622C51F2C33B23E75EA9A3F05 /* LWZCollectionLayoutFittingSize.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 5D50C92481B31B9177A0C3E8737F5DC4 /* NSArray+MASAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = FDAA6C22C1F233102759CF1891BA12BB /* NSArray+MASAdditions.m */; }; + 686716815AAF1182003F297F442E386B /* SJUTAttributes.h in Headers */ = {isa = PBXBuildFile; fileRef = 94782856D03A90A58C1D01E67F4A33F8 /* SJUTAttributes.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 6B4862F526C278B92973D7E0EFD8377E /* SJAttributesFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = EB28D61D472C7852D604DE6856B64E2B /* SJAttributesFactory.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 6C5ED9A74F061AD255AF9477A5B71493 /* LWZCollectionViewDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 3FE1EA065E3A2FB4AFEE7296E433A8A0 /* LWZCollectionViewDelegateProxy.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 6DFCEB06E2821A5002D543D61EB4ADD0 /* YYModel.h in Headers */ = {isa = PBXBuildFile; fileRef = 6B1407072159E415A6E2BF1677C46DC2 /* YYModel.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 704E6E494A06EA86348E495BBF3C0590 /* MASViewAttribute.h in Headers */ = {isa = PBXBuildFile; fileRef = D3189C6678644986922CF8AB1F6FD8DF /* MASViewAttribute.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 7A06B3C71E77EB32725A0ABD235E5E83 /* LWZCollectionViewLayoutSubclass.h in Headers */ = {isa = PBXBuildFile; fileRef = B90ADD5A9C3053C51B13F95E94AFFD4E /* LWZCollectionViewLayoutSubclass.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 7D19EBD7D8D124AC8CCD45899D84AEE8 /* LWZCollectionSectionHeaderFooter.h in Headers */ = {isa = PBXBuildFile; fileRef = B49A622759D86D8A95B5AE7BD9906DFF /* LWZCollectionSectionHeaderFooter.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 7EFDAA3614A18A2495551C4668934CEE /* LWZCollectionSection.h in Headers */ = {isa = PBXBuildFile; fileRef = BAA19586950ECA5BDBA70A12B97772BA /* LWZCollectionSection.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 8036AB5534CE9F01EB358ADDE7431DD6 /* LWZCollectionItem.h in Headers */ = {isa = PBXBuildFile; fileRef = 6E765498F4707A1237AC91B0A1C62DA4 /* LWZCollectionItem.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 80BAC64599AA4895FF65E3E6C9E8165F /* LWZComponents.h in Headers */ = {isa = PBXBuildFile; fileRef = E987A6B65FE2F1FFA094D207C585303A /* LWZComponents.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 827971675DDE1BD225031B3F3C89450B /* MASConstraint.h in Headers */ = {isa = PBXBuildFile; fileRef = F61283F85BF1A0156D41E390877DA863 /* MASConstraint.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 82A621096637C98A94F88BD69D42E8CB /* NSAttributedString+SJMake.m in Sources */ = {isa = PBXBuildFile; fileRef = 69F6C982410DA0ABBDF8843E636A1CB8 /* NSAttributedString+SJMake.m */; }; + 843A14508DBBE2100B31FDBA28A0A06C /* MASViewAttribute.m in Sources */ = {isa = PBXBuildFile; fileRef = 967F1FDC28205953D6A0FA2EA30D6391 /* MASViewAttribute.m */; }; + 87ACDF930ECA3C65D213D88EF3FC86D5 /* MASCompositeConstraint.m in Sources */ = {isa = PBXBuildFile; fileRef = 7E83E25B1C5272BB4CC50596AEC6704C /* MASCompositeConstraint.m */; }; + 898B0581F39F380905F18DE0373DCFF9 /* LWZCollectionViewPresenter.m in Sources */ = {isa = PBXBuildFile; fileRef = DE1AB05E02F8AFEB756BFEC258CA1CFD /* LWZCollectionViewPresenter.m */; }; + 89AB7D96102E7D63C630332403CB1010 /* YYClassInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = C57F6720665B21FBCF674217CB676C76 /* YYClassInfo.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 8B4599299A907D38EE028ED0DC87D4BF /* YYModel-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = E8C5D9C281FC267C7906BEF618BC5317 /* YYModel-dummy.m */; }; + 8C1C666EA02E741AF806D028D8760E7F /* Masonry-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 84EE90D026FC99189B60ACF3BF783C0A /* Masonry-dummy.m */; }; + 8E374C128F8B514733E01147341BDDD5 /* LWZCollectionView.m in Sources */ = {isa = PBXBuildFile; fileRef = DEE0A0C83C6D137D6076B2051D400F27 /* LWZCollectionView.m */; }; + 922577EF8E0E46B4EF322F2FDF6D192F /* SJAttributeWorker.m in Sources */ = {isa = PBXBuildFile; fileRef = F09A5D8748F86D7305E9CC6906ECCEDB /* SJAttributeWorker.m */; }; + 940A1DE08AC105D4CEFF6C6CCE3A7BE6 /* LWZCollectionViewComponents.h in Headers */ = {isa = PBXBuildFile; fileRef = C2F86F20F4F89C7557710C961D5BBAD9 /* LWZCollectionViewComponents.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 99626E8F5686EB830D21F97B1A313A67 /* View+MASAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 38946988C77BAFD5E41E02F8AD5C7A0D /* View+MASAdditions.m */; }; + 9A70327DAE5D90469D5575DD4E96B8D1 /* LWZCollectionViewLayoutAttributes.h in Headers */ = {isa = PBXBuildFile; fileRef = 1E7B13AEFD0DBE2589819878360BB18F /* LWZCollectionViewLayoutAttributes.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 9A870A75623AEA754B69F07911EE05A1 /* SJUIKitTextMaker.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8E4D322F4EB15FC5D842B5B7D194A3 /* SJUIKitTextMaker.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 9B2A4D7BB7AE22DE97571FE52731EB0B /* SJAttributesRecorder.m in Sources */ = {isa = PBXBuildFile; fileRef = C3E774322617500EC2CB03945A0EBF20 /* SJAttributesRecorder.m */; }; + 9B9D5BF5A9583E295E2DC177F33044E5 /* MASConstraintMaker.h in Headers */ = {isa = PBXBuildFile; fileRef = 6C8508D02EABAA0BEDFFCAE718DC3305 /* MASConstraintMaker.h */; settings = {ATTRIBUTES = (Project, ); }; }; + A3B0237CCC23412C66737AB7298A71F8 /* LWZCollectionViewLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 9EFBA129D37F7A6E91D848B56F3F06C7 /* LWZCollectionViewLayout.m */; }; + A5860E9D7CBD5BE1EFF4813B1E7D260C /* SJAttributeWorker.h in Headers */ = {isa = PBXBuildFile; fileRef = 41A41AD81E31D5B0349F1FA474C88DBA /* SJAttributeWorker.h */; settings = {ATTRIBUTES = (Project, ); }; }; + AA76CA2B1A56E25902CAA18D10AC37DA /* NSAttributedString+SJMake.h in Headers */ = {isa = PBXBuildFile; fileRef = A0A33B0BA3240E86BA02A4EDA945A8CA /* NSAttributedString+SJMake.h */; settings = {ATTRIBUTES = (Project, ); }; }; + AAFCA01A3D375DCF9E4FA7E2987333A6 /* LWZCollectionViewRegister.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A1CF4EBA07E317DBB27E819C6C568E1 /* LWZCollectionViewRegister.h */; settings = {ATTRIBUTES = (Project, ); }; }; + AB5B9760C06C41C45FE11CBC748A2025 /* SJUTRecorder.h in Headers */ = {isa = PBXBuildFile; fileRef = 01AC1B385D410552C004A1B119F9C87B /* SJUTRecorder.h */; settings = {ATTRIBUTES = (Project, ); }; }; + ACE84EB117EDD90B79AF71B105CCCBD9 /* NSArray+MASShorthandAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = A6258AB15AE10CA0FEC7A38B2732C7E4 /* NSArray+MASShorthandAdditions.h */; settings = {ATTRIBUTES = (Project, ); }; }; + AD130678CBCB10084BF6924738DAA8BE /* LWZCollectionProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = AD859C3208B71D338C437778645CB3B5 /* LWZCollectionProvider.m */; }; + B2ACFC43BCD0B27E9921FA096768DA17 /* LWZCollectionLayoutContentContainer.h in Headers */ = {isa = PBXBuildFile; fileRef = A832759709A6BCCF77B50501E58D3E69 /* LWZCollectionLayoutContentContainer.h */; settings = {ATTRIBUTES = (Project, ); }; }; + B51FE2A96E0B109993E31E0F5815C855 /* LWZCollectionSection.m in Sources */ = {isa = PBXBuildFile; fileRef = 396B58BB42E45DBCDD42790FF914C7A4 /* LWZCollectionSection.m */; }; + B722896AB3BE966816BD78F7F7F8892C /* Pods-LWZComponents_Tests-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 5EE6FF75F57C957768C9232A1831A0E8 /* Pods-LWZComponents_Tests-dummy.m */; }; + B84D2850577F954B327C9504C1921CF1 /* SJUTRangeHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = E883A813ACD172F52D888671BB5EB7A3 /* SJUTRangeHandler.m */; }; + BCC3DF893CF191CDC770E05ABDF72FA6 /* LWZCollectionInternals.h in Headers */ = {isa = PBXBuildFile; fileRef = 5F288C5F078B9BC0F5B4A7EBBEE9CB70 /* LWZCollectionInternals.h */; settings = {ATTRIBUTES = (Project, ); }; }; + BFABD2F11397B2C30573FB7E114C9FC8 /* LWZCollectionView.h in Headers */ = {isa = PBXBuildFile; fileRef = E7A4B1859A7AEEB017B3E54F6D96A0B4 /* LWZCollectionView.h */; settings = {ATTRIBUTES = (Project, ); }; }; + C2CFF98781D573C7ACEE95DD619B868A /* LWZCollectionProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = 310997A1DAE7CB013017C0C628455EB2 /* LWZCollectionProvider.h */; settings = {ATTRIBUTES = (Project, ); }; }; + C918939848F1B616E9D9A8AC462A9ACD /* LWZCollectionLayoutContentContainer.m in Sources */ = {isa = PBXBuildFile; fileRef = 6A53BB7E5F0CC0769C8FD5998AD36DA1 /* LWZCollectionLayoutContentContainer.m */; }; + CA726C95A49D7F7E1D53D0E54B38CD65 /* LWZCollectionItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 80579969D9C7915C76FEE4229154BBBA /* LWZCollectionItem.m */; }; + CB191345BCC56C623BB5B802261183A7 /* MASUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 053872807C78706307B94337FFCE4BB2 /* MASUtilities.h */; settings = {ATTRIBUTES = (Project, ); }; }; + CB68C5F4AD3107402F58C7A84E359712 /* MASConstraint.m in Sources */ = {isa = PBXBuildFile; fileRef = B59856367D261CB90654ED1C761F3BD4 /* MASConstraint.m */; }; + CD12B6AC301CC6D3E89D64742EADC39F /* Masonry.h in Headers */ = {isa = PBXBuildFile; fileRef = 35C860918C85ADD19559FC9C8FB70B4D /* Masonry.h */; settings = {ATTRIBUTES = (Project, ); }; }; + D282CD515ECB4D344D99048D72F2186D /* MASCompositeConstraint.h in Headers */ = {isa = PBXBuildFile; fileRef = 0808292A1C9288010338A7BBA6302124 /* MASCompositeConstraint.h */; settings = {ATTRIBUTES = (Project, ); }; }; + D43380309815EEA67C52B47B4DE0A025 /* NSArray+MASAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D79C131EC9C571EA91991627C7B78686 /* NSArray+MASAdditions.h */; settings = {ATTRIBUTES = (Project, ); }; }; + D94606FD6932B455C44EDAE275073C9F /* View+MASShorthandAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = E6857703780531C2E5F76C366943F5F9 /* View+MASShorthandAdditions.h */; settings = {ATTRIBUTES = (Project, ); }; }; + DB4C9007D1248C47029B3965AE09EC0D /* SJUTUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E12C157FFF5DB4549B6EE05A9863DDD /* SJUTUtils.h */; settings = {ATTRIBUTES = (Project, ); }; }; + DCB63376C4D7CBE0B3AE95617775B0AF /* LWZComponents-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 2936ABC78E54DF5A988A05621299190B /* LWZComponents-dummy.m */; }; + E1A942C89621BAC8CA3E401B3DC3AAB2 /* SJUIKitAttributesDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 98DCCD8D343BEA3A43B791D33A3DDC22 /* SJUIKitAttributesDefines.h */; settings = {ATTRIBUTES = (Project, ); }; }; + E532EBD440F01A9C31F42B0DD75ADE8A /* LWZCollectionLayoutFittingSize.m in Sources */ = {isa = PBXBuildFile; fileRef = 080E175C4A5E52FCFE420059475FA338 /* LWZCollectionLayoutFittingSize.m */; }; + EB5879C4B0304C68337AA2D8A68F3DB2 /* LWZCollectionViewLayoutAttributes.m in Sources */ = {isa = PBXBuildFile; fileRef = 1A772FF74D231711D6FCE59E28762180 /* LWZCollectionViewLayoutAttributes.m */; }; + EC32EFBDDB5B15DBEF034E7FFE3C79D1 /* SJUTRecorder.m in Sources */ = {isa = PBXBuildFile; fileRef = E57BDDF8E8CCE357A978D6CBC65CF3CE /* SJUTRecorder.m */; }; + F0AF27D78E8C89E71E9CE72694714F3C /* LWZCollectionViewPresenter.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B627B48BEAF637276A1AF3DA3539585 /* LWZCollectionViewPresenter.h */; settings = {ATTRIBUTES = (Project, ); }; }; + F0EF177407E0300D559261EBD49F0EDA /* Pods-LWZComponents_Example-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = B26B9D7A8C24DE4932F26D332356B867 /* Pods-LWZComponents_Example-dummy.m */; }; + F12EB7DF51A64F59A4D4DF3D69E4A5AF /* MASViewConstraint.h in Headers */ = {isa = PBXBuildFile; fileRef = 40FAFA7C566E4F7522912CABE399BFF3 /* MASViewConstraint.h */; settings = {ATTRIBUTES = (Project, ); }; }; + F1748421CEBAA56C15CD85F51519CAA8 /* SJAttributesRecorder.h in Headers */ = {isa = PBXBuildFile; fileRef = AA5BDAB6288137D4A417236E0A020FA5 /* SJAttributesRecorder.h */; settings = {ATTRIBUTES = (Project, ); }; }; + F430ECA30936E1C7CD126369B9EFC699 /* LWZCollectionViewDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 05813EFE95DAF4C5420BA5FA936C2B4D /* LWZCollectionViewDelegateProxy.m */; }; + F5BDBF8C04769355DBCC607EC0AFB013 /* SJUTAttributes.m in Sources */ = {isa = PBXBuildFile; fileRef = 3E474A7EE06BCE449959EDEB426FD03E /* SJUTAttributes.m */; }; + FD2B7DCBE9FCBBB7044804478E7ABD18 /* NSLayoutConstraint+MASDebugAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 28C62297F273DCE6CA24B17B629A6D37 /* NSLayoutConstraint+MASDebugAdditions.m */; }; + FD515EA00C1136C86B78FC74C9C1ACAF /* ViewController+MASAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 6782DB76632E3231CDFC81C5EE451D43 /* ViewController+MASAdditions.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 1A328B8F1E78F5DE980B5C9A4946C6F3 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 0D3052D198012FD40480C6882ED3C5D8; + remoteInfo = "Pods-LWZComponents_Example"; + }; + 1AFB542D708AE0C594E2D57D07EE5C60 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 66EF777DAC384165A40F94DC7D0BD98F; + remoteInfo = SJUIKit; + }; + 42DDE156202B6BA0738B642CFDF0DFA9 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 55AF53E6C77A10ED4985E04D74A8878E; + remoteInfo = Masonry; + }; + 460A4F48C4BA96844AB530468F0E9C45 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 84B44807A12996D487A4A591A481D6A0; + remoteInfo = YYModel; + }; + EC98DEB1A8C663B9270425E2FA7E032A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = B9C17818002F346E669AF8CD98BF6374; + remoteInfo = LWZComponents; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 01AC1B385D410552C004A1B119F9C87B /* SJUTRecorder.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SJUTRecorder.h; path = SJUIKit/AttributesFactory/UIKitText/SJUTRecorder.h; sourceTree = ""; }; + 04224F8A8B277293862FC8BE2F7DDCD1 /* LWZCollectionDecoration.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = LWZCollectionDecoration.h; sourceTree = ""; }; + 053872807C78706307B94337FFCE4BB2 /* MASUtilities.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MASUtilities.h; path = Masonry/MASUtilities.h; sourceTree = ""; }; + 05813EFE95DAF4C5420BA5FA936C2B4D /* LWZCollectionViewDelegateProxy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = LWZCollectionViewDelegateProxy.m; sourceTree = ""; }; + 065C7AEE9AA469AFA2509F78ADFE464C /* SJUTUtils.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = SJUTUtils.m; path = SJUIKit/AttributesFactory/UIKitText/SJUTUtils.m; sourceTree = ""; }; + 0808292A1C9288010338A7BBA6302124 /* MASCompositeConstraint.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MASCompositeConstraint.h; path = Masonry/MASCompositeConstraint.h; sourceTree = ""; }; + 080E175C4A5E52FCFE420059475FA338 /* LWZCollectionLayoutFittingSize.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = LWZCollectionLayoutFittingSize.m; sourceTree = ""; }; + 0A0F23C6C82799A5BC8ED7D20DC5EC6F /* MASLayoutConstraint.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MASLayoutConstraint.m; path = Masonry/MASLayoutConstraint.m; sourceTree = ""; }; + 0CB4CAB3E861358EC1FFDDA3C5B04E83 /* SJUIKitTextMaker.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = SJUIKitTextMaker.m; path = SJUIKit/AttributesFactory/UIKitText/SJUIKitTextMaker.m; sourceTree = ""; }; + 1A772FF74D231711D6FCE59E28762180 /* LWZCollectionViewLayoutAttributes.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = LWZCollectionViewLayoutAttributes.m; sourceTree = ""; }; + 1BEC500A7F9CDC69815C95C3B57F971B /* MASConstraintMaker.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MASConstraintMaker.m; path = Masonry/MASConstraintMaker.m; sourceTree = ""; }; + 1D4ABDBAA80FEE0462DF1D4C1CC9AEA9 /* LWZComponents.podspec */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; lastKnownFileType = text; path = LWZComponents.podspec; sourceTree = ""; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; + 1E7B13AEFD0DBE2589819878360BB18F /* LWZCollectionViewLayoutAttributes.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = LWZCollectionViewLayoutAttributes.h; sourceTree = ""; }; + 1F9FD3F6B22E906112475AA2320CA9B3 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; path = README.md; sourceTree = ""; }; + 1FFED36A657123030ABB700256D73F15 /* Masonry */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = Masonry; path = libMasonry.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 21B36C58B63F486BEB91CAFE492C84F8 /* SJUIKit */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = SJUIKit; path = libSJUIKit.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 2491CAF63AAA2DEC67456689FB60E5E4 /* LWZCollectionSectionHeaderFooter.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = LWZCollectionSectionHeaderFooter.m; sourceTree = ""; }; + 261D19AD743D74BF081AE2ED8C4BACA9 /* View+MASAdditions.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "View+MASAdditions.h"; path = "Masonry/View+MASAdditions.h"; sourceTree = ""; }; + 28C62297F273DCE6CA24B17B629A6D37 /* NSLayoutConstraint+MASDebugAdditions.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSLayoutConstraint+MASDebugAdditions.m"; path = "Masonry/NSLayoutConstraint+MASDebugAdditions.m"; sourceTree = ""; }; + 2930DF723A02657994DDEB9EF25B2A86 /* SJUIKit.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SJUIKit.debug.xcconfig; sourceTree = ""; }; + 2936ABC78E54DF5A988A05621299190B /* LWZComponents-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "LWZComponents-dummy.m"; sourceTree = ""; }; + 310997A1DAE7CB013017C0C628455EB2 /* LWZCollectionProvider.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = LWZCollectionProvider.h; sourceTree = ""; }; + 35C860918C85ADD19559FC9C8FB70B4D /* Masonry.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = Masonry.h; path = Masonry/Masonry.h; sourceTree = ""; }; + 38946988C77BAFD5E41E02F8AD5C7A0D /* View+MASAdditions.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "View+MASAdditions.m"; path = "Masonry/View+MASAdditions.m"; sourceTree = ""; }; + 391DBE8F46B7E8F62504992921799BBC /* SJUTRegexHandler.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SJUTRegexHandler.h; path = SJUIKit/AttributesFactory/UIKitText/SJUTRegexHandler.h; sourceTree = ""; }; + 396B58BB42E45DBCDD42790FF914C7A4 /* LWZCollectionSection.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = LWZCollectionSection.m; sourceTree = ""; }; + 3A1CF4EBA07E317DBB27E819C6C568E1 /* LWZCollectionViewRegister.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = LWZCollectionViewRegister.h; sourceTree = ""; }; + 3CFF0520E5D319986B9B361906807734 /* Pods-LWZComponents_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-LWZComponents_Example.debug.xcconfig"; sourceTree = ""; }; + 3E474A7EE06BCE449959EDEB426FD03E /* SJUTAttributes.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = SJUTAttributes.m; path = SJUIKit/AttributesFactory/UIKitText/SJUTAttributes.m; sourceTree = ""; }; + 3FE1EA065E3A2FB4AFEE7296E433A8A0 /* LWZCollectionViewDelegateProxy.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = LWZCollectionViewDelegateProxy.h; sourceTree = ""; }; + 408C5AA4A8A89A29D9D5974BDAD09407 /* LWZCollectionViewRegister.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = LWZCollectionViewRegister.m; sourceTree = ""; }; + 40FAFA7C566E4F7522912CABE399BFF3 /* MASViewConstraint.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MASViewConstraint.h; path = Masonry/MASViewConstraint.h; sourceTree = ""; }; + 41A41AD81E31D5B0349F1FA474C88DBA /* SJAttributeWorker.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SJAttributeWorker.h; path = SJUIKit/AttributesFactory/Deprecated/SJAttributeWorker.h; sourceTree = ""; }; + 449521E650DA465760580EF8CC21A62D /* YYModel.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = YYModel.debug.xcconfig; sourceTree = ""; }; + 4B627B48BEAF637276A1AF3DA3539585 /* LWZCollectionViewPresenter.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = LWZCollectionViewPresenter.h; sourceTree = ""; }; + 4E12C157FFF5DB4549B6EE05A9863DDD /* SJUTUtils.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SJUTUtils.h; path = SJUIKit/AttributesFactory/UIKitText/SJUTUtils.h; sourceTree = ""; }; + 4E3EBD27B57FEC7FB64B7C563EF44D60 /* Pods-LWZComponents_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-LWZComponents_Tests.debug.xcconfig"; sourceTree = ""; }; + 5294BC701AFFADFA0518056C36CEB0E4 /* LWZComponents.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = LWZComponents.debug.xcconfig; sourceTree = ""; }; + 5EE6FF75F57C957768C9232A1831A0E8 /* Pods-LWZComponents_Tests-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-LWZComponents_Tests-dummy.m"; sourceTree = ""; }; + 5F288C5F078B9BC0F5B4A7EBBEE9CB70 /* LWZCollectionInternals.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = LWZCollectionInternals.h; sourceTree = ""; }; + 610B6D0C80916889668B79B7149F9710 /* Pods-LWZComponents_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-LWZComponents_Tests.release.xcconfig"; sourceTree = ""; }; + 6782DB76632E3231CDFC81C5EE451D43 /* ViewController+MASAdditions.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "ViewController+MASAdditions.m"; path = "Masonry/ViewController+MASAdditions.m"; sourceTree = ""; }; + 679C48DD500F83BA6C01C766263BCA07 /* Pods-LWZComponents_Tests */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = "Pods-LWZComponents_Tests"; path = "libPods-LWZComponents_Tests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 6862777F355280CFA2CBF258AFDEE5E8 /* ViewController+MASAdditions.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "ViewController+MASAdditions.h"; path = "Masonry/ViewController+MASAdditions.h"; sourceTree = ""; }; + 69F6C982410DA0ABBDF8843E636A1CB8 /* NSAttributedString+SJMake.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSAttributedString+SJMake.m"; path = "SJUIKit/AttributesFactory/NSAttributedString+SJMake.m"; sourceTree = ""; }; + 6A53BB7E5F0CC0769C8FD5998AD36DA1 /* LWZCollectionLayoutContentContainer.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = LWZCollectionLayoutContentContainer.m; sourceTree = ""; }; + 6B1407072159E415A6E2BF1677C46DC2 /* YYModel.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = YYModel.h; path = YYModel/YYModel.h; sourceTree = ""; }; + 6C801BEBBEA03843F51B8B204EFB3FC1 /* LWZComponents-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "LWZComponents-prefix.pch"; sourceTree = ""; }; + 6C8508D02EABAA0BEDFFCAE718DC3305 /* MASConstraintMaker.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MASConstraintMaker.h; path = Masonry/MASConstraintMaker.h; sourceTree = ""; }; + 6E765498F4707A1237AC91B0A1C62DA4 /* LWZCollectionItem.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = LWZCollectionItem.h; sourceTree = ""; }; + 707B3C6A875793A57A49DFCC1070AC3D /* Pods-LWZComponents_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-LWZComponents_Example.release.xcconfig"; sourceTree = ""; }; + 78ED14447625C02E046A09AE4154C688 /* NSLayoutConstraint+MASDebugAdditions.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSLayoutConstraint+MASDebugAdditions.h"; path = "Masonry/NSLayoutConstraint+MASDebugAdditions.h"; sourceTree = ""; }; + 7A6EDAEDA7A67188E0933FAC7CAE264A /* LWZComponents.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = LWZComponents.release.xcconfig; sourceTree = ""; }; + 7BD1DFA8983B94D88942269676973148 /* Pods-LWZComponents_Example */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = "Pods-LWZComponents_Example"; path = "libPods-LWZComponents_Example.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 7E83E25B1C5272BB4CC50596AEC6704C /* MASCompositeConstraint.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MASCompositeConstraint.m; path = Masonry/MASCompositeConstraint.m; sourceTree = ""; }; + 7F45DAE7A5CE78624F34982144EE660B /* NSObject+YYModel.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSObject+YYModel.h"; path = "YYModel/NSObject+YYModel.h"; sourceTree = ""; }; + 8015277A23458DA0452D39A1EA5E9919 /* Masonry.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Masonry.release.xcconfig; sourceTree = ""; }; + 80579969D9C7915C76FEE4229154BBBA /* LWZCollectionItem.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = LWZCollectionItem.m; sourceTree = ""; }; + 82C58FF9418D9C89783C633134DC2453 /* YYModel-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "YYModel-prefix.pch"; sourceTree = ""; }; + 84EE90D026FC99189B60ACF3BF783C0A /* Masonry-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Masonry-dummy.m"; sourceTree = ""; }; + 877D35E683F5B16821D47AAD7BD01A50 /* SJUTRangeHandler.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SJUTRangeHandler.h; path = SJUIKit/AttributesFactory/UIKitText/SJUTRangeHandler.h; sourceTree = ""; }; + 8840388D041A9E6A776F4CBC4519AA3E /* Pods-LWZComponents_Example-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-LWZComponents_Example-acknowledgements.plist"; sourceTree = ""; }; + 8C188C2E75FA8F56F8378E7D53064FBB /* SJUIKit.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SJUIKit.release.xcconfig; sourceTree = ""; }; + 9190EE6D1D78F2945E634A03AAC2E078 /* Masonry.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Masonry.debug.xcconfig; sourceTree = ""; }; + 94782856D03A90A58C1D01E67F4A33F8 /* SJUTAttributes.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SJUTAttributes.h; path = SJUIKit/AttributesFactory/UIKitText/SJUTAttributes.h; sourceTree = ""; }; + 95C377583286BDAA89983F17FDE855A1 /* MASLayoutConstraint.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MASLayoutConstraint.h; path = Masonry/MASLayoutConstraint.h; sourceTree = ""; }; + 967F1FDC28205953D6A0FA2EA30D6391 /* MASViewAttribute.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MASViewAttribute.m; path = Masonry/MASViewAttribute.m; sourceTree = ""; }; + 98DCCD8D343BEA3A43B791D33A3DDC22 /* SJUIKitAttributesDefines.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SJUIKitAttributesDefines.h; path = SJUIKit/AttributesFactory/UIKitText/SJUIKitAttributesDefines.h; sourceTree = ""; }; + 9C45598171F83D1C7051387FE2224FCD /* LWZCollectionDefines.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = LWZCollectionDefines.h; sourceTree = ""; }; + 9D8354AEE2E1E862FCFB7F8105D04C0F /* SJUIKit-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "SJUIKit-dummy.m"; sourceTree = ""; }; + 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; + 9DCA23ACB94AFE9E857CB674FFC60814 /* YYClassInfo.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = YYClassInfo.m; path = YYModel/YYClassInfo.m; sourceTree = ""; }; + 9EFBA129D37F7A6E91D848B56F3F06C7 /* LWZCollectionViewLayout.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = LWZCollectionViewLayout.m; sourceTree = ""; }; + A0A33B0BA3240E86BA02A4EDA945A8CA /* NSAttributedString+SJMake.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSAttributedString+SJMake.h"; path = "SJUIKit/AttributesFactory/NSAttributedString+SJMake.h"; sourceTree = ""; }; + A47BD0E60A44800D869D847839FEAAE8 /* SJUTRegexHandler.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = SJUTRegexHandler.m; path = SJUIKit/AttributesFactory/UIKitText/SJUTRegexHandler.m; sourceTree = ""; }; + A6258AB15AE10CA0FEC7A38B2732C7E4 /* NSArray+MASShorthandAdditions.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSArray+MASShorthandAdditions.h"; path = "Masonry/NSArray+MASShorthandAdditions.h"; sourceTree = ""; }; + A832759709A6BCCF77B50501E58D3E69 /* LWZCollectionLayoutContentContainer.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = LWZCollectionLayoutContentContainer.h; sourceTree = ""; }; + AA5BDAB6288137D4A417236E0A020FA5 /* SJAttributesRecorder.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SJAttributesRecorder.h; path = SJUIKit/AttributesFactory/Deprecated/SJAttributesRecorder.h; sourceTree = ""; }; + ACAEF511C9ACEF5070AD4E42309528FF /* Pods-LWZComponents_Tests-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-LWZComponents_Tests-acknowledgements.markdown"; sourceTree = ""; }; + AD859C3208B71D338C437778645CB3B5 /* LWZCollectionProvider.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = LWZCollectionProvider.m; sourceTree = ""; }; + B26B9D7A8C24DE4932F26D332356B867 /* Pods-LWZComponents_Example-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-LWZComponents_Example-dummy.m"; sourceTree = ""; }; + B49A622759D86D8A95B5AE7BD9906DFF /* LWZCollectionSectionHeaderFooter.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = LWZCollectionSectionHeaderFooter.h; sourceTree = ""; }; + B53F641B5EA74B4C47042108328689A4 /* Pods-LWZComponents_Example-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-LWZComponents_Example-acknowledgements.markdown"; sourceTree = ""; }; + B59856367D261CB90654ED1C761F3BD4 /* MASConstraint.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MASConstraint.m; path = Masonry/MASConstraint.m; sourceTree = ""; }; + B90ADD5A9C3053C51B13F95E94AFFD4E /* LWZCollectionViewLayoutSubclass.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = LWZCollectionViewLayoutSubclass.h; sourceTree = ""; }; + BAA19586950ECA5BDBA70A12B97772BA /* LWZCollectionSection.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = LWZCollectionSection.h; sourceTree = ""; }; + BF28941A9CC714B7B12C72F8D6D5EB42 /* LWZComponents */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = LWZComponents; path = libLWZComponents.a; sourceTree = BUILT_PRODUCTS_DIR; }; + BFBAA38C64166B552897C7C5D9594FE6 /* YYModel.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = YYModel.release.xcconfig; sourceTree = ""; }; + C021EC3142A5919661B0268472C0FAC9 /* LWZCollectionDecoration.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = LWZCollectionDecoration.m; sourceTree = ""; }; + C2F86F20F4F89C7557710C961D5BBAD9 /* LWZCollectionViewComponents.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = LWZCollectionViewComponents.h; path = LWZComponents/LWZCollectionViewComponents/LWZCollectionViewComponents.h; sourceTree = ""; }; + C3E774322617500EC2CB03945A0EBF20 /* SJAttributesRecorder.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = SJAttributesRecorder.m; path = SJUIKit/AttributesFactory/Deprecated/SJAttributesRecorder.m; sourceTree = ""; }; + C57F6720665B21FBCF674217CB676C76 /* YYClassInfo.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = YYClassInfo.h; path = YYModel/YYClassInfo.h; sourceTree = ""; }; + C6CEA3FBF9A1F91A57EFF6B6D04C0168 /* Pods-LWZComponents_Tests-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-LWZComponents_Tests-acknowledgements.plist"; sourceTree = ""; }; + CB1B2D166AA6ECACE8CBD61737159886 /* Masonry-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Masonry-prefix.pch"; sourceTree = ""; }; + CC7E6A965C764F1DAC4CFC660D7F56E5 /* NSObject+YYModel.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSObject+YYModel.m"; path = "YYModel/NSObject+YYModel.m"; sourceTree = ""; }; + CE64B6C00807A34C54274710C494F424 /* MASViewConstraint.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MASViewConstraint.m; path = Masonry/MASViewConstraint.m; sourceTree = ""; }; + D1E894E0D4B6F447C01DA0AB768E5A69 /* LWZCollectionViewLayout.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = LWZCollectionViewLayout.h; sourceTree = ""; }; + D3189C6678644986922CF8AB1F6FD8DF /* MASViewAttribute.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MASViewAttribute.h; path = Masonry/MASViewAttribute.h; sourceTree = ""; }; + D79C131EC9C571EA91991627C7B78686 /* NSArray+MASAdditions.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSArray+MASAdditions.h"; path = "Masonry/NSArray+MASAdditions.h"; sourceTree = ""; }; + DA5F37152F6D51C1F6ED1E5B5D4F2D19 /* SJUIKit-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SJUIKit-prefix.pch"; sourceTree = ""; }; + DA8E4D322F4EB15FC5D842B5B7D194A3 /* SJUIKitTextMaker.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SJUIKitTextMaker.h; path = SJUIKit/AttributesFactory/UIKitText/SJUIKitTextMaker.h; sourceTree = ""; }; + DE1AB05E02F8AFEB756BFEC258CA1CFD /* LWZCollectionViewPresenter.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = LWZCollectionViewPresenter.m; sourceTree = ""; }; + DEE0A0C83C6D137D6076B2051D400F27 /* LWZCollectionView.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = LWZCollectionView.m; sourceTree = ""; }; + E3121C7622C51F2C33B23E75EA9A3F05 /* LWZCollectionLayoutFittingSize.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = LWZCollectionLayoutFittingSize.h; sourceTree = ""; }; + E460D5B0416D36F66EE8EC89E5D2FA0A /* YYModel */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = YYModel; path = libYYModel.a; sourceTree = BUILT_PRODUCTS_DIR; }; + E57BDDF8E8CCE357A978D6CBC65CF3CE /* SJUTRecorder.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = SJUTRecorder.m; path = SJUIKit/AttributesFactory/UIKitText/SJUTRecorder.m; sourceTree = ""; }; + E6857703780531C2E5F76C366943F5F9 /* View+MASShorthandAdditions.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "View+MASShorthandAdditions.h"; path = "Masonry/View+MASShorthandAdditions.h"; sourceTree = ""; }; + E71B7BA6F0F6B0FD381B4764869FFBC4 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; path = LICENSE; sourceTree = ""; }; + E7A4B1859A7AEEB017B3E54F6D96A0B4 /* LWZCollectionView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = LWZCollectionView.h; sourceTree = ""; }; + E883A813ACD172F52D888671BB5EB7A3 /* SJUTRangeHandler.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = SJUTRangeHandler.m; path = SJUIKit/AttributesFactory/UIKitText/SJUTRangeHandler.m; sourceTree = ""; }; + E8C5D9C281FC267C7906BEF618BC5317 /* YYModel-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "YYModel-dummy.m"; sourceTree = ""; }; + E987A6B65FE2F1FFA094D207C585303A /* LWZComponents.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = LWZComponents.h; path = LWZComponents/LWZComponents.h; sourceTree = ""; }; + EA6EA2532BDE2069BDFB7664C0A50D17 /* MASConstraint+Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "MASConstraint+Private.h"; path = "Masonry/MASConstraint+Private.h"; sourceTree = ""; }; + EB28D61D472C7852D604DE6856B64E2B /* SJAttributesFactory.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SJAttributesFactory.h; path = SJUIKit/AttributesFactory/SJAttributesFactory.h; sourceTree = ""; }; + F09A5D8748F86D7305E9CC6906ECCEDB /* SJAttributeWorker.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = SJAttributeWorker.m; path = SJUIKit/AttributesFactory/Deprecated/SJAttributeWorker.m; sourceTree = ""; }; + F467CB9BDEEB5168823F7F2EBCE50109 /* LWZCollectionDefines.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = LWZCollectionDefines.m; sourceTree = ""; }; + F61283F85BF1A0156D41E390877DA863 /* MASConstraint.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MASConstraint.h; path = Masonry/MASConstraint.h; sourceTree = ""; }; + FDAA6C22C1F233102759CF1891BA12BB /* NSArray+MASAdditions.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSArray+MASAdditions.m"; path = "Masonry/NSArray+MASAdditions.m"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 78174EF8EBA99B2D2F07AD2ADD024EAD /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7B3A0BF4F103BCC5CA1255F38E1DDFDB /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 907AD26A9F98C723CE396A46535B55F5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C9C5C973C6E05EAD4EC7303825F22920 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + CEE065CDC2605814DA0167E14593C530 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D4448887A5B5119A191EFF8E97CDD774 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 0543AA1E9C2ADFD8274585D0D2562384 /* LWZCollectionViewComponents */ = { + isa = PBXGroup; + children = ( + C2F86F20F4F89C7557710C961D5BBAD9 /* LWZCollectionViewComponents.h */, + AEFC13E8147A66D95494FC0A03F550E5 /* Defines */, + 36E02E1FF1835A9974BA882EFEB31543 /* Layout */, + B340593B8464FD0DADEF4BFE4A28B82D /* Presenter */, + 880BFE59067E6F93F6717EB1902EB656 /* Provider */, + 6A24B30861D0334F8AD7F658F073CD59 /* View */, + ); + name = LWZCollectionViewComponents; + sourceTree = ""; + }; + 2FB47CBB6EB88F67E2405E30B1BA92A3 /* Masonry */ = { + isa = PBXGroup; + children = ( + 0808292A1C9288010338A7BBA6302124 /* MASCompositeConstraint.h */, + 7E83E25B1C5272BB4CC50596AEC6704C /* MASCompositeConstraint.m */, + F61283F85BF1A0156D41E390877DA863 /* MASConstraint.h */, + B59856367D261CB90654ED1C761F3BD4 /* MASConstraint.m */, + EA6EA2532BDE2069BDFB7664C0A50D17 /* MASConstraint+Private.h */, + 6C8508D02EABAA0BEDFFCAE718DC3305 /* MASConstraintMaker.h */, + 1BEC500A7F9CDC69815C95C3B57F971B /* MASConstraintMaker.m */, + 95C377583286BDAA89983F17FDE855A1 /* MASLayoutConstraint.h */, + 0A0F23C6C82799A5BC8ED7D20DC5EC6F /* MASLayoutConstraint.m */, + 35C860918C85ADD19559FC9C8FB70B4D /* Masonry.h */, + 053872807C78706307B94337FFCE4BB2 /* MASUtilities.h */, + D3189C6678644986922CF8AB1F6FD8DF /* MASViewAttribute.h */, + 967F1FDC28205953D6A0FA2EA30D6391 /* MASViewAttribute.m */, + 40FAFA7C566E4F7522912CABE399BFF3 /* MASViewConstraint.h */, + CE64B6C00807A34C54274710C494F424 /* MASViewConstraint.m */, + D79C131EC9C571EA91991627C7B78686 /* NSArray+MASAdditions.h */, + FDAA6C22C1F233102759CF1891BA12BB /* NSArray+MASAdditions.m */, + A6258AB15AE10CA0FEC7A38B2732C7E4 /* NSArray+MASShorthandAdditions.h */, + 78ED14447625C02E046A09AE4154C688 /* NSLayoutConstraint+MASDebugAdditions.h */, + 28C62297F273DCE6CA24B17B629A6D37 /* NSLayoutConstraint+MASDebugAdditions.m */, + 261D19AD743D74BF081AE2ED8C4BACA9 /* View+MASAdditions.h */, + 38946988C77BAFD5E41E02F8AD5C7A0D /* View+MASAdditions.m */, + E6857703780531C2E5F76C366943F5F9 /* View+MASShorthandAdditions.h */, + 6862777F355280CFA2CBF258AFDEE5E8 /* ViewController+MASAdditions.h */, + 6782DB76632E3231CDFC81C5EE451D43 /* ViewController+MASAdditions.m */, + 92CDDDA8E6FB03BD6FF47EEAB1503944 /* Support Files */, + ); + name = Masonry; + path = Masonry; + sourceTree = ""; + }; + 36E02E1FF1835A9974BA882EFEB31543 /* Layout */ = { + isa = PBXGroup; + children = ( + A832759709A6BCCF77B50501E58D3E69 /* LWZCollectionLayoutContentContainer.h */, + 6A53BB7E5F0CC0769C8FD5998AD36DA1 /* LWZCollectionLayoutContentContainer.m */, + E3121C7622C51F2C33B23E75EA9A3F05 /* LWZCollectionLayoutFittingSize.h */, + 080E175C4A5E52FCFE420059475FA338 /* LWZCollectionLayoutFittingSize.m */, + D1E894E0D4B6F447C01DA0AB768E5A69 /* LWZCollectionViewLayout.h */, + 9EFBA129D37F7A6E91D848B56F3F06C7 /* LWZCollectionViewLayout.m */, + 1E7B13AEFD0DBE2589819878360BB18F /* LWZCollectionViewLayoutAttributes.h */, + 1A772FF74D231711D6FCE59E28762180 /* LWZCollectionViewLayoutAttributes.m */, + B90ADD5A9C3053C51B13F95E94AFFD4E /* LWZCollectionViewLayoutSubclass.h */, + ); + name = Layout; + path = LWZComponents/LWZCollectionViewComponents/Layout; + sourceTree = ""; + }; + 3A42443D781B75C0141B62176524418F /* LWZComponents */ = { + isa = PBXGroup; + children = ( + E987A6B65FE2F1FFA094D207C585303A /* LWZComponents.h */, + 0543AA1E9C2ADFD8274585D0D2562384 /* LWZCollectionViewComponents */, + EE236C911C9E72B5662281907791013A /* Pod */, + D32CAE11F55619D4BCC48A20F364420F /* Support Files */, + ); + name = LWZComponents; + path = ../..; + sourceTree = ""; + }; + 5ACBA4B291F44D9F0FC376DC6F628378 /* Support Files */ = { + isa = PBXGroup; + children = ( + E8C5D9C281FC267C7906BEF618BC5317 /* YYModel-dummy.m */, + 82C58FF9418D9C89783C633134DC2453 /* YYModel-prefix.pch */, + 449521E650DA465760580EF8CC21A62D /* YYModel.debug.xcconfig */, + BFBAA38C64166B552897C7C5D9594FE6 /* YYModel.release.xcconfig */, + ); + name = "Support Files"; + path = "../Target Support Files/YYModel"; + sourceTree = ""; + }; + 61956BA44F5C954C2B263BA4DD1CA09F /* YYModel */ = { + isa = PBXGroup; + children = ( + 7F45DAE7A5CE78624F34982144EE660B /* NSObject+YYModel.h */, + CC7E6A965C764F1DAC4CFC660D7F56E5 /* NSObject+YYModel.m */, + C57F6720665B21FBCF674217CB676C76 /* YYClassInfo.h */, + 9DCA23ACB94AFE9E857CB674FFC60814 /* YYClassInfo.m */, + 6B1407072159E415A6E2BF1677C46DC2 /* YYModel.h */, + 5ACBA4B291F44D9F0FC376DC6F628378 /* Support Files */, + ); + name = YYModel; + path = YYModel; + sourceTree = ""; + }; + 6A24B30861D0334F8AD7F658F073CD59 /* View */ = { + isa = PBXGroup; + children = ( + E7A4B1859A7AEEB017B3E54F6D96A0B4 /* LWZCollectionView.h */, + DEE0A0C83C6D137D6076B2051D400F27 /* LWZCollectionView.m */, + 3FE1EA065E3A2FB4AFEE7296E433A8A0 /* LWZCollectionViewDelegateProxy.h */, + 05813EFE95DAF4C5420BA5FA936C2B4D /* LWZCollectionViewDelegateProxy.m */, + ); + name = View; + path = LWZComponents/LWZCollectionViewComponents/View; + sourceTree = ""; + }; + 6FD5483B8ACB71E8B5AFE261E34AE4C1 /* Pods-LWZComponents_Example */ = { + isa = PBXGroup; + children = ( + B53F641B5EA74B4C47042108328689A4 /* Pods-LWZComponents_Example-acknowledgements.markdown */, + 8840388D041A9E6A776F4CBC4519AA3E /* Pods-LWZComponents_Example-acknowledgements.plist */, + B26B9D7A8C24DE4932F26D332356B867 /* Pods-LWZComponents_Example-dummy.m */, + 3CFF0520E5D319986B9B361906807734 /* Pods-LWZComponents_Example.debug.xcconfig */, + 707B3C6A875793A57A49DFCC1070AC3D /* Pods-LWZComponents_Example.release.xcconfig */, + ); + name = "Pods-LWZComponents_Example"; + path = "Target Support Files/Pods-LWZComponents_Example"; + sourceTree = ""; + }; + 74F96B31149FF502213D152DC748E420 /* Deprecated */ = { + isa = PBXGroup; + children = ( + AA5BDAB6288137D4A417236E0A020FA5 /* SJAttributesRecorder.h */, + C3E774322617500EC2CB03945A0EBF20 /* SJAttributesRecorder.m */, + 41A41AD81E31D5B0349F1FA474C88DBA /* SJAttributeWorker.h */, + F09A5D8748F86D7305E9CC6906ECCEDB /* SJAttributeWorker.m */, + ); + name = Deprecated; + sourceTree = ""; + }; + 750C433345BA6FA5EBEE7C59EDE5D409 /* Pods-LWZComponents_Tests */ = { + isa = PBXGroup; + children = ( + ACAEF511C9ACEF5070AD4E42309528FF /* Pods-LWZComponents_Tests-acknowledgements.markdown */, + C6CEA3FBF9A1F91A57EFF6B6D04C0168 /* Pods-LWZComponents_Tests-acknowledgements.plist */, + 5EE6FF75F57C957768C9232A1831A0E8 /* Pods-LWZComponents_Tests-dummy.m */, + 4E3EBD27B57FEC7FB64B7C563EF44D60 /* Pods-LWZComponents_Tests.debug.xcconfig */, + 610B6D0C80916889668B79B7149F9710 /* Pods-LWZComponents_Tests.release.xcconfig */, + ); + name = "Pods-LWZComponents_Tests"; + path = "Target Support Files/Pods-LWZComponents_Tests"; + sourceTree = ""; + }; + 880BFE59067E6F93F6717EB1902EB656 /* Provider */ = { + isa = PBXGroup; + children = ( + 04224F8A8B277293862FC8BE2F7DDCD1 /* LWZCollectionDecoration.h */, + C021EC3142A5919661B0268472C0FAC9 /* LWZCollectionDecoration.m */, + 5F288C5F078B9BC0F5B4A7EBBEE9CB70 /* LWZCollectionInternals.h */, + 6E765498F4707A1237AC91B0A1C62DA4 /* LWZCollectionItem.h */, + 80579969D9C7915C76FEE4229154BBBA /* LWZCollectionItem.m */, + 310997A1DAE7CB013017C0C628455EB2 /* LWZCollectionProvider.h */, + AD859C3208B71D338C437778645CB3B5 /* LWZCollectionProvider.m */, + BAA19586950ECA5BDBA70A12B97772BA /* LWZCollectionSection.h */, + 396B58BB42E45DBCDD42790FF914C7A4 /* LWZCollectionSection.m */, + B49A622759D86D8A95B5AE7BD9906DFF /* LWZCollectionSectionHeaderFooter.h */, + 2491CAF63AAA2DEC67456689FB60E5E4 /* LWZCollectionSectionHeaderFooter.m */, + ); + name = Provider; + path = LWZComponents/LWZCollectionViewComponents/Provider; + sourceTree = ""; + }; + 8F5DC288F74BCC52C26FA77504076BE1 /* SJUIKit */ = { + isa = PBXGroup; + children = ( + AA0966B96244FFC8AFCA2A280893F962 /* AttributesFactory */, + E0AB388FA423859A65582D41FE48BD24 /* Support Files */, + ); + name = SJUIKit; + path = SJUIKit; + sourceTree = ""; + }; + 92CDDDA8E6FB03BD6FF47EEAB1503944 /* Support Files */ = { + isa = PBXGroup; + children = ( + 84EE90D026FC99189B60ACF3BF783C0A /* Masonry-dummy.m */, + CB1B2D166AA6ECACE8CBD61737159886 /* Masonry-prefix.pch */, + 9190EE6D1D78F2945E634A03AAC2E078 /* Masonry.debug.xcconfig */, + 8015277A23458DA0452D39A1EA5E9919 /* Masonry.release.xcconfig */, + ); + name = "Support Files"; + path = "../Target Support Files/Masonry"; + sourceTree = ""; + }; + 976D9413FFC3D6AD6F59B21FCC8A7DDE /* Pods */ = { + isa = PBXGroup; + children = ( + 2FB47CBB6EB88F67E2405E30B1BA92A3 /* Masonry */, + 8F5DC288F74BCC52C26FA77504076BE1 /* SJUIKit */, + 61956BA44F5C954C2B263BA4DD1CA09F /* YYModel */, + ); + name = Pods; + sourceTree = ""; + }; + AA0966B96244FFC8AFCA2A280893F962 /* AttributesFactory */ = { + isa = PBXGroup; + children = ( + A0A33B0BA3240E86BA02A4EDA945A8CA /* NSAttributedString+SJMake.h */, + 69F6C982410DA0ABBDF8843E636A1CB8 /* NSAttributedString+SJMake.m */, + EB28D61D472C7852D604DE6856B64E2B /* SJAttributesFactory.h */, + 74F96B31149FF502213D152DC748E420 /* Deprecated */, + E5820F08314D0CEA1AC6FD0CB7674E70 /* UIKitText */, + ); + name = AttributesFactory; + sourceTree = ""; + }; + AEFC13E8147A66D95494FC0A03F550E5 /* Defines */ = { + isa = PBXGroup; + children = ( + 9C45598171F83D1C7051387FE2224FCD /* LWZCollectionDefines.h */, + F467CB9BDEEB5168823F7F2EBCE50109 /* LWZCollectionDefines.m */, + ); + name = Defines; + path = LWZComponents/LWZCollectionViewComponents/Defines; + sourceTree = ""; + }; + B340593B8464FD0DADEF4BFE4A28B82D /* Presenter */ = { + isa = PBXGroup; + children = ( + 4B627B48BEAF637276A1AF3DA3539585 /* LWZCollectionViewPresenter.h */, + DE1AB05E02F8AFEB756BFEC258CA1CFD /* LWZCollectionViewPresenter.m */, + 3A1CF4EBA07E317DBB27E819C6C568E1 /* LWZCollectionViewRegister.h */, + 408C5AA4A8A89A29D9D5974BDAD09407 /* LWZCollectionViewRegister.m */, + ); + name = Presenter; + path = LWZComponents/LWZCollectionViewComponents/Presenter; + sourceTree = ""; + }; + BAD0B3C677F4E0C47A6B260DD73FEB15 /* Products */ = { + isa = PBXGroup; + children = ( + BF28941A9CC714B7B12C72F8D6D5EB42 /* LWZComponents */, + 1FFED36A657123030ABB700256D73F15 /* Masonry */, + 7BD1DFA8983B94D88942269676973148 /* Pods-LWZComponents_Example */, + 679C48DD500F83BA6C01C766263BCA07 /* Pods-LWZComponents_Tests */, + 21B36C58B63F486BEB91CAFE492C84F8 /* SJUIKit */, + E460D5B0416D36F66EE8EC89E5D2FA0A /* YYModel */, + ); + name = Products; + sourceTree = ""; + }; + C40284BEE7742A15647A23F4BE15EA2A /* Development Pods */ = { + isa = PBXGroup; + children = ( + 3A42443D781B75C0141B62176524418F /* LWZComponents */, + ); + name = "Development Pods"; + sourceTree = ""; + }; + CF1408CF629C7361332E53B88F7BD30C = { + isa = PBXGroup; + children = ( + 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */, + C40284BEE7742A15647A23F4BE15EA2A /* Development Pods */, + D89477F20FB1DE18A04690586D7808C4 /* Frameworks */, + 976D9413FFC3D6AD6F59B21FCC8A7DDE /* Pods */, + BAD0B3C677F4E0C47A6B260DD73FEB15 /* Products */, + E51C58A36DADA8B90A0FFB3294BE3689 /* Targets Support Files */, + ); + sourceTree = ""; + }; + D32CAE11F55619D4BCC48A20F364420F /* Support Files */ = { + isa = PBXGroup; + children = ( + 2936ABC78E54DF5A988A05621299190B /* LWZComponents-dummy.m */, + 6C801BEBBEA03843F51B8B204EFB3FC1 /* LWZComponents-prefix.pch */, + 5294BC701AFFADFA0518056C36CEB0E4 /* LWZComponents.debug.xcconfig */, + 7A6EDAEDA7A67188E0933FAC7CAE264A /* LWZComponents.release.xcconfig */, + ); + name = "Support Files"; + path = "Example/Pods/Target Support Files/LWZComponents"; + sourceTree = ""; + }; + D89477F20FB1DE18A04690586D7808C4 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; + E0AB388FA423859A65582D41FE48BD24 /* Support Files */ = { + isa = PBXGroup; + children = ( + 9D8354AEE2E1E862FCFB7F8105D04C0F /* SJUIKit-dummy.m */, + DA5F37152F6D51C1F6ED1E5B5D4F2D19 /* SJUIKit-prefix.pch */, + 2930DF723A02657994DDEB9EF25B2A86 /* SJUIKit.debug.xcconfig */, + 8C188C2E75FA8F56F8378E7D53064FBB /* SJUIKit.release.xcconfig */, + ); + name = "Support Files"; + path = "../Target Support Files/SJUIKit"; + sourceTree = ""; + }; + E51C58A36DADA8B90A0FFB3294BE3689 /* Targets Support Files */ = { + isa = PBXGroup; + children = ( + 6FD5483B8ACB71E8B5AFE261E34AE4C1 /* Pods-LWZComponents_Example */, + 750C433345BA6FA5EBEE7C59EDE5D409 /* Pods-LWZComponents_Tests */, + ); + name = "Targets Support Files"; + sourceTree = ""; + }; + E5820F08314D0CEA1AC6FD0CB7674E70 /* UIKitText */ = { + isa = PBXGroup; + children = ( + 98DCCD8D343BEA3A43B791D33A3DDC22 /* SJUIKitAttributesDefines.h */, + DA8E4D322F4EB15FC5D842B5B7D194A3 /* SJUIKitTextMaker.h */, + 0CB4CAB3E861358EC1FFDDA3C5B04E83 /* SJUIKitTextMaker.m */, + 94782856D03A90A58C1D01E67F4A33F8 /* SJUTAttributes.h */, + 3E474A7EE06BCE449959EDEB426FD03E /* SJUTAttributes.m */, + 877D35E683F5B16821D47AAD7BD01A50 /* SJUTRangeHandler.h */, + E883A813ACD172F52D888671BB5EB7A3 /* SJUTRangeHandler.m */, + 01AC1B385D410552C004A1B119F9C87B /* SJUTRecorder.h */, + E57BDDF8E8CCE357A978D6CBC65CF3CE /* SJUTRecorder.m */, + 391DBE8F46B7E8F62504992921799BBC /* SJUTRegexHandler.h */, + A47BD0E60A44800D869D847839FEAAE8 /* SJUTRegexHandler.m */, + 4E12C157FFF5DB4549B6EE05A9863DDD /* SJUTUtils.h */, + 065C7AEE9AA469AFA2509F78ADFE464C /* SJUTUtils.m */, + ); + name = UIKitText; + sourceTree = ""; + }; + EE236C911C9E72B5662281907791013A /* Pod */ = { + isa = PBXGroup; + children = ( + E71B7BA6F0F6B0FD381B4764869FFBC4 /* LICENSE */, + 1D4ABDBAA80FEE0462DF1D4C1CC9AEA9 /* LWZComponents.podspec */, + 1F9FD3F6B22E906112475AA2320CA9B3 /* README.md */, + ); + name = Pod; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 10F411BE61D70121A6EF646569FFF8D1 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2A3EBFC0646B1C38B42A258D68224E28 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + D282CD515ECB4D344D99048D72F2186D /* MASCompositeConstraint.h in Headers */, + 827971675DDE1BD225031B3F3C89450B /* MASConstraint.h in Headers */, + 42E543C6C23AF4792D82D23CAC723C12 /* MASConstraint+Private.h in Headers */, + 9B9D5BF5A9583E295E2DC177F33044E5 /* MASConstraintMaker.h in Headers */, + 01D5FBC350E24AD4BF493C0AF742E47E /* MASLayoutConstraint.h in Headers */, + CD12B6AC301CC6D3E89D64742EADC39F /* Masonry.h in Headers */, + CB191345BCC56C623BB5B802261183A7 /* MASUtilities.h in Headers */, + 704E6E494A06EA86348E495BBF3C0590 /* MASViewAttribute.h in Headers */, + F12EB7DF51A64F59A4D4DF3D69E4A5AF /* MASViewConstraint.h in Headers */, + D43380309815EEA67C52B47B4DE0A025 /* NSArray+MASAdditions.h in Headers */, + ACE84EB117EDD90B79AF71B105CCCBD9 /* NSArray+MASShorthandAdditions.h in Headers */, + 3B749B0DA006A8EC75B9F9D0765EBE6F /* NSLayoutConstraint+MASDebugAdditions.h in Headers */, + 379902484CC10B58ACBD21D949702DC1 /* View+MASAdditions.h in Headers */, + D94606FD6932B455C44EDAE275073C9F /* View+MASShorthandAdditions.h in Headers */, + 3AF45D1B033D1A4B64453248E928BC36 /* ViewController+MASAdditions.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 38C81EDC6C1DAD6BB38585C9F5C31AF9 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 2A5FD5F8E43D704474602A2AC2CDC340 /* NSObject+YYModel.h in Headers */, + 89AB7D96102E7D63C630332403CB1010 /* YYClassInfo.h in Headers */, + 6DFCEB06E2821A5002D543D61EB4ADD0 /* YYModel.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 627D47D3BD15E635BB215C39D651B2EE /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A8F4BEB141EB7D086ECDD58CE062C70B /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + AA76CA2B1A56E25902CAA18D10AC37DA /* NSAttributedString+SJMake.h in Headers */, + 6B4862F526C278B92973D7E0EFD8377E /* SJAttributesFactory.h in Headers */, + F1748421CEBAA56C15CD85F51519CAA8 /* SJAttributesRecorder.h in Headers */, + A5860E9D7CBD5BE1EFF4813B1E7D260C /* SJAttributeWorker.h in Headers */, + E1A942C89621BAC8CA3E401B3DC3AAB2 /* SJUIKitAttributesDefines.h in Headers */, + 9A870A75623AEA754B69F07911EE05A1 /* SJUIKitTextMaker.h in Headers */, + 686716815AAF1182003F297F442E386B /* SJUTAttributes.h in Headers */, + 04284103476CBD782B3F0131944C7828 /* SJUTRangeHandler.h in Headers */, + AB5B9760C06C41C45FE11CBC748A2025 /* SJUTRecorder.h in Headers */, + 092DA04AF177E74D9C68CF25D73F725B /* SJUTRegexHandler.h in Headers */, + DB4C9007D1248C47029B3965AE09EC0D /* SJUTUtils.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + EBFA73CBFA98B5174FA970F8939EC528 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 01337680A3F6A694CB318554492D3958 /* LWZCollectionDecoration.h in Headers */, + 04FB00A0608BB8472342FB38DAF2B5DE /* LWZCollectionDefines.h in Headers */, + BCC3DF893CF191CDC770E05ABDF72FA6 /* LWZCollectionInternals.h in Headers */, + 8036AB5534CE9F01EB358ADDE7431DD6 /* LWZCollectionItem.h in Headers */, + B2ACFC43BCD0B27E9921FA096768DA17 /* LWZCollectionLayoutContentContainer.h in Headers */, + 4DED4222581677326F0EEA9264D3649B /* LWZCollectionLayoutFittingSize.h in Headers */, + C2CFF98781D573C7ACEE95DD619B868A /* LWZCollectionProvider.h in Headers */, + 7EFDAA3614A18A2495551C4668934CEE /* LWZCollectionSection.h in Headers */, + 7D19EBD7D8D124AC8CCD45899D84AEE8 /* LWZCollectionSectionHeaderFooter.h in Headers */, + BFABD2F11397B2C30573FB7E114C9FC8 /* LWZCollectionView.h in Headers */, + 940A1DE08AC105D4CEFF6C6CCE3A7BE6 /* LWZCollectionViewComponents.h in Headers */, + 6C5ED9A74F061AD255AF9477A5B71493 /* LWZCollectionViewDelegateProxy.h in Headers */, + 1AE43C529000CD4C288A06B13EF8CA0C /* LWZCollectionViewLayout.h in Headers */, + 9A70327DAE5D90469D5575DD4E96B8D1 /* LWZCollectionViewLayoutAttributes.h in Headers */, + 7A06B3C71E77EB32725A0ABD235E5E83 /* LWZCollectionViewLayoutSubclass.h in Headers */, + F0AF27D78E8C89E71E9CE72694714F3C /* LWZCollectionViewPresenter.h in Headers */, + AAFCA01A3D375DCF9E4FA7E2987333A6 /* LWZCollectionViewRegister.h in Headers */, + 80BAC64599AA4895FF65E3E6C9E8165F /* LWZComponents.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 0D3052D198012FD40480C6882ED3C5D8 /* Pods-LWZComponents_Example */ = { + isa = PBXNativeTarget; + buildConfigurationList = 9D739216B731DEA8BE67BA2DA6EA14E5 /* Build configuration list for PBXNativeTarget "Pods-LWZComponents_Example" */; + buildPhases = ( + 10F411BE61D70121A6EF646569FFF8D1 /* Headers */, + 91B3E2966EE7FF2614882E7462308C48 /* Sources */, + 7B3A0BF4F103BCC5CA1255F38E1DDFDB /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 8BA3119E94DDBB814D6BB2C673EAE4D5 /* PBXTargetDependency */, + 69C88E21978B280F6BD631B8B245DC82 /* PBXTargetDependency */, + B59D605E1E25D93EE2E07E2135448FD5 /* PBXTargetDependency */, + 19AC35A365AD2527E0B3224E255000DA /* PBXTargetDependency */, + ); + name = "Pods-LWZComponents_Example"; + productName = "Pods-LWZComponents_Example"; + productReference = 7BD1DFA8983B94D88942269676973148 /* Pods-LWZComponents_Example */; + productType = "com.apple.product-type.library.static"; + }; + 55AF53E6C77A10ED4985E04D74A8878E /* Masonry */ = { + isa = PBXNativeTarget; + buildConfigurationList = 6525CCFB5FEEBC36FDDE16A88CD85B83 /* Build configuration list for PBXNativeTarget "Masonry" */; + buildPhases = ( + 2A3EBFC0646B1C38B42A258D68224E28 /* Headers */, + 375FDB568BC4E6D5E37AEEA9CD7D77A6 /* Sources */, + 907AD26A9F98C723CE396A46535B55F5 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Masonry; + productName = Masonry; + productReference = 1FFED36A657123030ABB700256D73F15 /* Masonry */; + productType = "com.apple.product-type.library.static"; + }; + 66EF777DAC384165A40F94DC7D0BD98F /* SJUIKit */ = { + isa = PBXNativeTarget; + buildConfigurationList = 29DCBDD65990C5E928BEB5D504D0350F /* Build configuration list for PBXNativeTarget "SJUIKit" */; + buildPhases = ( + A8F4BEB141EB7D086ECDD58CE062C70B /* Headers */, + 7722CDF1BCB844DFB588A5D839B678DB /* Sources */, + CEE065CDC2605814DA0167E14593C530 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = SJUIKit; + productName = SJUIKit; + productReference = 21B36C58B63F486BEB91CAFE492C84F8 /* SJUIKit */; + productType = "com.apple.product-type.library.static"; + }; + 84B44807A12996D487A4A591A481D6A0 /* YYModel */ = { + isa = PBXNativeTarget; + buildConfigurationList = A9818D8B3BDFE7C917B7EE4D9074797C /* Build configuration list for PBXNativeTarget "YYModel" */; + buildPhases = ( + 38C81EDC6C1DAD6BB38585C9F5C31AF9 /* Headers */, + CB8D921F3FB6DBCF285C29F50E43FA3B /* Sources */, + 78174EF8EBA99B2D2F07AD2ADD024EAD /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = YYModel; + productName = YYModel; + productReference = E460D5B0416D36F66EE8EC89E5D2FA0A /* YYModel */; + productType = "com.apple.product-type.library.static"; + }; + 8B48FCC08D7ACA188D8335784C476B02 /* Pods-LWZComponents_Tests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2834199DFBDE098A524D12A7125B85CA /* Build configuration list for PBXNativeTarget "Pods-LWZComponents_Tests" */; + buildPhases = ( + 627D47D3BD15E635BB215C39D651B2EE /* Headers */, + DCB784F1FF17DC29F0A491ACDEA77FF3 /* Sources */, + D4448887A5B5119A191EFF8E97CDD774 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 868FB4BFB777C37025ACA18BF726603F /* PBXTargetDependency */, + ); + name = "Pods-LWZComponents_Tests"; + productName = "Pods-LWZComponents_Tests"; + productReference = 679C48DD500F83BA6C01C766263BCA07 /* Pods-LWZComponents_Tests */; + productType = "com.apple.product-type.library.static"; + }; + B9C17818002F346E669AF8CD98BF6374 /* LWZComponents */ = { + isa = PBXNativeTarget; + buildConfigurationList = 598C096842C239E7B4B613B1BAD48FE3 /* Build configuration list for PBXNativeTarget "LWZComponents" */; + buildPhases = ( + EBFA73CBFA98B5174FA970F8939EC528 /* Headers */, + B2E31ED56279DA68B9EB469EA2B01B4D /* Sources */, + C9C5C973C6E05EAD4EC7303825F22920 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = LWZComponents; + productName = LWZComponents; + productReference = BF28941A9CC714B7B12C72F8D6D5EB42 /* LWZComponents */; + productType = "com.apple.product-type.library.static"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + BFDFE7DC352907FC980B868725387E98 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1240; + LastUpgradeCheck = 1240; + }; + buildConfigurationList = 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + Base, + en, + ); + mainGroup = CF1408CF629C7361332E53B88F7BD30C; + productRefGroup = BAD0B3C677F4E0C47A6B260DD73FEB15 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + B9C17818002F346E669AF8CD98BF6374 /* LWZComponents */, + 55AF53E6C77A10ED4985E04D74A8878E /* Masonry */, + 0D3052D198012FD40480C6882ED3C5D8 /* Pods-LWZComponents_Example */, + 8B48FCC08D7ACA188D8335784C476B02 /* Pods-LWZComponents_Tests */, + 66EF777DAC384165A40F94DC7D0BD98F /* SJUIKit */, + 84B44807A12996D487A4A591A481D6A0 /* YYModel */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 375FDB568BC4E6D5E37AEEA9CD7D77A6 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 87ACDF930ECA3C65D213D88EF3FC86D5 /* MASCompositeConstraint.m in Sources */, + CB68C5F4AD3107402F58C7A84E359712 /* MASConstraint.m in Sources */, + 457516E4EEE536D0D40BD90D79E5ADDD /* MASConstraintMaker.m in Sources */, + 387523916AB680AC722992079BB01F67 /* MASLayoutConstraint.m in Sources */, + 8C1C666EA02E741AF806D028D8760E7F /* Masonry-dummy.m in Sources */, + 843A14508DBBE2100B31FDBA28A0A06C /* MASViewAttribute.m in Sources */, + 011DEAEC47C5802504A51B75A1AC15D9 /* MASViewConstraint.m in Sources */, + 5D50C92481B31B9177A0C3E8737F5DC4 /* NSArray+MASAdditions.m in Sources */, + FD2B7DCBE9FCBBB7044804478E7ABD18 /* NSLayoutConstraint+MASDebugAdditions.m in Sources */, + 99626E8F5686EB830D21F97B1A313A67 /* View+MASAdditions.m in Sources */, + FD515EA00C1136C86B78FC74C9C1ACAF /* ViewController+MASAdditions.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7722CDF1BCB844DFB588A5D839B678DB /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 82A621096637C98A94F88BD69D42E8CB /* NSAttributedString+SJMake.m in Sources */, + 9B2A4D7BB7AE22DE97571FE52731EB0B /* SJAttributesRecorder.m in Sources */, + 922577EF8E0E46B4EF322F2FDF6D192F /* SJAttributeWorker.m in Sources */, + 26E8B1A2A8178B34E03D58A93F9F0F54 /* SJUIKit-dummy.m in Sources */, + 26B07D6380F2ADBBE886C33B6E13827A /* SJUIKitTextMaker.m in Sources */, + F5BDBF8C04769355DBCC607EC0AFB013 /* SJUTAttributes.m in Sources */, + B84D2850577F954B327C9504C1921CF1 /* SJUTRangeHandler.m in Sources */, + EC32EFBDDB5B15DBEF034E7FFE3C79D1 /* SJUTRecorder.m in Sources */, + 1E62EE14D4E286FC91276863872B5963 /* SJUTRegexHandler.m in Sources */, + 2E00EFB627E61E85DA97CD1BE920BB2E /* SJUTUtils.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 91B3E2966EE7FF2614882E7462308C48 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F0EF177407E0300D559261EBD49F0EDA /* Pods-LWZComponents_Example-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B2E31ED56279DA68B9EB469EA2B01B4D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 42FE6C58DFEB078169BB1176572C6C9E /* LWZCollectionDecoration.m in Sources */, + 2773A6BCB8CA4308AD23AB54F00C5085 /* LWZCollectionDefines.m in Sources */, + CA726C95A49D7F7E1D53D0E54B38CD65 /* LWZCollectionItem.m in Sources */, + C918939848F1B616E9D9A8AC462A9ACD /* LWZCollectionLayoutContentContainer.m in Sources */, + E532EBD440F01A9C31F42B0DD75ADE8A /* LWZCollectionLayoutFittingSize.m in Sources */, + AD130678CBCB10084BF6924738DAA8BE /* LWZCollectionProvider.m in Sources */, + B51FE2A96E0B109993E31E0F5815C855 /* LWZCollectionSection.m in Sources */, + 2D396745A85D7D9181FB4CE2F5D8E6FC /* LWZCollectionSectionHeaderFooter.m in Sources */, + 8E374C128F8B514733E01147341BDDD5 /* LWZCollectionView.m in Sources */, + F430ECA30936E1C7CD126369B9EFC699 /* LWZCollectionViewDelegateProxy.m in Sources */, + A3B0237CCC23412C66737AB7298A71F8 /* LWZCollectionViewLayout.m in Sources */, + EB5879C4B0304C68337AA2D8A68F3DB2 /* LWZCollectionViewLayoutAttributes.m in Sources */, + 898B0581F39F380905F18DE0373DCFF9 /* LWZCollectionViewPresenter.m in Sources */, + 216EDBA2722CB21C07C8798E79FBB6ED /* LWZCollectionViewRegister.m in Sources */, + DCB63376C4D7CBE0B3AE95617775B0AF /* LWZComponents-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + CB8D921F3FB6DBCF285C29F50E43FA3B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 064E87F1C465D5F44DD89D96B011C4AE /* NSObject+YYModel.m in Sources */, + 4CF80DC5638027542DDBAEDA4A3A0D65 /* YYClassInfo.m in Sources */, + 8B4599299A907D38EE028ED0DC87D4BF /* YYModel-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCB784F1FF17DC29F0A491ACDEA77FF3 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B722896AB3BE966816BD78F7F7F8892C /* Pods-LWZComponents_Tests-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 19AC35A365AD2527E0B3224E255000DA /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = YYModel; + target = 84B44807A12996D487A4A591A481D6A0 /* YYModel */; + targetProxy = 460A4F48C4BA96844AB530468F0E9C45 /* PBXContainerItemProxy */; + }; + 69C88E21978B280F6BD631B8B245DC82 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = Masonry; + target = 55AF53E6C77A10ED4985E04D74A8878E /* Masonry */; + targetProxy = 42DDE156202B6BA0738B642CFDF0DFA9 /* PBXContainerItemProxy */; + }; + 868FB4BFB777C37025ACA18BF726603F /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "Pods-LWZComponents_Example"; + target = 0D3052D198012FD40480C6882ED3C5D8 /* Pods-LWZComponents_Example */; + targetProxy = 1A328B8F1E78F5DE980B5C9A4946C6F3 /* PBXContainerItemProxy */; + }; + 8BA3119E94DDBB814D6BB2C673EAE4D5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = LWZComponents; + target = B9C17818002F346E669AF8CD98BF6374 /* LWZComponents */; + targetProxy = EC98DEB1A8C663B9270425E2FA7E032A /* PBXContainerItemProxy */; + }; + B59D605E1E25D93EE2E07E2135448FD5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = SJUIKit; + target = 66EF777DAC384165A40F94DC7D0BD98F /* SJUIKit */; + targetProxy = 1AFB542D708AE0C594E2D57D07EE5C60 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 105B8A86C900DA3D5715E73A04825046 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 2930DF723A02657994DDEB9EF25B2A86 /* SJUIKit.debug.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + GCC_PREFIX_HEADER = "Target Support Files/SJUIKit/SJUIKit-prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "LWZ_DEBUG=1", + ); + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRIVATE_HEADERS_FOLDER_PATH = ""; + PRODUCT_MODULE_NAME = SJUIKit; + PRODUCT_NAME = SJUIKit; + PUBLIC_HEADERS_FOLDER_PATH = ""; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 25AD9454612BF454A1E3DC4CD4FA8C6D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "POD_CONFIGURATION_DEBUG=1", + "DEBUG=1", + "$(inherited)", + ); + 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 = 9.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRIP_INSTALLED_PRODUCT = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + SYMROOT = "${SRCROOT}/../build"; + }; + name = Debug; + }; + 5029F8429E2AB7274501D58B9A94684D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 4E3EBD27B57FEC7FB64B7C563EF44D60 /* Pods-LWZComponents_Tests.debug.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "LWZ_DEBUG=1", + ); + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MACH_O_TYPE = staticlib; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 67AC0BB48473FAEA9587E5AC6D834F9D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7A6EDAEDA7A67188E0933FAC7CAE264A /* LWZComponents.release.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + GCC_PREFIX_HEADER = "Target Support Files/LWZComponents/LWZComponents-prefix.pch"; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRIVATE_HEADERS_FOLDER_PATH = ""; + PRODUCT_MODULE_NAME = LWZComponents; + PRODUCT_NAME = LWZComponents; + PUBLIC_HEADERS_FOLDER_PATH = ""; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 760D07AFA0346E82A443BC6601EE4A3B /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 8C188C2E75FA8F56F8378E7D53064FBB /* SJUIKit.release.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + GCC_PREFIX_HEADER = "Target Support Files/SJUIKit/SJUIKit-prefix.pch"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRIVATE_HEADERS_FOLDER_PATH = ""; + PRODUCT_MODULE_NAME = SJUIKit; + PRODUCT_NAME = SJUIKit; + PUBLIC_HEADERS_FOLDER_PATH = ""; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 7628E77745F1D49AFC8EBD35E28DC1EE /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 8015277A23458DA0452D39A1EA5E9919 /* Masonry.release.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + GCC_PREFIX_HEADER = "Target Support Files/Masonry/Masonry-prefix.pch"; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRIVATE_HEADERS_FOLDER_PATH = ""; + PRODUCT_MODULE_NAME = Masonry; + PRODUCT_NAME = Masonry; + PUBLIC_HEADERS_FOLDER_PATH = ""; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 8B443F7DF538ACBA5158E5DD0C2EF3CB /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3CFF0520E5D319986B9B361906807734 /* Pods-LWZComponents_Example.debug.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "LWZ_DEBUG=1", + ); + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MACH_O_TYPE = staticlib; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + A9D048A63E21FD07ABAC9E193E143103 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 5294BC701AFFADFA0518056C36CEB0E4 /* LWZComponents.debug.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + GCC_PREFIX_HEADER = "Target Support Files/LWZComponents/LWZComponents-prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "LWZ_DEBUG=1", + ); + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRIVATE_HEADERS_FOLDER_PATH = ""; + PRODUCT_MODULE_NAME = LWZComponents; + PRODUCT_NAME = LWZComponents; + PUBLIC_HEADERS_FOLDER_PATH = ""; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + AD788B4F58B5B1BEB897957A60A74DE5 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 707B3C6A875793A57A49DFCC1070AC3D /* Pods-LWZComponents_Example.release.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MACH_O_TYPE = staticlib; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + BB54C09151208FB8CF8E5B95DA8F6592 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = BFBAA38C64166B552897C7C5D9594FE6 /* YYModel.release.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + GCC_PREFIX_HEADER = "Target Support Files/YYModel/YYModel-prefix.pch"; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRIVATE_HEADERS_FOLDER_PATH = ""; + PRODUCT_MODULE_NAME = YYModel; + PRODUCT_NAME = YYModel; + PUBLIC_HEADERS_FOLDER_PATH = ""; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + C02329919DD2F068C343CA81C9347FA4 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9190EE6D1D78F2945E634A03AAC2E078 /* Masonry.debug.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + GCC_PREFIX_HEADER = "Target Support Files/Masonry/Masonry-prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "LWZ_DEBUG=1", + ); + IPHONEOS_DEPLOYMENT_TARGET = 6.0; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRIVATE_HEADERS_FOLDER_PATH = ""; + PRODUCT_MODULE_NAME = Masonry; + PRODUCT_NAME = Masonry; + PUBLIC_HEADERS_FOLDER_PATH = ""; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + CA547D2C7E9A8A153DC2B27FBE00B112 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "POD_CONFIGURATION_RELEASE=1", + "$(inherited)", + ); + 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 = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRIP_INSTALLED_PRODUCT = NO; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; + SYMROOT = "${SRCROOT}/../build"; + }; + name = Release; + }; + DDE1BB51C515F1DDE0C033800511AA60 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 449521E650DA465760580EF8CC21A62D /* YYModel.debug.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + GCC_PREFIX_HEADER = "Target Support Files/YYModel/YYModel-prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "LWZ_DEBUG=1", + ); + IPHONEOS_DEPLOYMENT_TARGET = 6.0; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRIVATE_HEADERS_FOLDER_PATH = ""; + PRODUCT_MODULE_NAME = YYModel; + PRODUCT_NAME = YYModel; + PUBLIC_HEADERS_FOLDER_PATH = ""; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + FF01E052168FD2EA77813410058C48D5 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 610B6D0C80916889668B79B7149F9710 /* Pods-LWZComponents_Tests.release.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MACH_O_TYPE = staticlib; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 2834199DFBDE098A524D12A7125B85CA /* Build configuration list for PBXNativeTarget "Pods-LWZComponents_Tests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 5029F8429E2AB7274501D58B9A94684D /* Debug */, + FF01E052168FD2EA77813410058C48D5 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 29DCBDD65990C5E928BEB5D504D0350F /* Build configuration list for PBXNativeTarget "SJUIKit" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 105B8A86C900DA3D5715E73A04825046 /* Debug */, + 760D07AFA0346E82A443BC6601EE4A3B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 25AD9454612BF454A1E3DC4CD4FA8C6D /* Debug */, + CA547D2C7E9A8A153DC2B27FBE00B112 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 598C096842C239E7B4B613B1BAD48FE3 /* Build configuration list for PBXNativeTarget "LWZComponents" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A9D048A63E21FD07ABAC9E193E143103 /* Debug */, + 67AC0BB48473FAEA9587E5AC6D834F9D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 6525CCFB5FEEBC36FDDE16A88CD85B83 /* Build configuration list for PBXNativeTarget "Masonry" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C02329919DD2F068C343CA81C9347FA4 /* Debug */, + 7628E77745F1D49AFC8EBD35E28DC1EE /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 9D739216B731DEA8BE67BA2DA6EA14E5 /* Build configuration list for PBXNativeTarget "Pods-LWZComponents_Example" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8B443F7DF538ACBA5158E5DD0C2EF3CB /* Debug */, + AD788B4F58B5B1BEB897957A60A74DE5 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + A9818D8B3BDFE7C917B7EE4D9074797C /* Build configuration list for PBXNativeTarget "YYModel" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DDE1BB51C515F1DDE0C033800511AA60 /* Debug */, + BB54C09151208FB8CF8E5B95DA8F6592 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = BFDFE7DC352907FC980B868725387E98 /* Project object */; +} diff --git a/Example/Pods/SJUIKit/LICENSE b/Example/Pods/SJUIKit/LICENSE new file mode 100644 index 0000000..c69e965 --- /dev/null +++ b/Example/Pods/SJUIKit/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2018 changsanjiang@gmail.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +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 THE +AUTHORS OR COPYRIGHT HOLDERS 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. diff --git a/Example/Pods/SJUIKit/README.md b/Example/Pods/SJUIKit/README.md new file mode 100644 index 0000000..688da91 --- /dev/null +++ b/Example/Pods/SJUIKit/README.md @@ -0,0 +1,29 @@ +# SJUIKit + +[![CI Status](https://img.shields.io/travis/changsanjiang@gmail.com/SJUIKit.svg?style=flat)](https://travis-ci.org/changsanjiang@gmail.com/SJUIKit) +[![Version](https://img.shields.io/cocoapods/v/SJUIKit.svg?style=flat)](https://cocoapods.org/pods/SJUIKit) +[![License](https://img.shields.io/cocoapods/l/SJUIKit.svg?style=flat)](https://cocoapods.org/pods/SJUIKit) +[![Platform](https://img.shields.io/cocoapods/p/SJUIKit.svg?style=flat)](https://cocoapods.org/pods/SJUIKit) + +## Example + +To run the example project, clone the repo, and run `pod install` from the Example directory first. + +## Requirements + +## Installation + +SJUIKit is available through [CocoaPods](https://cocoapods.org). To install +it, simply add the following line to your Podfile: + +```ruby +pod 'SJUIKit' +``` + +## Author + +changsanjiang@gmail.com, changsanjiang@gmail.com + +## License + +SJUIKit is available under the MIT license. See the LICENSE file for more info. diff --git a/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/Deprecated/SJAttributeWorker.h b/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/Deprecated/SJAttributeWorker.h new file mode 100755 index 0000000..666634d --- /dev/null +++ b/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/Deprecated/SJAttributeWorker.h @@ -0,0 +1,312 @@ +// +// SJAttributeWorker.h +// SJAttributeWorker +// +// Created by 畅三江 on 2017/11/12. +// Copyright © 2017年 畅三江. All rights reserved. +// +// Project Address: https://github.com/changsanjiang/SJAttributesFactory +// Email: changsanjiang@gmail.com +// + +#import +#import "SJAttributesRecorder.h" + +// - deprecated (use `NSAttributedString+SJMake.h`). +// - 已弃用, 未来可能会删除 + +@class SJAttributeWorker; + +NS_ASSUME_NONNULL_BEGIN + +@interface SJAttributesRangeOperator: NSObject +- (instancetype)initWithRange:(NSRange)range target:(__strong NSMutableAttributedString *)attrStr; +- (instancetype)initWithRecorder:(SJAttributesRecorder *)recorder target:(__strong NSMutableAttributedString *)attrStr; +@end + + +#pragma mark - + +@interface SJAttributeWorker : SJAttributesRangeOperator + +- (instancetype)init NS_DESIGNATED_INITIALIZER; + +@property (nonatomic, readonly) NSRange range; + +@property (nonatomic, readonly) NSMutableAttributedString *workInProcess; + +- (NSMutableAttributedString *)endTask; + +- (NSMutableAttributedString *)endTaskAndComplete:(void(^)(SJAttributeWorker *worker))block; + +/*! + * default font. + * + * default is UIFont.systemFont(ofSize: 14) + **/ +@property (nonatomic, strong, null_resettable) UIFont *defaultFont; + +/*! + * default textColor. + * + * default is UIColor.black + **/ +@property (nonatomic, strong, null_resettable) UIColor *defaultTextColor; + +/*! + * range editing. can be used it with `regexp` method. + * + * 范围编辑, 可以配合正则使用. + **/ +@property (nonatomic, copy, readonly) SJAttributeWorker *(^rangeEdit)(NSRange range, void (^task)(SJAttributesRangeOperator *make)); + +/*! + * get sub attributedString by `range`. + * + * 按照范围获取文本 + **/ +@property (nonatomic, copy, readonly) NSAttributedString *(^subAttrStr)(NSRange subRange); + +@property (nonatomic, readonly) NSInteger length; + +@end + + +#pragma mark - 正则 - regexp +@interface SJAttributeWorker(Regexp) + +/** + default is kNilOptions + */ +@property (nonatomic, readwrite) NSRegularExpressionOptions regexpOptions; + + +/*! + * regular expression. + * + * 正则匹配 + * + make.regexp(@"Hello", ^(SJAttributesRangeOperator * _Nonnull matched) { + matched.font([UIFont systemFontOfSize:18]).textColor([UIColor redColor]); + }); + **/ +@property (nonatomic, copy, readonly) SJAttributeWorker *(^regexp)(NSString *regStr, void(^matchedTask)(SJAttributesRangeOperator *make)); + + +/*! + * regular expression. value is [NSRange]. + * + * 正则匹配 + * + make.regexp_r(@"H", ^(NSArray * _Nonnull matchedRanges) { + [matchedRanges enumerateObjectsUsingBlock:^(NSValue * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + NSRange matchedRange = [obj rangeValue]; + + make.replace(matchedRange, @"h"); + NSInteger index = matchedRange.location + matchedRange.length; + make.insert(@"ello", index); // h + ello == hello + }]; + }, YES); + **/ +@property (nonatomic, copy, readonly) SJAttributeWorker *(^regexp_r)(NSString *regStr, void(^matchedTask)(NSArray *matchedRanges), BOOL reverse); + + +/** + make.regexp_replace(@"Hello", @" World!"); + make.regexp_replace(@"Hello", [UIImage imageNamed:@"sample2"], CGPointZero, CGSizeZero); + */ +@property (nonatomic, copy, readonly) void(^regexp_replace)(NSString *regexp, id replaceByStrOrAttrStrOrImg, ...); + + +typedef NS_ENUM(NSUInteger, SJAttributeRegexpInsertPosition) { + SJAttributeRegexpInsertPositionLeft, + SJAttributeRegexpInsertPositionRight, +}; +/** + make.regexp_insert(@"Hello", SJAttributeRegexpInsertPositionRight, @" World!"); + make.regexp_insert(@"Hello", SJAttributeRegexpInsertPositionRight, [UIImage imageNamed:@"sample2"], CGPointZero, CGSizeZero); + */ +@property (nonatomic, copy, readonly) void(^regexp_insert)(NSString *regexp, SJAttributeRegexpInsertPosition position, id insertingStrOrAttrStrOrImg, ...); + +@end + + + + +#pragma mark - 大小 - size +@interface SJAttributeWorker(Size) +@property (nonatomic, copy, readonly) CGSize(^size)(void); +@property (nonatomic, copy, readonly) CGSize(^sizeByRange)(NSRange range); +@property (nonatomic, copy, readonly) CGSize(^sizeByWidth)(double maxWidth); +@property (nonatomic, copy, readonly) CGSize(^sizeByHeight)(double maxHeight); +@end + + + + +#pragma mark - 插入 - insert +@interface SJAttributeWorker(Insert) + +#pragma mark - 常用方法 + +/** + append text. + + make.append(@"Hello").font([UIFont systemFontOfSize:14]).textColor([UIColor yellowColor]); + make.append([UIImage imageNamed:@"sample2"], CGPointZero, CGSizeZero); + */ +@property (nonatomic, copy, readonly) SJAttributesRangeOperator *(^append)(id strOrImg, ...); + + + + +#pragma mark - +/*! + * the range of the last inserted text. + * + * 最近一次插入的文本的范围. + **/ +@property (nonatomic, readonly) NSRange lastInsertedRange; +/*! + * editing the last inserted text + * + * 编辑最后插入的文本. + + make.lastInserted(^(SJAttributesRangeOperator * _Nonnull lastOperator) { + lastOperator.textColor([UIColor redColor]); + lastOperator.font([UIFont boldSystemFontOfSize:17]); + }); + + 4/10 2018 : Because someone ignores the `last operator` and uses `make`, I change the `last operator` to `make` + + For example: editing the last inserted text. + make.lastInserted(^(SJAttributesRangeOperator * _Nonnull make) { + // so it's editing the last inserted text + make.textColor([UIColor redColor]); + make.font([UIFont boldSystemFontOfSize:17]); + }); + **/ +@property (nonatomic, copy, readonly) SJAttributeWorker *(^lastInserted)(void(^task)(SJAttributesRangeOperator *make)); +/*! + * add attribute of `key, value, range`. + * + * 添加 + * + make.add(YYTextBackgroundBorderAttributeName, border, range); + **/ +@property (nonatomic, copy, readonly) SJAttributeWorker *(^add)(NSAttributedStringKey key, id value, NSRange range); +/*! + * insert text, `-1` indicates the insertion to the end. + * + * 插入文本, `-1` 表示插入到最后 + **/ +@property (nonatomic, copy, readonly) SJAttributeWorker *(^insertText)(NSString *text, NSInteger index); +/*! + * insert image, `-1` indicates the insertion to the end. + * if size == CGSizeZero, the image size will be used. + * + * 插入图片, `-1` 表示插入到最后 + * 如果 size == CGSizeZero, 将使用图片的size + make.insert([UIImage imageNamed:@"sample2"], -1, CGPointZero, CGSizeZero); + **/ +@property (nonatomic, copy, readonly) SJAttributeWorker *(^insertImage)(UIImage *image, NSInteger index, CGPoint offset, CGSize size); +/*! + * inset text, `-1` indicates the insertion to the end. + * + * 插入文本, `-1` 表示插入到最后 + **/ +@property (nonatomic, copy, readonly) SJAttributeWorker *(^insertAttrStr)(NSAttributedString *text, NSInteger index); + +/*! + * inset text or image, `-1` indicates the insertion to the end. + * + * 插入, `-1` 表示插入到最后 + * + make.insert(@"Hello", -1); + make.insert([UIImage imageNamed:@"sample2"], 12, CGPointZero, CGSizeMake(50, 50)); + make.insert([UIImage imageNamed:@"sample2"], -1, CGPointZero, CGSizeZero); + **/ +@property (nonatomic, copy, readonly) SJAttributeWorker *(^insert)(id strOrAttrStrOrImg, NSInteger idx, ...); + +@end + + + +#pragma mark - 替换 - replace +@interface SJAttributeWorker(Replace) +/** + make.replace(NSMakeRange(0, 2), @"Hello world!"); + make.replace(NSMakeRange(0, 2), [UIImage imageNamed:@"name"], CGPointZero, CGSizeZero); + + or use regular expression + + make.regexp_r(@"[#][^#]+#", ^(NSArray * _Nonnull matchedRanges) { + [matchedRanges enumerateObjectsUsingBlock:^(NSValue * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + make.replace([obj rangeValue], @"replace the result"); + }]; + }, YES); + */ +@property (nonatomic, copy, readonly) void(^replace)(NSRange range, id strOrAttrStrOrImg, ...); +@end + + + +#pragma mark - 删除 - remove +@interface SJAttributeWorker(Delete) +@property (nonatomic, copy, readonly) void(^removeText)(NSRange range); +@property (nonatomic, copy, readonly) void(^removeAttribute)(NSAttributedStringKey key, NSRange range); +@property (nonatomic, copy, readonly) void(^removeAttributes)(NSRange range); +@end + + + +#pragma mark - 属性 - property +@interface SJAttributesRangeOperator(Property) + +/// 字体 +@property (nonatomic, copy, readonly) SJAttributesRangeOperator *(^font)(UIFont *font); +/// 文本颜色 +@property (nonatomic, copy, readonly) SJAttributesRangeOperator *(^textColor)(UIColor *color); +/// 放大, 扩大 +@property (nonatomic, copy, readonly) SJAttributesRangeOperator *(^expansion)(double expansion); +/// 阴影. note: `shadow`会与`backgroundColor`冲突, 设置了`backgroundColor`后, `shadow`将不会显示. +@property (nonatomic, copy, readonly) SJAttributesRangeOperator *(^shadow)(CGSize shadowOffset, CGFloat shadowBlurRadius, UIColor *shadowColor); +/// 背景颜色 +@property (nonatomic, copy, readonly) SJAttributesRangeOperator *(^backgroundColor)(UIColor *color); +/// 下划线 +@property (nonatomic, copy, readonly) SJAttributesRangeOperator *(^underLine)(NSUnderlineStyle style, UIColor *color); +/// 删除线 +@property (nonatomic, copy, readonly) SJAttributesRangeOperator *(^strikethrough)(NSUnderlineStyle style, UIColor *color); +/// 边界 +@property (nonatomic, copy, readonly) SJAttributesRangeOperator *(^stroke)(UIColor *color, double stroke); +/// 倾斜(-1 ... 1) +@property (nonatomic, copy, readonly) SJAttributesRangeOperator *(^obliqueness)(double obliqueness); +/// 字间隔 +@property (nonatomic, copy, readonly) SJAttributesRangeOperator *(^letterSpacing)(double letterSpacing); +/// 上下偏移, 正值向上, 负值向下 +@property (nonatomic, copy, readonly) SJAttributesRangeOperator *(^offset)(double offset); +/// 链接 +@property (nonatomic, copy, readonly) SJAttributesRangeOperator *(^isLink)(void); +/// 段落 style +@property (nonatomic, copy, readonly) SJAttributesRangeOperator *(^paragraphStyle)(NSParagraphStyle *style); +/// 行间隔 +@property (nonatomic, copy, readonly) SJAttributesRangeOperator *(^lineSpacing)(double lineSpacing); +/// 段后间隔(\n) +@property (nonatomic, copy, readonly) SJAttributesRangeOperator *(^paragraphSpacing)(double paragraphSpacing); +/// 段前间隔(\n) +@property (nonatomic, copy, readonly) SJAttributesRangeOperator *(^paragraphSpacingBefore)(double paragraphSpacingBefore); +/// 首行头缩进 +@property (nonatomic, copy, readonly) SJAttributesRangeOperator *(^firstLineHeadIndent)(double firstLineHeadIndent); +/// 左缩进 +@property (nonatomic, copy, readonly) SJAttributesRangeOperator *(^headIndent)(double headIndent); +/// 右缩进(正值从左算起, 负值从右算起) +@property (nonatomic, copy, readonly) SJAttributesRangeOperator *(^tailIndent)(double tailIndent); +/// 对齐方式 +@property (nonatomic, copy, readonly) SJAttributesRangeOperator *(^alignment)(NSTextAlignment alignment); +/// 截断模式 +@property (nonatomic, copy, readonly) SJAttributesRangeOperator *(^lineBreakMode)(NSLineBreakMode lineBreakMode); + +@end + +extern NSMutableAttributedString *sj_makeAttributesString(void(^block)(SJAttributeWorker *make)); +NS_ASSUME_NONNULL_END diff --git a/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/Deprecated/SJAttributeWorker.m b/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/Deprecated/SJAttributeWorker.m new file mode 100755 index 0000000..b42fc25 --- /dev/null +++ b/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/Deprecated/SJAttributeWorker.m @@ -0,0 +1,1018 @@ +// +// SJAttributeWorker.m +// SJAttributeWorker +// +// Created by 畅三江 on 2017/11/12. +// Copyright © 2017年 畅三江. All rights reserved. +// + +#import "SJAttributeWorker.h" +#import +#import +#import "SJAttributesRecorder.h" + +NS_ASSUME_NONNULL_BEGIN + +NSMutableAttributedString *sj_makeAttributesString(void(^block)(SJAttributeWorker *make)) { + SJAttributeWorker *worker = [SJAttributeWorker new]; + block(worker); + return worker.endTask; +} + +inline static BOOL _rangeContains(NSRange range, NSRange subRange) { + return (range.location <= subRange.location) && (range.location + range.length >= subRange.location + subRange.length); +} + +#ifdef DEBUG +inline static void _errorLog(NSString *msg, id __nullable target) { + NSLog(@"\n__Error__: %@\nTarget: %@", msg, target); +} +#else +#define _errorLog(...) +#endif + +#pragma mark - + +@interface SJAttributesRangeOperator () +@property (nonatomic, strong, readonly) SJAttributesRecorder *recorder; +@property (nonatomic) BOOL needToAdd; // deafult is Yes. + +- (void)reset:(SJAttributesRecorder *)recorder; +@end + +@implementation SJAttributesRangeOperator { + NSMutableAttributedString *_target; +} + +- (instancetype)initWithRange:(NSRange)range target:(__strong NSMutableAttributedString *)attrStr { + SJAttributesRecorder *obj = [SJAttributesRecorder new]; + obj.range = range; + return [self initWithRecorder:obj target:attrStr]; +} + +- (instancetype)initWithRecorder:(SJAttributesRecorder *)recorder target:(__strong NSMutableAttributedString *)attrStr { + self = [super init]; + if ( !self ) return nil; + _target = attrStr; + [self reset:recorder]; + return self; +} + +- (void)reset:(SJAttributesRecorder *)recorder { + _recorder = recorder; + _needToAdd = YES; + __weak typeof(self) _self = self; + _recorder.propertyDidChangeExeBlock = ^(SJAttributesRecorder * _Nonnull recorder) { + __strong typeof(_self) self = _self; + if ( !self ) return ; + self.needToAdd = YES; + }; +} + +- (void)addAttributesToTargetIfNeeded { + if ( !_needToAdd ) return; + else _needToAdd = NO; + +#ifdef SJ_MAC + NSLog(@"%@", self); +#endif + + NSRange range = _recorder.range; + if ( range.location == 0 && range.length == 0 ) { + range = NSMakeRange(0, _target.length); + } + if ( range.length == 0 ) return; + if ( nil != _recorder.font ) { + [_target addAttribute:NSFontAttributeName value:_recorder.font range:range]; + } + if ( nil != _recorder.textColor ) { + [_target addAttribute:NSForegroundColorAttributeName value:_recorder.textColor range:range]; + } + if ( 0 != _recorder.expansion ) { + [_target addAttribute:NSExpansionAttributeName value:@(_recorder.expansion) range:range]; + } + if ( nil != _recorder.shadow ) { + [_target addAttribute:NSShadowAttributeName value:_recorder.shadow range:range]; + } + if ( nil != _recorder.backgroundColor ) { + [_target addAttribute:NSBackgroundColorAttributeName value:_recorder.backgroundColor range:range]; + } + if ( nil != _recorder.underLine ) { + [_target addAttribute:NSUnderlineStyleAttributeName value:@(_recorder.underLine.value) range:range]; + [_target addAttribute:NSUnderlineColorAttributeName value:_recorder.underLine.color range:range]; + } + if ( nil != _recorder.strikethrough ) { + [_target addAttribute:NSStrikethroughStyleAttributeName value:@(_recorder.strikethrough.value) range:range]; + [_target addAttribute:NSStrikethroughColorAttributeName value:_recorder.strikethrough.color range:range]; + } + if ( nil != _recorder.stroke ) { + [_target addAttribute:NSStrokeWidthAttributeName value:@(_recorder.stroke.value) range:range]; + [_target addAttribute:NSStrokeColorAttributeName value:_recorder.stroke.color range:range]; + } + if ( 0 != _recorder.obliqueness ) { + [_target addAttribute:NSObliquenessAttributeName value:@(_recorder.obliqueness) range:range]; + } + if ( 0 != _recorder.letterSpacing ) { + [_target addAttribute:NSKernAttributeName value:@(_recorder.letterSpacing) range:range]; + } + if ( 0 != _recorder.offset ) { + [_target addAttribute:NSBaselineOffsetAttributeName value:@(_recorder.offset) range:range]; + } + if ( YES == _recorder.link ) { + [_target addAttribute:NSLinkAttributeName value:@(1) range:range]; + } + if ( nil != _recorder.paragraphStyleM ) { + [_target addAttribute:NSParagraphStyleAttributeName value:_recorder.paragraphStyleM range:range]; + } +} + +- (void)removeAttributeWithKey:(NSAttributedStringKey)attributedStringKey { + if ( attributedStringKey == NSFontAttributeName ) _recorder.font = nil; + else if ( attributedStringKey == NSForegroundColorAttributeName ) _recorder.textColor = nil; + else if ( attributedStringKey == NSExpansionAttributeName ) _recorder.expansion = 0; + else if ( attributedStringKey == NSShadowAttributeName ) _recorder.shadow = nil; + else if ( attributedStringKey == NSBackgroundColorAttributeName ) _recorder.backgroundColor = nil; + else if ( attributedStringKey == NSUnderlineStyleAttributeName ) _recorder.underLine = nil; + else if ( attributedStringKey == NSStrikethroughStyleAttributeName ) _recorder.strikethrough = nil; + else if ( attributedStringKey == NSStrokeWidthAttributeName ) _recorder.stroke = nil; + else if ( attributedStringKey == NSObliquenessAttributeName ) _recorder.obliqueness = 0; + else if ( attributedStringKey == NSKernAttributeName ) _recorder.letterSpacing = 0; + else if ( attributedStringKey == NSBaselineOffsetAttributeName ) _recorder.offset = 0; + else if ( attributedStringKey == NSLinkAttributeName ) _recorder.link = NO; + else if ( attributedStringKey == NSParagraphStyleAttributeName ) _recorder.paragraphStyleM = [NSParagraphStyle defaultParagraphStyle].mutableCopy; +} +@end + +#pragma mark - + +@interface SJAttributeWorker () +@property (nonatomic, strong, readonly) NSMutableAttributedString *attrStr; +@property (nonatomic, strong, readonly) NSMutableArray *rangeOperatorsM; +@end + +@implementation SJAttributeWorker +- (instancetype)init { + NSMutableAttributedString *attrStr = [NSMutableAttributedString new]; + self = [super initWithRange:NSMakeRange(0, 0) target:attrStr]; + if ( !self ) return nil; + _rangeOperatorsM = [NSMutableArray array]; + self->_attrStr = attrStr; + return self; +} + +- (NSRange)range { + return NSMakeRange(0, self->_attrStr.length); +} + +- (NSInteger)length { + return self->_attrStr.length; +} + +- (void)pauseTask { + [self endTask]; +} + +- (NSMutableAttributedString *)workInProcess { + return self->_attrStr; +} + +- (void)setDefaultFont:(UIFont *_Nullable)defaultFont { + self.recorder.font = defaultFont; +} + +- (UIFont *)defaultFont { + return self.recorder.font?:[UIFont systemFontOfSize:14]; +} + +- (void)setDefaultTextColor:(UIColor *_Nullable)defaultTextColor { + self.recorder.textColor = defaultTextColor; +} + +- (UIColor *)defaultTextColor { + return self.recorder.textColor?:[UIColor blackColor]; +} + +- (NSMutableAttributedString *)endTask { + if ( 0 == self->_attrStr.length ) return self->_attrStr; + [self addAttributesToTargetIfNeeded]; + [self.rangeOperatorsM enumerateObjectsUsingBlock:^(SJAttributesRangeOperator * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + [self _addCommonValuesToRecorderIfNeed:obj.recorder]; + [obj addAttributesToTargetIfNeeded]; + }]; + return self->_attrStr; +} + +- (void)_addCommonValuesToRecorderIfNeed:(SJAttributesRecorder *)recorder { + if ( nil == recorder.font ) recorder.font = self.defaultFont; + if ( nil == recorder.textColor ) recorder.textColor = self.defaultTextColor; + if ( 0 == recorder.lineSpacing ) recorder.lineSpacing = self.recorder.lineSpacing; + if ( nil == recorder.alignment ) recorder.alignment = self.recorder.alignment; +} + +- (NSMutableAttributedString *)endTaskAndComplete:(void(^)(SJAttributeWorker *worker))block; { + [self endTask]; + if ( block ) block(self); + return self->_attrStr; +} + +/// 范围编辑. 可以配合正则使用. +- (SJAttributeWorker * _Nonnull (^)(NSRange, void (^ _Nonnull)(SJAttributesRangeOperator * _Nonnull)))rangeEdit { + return ^ SJAttributeWorker *(NSRange range, void(^task)(SJAttributesRangeOperator *matched)) { + if ( !_rangeContains(self.range, range) ) { + _errorLog(@"Edit Failed! param 'range' is unlawfulness!", self->_attrStr.string); + return self; + } + SJAttributesRangeOperator *rangeOperator = [self _getOperatorWithRange:range]; + task(rangeOperator); + return self; + }; +} + +/// sub attr str +- (NSAttributedString * _Nonnull (^)(NSRange))subAttrStr { + return ^ NSAttributedString *(NSRange subRange) { + if ( !_rangeContains(self.range, subRange) ) { + _errorLog(@"Get `subAttributedString` Failed! param 'range' is unlawfulness!", self->_attrStr.string); + return nil; + } + [self pauseTask]; + return [self->_attrStr attributedSubstringFromRange:subRange]; + }; +} + +- (SJAttributesRangeOperator *)_getOperatorWithRange:(NSRange)range { + __block SJAttributesRangeOperator *rangeOperator = nil; + [self.rangeOperatorsM enumerateObjectsUsingBlock:^(SJAttributesRangeOperator * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + NSRange objRange = obj.recorder.range; + if ( objRange.location == range.location && objRange.length == range.length ) { + rangeOperator = obj; + *stop = YES; + } + }]; + + if ( rangeOperator ) return rangeOperator; + + [self.rangeOperatorsM enumerateObjectsUsingBlock:^(SJAttributesRangeOperator * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + NSRange objRange = obj.recorder.range; + if ( _rangeContains(objRange, range) ) { + rangeOperator = [[SJAttributesRangeOperator alloc] initWithRecorder:obj.recorder.mutableCopy target:self->_attrStr]; + rangeOperator.recorder.range = range; + [self.rangeOperatorsM addObject:rangeOperator]; + *stop = YES; + } + }]; + + if ( rangeOperator ) return rangeOperator; + + rangeOperator = [[SJAttributesRangeOperator alloc] initWithRange:range target:self->_attrStr]; + [self.rangeOperatorsM addObject:rangeOperator]; + return rangeOperator; +} + +- (void)_adjustOperatorsWhenRemovingText:(NSRange)deletingRange { + NSInteger deletingLinePoint = deletingRange.location + deletingRange.length; + [self.rangeOperatorsM enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(SJAttributesRangeOperator * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + NSRange objRange = obj.recorder.range; + NSInteger objLinePoint = objRange.location + objRange.length; + /** + 1 2 + -----------|<------------ObjRange--------->|---------------------- + 3 4 + ---------------|<---DeletingRange--->|---------------------------- + - 1 objRange.location + - 2 objLinePoint (objRange.location + objRange.length) + - 3 deletingRange.location + - 4 deletingLinePoint (deletingRange.location + deletingRange.length) + */ + if ( _rangeContains(deletingRange, objRange) ) { + [self.rangeOperatorsM removeObject:obj]; + } + /** + -----------|<------------ObjRange--------->|---------------------- + -------------------|<------------DeletingRange--------->|--------- + */ + else if ( objRange.location <= deletingRange.location && deletingRange.location < objLinePoint ) { + objRange.length = objLinePoint - deletingRange.location; + obj.recorder.range = objRange; // adjust + } + /** + ----------------------|<------------ObjRange--------->|----------- + -----------|<------------DeletingRange--------->|----------------- + */ + else if ( deletingRange.location <= objRange.location && objRange.location < deletingLinePoint ) { + objRange.location = deletingRange.location; + objRange.length = objLinePoint - deletingLinePoint; + obj.recorder.range = objRange; // adjust + } + /** + -------------------------------|<---ObjRange--->|---------------- + ---|<---DeletingRange--->|--------------------------------------- + */ + else if ( deletingLinePoint < objRange.location ) { + objRange.location -= deletingRange.length; + obj.recorder.range = objRange; // adjust + } + /** + ---|<---ObjRange--->|-------------------------------------------- + -------------------------------|<---DeletingRange--->|----------- + */ +// else { + +// } + }]; +} + +- (void)_adjustOperatorsWhenRemovingAttributes:(NSRange)deletingRange { + NSInteger deletingLinePoint = deletingRange.location + deletingRange.length; + [self.rangeOperatorsM enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(SJAttributesRangeOperator * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + NSRange objRange = obj.recorder.range; + NSInteger objLinePoint = objRange.location + objRange.length; + if ( _rangeContains(deletingRange, objRange) ) { + [self.rangeOperatorsM removeObject:obj]; + } + /** + -----------|<------------ObjRange--------->|---------------------- + -------------------|<------------DeletingRange--------->|--------- + */ + else if ( objRange.location <= deletingRange.location && deletingRange.location < objLinePoint ) { + objRange.length = deletingRange.location - objRange.location; + obj.recorder.range = objRange; // adjust + } + /** + ----------------------|<------------ObjRange--------->|----------- + -----------|<------------DeletingRange--------->|----------------- + */ + else if ( deletingRange.location <= objRange.location && objRange.location < deletingLinePoint ) { + objRange.location = deletingLinePoint; + objRange.length = objLinePoint - deletingLinePoint; + obj.recorder.range = objRange; // adjust + } + /** + -------------------------------|<---ObjRange--->|---------------- + ---|<---DeletingRange--->|--------------------------------------- + */ +// else if ( deleteingLinePoint < objRange.location ) { +// } + /** + ---|<---ObjRange--->|-------------------------------------------- + -------------------------------|<---DeletingRange--->|----------- + */ +// else { +// } + }]; +} + +- (void)_adjustOperatorsWhenRemovingAttribute:(NSAttributedStringKey)key deleteingRange:(NSRange)deletingRange { + NSInteger deletingLinePoint = deletingRange.location + deletingRange.length; + [self.rangeOperatorsM enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(SJAttributesRangeOperator * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + NSRange objRange = obj.recorder.range; + NSInteger objLinePoint = objRange.location + objRange.length; + if ( _rangeContains(deletingRange, objRange) ) { + [obj removeAttributeWithKey:key]; + } + /** + -----------|<------------ObjRange--------->|---------------------- + -------------------|<------------DeletingRange--------->|--------- + */ + else if ( objRange.location <= deletingRange.location && deletingRange.location < objLinePoint ) { + obj.recorder.range = NSMakeRange(objRange.location, deletingRange.location - objRange.location); // adjust + NSRange range_new = NSMakeRange(deletingRange.location, objLinePoint - deletingRange.location); + SJAttributesRangeOperator *operator_new = [self _getOperatorWithRange:range_new]; + [operator_new removeAttributeWithKey:key]; + } + /** + ----------------------|<------------ObjRange--------->|----------- + -----------|<------------DeletingRange--------->|----------------- + */ + else if ( deletingRange.location <= objRange.location && objRange.location < deletingLinePoint ) { + obj.recorder.range = NSMakeRange(deletingLinePoint, objLinePoint - deletingLinePoint); // adjust + NSRange range_new = NSMakeRange(objRange.location, deletingLinePoint - objRange.location); + SJAttributesRangeOperator *operator_new = [self _getOperatorWithRange:range_new]; + [operator_new removeAttributeWithKey:key]; + } + /** + -------------------------------|<---ObjRange--->|---------------- + ---|<---DeletingRange--->|--------------------------------------- + */ +// else if ( deleteingLinePoint < objRange.location ) { +// } + /** + ---|<---ObjRange--->|-------------------------------------------- + -------------------------------|<---DeletingRange--->|----------- + */ +// else { +// } + }]; +} + +- (void)_adjustOperatorsWhenInsertingText:(NSRange)insertingRange { + NSInteger insertingLinePoint = insertingRange.location + insertingRange.length; + [self.rangeOperatorsM enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(SJAttributesRangeOperator * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + NSRange objRange = obj.recorder.range; + NSInteger objLinePoint = objRange.location + objRange.length; + /** + -----------|<------------ObjRange--------->|---------------------- + ---------------|<---InsertingRange--->|--------------------------- + */ + if ( _rangeContains(insertingRange, objRange) ) { + NSRange leftRange = NSMakeRange(objRange.location, insertingRange.location - objRange.location); + NSRange rightRange = NSMakeRange(insertingLinePoint, objRange.length - leftRange.length); + if ( leftRange.length != 0 ) [self _getOperatorWithRange:leftRange]; + if ( rightRange.length != 0 ) { + // adjust + SJAttributesRangeOperator *operator = [self _getOperatorWithRange:rightRange]; + SJAttributesRecorder *recorder = obj.recorder.mutableCopy; + recorder.range = rightRange; + [operator reset:recorder]; + } + [self.rangeOperatorsM removeObject:obj]; + } + /** + -----------|<------------ObjRange--------->|---------------------- + -------------------|<------------InsertingRange--------->|-------- + */ + else if ( objRange.location <= insertingRange.location && insertingRange.location < objLinePoint ) { + NSRange leftRange = NSMakeRange(objRange.location, insertingRange.location - objRange.location); + NSRange rightRange = NSMakeRange(insertingLinePoint, objRange.length - leftRange.length); + if ( leftRange.length != 0 ) [self _getOperatorWithRange:leftRange]; + if ( rightRange.length != 0 ) { + // adjust + SJAttributesRangeOperator *operator = [self _getOperatorWithRange:rightRange]; + SJAttributesRecorder *recorder = obj.recorder.mutableCopy; + recorder.range = rightRange; + [operator reset:recorder]; + } + [self.rangeOperatorsM removeObject:obj]; + } + /** + ----------------------|<------------ObjRange--------->|----------- + -----------|<------------InsertingRange--------->|---------------- + */ + else if ( insertingRange.location <= objRange.location && objRange.location < insertingLinePoint ) { + NSRange range_new = NSMakeRange(insertingLinePoint, objRange.length); + obj.recorder.range = range_new; + } + /** + -------------------------------|<---ObjRange--->|---------------- + ---|<---InsertingRange--->|-------------------------------------- + */ + else if ( insertingLinePoint < objRange.location ) { + NSRange range_new = NSMakeRange(objRange.location + insertingRange.length, objRange.length); + obj.recorder.range = range_new; + } + /** + ---|<---ObjRange--->|-------------------------------------------- + -------------------------------|<---InsertingRange--->|---------- + */ +// else { +// } + }]; +} + +- (void)_adjustOperatorsWhenReplaceCharactersInRange:(NSRange)range_old textLength:(NSInteger)textLength { + NSRange range_new = NSMakeRange(range_old.location, textLength); + if ( NSEqualRanges(range_old, range_new) ) return; + NSInteger range_old_linePoint = range_old.length + range_old.location; + NSInteger sub = (NSInteger)range_old.length - (NSInteger)range_new.length; + + NSInteger range_new_linePoint = range_new.length + range_new.location; + + [self.rangeOperatorsM enumerateObjectsUsingBlock:^(SJAttributesRangeOperator * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + NSRange objRange = obj.recorder.range; + NSInteger objLinePoint = objRange.location + objRange.length; + + /** + -----------|<------------ObjRange--------->|---------------------- + -----------------|<----Range_old---->|---------------------------- + -----------------|<--Range_new-->|-------------------------------- + */ + if ( _rangeContains(objRange, range_old) ) { + objRange.length -= sub; + obj.recorder.range = objRange; + } + /** + -----------|<------------ObjRange--------->|---------------------- + -----------------|<------------Range_old------------>|------------ + -----------------|<--Range_new-->|-------------------------------- + */ + else if ( objRange.location <= range_old.location && range_old_linePoint > objLinePoint ) { + // 只保留未替换部分 + obj.recorder.range = NSMakeRange(objRange.location, range_old.location - objRange.location); + } + /** + -----------|<------------ObjRange--------->|---------------------- + ------|<-------Range_old------>|---------------------------------- + ------|<--Range_new-->|------------------------------------------- + */ + else if ( range_old.location <= objRange.location && objRange.location < range_old_linePoint ) { + obj.recorder.range = NSMakeRange(range_new_linePoint, objLinePoint - range_old_linePoint); + } + /** + -----------------------------------|<---ObjRange--->|------------- + ------|<-------Range_old------>|---------------------------------- + ------|<--Range_new-->|------------------------------------------- + */ + else if ( range_old_linePoint <= objRange.location ) { + obj.recorder.range = NSMakeRange(objRange.location - sub, objRange.length); + } + }]; +} + +@end + +#pragma mark - regular +@implementation SJAttributeWorker(Regexp) + +- (void)setRegexpOptions:(NSRegularExpressionOptions)regexpOptions { + objc_setAssociatedObject(self, @selector(regexpOptions), @(regexpOptions), OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (NSRegularExpressionOptions)regexpOptions { + return [objc_getAssociatedObject(self, _cmd) integerValue]; +} + +/// 正则匹配 +- (SJAttributeWorker * _Nonnull (^)(NSString * _Nonnull, void (^ _Nonnull)(SJAttributesRangeOperator * _Nonnull)))regexp { + return ^ SJAttributeWorker *(NSString *regStr, void(^task)(SJAttributesRangeOperator *matched)) { + return self.regexp_r(regStr, ^(NSArray * _Nonnull matchedRanges) { + [matchedRanges enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(NSValue * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + NSRange matchedRange = [obj rangeValue]; + self.rangeEdit(matchedRange, task); + }]; + }, YES); + }; +} +/// 正则匹配. [NSRange] +- (SJAttributeWorker * _Nonnull (^)(NSString * _Nonnull, void (^ _Nonnull)(NSArray * _Nonnull), BOOL reverse))regexp_r { + return ^ SJAttributeWorker *(NSString *regStr, void(^task)(NSArray *ranges), BOOL reverse) { + NSMutableArray *rangesM = [NSMutableArray array]; + NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:regStr options:self.regexpOptions error:nil]; + [regex enumerateMatchesInString:self->_attrStr.string options:NSMatchingWithoutAnchoringBounds range:self.range usingBlock:^(NSTextCheckingResult * _Nullable result, NSMatchingFlags flags, BOOL * _Nonnull stop) { + if ( result ) { [rangesM addObject:[NSValue valueWithRange:result.range]];} + }]; + if ( reverse ) { + NSMutableArray *reverseM = [NSMutableArray array]; + [rangesM enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(NSValue * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { [reverseM addObject:obj];}]; + rangesM = reverseM; + } + task(rangesM); + return self; + }; +} + +- (void (^)(NSString * _Nonnull, id _Nonnull, ...))regexp_replace { + return ^ (NSString *regexp, id replaceByStrOrAttrStrOrImg, ...) { + CGPoint origin = CGPointZero; + CGSize size = CGSizeZero; + va_list args; + va_start(args, replaceByStrOrAttrStrOrImg); + if ( [replaceByStrOrAttrStrOrImg isKindOfClass:[UIImage class]] ) { + origin = va_arg(args, CGPoint); + size = va_arg(args, CGSize); + } + self.regexp_r(regexp, ^(NSArray * _Nonnull matchedRanges) { + [matchedRanges enumerateObjectsUsingBlock:^(NSValue * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + if ( [replaceByStrOrAttrStrOrImg isKindOfClass:[NSString class]] || + [replaceByStrOrAttrStrOrImg isKindOfClass:[NSAttributedString class]] ) { + self.replace([obj rangeValue], replaceByStrOrAttrStrOrImg); + } + else if ( [replaceByStrOrAttrStrOrImg isKindOfClass:[UIImage class]] ) { + self.replace([obj rangeValue], replaceByStrOrAttrStrOrImg, origin, size); + } + else { + _errorLog(@"inset `text` Failed! param `strOrAttrStrOrImg` is Unlawfulness!", self->_attrStr.string); + } + }]; + }, YES); + va_end(args); + }; +} + +- (void (^)(NSString * _Nonnull, SJAttributeRegexpInsertPosition, id _Nonnull, ...))regexp_insert { + return ^ (NSString *regexp, SJAttributeRegexpInsertPosition position, id insertingStrOrAttrStrOrImg, ...) { + va_list args; + va_start(args, insertingStrOrAttrStrOrImg); + CGPoint origin = CGPointZero; + CGSize size = CGSizeZero; + if ( [insertingStrOrAttrStrOrImg isKindOfClass:[UIImage class]] ) { + origin = va_arg(args, CGPoint); + size = va_arg(args, CGSize); + } + self.regexp_r(regexp, ^(NSArray * _Nonnull matchedRanges) { + [matchedRanges enumerateObjectsUsingBlock:^(NSValue * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + NSRange objRange = [obj rangeValue]; + NSInteger index = -1; + switch ( position ) { + case SJAttributeRegexpInsertPositionLeft: { + index = objRange.location; + } + break; + case SJAttributeRegexpInsertPositionRight: { + index = objRange.location + objRange.length; + } + break; + } + if ( [insertingStrOrAttrStrOrImg isKindOfClass:[NSString class]] ) { + self.insertText(insertingStrOrAttrStrOrImg, index); + } + else if ( [insertingStrOrAttrStrOrImg isKindOfClass:[NSAttributedString class]] ) { + self.insertAttrStr(insertingStrOrAttrStrOrImg, index); + } + else if ( [insertingStrOrAttrStrOrImg isKindOfClass:[UIImage class]] ) { + self.insertImage(insertingStrOrAttrStrOrImg, index, origin, size); + } + else { + _errorLog(@"inset `text` Failed! param `strOrAttrStrOrImg` is Unlawfulness!", self->_attrStr.string); + } + }]; + }, YES); + va_end(args); + }; +} +@end + + +#pragma mark - size +@implementation SJAttributeWorker(Size) + +- (CGSize (^)(void))size { + return ^ CGSize() { + return [self sizeWithAttrString:self->_attrStr width:CGFLOAT_MAX height:CGFLOAT_MAX]; + }; +} + +- (CGSize (^)(NSRange))sizeByRange { + return ^ CGSize (NSRange byRange) { + return [self sizeWithAttrString:self.subAttrStr(byRange) width:CGFLOAT_MAX height:CGFLOAT_MAX]; + }; +} +- (CGSize (^)(double))sizeByHeight { + return ^ CGSize (double height) { + return [self sizeWithAttrString:self->_attrStr width:CGFLOAT_MAX height:height]; + }; +} +- (CGSize (^)(double))sizeByWidth { + return ^ CGSize (double width) { + return [self sizeWithAttrString:self->_attrStr width:width height:CGFLOAT_MAX]; + }; +} +- (CGSize)sizeWithAttrString:(NSAttributedString *)attrStr width:(double)width height:(double)height { + if ( 0 == attrStr ) { + _errorLog(@"Get `size` Failed! param 'attrStr' is empty!", nil); + return CGSizeZero; + } + [self pauseTask]; + CGRect bounds = [attrStr boundingRectWithSize:CGSizeMake(width, height) options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading context:nil]; + bounds.size.width = ceil(bounds.size.width); + bounds.size.height = ceil(bounds.size.height); + return bounds.size; +} +@end + + + +#pragma mark - insert +@implementation SJAttributeWorker(Insert) + +- (void)setLastInsertedRange:(NSRange)lastInsertedRange { + objc_setAssociatedObject(self, @selector(lastInsertedRange), [NSValue valueWithRange:lastInsertedRange], OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} +- (NSRange)lastInsertedRange { + return [objc_getAssociatedObject(self, _cmd) rangeValue]; +} +- (SJAttributeWorker * _Nonnull (^)(void (^ _Nonnull)(SJAttributesRangeOperator * _Nonnull)))lastInserted { + return ^ SJAttributeWorker *(void(^task)(SJAttributesRangeOperator *lastOperator)) { + return self.rangeEdit(self.lastInsertedRange, task); + }; +} +- (SJAttributeWorker * _Nonnull (^)(NSAttributedStringKey _Nonnull, id _Nonnull, NSRange))add { + return ^ SJAttributeWorker *(NSAttributedStringKey key, id value, NSRange range) { + if ( !key || !value ) { + _errorLog(@"Added Attribute Failed! param `key or value` is Empty!", self->_attrStr.string); + return self; + } + if ( !_rangeContains(self.range, range) ) { + _errorLog(@"Add Failed! param 'range' is unlawfulness!", self->_attrStr.string); + return self; + } + [self->_attrStr addAttribute:key value:value range:range]; + return self; + }; +} +- (SJAttributesRangeOperator * _Nonnull (^)(id _Nonnull, ...))append { + return ^ SJAttributesRangeOperator *(id strOrImg, ...) { + va_list args; + va_start(args, strOrImg); + if ( [strOrImg isKindOfClass:[NSString class]] ) { + self.insertText(strOrImg, -1); + } + else if ( [strOrImg isKindOfClass:[UIImage class]] ) { + self.insertImage(strOrImg, -1, va_arg(args, CGPoint), va_arg(args, CGSize)); + } + else { + _errorLog(@"append `text` Failed! param `strOrImg` is Unlawfulness!", self->_attrStr.string); + } + va_end(args); + return [self _getOperatorWithRange:self.lastInsertedRange]; + }; +} +- (SJAttributeWorker * _Nonnull (^)(NSString * _Nonnull, NSInteger))insertText { + return ^ SJAttributeWorker *(NSString *text, NSInteger idx) { + if ( 0 == text.length ) { + _errorLog(@"inset `text` Failed! param `text` is Empty!", self->_attrStr.string); + return self; + } + return self.insertAttrStr([[NSAttributedString alloc] initWithString:text], idx); + }; +} +- (SJAttributeWorker * _Nonnull (^)(UIImage * _Nonnull, NSInteger, CGPoint, CGSize))insertImage { + return ^ SJAttributeWorker *(UIImage *image, NSInteger idx, CGPoint offset, CGSize size) { + if ( nil == image ) { + _errorLog(@"inset `image` Failed! param `image` is Empty!", self->_attrStr.string); + return self; + } + NSTextAttachment *attachment = [NSTextAttachment new]; + attachment.image = image; + if ( CGSizeEqualToSize(size, CGSizeZero) ) size = image.size; + attachment.bounds = (CGRect){offset, size}; + return self.insertAttrStr([NSAttributedString attributedStringWithAttachment:attachment], idx); + }; +} +- (SJAttributeWorker * _Nonnull (^)(NSAttributedString * _Nonnull, NSInteger))insertAttrStr { + return ^ SJAttributeWorker *(NSAttributedString *text, NSInteger idx) { + if ( 0 == text.length ) { + _errorLog(@"inset `text` Failed! param `text` is Empty!", self->_attrStr.string); + return self; + } + if ( -1 == idx || idx > self->_attrStr.length ) { + idx = self->_attrStr.length; + } + self.lastInsertedRange = NSMakeRange(idx, text.length); + [self _adjustOperatorsWhenInsertingText:self.lastInsertedRange]; + [self->_attrStr insertAttributedString:text atIndex:idx]; + return self; + }; +} +- (SJAttributeWorker * _Nonnull (^)(id _Nonnull, NSInteger, ...))insert { + return ^ SJAttributeWorker *(id strOrAttrStrOrImg, NSInteger idx, ...) { + va_list args; + va_start(args, idx); + if ( [strOrAttrStrOrImg isKindOfClass:[NSString class]] ) { + self.insertText(strOrAttrStrOrImg, idx); + } + else if ( [strOrAttrStrOrImg isKindOfClass:[NSAttributedString class]] ) { + self.insertAttrStr(strOrAttrStrOrImg, idx); + } + else if ( [strOrAttrStrOrImg isKindOfClass:[UIImage class]] ) { + self.insertImage(strOrAttrStrOrImg, idx, va_arg(args, CGPoint), va_arg(args, CGSize)); + } + else { + _errorLog(@"inset `text` Failed! param `strOrAttrStrOrImg` is Unlawfulness!", self->_attrStr.string); + } + va_end(args); + return self; + }; +} +@end + + + + +#pragma mark - replace +@implementation SJAttributeWorker(Replace) +- (void (^)(NSRange, id _Nonnull, ...))replace { + return ^ void (NSRange range, id strOrAttrStrOrImg, ...) { + if ( !_rangeContains(self.range, range) ) { + _errorLog(@"Replace Failed! param 'range' is unlawfulness!", self->_attrStr.string); + return; + } + + NSAttributedString *text = nil; + if ( [strOrAttrStrOrImg isKindOfClass:[NSString class]] ) { + text = [[NSAttributedString alloc] initWithString:strOrAttrStrOrImg]; + } + else if ( [strOrAttrStrOrImg isKindOfClass:[NSAttributedString class]] ) { + text = strOrAttrStrOrImg; + } + else if ( [strOrAttrStrOrImg isKindOfClass:[UIImage class]] ) { + va_list args; + va_start(args, strOrAttrStrOrImg); + NSTextAttachment *attachment = [NSTextAttachment new]; + attachment.image = strOrAttrStrOrImg; + CGPoint origin = va_arg(args, CGPoint); + CGSize size = va_arg(args, CGSize); if ( CGSizeEqualToSize(size, CGSizeZero) ) size = attachment.image.size; + attachment.bounds = (CGRect){origin, size}; + text = [NSAttributedString attributedStringWithAttachment:attachment]; + va_end(args); + } + else { + _errorLog(@"inset `text` Failed! param `strOrAttrStrOrImg` is Unlawfulness!", self->_attrStr.string); + } + + if ( !text ) return; + + [self->_attrStr replaceCharactersInRange:range withAttributedString:text]; + [self _adjustOperatorsWhenReplaceCharactersInRange:range textLength:[text length]]; + }; +} +@end + + +#pragma mark - delete +@implementation SJAttributeWorker(Delete) + +- (void (^)(NSRange))removeText { + return ^ (NSRange range) { + if ( !_rangeContains(self.range, range) ) { + _errorLog(@"Remove Failed! param 'range' is unlawfulness!", self->_attrStr.string); + return ; + } + [self _adjustOperatorsWhenRemovingText:range]; + [self->_attrStr deleteCharactersInRange:range]; + }; +} +- (void (^)(NSAttributedStringKey _Nonnull, NSRange))removeAttribute { + return ^ (NSAttributedStringKey key, NSRange range) { + if ( !_rangeContains(self.range, range) ) { + _errorLog(@"Remove Failed! param 'range' is unlawfulness!", self->_attrStr.string); + return ; + } + [self _adjustOperatorsWhenRemovingAttribute:key deleteingRange:range]; + [self->_attrStr removeAttribute:key range:range]; + }; +} +- (void (^)(NSRange))removeAttributes { + return ^ (NSRange range) { + if ( !_rangeContains(self.range, range) ) { + _errorLog(@"Remove Failed! param 'range' is unlawfulness!", self->_attrStr.string); + return ; + } + [self _adjustOperatorsWhenRemovingAttributes:range]; + NSString *subAttrStr = self.subAttrStr(range).string; + self.replace(range, subAttrStr); + }; +} +@end + + +#pragma mark - property +@implementation SJAttributesRangeOperator(Property) +/// 字体 +- (SJAttributesRangeOperator * _Nonnull (^)(UIFont * _Nonnull))font { + return ^ SJAttributesRangeOperator *(UIFont *font) { + self.recorder.font = font; + return self; + }; +} +/// 文本颜色 +- (SJAttributesRangeOperator * _Nonnull (^)(UIColor * _Nonnull))textColor { + return ^ SJAttributesRangeOperator *(UIColor *textColor) { + self.recorder.textColor = textColor; + return self; + }; +} +/// 放大, 扩大 +- (SJAttributesRangeOperator * _Nonnull (^)(double))expansion { + return ^ SJAttributesRangeOperator *(double expansion) { + self.recorder.expansion = expansion; + return self; + }; +} +/// 阴影 +- (SJAttributesRangeOperator * _Nonnull (^)(CGSize, CGFloat, UIColor * _Nonnull))shadow { + return ^ SJAttributesRangeOperator *(CGSize shadowOffset, CGFloat shadowBlurRadius, UIColor *shadowColor) { + if ( nil != self.recorder.backgroundColor ) { + _errorLog(@"`shadow`会与`backgroundColor`冲突, 设置了`backgroundColor`后, `shadow`将不会显示.", [NSValue valueWithRange:self.recorder.range]); + } + NSShadow *shadow = [NSShadow new]; + shadow.shadowOffset = shadowOffset; + shadow.shadowBlurRadius = shadowBlurRadius; + shadow.shadowColor = shadowColor; + self.recorder.shadow = shadow; + return self; + }; +} +/// 背景颜色 +- (SJAttributesRangeOperator * _Nonnull (^)(UIColor * _Nonnull))backgroundColor { + return ^ SJAttributesRangeOperator *(UIColor *color) { + if ( nil != self.recorder.shadow ) { + _errorLog(@"`shadow`会与`backgroundColor`冲突, 设置了`backgroundColor`后, `shadow`将不会显示.", [NSValue valueWithRange:self.recorder.range]); + } + self.recorder.backgroundColor = color; + return self; + }; +} +/// 下划线 +- (SJAttributesRangeOperator * _Nonnull (^)(NSUnderlineStyle, UIColor * _Nonnull))underLine { + return ^ SJAttributesRangeOperator *(NSUnderlineStyle style, UIColor *color) { + self.recorder.underLine = [SJUnderlineAttribute underLineWithStyle:style color:color]; + return self; + }; +} +/// 删除线 +- (SJAttributesRangeOperator * _Nonnull (^)(NSUnderlineStyle, UIColor * _Nonnull))strikethrough { + return ^ SJAttributesRangeOperator *(NSUnderlineStyle style, UIColor *color) { + self.recorder.strikethrough = [SJUnderlineAttribute underLineWithStyle:style color:color]; + return self; + }; +} +/// 边界`border` +- (SJAttributesRangeOperator * _Nonnull (^)(UIColor * _Nonnull, double))stroke { + return ^ SJAttributesRangeOperator *(UIColor * color, double stroke) { + self.recorder.stroke = [SJStrokeAttribute strokeWithValue:stroke color:color]; + return self; + }; +} +/// 倾斜(-1 ... 1) +- (SJAttributesRangeOperator * _Nonnull (^)(double))obliqueness { + return ^ SJAttributesRangeOperator *(double obliqueness) { + self.recorder.obliqueness = obliqueness; + return self; + }; +} +/// 字间隔 +- (SJAttributesRangeOperator * _Nonnull (^)(double))letterSpacing { + return ^ SJAttributesRangeOperator *(double letterSpacing) { + self.recorder.letterSpacing = letterSpacing; + return self; + }; +} +/// 上下偏移 +- (SJAttributesRangeOperator * _Nonnull (^)(double))offset { + return ^ SJAttributesRangeOperator *(double offset) { + self.recorder.offset = offset; + return self; + }; +} +/// 链接 +- (SJAttributesRangeOperator * _Nonnull (^)(void))isLink { + return ^ SJAttributesRangeOperator *() { + self.recorder.link = YES; + return self; + }; +} +/// 段落 style +- (SJAttributesRangeOperator * _Nonnull (^)(NSParagraphStyle * _Nonnull))paragraphStyle { + return ^ SJAttributesRangeOperator *(NSParagraphStyle *style) { + self.recorder.paragraphStyleM = style.mutableCopy; + return self; + }; +} +/// 行间隔 +- (SJAttributesRangeOperator * _Nonnull (^)(double))lineSpacing { + return ^ SJAttributesRangeOperator *(double lineSpacing) { + self.recorder.lineSpacing = lineSpacing; + return self; + }; +} +/// 段后间隔(\n) +- (SJAttributesRangeOperator * _Nonnull (^)(double))paragraphSpacing { + return ^ SJAttributesRangeOperator *(double paragraphSpacing) { + self.recorder.paragraphSpacing = paragraphSpacing; + return self; + }; +} +/// 段前间隔(\n) +- (SJAttributesRangeOperator * _Nonnull (^)(double))paragraphSpacingBefore { + return ^ SJAttributesRangeOperator *(double paragraphSpacingBefore) { + self.recorder.paragraphSpacingBefore = paragraphSpacingBefore; + return self; + }; +} +/// 首行头缩进 +- (SJAttributesRangeOperator * _Nonnull (^)(double))firstLineHeadIndent { + return ^ SJAttributesRangeOperator *(double firstLineHeadIndent) { + self.recorder.firstLineHeadIndent = firstLineHeadIndent; + return self; + }; +} +/// 左缩进 +- (SJAttributesRangeOperator * _Nonnull (^)(double))headIndent { + return ^ SJAttributesRangeOperator *(double headIndent) { + self.recorder.headIndent = headIndent; + return self; + }; +} +/// 右缩进(正值从左算起, 负值从右算起) +- (SJAttributesRangeOperator * _Nonnull (^)(double))tailIndent { + return ^ SJAttributesRangeOperator *(double tailIndent) { + self.recorder.tailIndent = tailIndent; + return self; + }; +} +/// 对齐方式 +- (SJAttributesRangeOperator * _Nonnull (^)(NSTextAlignment))alignment { + return ^ SJAttributesRangeOperator *(NSTextAlignment alignment) { + self.recorder.alignment = @(alignment); + return self; + }; +} +/// 截断模式 +- (SJAttributesRangeOperator * _Nonnull (^)(NSLineBreakMode))lineBreakMode { + return ^ SJAttributesRangeOperator *(NSLineBreakMode lineBreakMode) { + self.recorder.lineBreakMode = lineBreakMode; + return self; + }; +} +@end +NS_ASSUME_NONNULL_END diff --git a/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/Deprecated/SJAttributesRecorder.h b/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/Deprecated/SJAttributesRecorder.h new file mode 100644 index 0000000..31a8465 --- /dev/null +++ b/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/Deprecated/SJAttributesRecorder.h @@ -0,0 +1,60 @@ +// +// SJAttributesRecorder.h +// SJAttributesFactory +// +// Created by 畅三江 on 2018/1/27. +// Copyright © 2018年 畅三江. All rights reserved. +// + +#import + +// - deprecated (use `NSAttributedString+SJMake.h`). +// - 已弃用, 未来可能会删除 + +NS_ASSUME_NONNULL_BEGIN +#pragma mark - +@interface SJStrokeAttribute: NSObject +@property (nonatomic) double value; +@property (nonatomic, strong) UIColor *color; ++ (instancetype)strokeWithValue:(double)value color:(UIColor *)color; +- (instancetype)initWithValue:(double)value color:(UIColor *)color; +@end + + +#pragma mark - +@interface SJUnderlineAttribute: NSObject +@property (nonatomic) NSUnderlineStyle value; +@property (nonatomic, strong) UIColor *color; ++ (instancetype)underLineWithStyle:(NSUnderlineStyle)value color:(UIColor *)color; +- (instancetype)initWithStyle:(NSUnderlineStyle)value color:(UIColor *)color; +@end + +#pragma mark - +@interface SJAttributesRecorder: NSObject +@property (nonatomic) NSRange range; +@property (nonatomic, strong, nullable) UIFont *font; +@property (nonatomic, strong, nullable) UIColor *textColor; +@property (nonatomic) double expansion; +@property (nonatomic, strong, nullable) NSShadow *shadow; +@property (nonatomic, strong, nullable) UIColor *backgroundColor; +@property (nonatomic, strong, nullable) SJUnderlineAttribute *underLine; +@property (nonatomic, strong, nullable) SJUnderlineAttribute *strikethrough; +@property (nonatomic, strong, nullable) SJStrokeAttribute *stroke; +@property (nonatomic) double obliqueness; +@property (nonatomic) double letterSpacing; +@property (nonatomic) double offset; +@property (nonatomic) BOOL link; + +@property (nonatomic, strong, null_resettable) NSMutableParagraphStyle *paragraphStyleM; +@property (nonatomic) double lineSpacing; +@property (nonatomic) double paragraphSpacing; +@property (nonatomic) double paragraphSpacingBefore; +@property (nonatomic) double firstLineHeadIndent; +@property (nonatomic) double headIndent; +@property (nonatomic) double tailIndent; +@property (nonatomic, strong, nullable) NSNumber *alignment; +@property (nonatomic) NSLineBreakMode lineBreakMode; + +@property (nonatomic, copy, nullable) void(^propertyDidChangeExeBlock)(SJAttributesRecorder *recorder); +@end +NS_ASSUME_NONNULL_END diff --git a/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/Deprecated/SJAttributesRecorder.m b/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/Deprecated/SJAttributesRecorder.m new file mode 100644 index 0000000..ea2b0d2 --- /dev/null +++ b/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/Deprecated/SJAttributesRecorder.m @@ -0,0 +1,185 @@ +// +// SJAttributesRecorder.m +// SJAttributesFactory +// +// Created by 畅三江 on 2018/1/27. +// Copyright © 2018年 畅三江. All rights reserved. +// + +#import "SJAttributesRecorder.h" +#import + +NS_ASSUME_NONNULL_BEGIN +#define __Set_Property(__value__) \ +if ( __value__ == _##__value__ ) \ + return; \ + \ +_##__value__ = __value__; \ +if ( _propertyDidChangeExeBlock ) \ + _propertyDidChangeExeBlock(self); + +#define __Set_Paragraph_Property(__value__) \ +if ( __value__ == self.paragraphStyleM.__value__ ) \ + return; \ +\ +self.paragraphStyleM.__value__ = __value__; \ +if ( _propertyDidChangeExeBlock ) \ + _propertyDidChangeExeBlock(self); + +@implementation SJStrokeAttribute ++ (instancetype)strokeWithValue:(double)value color:(UIColor *)color { + return [[self alloc] initWithValue:value color:color]; +} +- (instancetype)initWithValue:(double)value color:(UIColor *)color { + self = [super init]; + if ( !self ) return nil; + _value = value; + _color = color; + return self; +} + +- (id)mutableCopyWithZone:(NSZone *_Nullable)zone { + return [SJStrokeAttribute strokeWithValue:_value color:_color]; +} +@end + +#pragma mark - +@implementation SJUnderlineAttribute ++ (instancetype)underLineWithStyle:(NSUnderlineStyle)value color:(UIColor *)color { + return [[self alloc] initWithStyle:value color:color]; +} +- (instancetype)initWithStyle:(NSUnderlineStyle)value color:(UIColor *)color { + self = [super init]; + if ( !self ) return nil; + _value = value; + _color = color; + return self; +} + +- (id)mutableCopyWithZone:(NSZone *_Nullable)zone { + return [SJUnderlineAttribute underLineWithStyle:_value color:_color]; +} +@end + +#pragma mark - +@implementation SJAttributesRecorder +- (void)setFont:(UIFont *_Nullable)font { + __Set_Property(font); +} + +- (void)setTextColor:(UIColor *_Nullable)textColor { + __Set_Property(textColor); +} + +- (void)setExpansion:(double)expansion { + __Set_Property(expansion); +} + +- (void)setShadow:(NSShadow *_Nullable)shadow { + __Set_Property(shadow); +} + +- (void)setBackgroundColor:(UIColor *_Nullable)backgroundColor { + __Set_Property(backgroundColor); +} + +- (void)setUnderLine:(SJUnderlineAttribute *_Nullable)underLine { + __Set_Property(underLine); +} + +- (void)setStrikethrough:(SJUnderlineAttribute *_Nullable)strikethrough { + __Set_Property(strikethrough); +} + +- (void)setStroke:(SJStrokeAttribute *_Nullable)stroke { + __Set_Property(stroke); +} + +- (void)setObliqueness:(double)obliqueness { + __Set_Property(obliqueness); +} + +- (void)setLetterSpacing:(double)letterSpacing { + __Set_Property(letterSpacing); +} + +- (void)setOffset:(double)offset { + __Set_Property(offset); +} + +- (void)setLink:(BOOL)link { + __Set_Property(link); +} + +@synthesize paragraphStyleM = _paragraphStyleM; +- (void)setParagraphStyleM:(NSMutableParagraphStyle *_Nullable)paragraphStyleM { + if ( [paragraphStyleM isMemberOfClass:[NSParagraphStyle class]] ) paragraphStyleM = paragraphStyleM.mutableCopy; + __Set_Property(paragraphStyleM); +} +- (NSMutableParagraphStyle *)paragraphStyleM { + if ( _paragraphStyleM ) return _paragraphStyleM; + _paragraphStyleM = [NSParagraphStyle defaultParagraphStyle].mutableCopy; + return _paragraphStyleM; +} +- (void)setLineSpacing:(double)lineSpacing { + __Set_Paragraph_Property(lineSpacing); +} +- (double)lineSpacing {return self.paragraphStyleM.lineSpacing;} + +- (void)setParagraphSpacing:(double)paragraphSpacing { + __Set_Paragraph_Property(paragraphSpacing); +} +- (double)paragraphSpacing {return self.paragraphStyleM.paragraphSpacing;} + +- (void)setParagraphSpacingBefore:(double)paragraphSpacingBefore { + __Set_Paragraph_Property(paragraphSpacingBefore); +} +- (double)paragraphSpacingBefore {return self.paragraphStyleM.paragraphSpacingBefore;} + +- (void)setFirstLineHeadIndent:(double)firstLineHeadIndent { + __Set_Paragraph_Property(firstLineHeadIndent); +} +- (double)firstLineHeadIndent {return self.paragraphStyleM.firstLineHeadIndent;} + +- (void)setHeadIndent:(double)headIndent { + __Set_Paragraph_Property(headIndent); +} +- (double)headIndent {return self.paragraphStyleM.headIndent;} + +- (void)setTailIndent:(double)tailIndent { + __Set_Paragraph_Property(tailIndent); +} +- (double)tailIndent {return self.paragraphStyleM.tailIndent;} + +@synthesize alignment = _alignment; +- (void)setAlignment:(NSNumber *_Nullable)ali { + _alignment = ali; + NSTextAlignment alignment = [ali integerValue]; + __Set_Paragraph_Property(alignment); +} + +- (void)setLineBreakMode:(NSLineBreakMode)lineBreakMode { + __Set_Paragraph_Property(lineBreakMode); +} +- (NSLineBreakMode)lineBreakMode {return self.paragraphStyleM.lineBreakMode;} + +- (id)mutableCopyWithZone:(NSZone *_Nullable)zone { + SJAttributesRecorder *obj = [SJAttributesRecorder new]; + obj.range = _range; + obj.font = _font; + obj.textColor = _textColor; + obj.expansion = _expansion; + obj.shadow = _shadow; + obj.backgroundColor = _backgroundColor; + obj.underLine = _underLine; + obj.strikethrough = _strikethrough; + obj.stroke = _stroke; + obj.obliqueness = _obliqueness; + obj.letterSpacing = _letterSpacing; + obj.offset = _offset; + obj.link = _link; + obj.paragraphStyleM = _paragraphStyleM; + return obj; +} +@end +NS_ASSUME_NONNULL_END diff --git a/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/NSAttributedString+SJMake.h b/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/NSAttributedString+SJMake.h new file mode 100644 index 0000000..76c96dd --- /dev/null +++ b/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/NSAttributedString+SJMake.h @@ -0,0 +1,39 @@ +// +// NSAttributedString+SJMake.h +// AttributesFactory +// +// Created by 畅三江 on 2019/4/12. +// Copyright © 2019 SanJiang. All rights reserved. +// +// Project: https://github.com/changsanjiang/SJAttributesFactory +// Email: changsanjiang@gmail.com +// + +#import +#import "SJUIKitAttributesDefines.h" + +NS_ASSUME_NONNULL_BEGIN +@interface NSAttributedString (SJMake) +/** + * - make attributed string: + * + * \code + * NSAttributedString *text = [NSAttributedString sj_UIKitText:^(id _Nonnull make) { + * make.append(@"String 1").font([UIFont boldSystemFontOfSize:14]); + * }]; + * + * // It's equivalent to below code. + * + * NSDictionary *attributes = @{NSFontAttributeName:[UIFont boldSystemFontOfSize:20]}; + * NSAttributedString *text1 = [[NSAttributedString alloc] initWithString:@"String 1" attributes:attributes]; + * + * \endcode + */ ++ (instancetype)sj_UIKitText:(void(^NS_NOESCAPE)(id make))block; + +- (CGSize)sj_textSize; +- (CGSize)sj_textSizeForRange:(NSRange)range; +- (CGSize)sj_textSizeForPreferredMaxLayoutWidth:(CGFloat)width; +- (CGSize)sj_textSizeForPreferredMaxLayoutHeight:(CGFloat)height; +@end +NS_ASSUME_NONNULL_END diff --git a/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/NSAttributedString+SJMake.m b/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/NSAttributedString+SJMake.m new file mode 100644 index 0000000..b6e3782 --- /dev/null +++ b/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/NSAttributedString+SJMake.m @@ -0,0 +1,45 @@ +// +// NSAttributedString+SJMake.m +// AttributesFactory +// +// Created by 畅三江 on 2019/4/12. +// Copyright © 2019 SanJiang. All rights reserved. +// + +#import "NSAttributedString+SJMake.h" +#import "SJUIKitTextMaker.h" + +NS_ASSUME_NONNULL_BEGIN +@implementation NSAttributedString (SJMake) ++ (instancetype)sj_UIKitText:(void(^NS_NOESCAPE)(id make))block { + SJUIKitTextMaker *maker = [SJUIKitTextMaker new]; + block(maker); + return maker.install; +} + +- (CGSize)sj_textSize { + return [self sj_textSizeForPreferredMaxLayoutWidth:CGFLOAT_MAX]; +} +- (CGSize)sj_textSizeForRange:(NSRange)range { + if ( range.length < 1 || range.length > self.length ) + return CGSizeZero; + return sj_textSize([self attributedSubstringFromRange:range], CGFLOAT_MAX, CGFLOAT_MAX); +} +- (CGSize)sj_textSizeForPreferredMaxLayoutWidth:(CGFloat)width { + return sj_textSize(self, width, CGFLOAT_MAX); +} +- (CGSize)sj_textSizeForPreferredMaxLayoutHeight:(CGFloat)height { + return sj_textSize(self, CGFLOAT_MAX, height); +} + +static CGSize +sj_textSize(NSAttributedString *attrStr, CGFloat width, CGFloat height) { + if ( attrStr.length < 1 ) + return CGSizeZero; + CGRect bounds = [attrStr boundingRectWithSize:CGSizeMake(width, height) options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading context:nil]; + bounds.size.width = ceil(bounds.size.width); + bounds.size.height = ceil(bounds.size.height); + return bounds.size; +} +@end +NS_ASSUME_NONNULL_END diff --git a/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/SJAttributesFactory.h b/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/SJAttributesFactory.h new file mode 100644 index 0000000..2805ab7 --- /dev/null +++ b/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/SJAttributesFactory.h @@ -0,0 +1,45 @@ +// +// SJAttributesFactory.h +// SJAttributesFactory +// +// Created by 畅三江 on 2018/12/10. +// Copyright © 2018 畅三江. All rights reserved. +// + +#ifndef SJAttributesFactory_h +#define SJAttributesFactory_h + +#import "NSAttributedString+SJMake.h" + + +// - deprecated (use `NSAttributedString+SJMake.h`). +// - deprecated (use `NSAttributedString+SJMake.h`). +// - 已弃用, 未来可能会删除 +#import "SJAttributeWorker.h" + +/*! + * - deprecated (use `NSAttributedString+SJMake.h`). + * + * - make attributed string: + + * NSAttributedString *attrStr = sj_makeAttributesString(^(SJAttributeWorker * _Nonnull make) { + + * // set font , text color. + * make.font([UIFont boldSystemFontOfSize:14]).textColor([UIColor blackColor]); + + * make.append(@"@迷你世界联机 :@江叔 用小淘气耍赖野人#迷你世界#"); + + * make.regexp(@"[@][^@]+\\s", ^(SJAttributesRangeOperator * _Nonnull matched) { + * matched.textColor([UIColor purpleColor]); + * // some code + * }); + + * make.regexp(@"[#][^#]+#", ^(SJAttributesRangeOperator * _Nonnull matched) { + * matched.textColor([UIColor orangeColor]); + * // some code + * }); + * }); + **/ +extern NSMutableAttributedString *sj_makeAttributesString(void(^block)(SJAttributeWorker *make)); + +#endif /* SJAttributesFactory_h */ diff --git a/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUIKitAttributesDefines.h b/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUIKitAttributesDefines.h new file mode 100644 index 0000000..acbc7dd --- /dev/null +++ b/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUIKitAttributesDefines.h @@ -0,0 +1,239 @@ +// +// SJUIKitAttributesDefines.h +// AttributesFactory +// +// Created by 畅三江 on 2019/4/12. +// Copyright © 2019 SanJiang. All rights reserved. +// + +#ifndef SJUIKitAttributesDefines_h +#define SJUIKitAttributesDefines_h +#import +@protocol SJUIKitTextMakerProtocol, + SJUTAttributesProtocol, + SJUTImageAttributesProtocol, + SJUTRegexHandlerProtocol, + SJUTRangeHandlerProtocol, + SJUTStroke, + SJUTDecoration, + SJUTImageAttachment; + +NS_ASSUME_NONNULL_BEGIN +@protocol SJUIKitTextMakerProtocol +/** + * - Append a `string` to the text. + * + * \code + * NSAttributedString *text = [NSAttributedString sj_UIKitText:^(id _Nonnull make) { + * make.append(@"String 1").font([UIFont boldSystemFontOfSize:14]); + * }]; + * + * // It's equivalent to below code. + * + * NSDictionary *attributes = @{NSFontAttributeName:[UIFont boldSystemFontOfSize:20]}; + * NSAttributedString *text1 = [[NSAttributedString alloc] initWithString:@"String 1" attributes:attributes]; + * + * \endcode + */ +@property (nonatomic, copy, readonly) id(^append)(NSString *str); + +typedef void(^SJUTAppendImageHandler)(id make); +/** + * - Append an `image attachment` to the text. + * + * \code + * NSAttributedString *text = [NSAttributedString sj_UIKitText:^(id _Nonnull make) { + * make.appendImage(^(id _Nonnull make) { + * make.image = [UIImage imageNamed:@"image"]; + * make.bounds = CGRectMake(0, 0, 50, 50); + * }); + * }]; + * + * // It's equivalent to below code. + * + * NSTextAttachment *attachment = [[NSTextAttachment alloc] init]; + * attachment.image = [UIImage imageNamed:@"image"]; + * attachment.bounds = CGRectMake(0, 0, 50, 50); + * NSAttributedString *text1 = [NSAttributedString attributedStringWithAttachment:attachment]; + * + * \endcode + */ +@property (nonatomic, copy, readonly) id(^appendImage)(NS_NOESCAPE SJUTAppendImageHandler block); + +/** + * - Append a `subtext` to the text. + * + * \code + * NSAttributedString *subtext = _label.attributedText; + * + * NSAttributedString *text = [NSAttributedString sj_UIKitText:^(id _Nonnull make) { + * make.appendText(subtext); + * }]; + * + * \endcode + */ +@property (nonatomic, copy, readonly) id(^appendText)(NSAttributedString *subtext); + +/** + * - Update the attributes for the specified range of `text`. + * + * \code + * NSAttributedString *text = [NSAttributedString sj_UIKitText:^(id _Nonnull make) { + * make.append(@"String 1"); + * make.update(NSMakeRange(0, 1)).font([UIFont boldSystemFontOfSize:20]); + * }]; + * \endcode + */ +@property (nonatomic, copy, readonly) id(^update)(NSRange range); + +/** + * - Use regular to process `text`. + * + * \code + * NSAttributedString *text1 = [NSAttributedString sj_UIKitText:^(id _Nonnull make) { + * make.append(@"123 123 4 123 123"); + * // Replace `123` with `oOo`. + * make.regex(@"123").replaceWithString(@"oOo").font([UIFont boldSystemFontOfSize:20]); + * }]; + * + * NSAttributedString *text2 = [NSAttributedString sj_UIKitText:^(id _Nonnull make) { + * make.append(@"123 123 4 123 123"); + * // Replace `123` with `oOo`. + * make.regex(@"123").replaceWithText(^(id _Nonnull make) { + * make.append(@"oOo").font([UIFont boldSystemFontOfSize:20]); + * }); + * }]; + * + * NSAttributedString *text3 = [NSAttributedString sj_UIKitText:^(id _Nonnull make) { + * make.append(@"123 123 4 123 123"); + * // Update the attributes of the matched text. + * make.regex(@"123").update(^(id _Nonnull make) { + * make.font([UIFont boldSystemFontOfSize:20]).textColor([UIColor redColor]); + * }); + * }]; + * \endcode + */ +@property (nonatomic, copy, readonly) id(^regex)(NSString *regularExpression); + +/** + * - Edit the subtext for the specified range of `text`. + * + * \code + * NSAttributedString *text = [NSAttributedString sj_UIKitText:^(id _Nonnull make) { + * make.append(@"123 123 M 123 123").font([UIFont boldSystemFontOfSize:20]); + * // Update the attributes for the specified range of `text`. + * make.range(NSMakeRange(0, 1)).update(^(id _Nonnull make) { + * make.font([UIFont boldSystemFontOfSize:20]).textColor([UIColor orangeColor]); + * }); + * + * // Replace the subtext for the specified range of `text`. + * make.range(NSMakeRange(1, 1)).replaceWithString(@"O").textColor([UIColor purpleColor]); + * + * // Replace the subtext for the specified range of `text`. + * make.range(NSMakeRange(2, 1)).replaceWithText(^(id _Nonnull make) { + * make.append(@"S").font([UIFont boldSystemFontOfSize:24]).textColor([UIColor greenColor]); + * }); + * }]; + * \endcode + */ +@property (nonatomic, copy, readonly) id(^range)(NSRange range); +@end + +typedef id_Nonnull(^SJUTFontAttribute)(UIFont *font); +typedef id_Nonnull(^SJUTColorAttribute)(UIColor *color); +typedef id_Nonnull(^SJUTKernAttribute)(CGFloat kern); +typedef id_Nonnull(^SJUTShadowAttribute)(void(^)(NSShadow *make)); +typedef id_Nonnull(^SJUTStrokeAttribute)(void(^block)(id make)); +typedef id_Nonnull(^SJUTDecorationAttribute)(void(^)(id make)); +typedef id_Nonnull(^SJUTBaseLineOffsetAttribute)(CGFloat offset); + +typedef id_Nonnull(^SJUTLineSpacingAttribute)(CGFloat lineSpacing); +typedef id_Nonnull(^SJUTParagraphSpacingAttribute)(CGFloat paragraphSpacing); +typedef id_Nonnull(^SJUTAlignmentAttribute)(NSTextAlignment alignment); +typedef id_Nonnull(^SJUTFirstLineHeadIndentAttribute)(CGFloat firstLineHeadIndent); +typedef id_Nonnull(^SJUTHeadIndentAttribute)(CGFloat headIndent); +typedef id_Nonnull(^SJUTTailIndentAttribute)(CGFloat tailIndent); +typedef id_Nonnull(^SJUTLineBreakModeAttribute)(NSLineBreakMode lineBreakMode); +typedef id_Nonnull(^SJUTMinimumLineHeightAttribute)(CGFloat minimumLineHeight); +typedef id_Nonnull(^SJUTMaximumLineHeightAttribute)(CGFloat maximumLineHeight); +typedef id_Nonnull(^SJUTBaseWritingDirectionAttribute)(NSWritingDirection baseWritingDirection); +typedef id_Nonnull(^SJUTLineHeightMultipleAttribute)(CGFloat lineHeightMultiple); +typedef id_Nonnull(^SJUTParagraphSpacingBeforeAttribute)(CGFloat paragraphSpacingBefore); +typedef id_Nonnull(^SJUTHyphenationFactorAttribute)(float hyphenationFactor); +typedef id_Nonnull(^SJUTTabStopsAttribute)(NSArray *tabStops); +typedef id_Nonnull(^SJUTDefaultTabIntervalAttribute)(CGFloat defaultTabInterval); +typedef id_Nonnull(^SJUTAllowsDefaultTighteningForTruncationAttribute)(BOOL allowsDefaultTighteningForTruncation) API_AVAILABLE(ios(9.0)); +typedef id_Nonnull(^SJUTLineBreakStrategyAttribute)(NSLineBreakStrategy lineBreakStrategy) API_AVAILABLE(ios(9.0)); + +typedef id_Nonnull(^SJUTSetAttribute)(id _Nullable value, NSString *forKey); + +@protocol SJUTAttributesProtocol +// text attributes +@property (nonatomic, copy, readonly) SJUTFontAttribute font; +@property (nonatomic, copy, readonly) SJUTColorAttribute textColor; +@property (nonatomic, copy, readonly) SJUTColorAttribute backgroundColor; +@property (nonatomic, copy, readonly) SJUTKernAttribute kern; +@property (nonatomic, copy, readonly) SJUTShadowAttribute shadow; +@property (nonatomic, copy, readonly) SJUTStrokeAttribute stroke; +@property (nonatomic, copy, readonly) SJUTDecorationAttribute underLine; +@property (nonatomic, copy, readonly) SJUTDecorationAttribute strikethrough; +@property (nonatomic, copy, readonly) SJUTBaseLineOffsetAttribute baseLineOffset; + +// paragraph attributes +@property (nonatomic, copy, readonly) SJUTLineSpacingAttribute lineSpacing; +@property (nonatomic, copy, readonly) SJUTParagraphSpacingAttribute paragraphSpacing; +@property (nonatomic, copy, readonly) SJUTAlignmentAttribute alignment; +@property (nonatomic, copy, readonly) SJUTFirstLineHeadIndentAttribute firstLineHeadIndent; +@property (nonatomic, copy, readonly) SJUTHeadIndentAttribute headIndent; +@property (nonatomic, copy, readonly) SJUTTailIndentAttribute tailIndent; +@property (nonatomic, copy, readonly) SJUTLineBreakModeAttribute lineBreakMode; +@property (nonatomic, copy, readonly) SJUTMinimumLineHeightAttribute minimumLineHeight; +@property (nonatomic, copy, readonly) SJUTMaximumLineHeightAttribute maximumLineHeight; +@property (nonatomic, copy, readonly) SJUTBaseWritingDirectionAttribute baseWritingDirection; +@property (nonatomic, copy, readonly) SJUTLineHeightMultipleAttribute lineHeightMultiple; +@property (nonatomic, copy, readonly) SJUTParagraphSpacingBeforeAttribute paragraphSpacingBefore; +@property (nonatomic, copy, readonly) SJUTHyphenationFactorAttribute hyphenationFactor; +@property (nonatomic, copy, readonly) SJUTTabStopsAttribute tabStops; +@property (nonatomic, copy, readonly) SJUTDefaultTabIntervalAttribute defaultTabInterval; +@property (nonatomic, copy, readonly) SJUTAllowsDefaultTighteningForTruncationAttribute allowsDefaultTighteningForTruncation API_AVAILABLE(ios(9.0)); +@property (nonatomic, copy, readonly) SJUTLineBreakStrategyAttribute lineBreakStrategy API_AVAILABLE(ios(9.0)); + +// custom attributes +@property (nonatomic, copy, readonly) SJUTSetAttribute set; // 添加或删除自定义的attribute +@end + +@protocol SJUTRangeHandlerProtocol +@property (nonatomic, copy, readonly) void(^update)(void(^)(id make)); +@property (nonatomic, copy, readonly) void(^replaceWithText)(void(^)(id make)); +@property (nonatomic, copy, readonly) id(^replaceWithString)(NSString *string); +@end + +@protocol SJUTRegexHandlerProtocol +@property (nonatomic, copy, readonly) void(^handler)(void(^)(NSMutableAttributedString *attrStr, NSTextCheckingResult *result)); + +@property (nonatomic, copy, readonly) id(^regularExpressionOptions)(NSRegularExpressionOptions ops); +@property (nonatomic, copy, readonly) id(^matchingOptions)(NSMatchingOptions ops); +@end + +@protocol SJUTStroke +@property (nonatomic, strong, nullable) UIColor *color; +@property (nonatomic) float width; +@end + +@protocol SJUTDecoration +@property (nonatomic, strong, nullable) UIColor *color; +@property (nonatomic) NSUnderlineStyle style; +@end + +typedef enum : NSUInteger { + SJUTVerticalAlignmentDefault = 0, + SJUTVerticalAlignmentCenter = 1, +} SJUTVerticalAlignment; + +@protocol SJUTImageAttachment +@property (nonatomic, strong, nullable) UIImage *image; +@property (nonatomic) SJUTVerticalAlignment alignment; ///< Text为统一的字体时生效 +@property (nonatomic) CGRect bounds; +@end +NS_ASSUME_NONNULL_END +#endif /* SJUIKitAttributesDefines_h */ diff --git a/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUIKitTextMaker.h b/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUIKitTextMaker.h new file mode 100644 index 0000000..57e86f5 --- /dev/null +++ b/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUIKitTextMaker.h @@ -0,0 +1,15 @@ +// +// SJUIKitTextMaker.h +// AttributesFactory +// +// Created by 畅三江 on 2019/4/12. +// Copyright © 2019 SanJiang. All rights reserved. +// + +#import "SJUTAttributes.h" + +NS_ASSUME_NONNULL_BEGIN +@interface SJUIKitTextMaker : SJUTAttributes +- (NSMutableAttributedString *)install; +@end +NS_ASSUME_NONNULL_END diff --git a/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUIKitTextMaker.m b/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUIKitTextMaker.m new file mode 100644 index 0000000..d09354d --- /dev/null +++ b/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUIKitTextMaker.m @@ -0,0 +1,295 @@ +// +// SJUIKitTextMaker.m +// AttributesFactory +// +// Created by 畅三江 on 2019/4/12. +// Copyright © 2019 SanJiang. All rights reserved. +// + +#import "SJUIKitTextMaker.h" +#import +#import "SJUTRegexHandler.h" +#import "SJUTRangeHandler.h" +#import "SJUTUtils.h" + +NS_ASSUME_NONNULL_BEGIN +@interface NSMutableAttributedString (SJUTExtended) +- (void)addAttributesForRecorder:(SJUTRecorder *)recorder range:(NSRange)range; +@end + +@implementation NSMutableAttributedString (SJUTExtended) +- (void)addAttributesForRecorder:(SJUTRecorder *)recorder range:(NSRange)subrange { + // text attributes + NSDictionary *textAttributes = recorder.textAttributes; + if ( textAttributes.count != 0 ) [self addAttributes:textAttributes range:subrange]; + + // paragraph attributes + NSRange styleRange = NSMakeRange(0, 0); + NSParagraphStyle *style = subrange.location < self.length ? [self attribute:NSParagraphStyleAttributeName atIndex:subrange.location effectiveRange:&styleRange] : nil; + NSParagraphStyle *paragraphAttributes = [recorder paragraphAttributesForStyle:SJUTRangeContains(styleRange, subrange) ? style : nil]; + [self addAttributes:@{NSParagraphStyleAttributeName : paragraphAttributes} range:subrange]; + + // custom attributes + NSDictionary *customAttributes = recorder.customAttributes; + if ( customAttributes.count != 0 ) [self addAttributes:customAttributes range:subrange]; +} +@end + +@interface SJUIKitTextMaker () +@property (nonatomic, strong, readonly) NSMutableArray *uts; +@property (nonatomic, strong, readonly) NSMutableArray *updates; +@property (nonatomic, strong, readonly) NSMutableArray *regexs; +@property (nonatomic, strong, readonly) NSMutableArray *ranges; +@end + +@implementation SJUIKitTextMaker +@synthesize uts = _uts; +- (NSMutableArray *)uts { + if ( !_uts ) _uts = [NSMutableArray array]; + return _uts; +} +@synthesize updates = _updates; +- (NSMutableArray *)updates { + if ( !_updates ) _updates = [NSMutableArray array]; + return _updates; +} +@synthesize regexs = _regexs; +- (NSMutableArray *)regexs { + if ( !_regexs ) _regexs = [NSMutableArray array]; + return _regexs; +} +@synthesize ranges = _ranges; +- (NSMutableArray *)ranges { + if ( !_ranges ) _ranges = [NSMutableArray array]; + return _ranges; +} + +- (id _Nonnull (^)(NSString * _Nonnull))append { + return ^id(NSString *str) { + SJUTAttributes *ut = [SJUTAttributes new]; + ut.recorder->string = str; + [self.uts addObject:ut]; + return ut; + }; +} +- (id _Nonnull (^)(NSRange))update { + return ^id(NSRange range) { + SJUTAttributes *ut = [SJUTAttributes new]; + ut.recorder->range = range; + [self.updates addObject:ut]; + return ut; + }; +} +- (id _Nonnull (^)(void (^ _Nonnull)(id _Nonnull)))appendImage { + return ^id(void(^block)(id make)) { + SJUTAttributes *ut = [SJUTAttributes new]; + SJUTImageAttachment *attachment = [SJUTImageAttachment new]; + ut.recorder->attachment = attachment; + block(attachment); + [self.uts addObject:ut]; + return ut; + }; +} +- (id _Nonnull (^)(NSAttributedString * _Nonnull))appendText { + return ^id(NSAttributedString *attrStr) { + SJUTAttributes *ut = [SJUTAttributes new]; + ut.recorder->attrStr = [attrStr mutableCopy]; + [ut.recorder setValuesForAttributedString:attrStr]; + [self.uts addObject:ut]; + return ut; + }; +} +- (id _Nonnull (^)(NSString * _Nonnull))regex { + return ^id(NSString *regex) { + SJUTRegexHandler *handler = [[SJUTRegexHandler alloc] initWithRegex:regex]; + [self.regexs addObject:handler]; + return handler; + }; +} +- (id _Nonnull (^)(NSRange))range { + return ^id(NSRange range) { + SJUTRangeHandler *handler = [[SJUTRangeHandler alloc] initWithRange:range]; + [self.ranges addObject:handler]; + return handler; + }; +} +- (NSMutableAttributedString *)install { + // default values + SJUTRecorder *recorder = self.recorder; + if ( recorder->font == nil ) recorder->font = [UIFont systemFontOfSize:14]; + if ( recorder->textColor == nil ) recorder->textColor = [UIColor blackColor]; + + NSMutableAttributedString *result = [[NSMutableAttributedString alloc] init]; + [self _appendUTAttributesToResultIfNeeded:result]; + [self _executeUpdateHandlersIfNeeded:result]; + [self _executeRangeHandlersIfNeeded:result]; + [self _executeUpdateHandlersIfNeeded:result]; + [self _executeRegexHandlersIfNeeded:result]; + [self _executeUpdateHandlersIfNeeded:result]; + return result; +} + +- (void)_appendUTAttributesToResultIfNeeded:(NSMutableAttributedString *)result { + if ( _uts ) { + for ( SJUTAttributes *ut in _uts ) { + id _Nullable current = [self _convertToUIKitTextForUTAttributes:ut]; + if ( current != nil ) { + [result appendAttributedString:current]; + } + } + _uts = nil; + } +} + +- (NSMutableAttributedString *_Nullable)_convertToUIKitTextForUTAttributes:(SJUTAttributes *)attr { + NSMutableAttributedString *_Nullable current = nil; + SJUTRecorder *recorder = attr.recorder; + if ( recorder->string != nil ) { + current = [[NSMutableAttributedString alloc] initWithString:recorder->string]; + } + else if ( recorder->attrStr != nil ) { + current = recorder->attrStr; + } + else if ( recorder->attachment != nil ) { + SJUTVerticalAlignment alignment = recorder->attachment.alignment; + UIImage *image = recorder->attachment.image; + CGRect bounds = recorder->attachment.bounds; + NSTextAttachment *attachment = [[NSTextAttachment alloc] init]; + attachment.image = recorder->attachment.image; + attachment.bounds = [self _adjustVerticalOffsetOfImageAttachmentForBounds:bounds imageSize:image.size alignment:alignment commonFont:self.recorder->font]; + current = [NSAttributedString attributedStringWithAttachment:attachment].mutableCopy; + } + + if ( current != nil ) { + [recorder setValuesForCommonRecorder:self.recorder]; + [current addAttributesForRecorder:recorder range:SJUTGetTextRange(current)]; + } + return current; +} + +- (void)_executeRangeHandlersIfNeeded:(NSMutableAttributedString *)result { + if ( _ranges ) { + for ( SJUTRangeHandler *handler in _ranges ) { + SJUTRangeRecorder *recorder = handler.recorder; + if ( SJUTRangeContains(SJUTGetTextRange(result), recorder.range) ) { + if ( recorder.utOfReplaceWithString != nil ) { + [self _executeReplaceWithString:result ut:recorder.utOfReplaceWithString inRange:recorder.range]; + } + else if ( recorder.replaceWithText != nil ) { + [self _executeReplaceWithText:result handler:recorder.replaceWithText inRange:recorder.range]; + } + else if ( recorder.update != nil ) { + [self _appendUpdateHandlerToUpdates:recorder.update inRange:recorder.range]; + } + } + } + _ranges = nil; + } +} + +- (void)_executeRegexHandlersIfNeeded:(NSMutableAttributedString *)result { + if ( _regexs ) { + for ( SJUTRegexHandler *handler in _regexs ) { + NSString *string = result.string; + NSRange resultRange = NSMakeRange(0, result.length); + SJUTRegexRecorder *recorder = handler.recorder; + if ( recorder.regex.length < 1 ) + continue; + + NSRegularExpression *regular = [NSRegularExpression regularExpressionWithPattern:recorder.regex options:recorder.regularExpressionOptions error:nil]; + NSMutableArray *results = [NSMutableArray new]; + [regular enumerateMatchesInString:string options:recorder.matchingOptions range:resultRange usingBlock:^(NSTextCheckingResult * _Nullable result, NSMatchingFlags flags, BOOL * _Nonnull stop) { + if ( result ) [results addObject:result]; + }]; + + [results enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(NSTextCheckingResult * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + NSRange range = obj.range; + if ( recorder.update != nil ) { + [self _appendUpdateHandlerToUpdates:recorder.update inRange:range]; + } + else if ( recorder.utOfReplaceWithString != nil ) { + [self _executeReplaceWithString:result ut:recorder.utOfReplaceWithString inRange:range]; + } + else if ( recorder.replaceWithText != nil ) { + [self _executeReplaceWithText:result handler:recorder.replaceWithText inRange:range]; + } + else if ( recorder.handler != nil ) { + recorder.handler(result, obj); + } + }]; + } + _regexs = nil; + } +} + +- (void)_executeReplaceWithString:(NSMutableAttributedString *)result ut:(id)ut inRange:(NSRange)range { + if ( SJUTRangeContains(SJUTGetTextRange(result), range) ) { + SJUTAttributes *uta = (id)ut; + [self _setSubtextCommonValuesToRecorder:uta.recorder inRange:range result:result]; + id _Nullable subtext = [self _convertToUIKitTextForUTAttributes:uta]; + if ( subtext ) { + [result replaceCharactersInRange:range withAttributedString:subtext]; + } + } +} + +- (void)_executeReplaceWithText:(NSMutableAttributedString *)result handler:(void(^)(id maker))handler inRange:(NSRange)range { + if ( SJUTRangeContains(SJUTGetTextRange(result), range) ) { + SJUIKitTextMaker *maker = [SJUIKitTextMaker new]; + [maker.recorder setValuesForCommonRecorder:self.recorder]; + [self _setSubtextCommonValuesToRecorder:maker.recorder inRange:range result:result]; + handler(maker); + [result replaceCharactersInRange:range withAttributedString:maker.install]; + } +} + +- (void)_executeUpdateHandlersIfNeeded:(NSMutableAttributedString *)result { + if ( _updates ) { + NSRange resultRange = NSMakeRange(0, result.length); + for ( SJUTAttributes *ut in _updates ) { + SJUTRecorder *recorder = ut.recorder; + NSRange range = recorder->range; + if ( SJUTRangeContains(resultRange, range) ) { + [recorder setValuesForCommonRecorder:self.recorder]; + [result addAttributesForRecorder:recorder range:range]; + } + } + _updates = nil; + } +} + +- (void)_appendUpdateHandlerToUpdates:(void(^)(id))handler inRange:(NSRange)range { + SJUTAttributes *ut = [SJUTAttributes new]; + ut.recorder->range = range; + handler(ut); + [self.updates addObject:ut]; +} + +- (void)_setSubtextCommonValuesToRecorder:(SJUTRecorder *)recorder inRange:(NSRange)range result:(NSAttributedString *)result { + if ( SJUTRangeContains(SJUTGetTextRange(result), range) ) { + NSAttributedString *subtext = [result attributedSubstringFromRange:range]; + NSDictionary *dict = [subtext attributesAtIndex:0 effectiveRange:NULL]; + recorder->font = dict[NSFontAttributeName]; + recorder->textColor = dict[NSForegroundColorAttributeName]; + } +} + +- (CGRect)_adjustVerticalOffsetOfImageAttachmentForBounds:(CGRect)bounds imageSize:(CGSize)imageSize alignment:(SJUTVerticalAlignment)alignment commonFont:(UIFont *)font { + switch ( alignment ) { + case SJUTVerticalAlignmentCenter: { + if ( CGSizeEqualToSize(CGSizeZero, bounds.size) ) { + bounds.size = imageSize; + } + + CGFloat fontHeight = font.lineHeight; + CGFloat centerline = fontHeight * 0.5 - ABS(font.descender); + bounds.origin.y = centerline - imageSize.height * 0.5; + } + break; + case SJUTVerticalAlignmentDefault: { } + break; + } + return bounds; +} +@end +NS_ASSUME_NONNULL_END diff --git a/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUTAttributes.h b/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUTAttributes.h new file mode 100644 index 0000000..2e11bfa --- /dev/null +++ b/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUTAttributes.h @@ -0,0 +1,17 @@ +// +// SJUTAttributes.h +// AttributesFactory +// +// Created by 畅三江 on 2019/4/12. +// Copyright © 2019 SanJiang. All rights reserved. +// + +#import +#import "SJUIKitAttributesDefines.h" +#import "SJUTRecorder.h" + +NS_ASSUME_NONNULL_BEGIN +@interface SJUTAttributes : NSObject +@property (nonatomic, strong, readonly) SJUTRecorder *recorder; +@end +NS_ASSUME_NONNULL_END diff --git a/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUTAttributes.m b/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUTAttributes.m new file mode 100644 index 0000000..0e6fe4b --- /dev/null +++ b/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUTAttributes.m @@ -0,0 +1,200 @@ +// +// SJUTAttributes.m +// AttributesFactory +// +// Created by 畅三江 on 2019/4/12. +// Copyright © 2019 SanJiang. All rights reserved. +// + +#import "SJUTAttributes.h" + +NS_ASSUME_NONNULL_BEGIN +#define SJUT_BLOCK_SET_ATTRIBUTE_Obj(__type__, __attr__) \ + ^id(__type__ __attr__) { \ + self.recorder->__attr__ = __attr__; \ + return self; \ + } + +#define SJUT_BLOCK_SET_ATTRIBUTE_Obj_copy(__type__, __attr__) \ + ^id(__type__ __attr__) { \ + self.recorder->__attr__ = __attr__.copy; \ + return self; \ + } + + +#define SJUT_BLOCK_SET_ATTRIBUTE_NSNumber(__type__, __attr__) \ + ^id(__type__ __attr__) { \ + self.recorder->__attr__ = @(__attr__); \ + return self; \ + } + +#define SJUT_BLOCK_SET_ATTRIBUTE_CGFloat(__attr__) SJUT_BLOCK_SET_ATTRIBUTE_NSNumber(CGFloat, __attr__) + + +@implementation SJUTAttributes +@synthesize recorder = _recorder; +- (SJUTRecorder *)recorder { + if ( !_recorder ) { + _recorder = [[SJUTRecorder alloc] init]; + } + return _recorder; +} + +- (SJUTFontAttribute)font { + return SJUT_BLOCK_SET_ATTRIBUTE_Obj(UIFont *, font); +} + +- (SJUTColorAttribute)textColor { + return SJUT_BLOCK_SET_ATTRIBUTE_Obj(UIColor *, textColor); +} + +/// +/// Thanks @donggelaile +/// https://github.com/changsanjiang/SJAttributesFactory/issues/9 +/// +- (SJUTKernAttribute)kern { + return SJUT_BLOCK_SET_ATTRIBUTE_CGFloat(kern); +} + +- (SJUTShadowAttribute)shadow { + return ^id(void(^block)(NSShadow *make)) { + NSShadow *_Nullable shadow = self.recorder->shadow; + if ( !shadow ) { + self.recorder->shadow = shadow = [NSShadow new]; + } + block(shadow); + return self; + }; +} + +- (SJUTColorAttribute)backgroundColor { + return SJUT_BLOCK_SET_ATTRIBUTE_Obj(UIColor *, backgroundColor); +} + +- (SJUTStrokeAttribute)stroke { + return ^id(void(^block)(id stroke)) { + SJUTStroke *_Nullable stroke = self.recorder->stroke; + if ( !stroke ) { + self.recorder->stroke = stroke = [SJUTStroke new]; + } + block(stroke); + return self; + }; +} + +- (SJUTDecorationAttribute)underLine { + return ^id(void(^block)(id decoration)) { + SJUTDecoration *_Nullable decoration = self.recorder->underLine; + if ( !decoration ) { + self.recorder->underLine = decoration = [SJUTDecoration new]; + } + block(decoration); + return self; + }; +} + +- (SJUTDecorationAttribute)strikethrough { + return ^id(void(^block)(id decoration)) { + SJUTDecoration *_Nullable decoration = self.recorder->strikethrough; + if ( !decoration ) { + self.recorder->strikethrough = decoration = [SJUTDecoration new]; + } + block(decoration); + return self; + }; +} +//typedef id_Nonnull(^SJUTBaseLineOffsetAttribute)(double offset); +//#define SJUT_BLOCK_SET_ATTRIBUTE_CGFloat(__attr__) SJUT_BLOCK_SET_ATTRIBUTE_NSNumber(CGFloat, __attr__) +//#define SJUT_BLOCK_SET_ATTRIBUTE_NSNumber(__type__, __attr__) \ +// ^id(__type__ __attr__) { \ +// self.recorder->__attr__ = @(__attr__); \ +// return self; \ +// } + +- (SJUTBaseLineOffsetAttribute)baseLineOffset { + return SJUT_BLOCK_SET_ATTRIBUTE_CGFloat(baseLineOffset); +} + +#pragma mark - mark + + +- (SJUTLineSpacingAttribute)lineSpacing { + return SJUT_BLOCK_SET_ATTRIBUTE_CGFloat(lineSpacing); +} + +- (SJUTParagraphSpacingAttribute)paragraphSpacing { + return SJUT_BLOCK_SET_ATTRIBUTE_CGFloat(paragraphSpacing); +} + +- (SJUTAlignmentAttribute)alignment { + return SJUT_BLOCK_SET_ATTRIBUTE_NSNumber(NSTextAlignment, alignment); +} + +- (SJUTFirstLineHeadIndentAttribute)firstLineHeadIndent { + return SJUT_BLOCK_SET_ATTRIBUTE_CGFloat(firstLineHeadIndent); +} + +- (SJUTHeadIndentAttribute)headIndent { + return SJUT_BLOCK_SET_ATTRIBUTE_CGFloat(headIndent); +} + +- (SJUTTailIndentAttribute)tailIndent { + return SJUT_BLOCK_SET_ATTRIBUTE_CGFloat(tailIndent); +} + +- (SJUTLineBreakModeAttribute)lineBreakMode { + return SJUT_BLOCK_SET_ATTRIBUTE_NSNumber(NSLineBreakMode, lineBreakMode); +} + +- (SJUTMinimumLineHeightAttribute)minimumLineHeight { + return SJUT_BLOCK_SET_ATTRIBUTE_CGFloat(minimumLineHeight); +} + +- (SJUTMaximumLineHeightAttribute)maximumLineHeight { + return SJUT_BLOCK_SET_ATTRIBUTE_CGFloat(maximumLineHeight); +} + +- (SJUTBaseWritingDirectionAttribute)baseWritingDirection { + return SJUT_BLOCK_SET_ATTRIBUTE_NSNumber(NSWritingDirection, baseWritingDirection); +} + +- (SJUTLineHeightMultipleAttribute)lineHeightMultiple { + return SJUT_BLOCK_SET_ATTRIBUTE_CGFloat(lineHeightMultiple); +} + +- (SJUTParagraphSpacingBeforeAttribute)paragraphSpacingBefore { + return SJUT_BLOCK_SET_ATTRIBUTE_CGFloat(paragraphSpacingBefore); +} + +- (SJUTHyphenationFactorAttribute)hyphenationFactor { + return SJUT_BLOCK_SET_ATTRIBUTE_NSNumber(float, hyphenationFactor); +} + +- (SJUTTabStopsAttribute)tabStops { + return SJUT_BLOCK_SET_ATTRIBUTE_Obj_copy(NSArray *, tabStops); +} + +- (SJUTDefaultTabIntervalAttribute)defaultTabInterval { + return SJUT_BLOCK_SET_ATTRIBUTE_CGFloat(defaultTabInterval); +} + +- (SJUTAllowsDefaultTighteningForTruncationAttribute)allowsDefaultTighteningForTruncation API_AVAILABLE(ios(9.0)) { + return SJUT_BLOCK_SET_ATTRIBUTE_NSNumber(BOOL, allowsDefaultTighteningForTruncation); +} + +- (SJUTLineBreakStrategyAttribute)lineBreakStrategy API_AVAILABLE(ios(9.0)) { + return SJUT_BLOCK_SET_ATTRIBUTE_NSNumber(NSLineBreakStrategy, lineBreakStrategy); +} + +- (SJUTSetAttribute)set { + return ^id(id _Nullable value, NSString *forKey) { + [self.recorder setValue:value forAttributeKey:forKey]; + return self; + }; +} +@end +#undef SJUT_BLOCK_SET_ATTRIBUTE_CGFloat +#undef SJUT_BLOCK_SET_ATTRIBUTE_NSNumber +#undef SJUT_BLOCK_SET_ATTRIBUTE_Obj_copy +#undef SJUT_BLOCK_SET_ATTRIBUTE_Obj +NS_ASSUME_NONNULL_END diff --git a/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUTRangeHandler.h b/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUTRangeHandler.h new file mode 100644 index 0000000..7aa513e --- /dev/null +++ b/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUTRangeHandler.h @@ -0,0 +1,24 @@ +// +// SJUTRangeHandler.h +// AttributesFactory +// +// Created by 畅三江 on 2019/4/13. +// Copyright © 2019 SanJiang. All rights reserved. +// + +#import "SJUIKitAttributesDefines.h" +@class SJUTRangeRecorder; + +NS_ASSUME_NONNULL_BEGIN +@interface SJUTRangeHandler : NSObject +- (instancetype)initWithRange:(NSRange)range; +@property (nonatomic, strong, readonly) SJUTRangeRecorder *recorder; +@end + +@interface SJUTRangeRecorder : NSObject +@property (nonatomic) NSRange range; +@property (nonatomic, strong, nullable) id utOfReplaceWithString; +@property (nonatomic, copy, nullable) void(^replaceWithText)(id make); +@property (nonatomic, copy, nullable) void(^update)(id make); +@end +NS_ASSUME_NONNULL_END diff --git a/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUTRangeHandler.m b/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUTRangeHandler.m new file mode 100644 index 0000000..9d4df36 --- /dev/null +++ b/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUTRangeHandler.m @@ -0,0 +1,44 @@ +// +// SJUTRangeHandler.m +// AttributesFactory +// +// Created by 畅三江 on 2019/4/13. +// Copyright © 2019 SanJiang. All rights reserved. +// + +#import "SJUTRangeHandler.h" +#import "SJUTAttributes.h" + +NS_ASSUME_NONNULL_BEGIN +@implementation SJUTRangeHandler +- (instancetype)initWithRange:(NSRange)range { + self = [super init]; + if ( !self ) return nil; + _recorder = [[SJUTRangeRecorder alloc] init]; + _recorder.range = range; + return self; +} + +- (void (^)(void (^ _Nonnull)(id _Nonnull)))replaceWithText { + return ^(void(^block)(id make)) { + self.recorder.replaceWithText = block; + }; +} +- (id _Nonnull (^)(NSString * _Nonnull))replaceWithString { + return ^id(NSString *string) { + SJUTAttributes *attr = [SJUTAttributes new]; + attr.recorder->string = string; + self.recorder.utOfReplaceWithString = attr; + return attr; + }; +} +- (void (^)(void (^ _Nonnull)(id _Nonnull)))update { + return ^(void(^block)(id make)) { + self.recorder.update = block; + }; +} +@end + +@implementation SJUTRangeRecorder +@end +NS_ASSUME_NONNULL_END diff --git a/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUTRecorder.h b/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUTRecorder.h new file mode 100644 index 0000000..de1186e --- /dev/null +++ b/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUTRecorder.h @@ -0,0 +1,84 @@ +// +// SJUTRecorder.h +// AttributesFactory +// +// Created by 畅三江 on 2019/4/12. +// Copyright © 2019 SanJiang. All rights reserved. +// + +#import +#import "SJUIKitAttributesDefines.h" + +NS_ASSUME_NONNULL_BEGIN +@interface SJUTStroke : NSObject +@property (nonatomic, strong, nullable) UIColor *color; +@property (nonatomic) float width; +@end + +@interface SJUTDecoration : NSObject +@property (nonatomic, strong, nullable) UIColor *color; +@property (nonatomic) NSUnderlineStyle style; +@end + +@interface SJUTImageAttachment : NSObject +@property (nonatomic, strong, nullable) UIImage *image; +@property (nonatomic) CGRect bounds; +@property (nonatomic) SJUTVerticalAlignment alignment; +@end + +@interface SJUTReplace : NSObject +@property (nonatomic, strong, nullable) NSString *fromString; +@property (nonatomic, copy, nullable) void(^block)(id make); +@end + +@interface SJUTRecorder : NSObject { + @package + // text attributes + UIFont *_Nullable font; + UIColor *_Nullable textColor; + UIColor *_Nullable backgroundColor; + NSNumber *_Nullable kern; // CGFloat + NSShadow *_Nullable shadow; + SJUTStroke *_Nullable stroke; + SJUTDecoration *_Nullable underLine; + SJUTDecoration *_Nullable strikethrough; + NSNumber *_Nullable baseLineOffset; // CGFloat + + // paragraph attributes + NSNumber *_Nullable lineSpacing; // CGFloat + NSNumber *_Nullable paragraphSpacing; // CGFloat + NSNumber *_Nullable alignment; // NSTextAlignment + NSNumber *_Nullable firstLineHeadIndent; // CGFloat + NSNumber *_Nullable headIndent; // CGFloat + NSNumber *_Nullable tailIndent; // CGFloat + NSNumber *_Nullable lineBreakMode; // NSLineBreakMode + NSNumber *_Nullable minimumLineHeight; // CGFloat + NSNumber *_Nullable maximumLineHeight; // CGFloat + NSNumber *_Nullable baseWritingDirection; // NSWritingDirection + NSNumber *_Nullable lineHeightMultiple; // CGFloat + NSNumber *_Nullable paragraphSpacingBefore; // CGFloat + NSNumber *_Nullable hyphenationFactor; // float + NSArray *_Nullable tabStops; + NSNumber *_Nullable defaultTabInterval; // CGFloat + NSNumber *_Nullable allowsDefaultTighteningForTruncation API_AVAILABLE(ios(9.0)); // BOOL + NSNumber *_Nullable lineBreakStrategy API_AVAILABLE(ios(9.0)); // NSLineBreakStrategy + + // custom attributes + NSMutableDictionary *_Nullable _customAttributes; + + // - sources + NSString *_Nullable string; + NSRange range; + SJUTImageAttachment *_Nullable attachment; + NSMutableAttributedString *_Nullable attrStr; +} + +- (void)setValue:(nullable id)value forAttributeKey:(NSString *)key; +- (void)setValuesForAttributeKeysWithDictionary:(NSDictionary *)keyedValues; +- (NSDictionary *)textAttributes; +- (NSParagraphStyle *)paragraphAttributesForStyle:(nullable NSParagraphStyle *)style; +- (NSDictionary *)customAttributes; +- (void)setValuesForCommonRecorder:(SJUTRecorder *)common; +- (void)setValuesForAttributedString:(NSAttributedString *)attributedString; +@end +NS_ASSUME_NONNULL_END diff --git a/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUTRecorder.m b/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUTRecorder.m new file mode 100644 index 0000000..b5ec19c --- /dev/null +++ b/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUTRecorder.m @@ -0,0 +1,263 @@ +// +// SJUTRecorder.m +// AttributesFactory +// +// Created by 畅三江 on 2019/4/12. +// Copyright © 2019 SanJiang. All rights reserved. +// + +#import "SJUTRecorder.h" +#import +#import "SJUTUtils.h" + +NS_ASSUME_NONNULL_BEGIN +@implementation SJUTStroke +@end +@implementation SJUTDecoration +@end +@implementation SJUTImageAttachment +@end +@implementation SJUTReplace +@end + +@implementation SJUTRecorder + +static NSArray *SJUT_Keys; + ++ (void)initialize { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSMutableArray *m = @[ + NSFontAttributeName, + NSForegroundColorAttributeName, + NSBackgroundColorAttributeName, + NSKernAttributeName, + NSShadowAttributeName, + NSStrokeColorAttributeName, + NSStrokeWidthAttributeName, + NSUnderlineStyleAttributeName, + NSUnderlineColorAttributeName, + NSStrikethroughStyleAttributeName, + NSStrikethroughColorAttributeName, + NSBaselineOffsetAttributeName, + NSParagraphStyleAttributeName, + ].mutableCopy; + + if (@available(iOS 11.0, *)) { + [m addObject:(__bridge NSString *)kCTBaselineOffsetAttributeName]; + } + + SJUT_Keys = m.copy; + }); +} + + +- (void)setValue:(nullable id)value forAttributeKey:(NSString *)key { + if ( key == nil ) return; + if ( _customAttributes == nil ) { + _customAttributes = NSMutableDictionary.dictionary; + } + _customAttributes[key] = value; +} + +- (void)setValuesForAttributeKeysWithDictionary:(NSDictionary *)keyedValues { + if ( keyedValues.count == 0 ) return; + if ( _customAttributes == nil ) { + _customAttributes = NSMutableDictionary.dictionary; + } + [_customAttributes setValuesForKeysWithDictionary:keyedValues]; +} + +- (NSDictionary *)textAttributes { + NSMutableDictionary *attrs = [NSMutableDictionary dictionary]; + attrs[NSFontAttributeName] = font; + attrs[NSForegroundColorAttributeName] = textColor; + if ( backgroundColor != nil ) attrs[NSBackgroundColorAttributeName] = backgroundColor; + if ( kern != nil ) attrs[NSKernAttributeName] = kern; + if ( shadow != nil ) attrs[NSShadowAttributeName] = shadow; + if ( stroke != nil ) { + attrs[NSStrokeColorAttributeName] = stroke.color; + attrs[NSStrokeWidthAttributeName] = @(stroke.width); + } + if ( underLine != nil ) { + attrs[NSUnderlineStyleAttributeName] = @(underLine.style); + attrs[NSUnderlineColorAttributeName] = underLine.color; + } + if ( strikethrough != nil ) { + attrs[NSStrikethroughStyleAttributeName] = @(strikethrough.style); + attrs[NSStrikethroughColorAttributeName] = strikethrough.color; + } + if ( @available(iOS 14.0, *) ) { + attrs[NSBaselineOffsetAttributeName] = @(baseLineOffset.doubleValue); + attrs[(__bridge NSString *)kCTBaselineOffsetAttributeName] = attrs[NSBaselineOffsetAttributeName]; + } + else if ( baseLineOffset != nil ) { + attrs[NSBaselineOffsetAttributeName] = baseLineOffset; + if ( @available(iOS 11.0, *) ) { + attrs[(__bridge NSString *)kCTBaselineOffsetAttributeName] = attrs[NSBaselineOffsetAttributeName]; + } + } + return attrs; +} + +- (NSParagraphStyle *)paragraphAttributesForStyle:(nullable NSParagraphStyle *)style { +#define SJUT_SET_ATTRIBUTE_VALUE(__attr__, __value__) if ( __attr__ != nil ) m.__attr__ = __attr__.__value__; + NSMutableParagraphStyle *m = (style ?: [NSParagraphStyle defaultParagraphStyle]).mutableCopy; + SJUT_SET_ATTRIBUTE_VALUE(lineSpacing, doubleValue); + SJUT_SET_ATTRIBUTE_VALUE(paragraphSpacing, doubleValue); + SJUT_SET_ATTRIBUTE_VALUE(alignment, integerValue); + SJUT_SET_ATTRIBUTE_VALUE(firstLineHeadIndent, doubleValue); + SJUT_SET_ATTRIBUTE_VALUE(headIndent, doubleValue); + SJUT_SET_ATTRIBUTE_VALUE(tailIndent, doubleValue); + SJUT_SET_ATTRIBUTE_VALUE(lineBreakMode, integerValue); + SJUT_SET_ATTRIBUTE_VALUE(minimumLineHeight, doubleValue); + SJUT_SET_ATTRIBUTE_VALUE(maximumLineHeight, doubleValue); + SJUT_SET_ATTRIBUTE_VALUE(baseWritingDirection, integerValue); + SJUT_SET_ATTRIBUTE_VALUE(lineHeightMultiple, doubleValue); + SJUT_SET_ATTRIBUTE_VALUE(paragraphSpacingBefore, doubleValue); + SJUT_SET_ATTRIBUTE_VALUE(hyphenationFactor, floatValue); + SJUT_SET_ATTRIBUTE_VALUE(tabStops, copy); + SJUT_SET_ATTRIBUTE_VALUE(defaultTabInterval, doubleValue); + if ( @available(iOS 9.0, *) ) { + SJUT_SET_ATTRIBUTE_VALUE(allowsDefaultTighteningForTruncation, boolValue); + SJUT_SET_ATTRIBUTE_VALUE(lineBreakStrategy, integerValue); + } + return m.copy; +#undef SJUT_SET_ATTRIBUTE_VALUE +} + +- (NSDictionary *)customAttributes { + return _customAttributes.copy; +} + +- (void)setValuesForCommonRecorder:(SJUTRecorder *)recorder { +#define SJUT_SET_ATTRIBUTE_COMMON_VALUE(__attr__) if ( __attr__ == nil ) __attr__ = recorder->__attr__; + SJUT_SET_ATTRIBUTE_COMMON_VALUE(_customAttributes); + + // text attributes + SJUT_SET_ATTRIBUTE_COMMON_VALUE(font); + SJUT_SET_ATTRIBUTE_COMMON_VALUE(textColor); + SJUT_SET_ATTRIBUTE_COMMON_VALUE(backgroundColor); + SJUT_SET_ATTRIBUTE_COMMON_VALUE(kern); + SJUT_SET_ATTRIBUTE_COMMON_VALUE(shadow); + SJUT_SET_ATTRIBUTE_COMMON_VALUE(stroke); + SJUT_SET_ATTRIBUTE_COMMON_VALUE(underLine); + SJUT_SET_ATTRIBUTE_COMMON_VALUE(strikethrough); + SJUT_SET_ATTRIBUTE_COMMON_VALUE(baseLineOffset); + + // paragraph attributes + SJUT_SET_ATTRIBUTE_COMMON_VALUE(lineSpacing); + SJUT_SET_ATTRIBUTE_COMMON_VALUE(paragraphSpacing); + SJUT_SET_ATTRIBUTE_COMMON_VALUE(alignment); + SJUT_SET_ATTRIBUTE_COMMON_VALUE(firstLineHeadIndent); + SJUT_SET_ATTRIBUTE_COMMON_VALUE(headIndent); + SJUT_SET_ATTRIBUTE_COMMON_VALUE(tailIndent); + SJUT_SET_ATTRIBUTE_COMMON_VALUE(lineBreakMode); + SJUT_SET_ATTRIBUTE_COMMON_VALUE(minimumLineHeight); + SJUT_SET_ATTRIBUTE_COMMON_VALUE(maximumLineHeight); + SJUT_SET_ATTRIBUTE_COMMON_VALUE(baseWritingDirection); + SJUT_SET_ATTRIBUTE_COMMON_VALUE(lineHeightMultiple); + SJUT_SET_ATTRIBUTE_COMMON_VALUE(paragraphSpacingBefore); + SJUT_SET_ATTRIBUTE_COMMON_VALUE(hyphenationFactor); + SJUT_SET_ATTRIBUTE_COMMON_VALUE(tabStops); + SJUT_SET_ATTRIBUTE_COMMON_VALUE(defaultTabInterval); + if ( @available(iOS 9.0, *) ) { + SJUT_SET_ATTRIBUTE_COMMON_VALUE(allowsDefaultTighteningForTruncation); + SJUT_SET_ATTRIBUTE_COMMON_VALUE(lineBreakStrategy); + } + + // custom attributes + SJUT_SET_ATTRIBUTE_COMMON_VALUE(_customAttributes); +#undef SJUT_SET_ATTRIBUTE_COMMON_VALUE +} + +- (void)setValuesForAttributedString:(NSAttributedString *)attributedString { + NSMutableDictionary *attrs = [[self _getGlobalAttributesForAttributedString:attributedString] mutableCopy]; + if ( attrs.count != 0 ) { + + // text attributes + font = attrs[NSFontAttributeName]; + textColor = attrs[NSForegroundColorAttributeName]; + backgroundColor = attrs[NSBackgroundColorAttributeName]; + kern = attrs[NSKernAttributeName]; + shadow = attrs[NSShadowAttributeName]; + UIColor *strokeColor = attrs[NSStrokeColorAttributeName]; + NSNumber *strokeWidth = attrs[NSStrokeWidthAttributeName]; + if ( strokeColor != nil || strokeWidth != nil ) { + stroke = SJUTStroke.alloc.init; + stroke.color = strokeColor; + stroke.width = strokeWidth.floatValue; + } + + UIColor *underLineColor = attrs[NSUnderlineColorAttributeName]; + NSNumber *underLineStyle = attrs[NSUnderlineStyleAttributeName]; + if ( underLineColor != nil || underLineStyle != nil ) { + underLine = SJUTDecoration.alloc.init; + underLine.color = underLineColor; + underLine.style = underLineStyle.integerValue; + } + + UIColor *strikethroughColor = attrs[NSStrikethroughColorAttributeName]; + NSNumber *strikethroughStyle = attrs[NSStrikethroughStyleAttributeName]; + if ( strikethroughColor != nil || strikethroughStyle != nil ) { + strikethrough = SJUTDecoration.alloc.init; + strikethrough.color = strikethroughColor; + strikethrough.style = strikethroughStyle.integerValue; + } + baseLineOffset = attrs[NSBaselineOffsetAttributeName]; + + // paragraph attributes + NSParagraphStyle *paragraphAttributes = attrs[NSParagraphStyleAttributeName]; + if ( paragraphAttributes != nil ) { +#define SJUT_SET_PARAGRAPH_ATTRIBUTE_NSNumber(__attr__) if ( paragraphAttributes.__attr__ != 0 ) __attr__ = @(paragraphAttributes.__attr__); + SJUT_SET_PARAGRAPH_ATTRIBUTE_NSNumber(lineSpacing); + SJUT_SET_PARAGRAPH_ATTRIBUTE_NSNumber(paragraphSpacing); + SJUT_SET_PARAGRAPH_ATTRIBUTE_NSNumber(alignment); + SJUT_SET_PARAGRAPH_ATTRIBUTE_NSNumber(firstLineHeadIndent); + SJUT_SET_PARAGRAPH_ATTRIBUTE_NSNumber(headIndent); + SJUT_SET_PARAGRAPH_ATTRIBUTE_NSNumber(tailIndent); + SJUT_SET_PARAGRAPH_ATTRIBUTE_NSNumber(lineBreakMode); + SJUT_SET_PARAGRAPH_ATTRIBUTE_NSNumber(minimumLineHeight); + SJUT_SET_PARAGRAPH_ATTRIBUTE_NSNumber(maximumLineHeight); + SJUT_SET_PARAGRAPH_ATTRIBUTE_NSNumber(baseWritingDirection); + SJUT_SET_PARAGRAPH_ATTRIBUTE_NSNumber(lineHeightMultiple); + SJUT_SET_PARAGRAPH_ATTRIBUTE_NSNumber(paragraphSpacingBefore); + SJUT_SET_PARAGRAPH_ATTRIBUTE_NSNumber(hyphenationFactor); + tabStops = paragraphAttributes.tabStops; + SJUT_SET_PARAGRAPH_ATTRIBUTE_NSNumber(defaultTabInterval); + if ( @available(iOS 9.0, *) ) { + SJUT_SET_PARAGRAPH_ATTRIBUTE_NSNumber(allowsDefaultTighteningForTruncation); + SJUT_SET_PARAGRAPH_ATTRIBUTE_NSNumber(lineBreakStrategy); + } +#undef SJUT_SET_PARAGRAPH_ATTRIBUTE_NSNumber + } + + // custom attributes + [attrs removeObjectsForKeys:SJUT_Keys]; + [self setValuesForAttributeKeysWithDictionary:attrs]; + } +} + +- (nullable NSDictionary *)_getGlobalAttributesForAttributedString:(NSAttributedString *)attributedString { + NSRange textRange = SJUTGetTextRange(attributedString); + NSRange longestEffectiveRange; + NSDictionary *attrs = [attributedString attributesAtIndex:0 longestEffectiveRange:&longestEffectiveRange inRange:textRange]; + if ( SJUTRangeContains(longestEffectiveRange, textRange) ) { + return attrs; + } + + NSDictionary *next = nil; + while ( attrs.count != 0 && NSMaxRange(longestEffectiveRange) != NSMaxRange(textRange) ) { + next = [attributedString attributesAtIndex:NSMaxRange(longestEffectiveRange) effectiveRange:&longestEffectiveRange]; + + NSMutableDictionary *tmp = [attrs mutableCopy]; + [attrs enumerateKeysAndObjectsUsingBlock:^(NSAttributedStringKey _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { + if ( ![obj isEqual:next[key]] ) + tmp[key] = nil; + }]; + attrs = tmp.copy; + } + return attrs; +} +@end +NS_ASSUME_NONNULL_END diff --git a/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUTRegexHandler.h b/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUTRegexHandler.h new file mode 100644 index 0000000..acd7fef --- /dev/null +++ b/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUTRegexHandler.h @@ -0,0 +1,27 @@ +// +// SJUTRegexHandler.h +// AttributesFactory +// +// Created by 畅三江 on 2019/4/12. +// Copyright © 2019 SanJiang. All rights reserved. +// + +#import "SJUIKitAttributesDefines.h" +@class SJUTRegexRecorder; + +NS_ASSUME_NONNULL_BEGIN +@interface SJUTRegexHandler : NSObject +- (instancetype)initWithRegex:(NSString *)regex; +@property (nonatomic, strong, readonly) SJUTRegexRecorder *recorder; +@end + +@interface SJUTRegexRecorder : NSObject +@property (nonatomic) NSRegularExpressionOptions regularExpressionOptions; +@property (nonatomic) NSMatchingOptions matchingOptions; +@property (nonatomic, strong, nullable) id utOfReplaceWithString; +@property (nonatomic, copy, nullable) NSString *regex; +@property (nonatomic, copy, nullable) void(^replaceWithText)(id make); +@property (nonatomic, copy, nullable) void(^update)(id make); +@property (nonatomic, copy, nullable) void(^handler)(NSMutableAttributedString *attrStr, NSTextCheckingResult *result); +@end +NS_ASSUME_NONNULL_END diff --git a/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUTRegexHandler.m b/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUTRegexHandler.m new file mode 100644 index 0000000..97a4f8b --- /dev/null +++ b/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUTRegexHandler.m @@ -0,0 +1,67 @@ +// +// SJUTRegexHandler.m +// AttributesFactory +// +// Created by 畅三江 on 2019/4/12. +// Copyright © 2019 SanJiang. All rights reserved. +// + +#import "SJUTRegexHandler.h" +#import "SJUTAttributes.h" + +NS_ASSUME_NONNULL_BEGIN +@implementation SJUTRegexHandler +- (instancetype)initWithRegex:(NSString *)regex { + self = [super init]; + if ( !self ) return nil; + _recorder = [SJUTRegexRecorder new]; + _recorder.regex = regex; + return self; +} + +- (void (^)(void (^ _Nonnull)(NSMutableAttributedString *attrStr, NSTextCheckingResult * _Nonnull)))handler { + return ^(void(^block)(NSMutableAttributedString *attrStr, NSTextCheckingResult *result)) { + self.recorder.handler = block; + }; +} +- (void (^)(void (^ _Nonnull)(id _Nonnull)))replaceWithText { + return ^(void(^block)(id make)) { + self.recorder.replaceWithText = block; + }; +} +- (id _Nonnull (^)(NSString * _Nonnull))replaceWithString { + return ^id(NSString *string) { + SJUTAttributes *attr = [SJUTAttributes new]; + attr.recorder->string = string; + self.recorder.utOfReplaceWithString = attr; + return attr; + }; +} +- (void (^)(void (^ _Nonnull)(id _Nonnull)))update { + return ^(void(^block)(id make)) { + self.recorder.update = block; + }; +} +- (id _Nonnull (^)(NSMatchingOptions))matchingOptions { + return ^id(NSMatchingOptions ops) { + self.recorder.matchingOptions = ops; + return self; + }; +} +- (id _Nonnull (^)(NSRegularExpressionOptions))regularExpressionOptions { + return ^id(NSRegularExpressionOptions ops) { + self.recorder.regularExpressionOptions = ops; + return self; + }; +} +@end + +@implementation SJUTRegexRecorder +- (instancetype)init { + self = [super init]; + if ( !self ) return nil; + _matchingOptions = NSMatchingWithoutAnchoringBounds; + return self; +} +@end +NS_ASSUME_NONNULL_END diff --git a/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUTUtils.h b/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUTUtils.h new file mode 100644 index 0000000..d2801ed --- /dev/null +++ b/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUTUtils.h @@ -0,0 +1,18 @@ +// +// SJUTUtils.h +// LWZAudioModule-LWZAudioModule +// +// Created by BlueDancer on 2020/12/2. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +FOUNDATION_EXPORT BOOL +SJUTRangeContains(NSRange main, NSRange sub); + +FOUNDATION_EXPORT NSRange +SJUTGetTextRange(NSAttributedString *text); + +NS_ASSUME_NONNULL_END diff --git a/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUTUtils.m b/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUTUtils.m new file mode 100644 index 0000000..4c028e8 --- /dev/null +++ b/Example/Pods/SJUIKit/SJUIKit/AttributesFactory/UIKitText/SJUTUtils.m @@ -0,0 +1,18 @@ +// +// SJUTUtils.m +// LWZAudioModule-LWZAudioModule +// +// Created by BlueDancer on 2020/12/2. +// + +#import "SJUTUtils.h" + +BOOL +SJUTRangeContains(NSRange main, NSRange sub) { + return (main.location <= sub.location) && (main.location + main.length >= sub.location + sub.length); +} + +NSRange +SJUTGetTextRange(NSAttributedString *text) { + return NSMakeRange(0, text.length); +} diff --git a/Example/Pods/Target Support Files/LWZComponents/LWZComponents-Info.plist b/Example/Pods/Target Support Files/LWZComponents/LWZComponents-Info.plist new file mode 100644 index 0000000..161a9d3 --- /dev/null +++ b/Example/Pods/Target Support Files/LWZComponents/LWZComponents-Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 0.1.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Example/Pods/Target Support Files/LWZComponents/LWZComponents-dummy.m b/Example/Pods/Target Support Files/LWZComponents/LWZComponents-dummy.m new file mode 100644 index 0000000..82d81a7 --- /dev/null +++ b/Example/Pods/Target Support Files/LWZComponents/LWZComponents-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_LWZComponents : NSObject +@end +@implementation PodsDummy_LWZComponents +@end diff --git a/Example/Pods/Target Support Files/LWZComponents/LWZComponents-prefix.pch b/Example/Pods/Target Support Files/LWZComponents/LWZComponents-prefix.pch new file mode 100644 index 0000000..beb2a24 --- /dev/null +++ b/Example/Pods/Target Support Files/LWZComponents/LWZComponents-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/Example/Pods/Target Support Files/LWZComponents/LWZComponents-umbrella.h b/Example/Pods/Target Support Files/LWZComponents/LWZComponents-umbrella.h new file mode 100644 index 0000000..eb83b76 --- /dev/null +++ b/Example/Pods/Target Support Files/LWZComponents/LWZComponents-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double LWZComponentsVersionNumber; +FOUNDATION_EXPORT const unsigned char LWZComponentsVersionString[]; + diff --git a/Example/Pods/Target Support Files/LWZComponents/LWZComponents.debug.xcconfig b/Example/Pods/Target Support Files/LWZComponents/LWZComponents.debug.xcconfig new file mode 100644 index 0000000..742336a --- /dev/null +++ b/Example/Pods/Target Support Files/LWZComponents/LWZComponents.debug.xcconfig @@ -0,0 +1,12 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/LWZComponents +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/LWZComponents" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/LWZComponents" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/../.. +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Example/Pods/Target Support Files/LWZComponents/LWZComponents.modulemap b/Example/Pods/Target Support Files/LWZComponents/LWZComponents.modulemap new file mode 100644 index 0000000..d923917 --- /dev/null +++ b/Example/Pods/Target Support Files/LWZComponents/LWZComponents.modulemap @@ -0,0 +1,6 @@ +framework module LWZComponents { + umbrella header "LWZComponents-umbrella.h" + + export * + module * { export * } +} diff --git a/Example/Pods/Target Support Files/LWZComponents/LWZComponents.release.xcconfig b/Example/Pods/Target Support Files/LWZComponents/LWZComponents.release.xcconfig new file mode 100644 index 0000000..742336a --- /dev/null +++ b/Example/Pods/Target Support Files/LWZComponents/LWZComponents.release.xcconfig @@ -0,0 +1,12 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/LWZComponents +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/LWZComponents" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/LWZComponents" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/../.. +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Example/Pods/Target Support Files/Masonry/Masonry-dummy.m b/Example/Pods/Target Support Files/Masonry/Masonry-dummy.m new file mode 100644 index 0000000..04001b1 --- /dev/null +++ b/Example/Pods/Target Support Files/Masonry/Masonry-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Masonry : NSObject +@end +@implementation PodsDummy_Masonry +@end diff --git a/Example/Pods/Target Support Files/Masonry/Masonry-prefix.pch b/Example/Pods/Target Support Files/Masonry/Masonry-prefix.pch new file mode 100644 index 0000000..beb2a24 --- /dev/null +++ b/Example/Pods/Target Support Files/Masonry/Masonry-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/Example/Pods/Target Support Files/Masonry/Masonry.debug.xcconfig b/Example/Pods/Target Support Files/Masonry/Masonry.debug.xcconfig new file mode 100644 index 0000000..f19c580 --- /dev/null +++ b/Example/Pods/Target Support Files/Masonry/Masonry.debug.xcconfig @@ -0,0 +1,12 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Masonry +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/Masonry" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/Masonry" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/Masonry +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Example/Pods/Target Support Files/Masonry/Masonry.release.xcconfig b/Example/Pods/Target Support Files/Masonry/Masonry.release.xcconfig new file mode 100644 index 0000000..f19c580 --- /dev/null +++ b/Example/Pods/Target Support Files/Masonry/Masonry.release.xcconfig @@ -0,0 +1,12 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Masonry +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/Masonry" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/Masonry" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/Masonry +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Example/Pods/Target Support Files/Pods-LWZComponents_Example/Pods-LWZComponents_Example-Info.plist b/Example/Pods/Target Support Files/Pods-LWZComponents_Example/Pods-LWZComponents_Example-Info.plist new file mode 100644 index 0000000..2243fe6 --- /dev/null +++ b/Example/Pods/Target Support Files/Pods-LWZComponents_Example/Pods-LWZComponents_Example-Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Example/Pods/Target Support Files/Pods-LWZComponents_Example/Pods-LWZComponents_Example-acknowledgements.markdown b/Example/Pods/Target Support Files/Pods-LWZComponents_Example/Pods-LWZComponents_Example-acknowledgements.markdown new file mode 100644 index 0000000..afc6fd2 --- /dev/null +++ b/Example/Pods/Target Support Files/Pods-LWZComponents_Example/Pods-LWZComponents_Example-acknowledgements.markdown @@ -0,0 +1,97 @@ +# Acknowledgements +This application makes use of the following third party libraries: + +## LWZComponents + +Copyright (c) 2021 changsanjiang@gmail.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +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 THE +AUTHORS OR COPYRIGHT HOLDERS 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. + + +## Masonry + +Copyright (c) 2011-2012 Masonry Team - https://github.com/Masonry + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +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 THE +AUTHORS OR COPYRIGHT HOLDERS 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. + +## SJUIKit + +Copyright (c) 2018 changsanjiang@gmail.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +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 THE +AUTHORS OR COPYRIGHT HOLDERS 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. + + +## YYModel + +The MIT License (MIT) + +Copyright (c) 2015 ibireme + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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 THE +AUTHORS OR COPYRIGHT HOLDERS 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. + + +Generated by CocoaPods - https://cocoapods.org diff --git a/Example/Pods/Target Support Files/Pods-LWZComponents_Example/Pods-LWZComponents_Example-acknowledgements.plist b/Example/Pods/Target Support Files/Pods-LWZComponents_Example/Pods-LWZComponents_Example-acknowledgements.plist new file mode 100644 index 0000000..4499e42 --- /dev/null +++ b/Example/Pods/Target Support Files/Pods-LWZComponents_Example/Pods-LWZComponents_Example-acknowledgements.plist @@ -0,0 +1,147 @@ + + + + + PreferenceSpecifiers + + + FooterText + This application makes use of the following third party libraries: + Title + Acknowledgements + Type + PSGroupSpecifier + + + FooterText + Copyright (c) 2021 changsanjiang@gmail.com <changsanjiang@gmail.com> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +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 THE +AUTHORS OR COPYRIGHT HOLDERS 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. + + License + MIT + Title + LWZComponents + Type + PSGroupSpecifier + + + FooterText + Copyright (c) 2011-2012 Masonry Team - https://github.com/Masonry + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +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 THE +AUTHORS OR COPYRIGHT HOLDERS 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. + License + MIT + Title + Masonry + Type + PSGroupSpecifier + + + FooterText + Copyright (c) 2018 changsanjiang@gmail.com <changsanjiang@gmail.com> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +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 THE +AUTHORS OR COPYRIGHT HOLDERS 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. + + License + MIT + Title + SJUIKit + Type + PSGroupSpecifier + + + FooterText + The MIT License (MIT) + +Copyright (c) 2015 ibireme <ibireme@gmail.com> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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 THE +AUTHORS OR COPYRIGHT HOLDERS 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. + + + License + MIT + Title + YYModel + Type + PSGroupSpecifier + + + FooterText + Generated by CocoaPods - https://cocoapods.org + Title + + Type + PSGroupSpecifier + + + StringsTable + Acknowledgements + Title + Acknowledgements + + diff --git a/Example/Pods/Target Support Files/Pods-LWZComponents_Example/Pods-LWZComponents_Example-dummy.m b/Example/Pods/Target Support Files/Pods-LWZComponents_Example/Pods-LWZComponents_Example-dummy.m new file mode 100644 index 0000000..d7915c4 --- /dev/null +++ b/Example/Pods/Target Support Files/Pods-LWZComponents_Example/Pods-LWZComponents_Example-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Pods_LWZComponents_Example : NSObject +@end +@implementation PodsDummy_Pods_LWZComponents_Example +@end diff --git a/Example/Pods/Target Support Files/Pods-LWZComponents_Example/Pods-LWZComponents_Example-frameworks.sh b/Example/Pods/Target Support Files/Pods-LWZComponents_Example/Pods-LWZComponents_Example-frameworks.sh new file mode 100755 index 0000000..3aa4bf4 --- /dev/null +++ b/Example/Pods/Target Support Files/Pods-LWZComponents_Example/Pods-LWZComponents_Example-frameworks.sh @@ -0,0 +1,186 @@ +#!/bin/sh +set -e +set -u +set -o pipefail + +function on_error { + echo "$(realpath -mq "${0}"):$1: error: Unexpected failure" +} +trap 'on_error $LINENO' ERR + +if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then + # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy + # frameworks to, so exit 0 (signalling the script phase was successful). + exit 0 +fi + +echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" +mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + +COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" +SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" +BCSYMBOLMAP_DIR="BCSymbolMaps" + + +# This protects against multiple targets copying the same framework dependency at the same time. The solution +# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html +RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") + +# Copies and strips a vendored framework +install_framework() +{ + if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then + local source="${BUILT_PRODUCTS_DIR}/$1" + elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then + local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" + elif [ -r "$1" ]; then + local source="$1" + fi + + local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + + if [ -L "${source}" ]; then + echo "Symlinked..." + source="$(readlink "${source}")" + fi + + if [ -d "${source}/${BCSYMBOLMAP_DIR}" ]; then + # Locate and install any .bcsymbolmaps if present, and remove them from the .framework before the framework is copied + find "${source}/${BCSYMBOLMAP_DIR}" -name "*.bcsymbolmap"|while read f; do + echo "Installing $f" + install_bcsymbolmap "$f" "$destination" + rm "$f" + done + rmdir "${source}/${BCSYMBOLMAP_DIR}" + fi + + # Use filter instead of exclude so missing patterns don't throw errors. + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" + + local basename + basename="$(basename -s .framework "$1")" + binary="${destination}/${basename}.framework/${basename}" + + if ! [ -r "$binary" ]; then + binary="${destination}/${basename}" + elif [ -L "${binary}" ]; then + echo "Destination binary is symlinked..." + dirname="$(dirname "${binary}")" + binary="${dirname}/$(readlink "${binary}")" + fi + + # Strip invalid architectures so "fat" simulator / device frameworks work on device + if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then + strip_invalid_archs "$binary" + fi + + # Resign the code if required by the build settings to avoid unstable apps + code_sign_if_enabled "${destination}/$(basename "$1")" + + # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. + if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then + local swift_runtime_libs + swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u) + for lib in $swift_runtime_libs; do + echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" + rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" + code_sign_if_enabled "${destination}/${lib}" + done + fi +} +# Copies and strips a vendored dSYM +install_dsym() { + local source="$1" + warn_missing_arch=${2:-true} + if [ -r "$source" ]; then + # Copy the dSYM into the targets temp dir. + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" + + local basename + basename="$(basename -s .dSYM "$source")" + binary_name="$(ls "$source/Contents/Resources/DWARF")" + binary="${DERIVED_FILES_DIR}/${basename}.dSYM/Contents/Resources/DWARF/${binary_name}" + + # Strip invalid architectures from the dSYM. + if [[ "$(file "$binary")" == *"Mach-O "*"dSYM companion"* ]]; then + strip_invalid_archs "$binary" "$warn_missing_arch" + fi + if [[ $STRIP_BINARY_RETVAL == 0 ]]; then + # Move the stripped file into its final destination. + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.dSYM" "${DWARF_DSYM_FOLDER_PATH}" + else + # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. + mkdir -p "${DWARF_DSYM_FOLDER_PATH}" + touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.dSYM" + fi + fi +} + +# Used as a return value for each invocation of `strip_invalid_archs` function. +STRIP_BINARY_RETVAL=0 + +# Strip invalid architectures +strip_invalid_archs() { + binary="$1" + warn_missing_arch=${2:-true} + # Get architectures for current target binary + binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" + # Intersect them with the architectures we are building for + intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" + # If there are no archs supported by this binary then warn the user + if [[ -z "$intersected_archs" ]]; then + if [[ "$warn_missing_arch" == "true" ]]; then + echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." + fi + STRIP_BINARY_RETVAL=1 + return + fi + stripped="" + for arch in $binary_archs; do + if ! [[ "${ARCHS}" == *"$arch"* ]]; then + # Strip non-valid architectures in-place + lipo -remove "$arch" -output "$binary" "$binary" + stripped="$stripped $arch" + fi + done + if [[ "$stripped" ]]; then + echo "Stripped $binary of architectures:$stripped" + fi + STRIP_BINARY_RETVAL=0 +} + +# Copies the bcsymbolmap files of a vendored framework +install_bcsymbolmap() { + local bcsymbolmap_path="$1" + local destination="${BUILT_PRODUCTS_DIR}" + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}" +} + +# Signs a framework with the provided identity +code_sign_if_enabled() { + if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then + # Use the current code_sign_identity + echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" + local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" + + if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then + code_sign_cmd="$code_sign_cmd &" + fi + echo "$code_sign_cmd" + eval "$code_sign_cmd" + fi +} + +if [[ "$CONFIGURATION" == "Debug" ]]; then + install_framework "${BUILT_PRODUCTS_DIR}/LWZComponents/LWZComponents.framework" +fi +if [[ "$CONFIGURATION" == "Release" ]]; then + install_framework "${BUILT_PRODUCTS_DIR}/LWZComponents/LWZComponents.framework" +fi +if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then + wait +fi diff --git a/Example/Pods/Target Support Files/Pods-LWZComponents_Example/Pods-LWZComponents_Example-umbrella.h b/Example/Pods/Target Support Files/Pods-LWZComponents_Example/Pods-LWZComponents_Example-umbrella.h new file mode 100644 index 0000000..b30d254 --- /dev/null +++ b/Example/Pods/Target Support Files/Pods-LWZComponents_Example/Pods-LWZComponents_Example-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double Pods_LWZComponents_ExampleVersionNumber; +FOUNDATION_EXPORT const unsigned char Pods_LWZComponents_ExampleVersionString[]; + diff --git a/Example/Pods/Target Support Files/Pods-LWZComponents_Example/Pods-LWZComponents_Example.debug.xcconfig b/Example/Pods/Target Support Files/Pods-LWZComponents_Example/Pods-LWZComponents_Example.debug.xcconfig new file mode 100644 index 0000000..7120693 --- /dev/null +++ b/Example/Pods/Target Support Files/Pods-LWZComponents_Example/Pods-LWZComponents_Example.debug.xcconfig @@ -0,0 +1,11 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/LWZComponents" "${PODS_ROOT}/Headers/Public/Masonry" "${PODS_ROOT}/Headers/Public/SJUIKit" "${PODS_ROOT}/Headers/Public/YYModel" +LIBRARY_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/LWZComponents" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "${PODS_CONFIGURATION_BUILD_DIR}/SJUIKit" "${PODS_CONFIGURATION_BUILD_DIR}/YYModel" +OTHER_LDFLAGS = $(inherited) -ObjC -l"LWZComponents" -l"Masonry" -l"SJUIKit" -l"YYModel" -framework "CoreFoundation" -framework "Foundation" -framework "UIKit" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Example/Pods/Target Support Files/Pods-LWZComponents_Example/Pods-LWZComponents_Example.modulemap b/Example/Pods/Target Support Files/Pods-LWZComponents_Example/Pods-LWZComponents_Example.modulemap new file mode 100644 index 0000000..9d12b41 --- /dev/null +++ b/Example/Pods/Target Support Files/Pods-LWZComponents_Example/Pods-LWZComponents_Example.modulemap @@ -0,0 +1,6 @@ +framework module Pods_LWZComponents_Example { + umbrella header "Pods-LWZComponents_Example-umbrella.h" + + export * + module * { export * } +} diff --git a/Example/Pods/Target Support Files/Pods-LWZComponents_Example/Pods-LWZComponents_Example.release.xcconfig b/Example/Pods/Target Support Files/Pods-LWZComponents_Example/Pods-LWZComponents_Example.release.xcconfig new file mode 100644 index 0000000..7120693 --- /dev/null +++ b/Example/Pods/Target Support Files/Pods-LWZComponents_Example/Pods-LWZComponents_Example.release.xcconfig @@ -0,0 +1,11 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/LWZComponents" "${PODS_ROOT}/Headers/Public/Masonry" "${PODS_ROOT}/Headers/Public/SJUIKit" "${PODS_ROOT}/Headers/Public/YYModel" +LIBRARY_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/LWZComponents" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "${PODS_CONFIGURATION_BUILD_DIR}/SJUIKit" "${PODS_CONFIGURATION_BUILD_DIR}/YYModel" +OTHER_LDFLAGS = $(inherited) -ObjC -l"LWZComponents" -l"Masonry" -l"SJUIKit" -l"YYModel" -framework "CoreFoundation" -framework "Foundation" -framework "UIKit" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Example/Pods/Target Support Files/Pods-LWZComponents_Tests/Pods-LWZComponents_Tests-Info.plist b/Example/Pods/Target Support Files/Pods-LWZComponents_Tests/Pods-LWZComponents_Tests-Info.plist new file mode 100644 index 0000000..2243fe6 --- /dev/null +++ b/Example/Pods/Target Support Files/Pods-LWZComponents_Tests/Pods-LWZComponents_Tests-Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Example/Pods/Target Support Files/Pods-LWZComponents_Tests/Pods-LWZComponents_Tests-acknowledgements.markdown b/Example/Pods/Target Support Files/Pods-LWZComponents_Tests/Pods-LWZComponents_Tests-acknowledgements.markdown new file mode 100644 index 0000000..102af75 --- /dev/null +++ b/Example/Pods/Target Support Files/Pods-LWZComponents_Tests/Pods-LWZComponents_Tests-acknowledgements.markdown @@ -0,0 +1,3 @@ +# Acknowledgements +This application makes use of the following third party libraries: +Generated by CocoaPods - https://cocoapods.org diff --git a/Example/Pods/Target Support Files/Pods-LWZComponents_Tests/Pods-LWZComponents_Tests-acknowledgements.plist b/Example/Pods/Target Support Files/Pods-LWZComponents_Tests/Pods-LWZComponents_Tests-acknowledgements.plist new file mode 100644 index 0000000..7acbad1 --- /dev/null +++ b/Example/Pods/Target Support Files/Pods-LWZComponents_Tests/Pods-LWZComponents_Tests-acknowledgements.plist @@ -0,0 +1,29 @@ + + + + + PreferenceSpecifiers + + + FooterText + This application makes use of the following third party libraries: + Title + Acknowledgements + Type + PSGroupSpecifier + + + FooterText + Generated by CocoaPods - https://cocoapods.org + Title + + Type + PSGroupSpecifier + + + StringsTable + Acknowledgements + Title + Acknowledgements + + diff --git a/Example/Pods/Target Support Files/Pods-LWZComponents_Tests/Pods-LWZComponents_Tests-dummy.m b/Example/Pods/Target Support Files/Pods-LWZComponents_Tests/Pods-LWZComponents_Tests-dummy.m new file mode 100644 index 0000000..fb99866 --- /dev/null +++ b/Example/Pods/Target Support Files/Pods-LWZComponents_Tests/Pods-LWZComponents_Tests-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Pods_LWZComponents_Tests : NSObject +@end +@implementation PodsDummy_Pods_LWZComponents_Tests +@end diff --git a/Example/Pods/Target Support Files/Pods-LWZComponents_Tests/Pods-LWZComponents_Tests-umbrella.h b/Example/Pods/Target Support Files/Pods-LWZComponents_Tests/Pods-LWZComponents_Tests-umbrella.h new file mode 100644 index 0000000..b3193ae --- /dev/null +++ b/Example/Pods/Target Support Files/Pods-LWZComponents_Tests/Pods-LWZComponents_Tests-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double Pods_LWZComponents_TestsVersionNumber; +FOUNDATION_EXPORT const unsigned char Pods_LWZComponents_TestsVersionString[]; + diff --git a/Example/Pods/Target Support Files/Pods-LWZComponents_Tests/Pods-LWZComponents_Tests.debug.xcconfig b/Example/Pods/Target Support Files/Pods-LWZComponents_Tests/Pods-LWZComponents_Tests.debug.xcconfig new file mode 100644 index 0000000..adc3cc5 --- /dev/null +++ b/Example/Pods/Target Support Files/Pods-LWZComponents_Tests/Pods-LWZComponents_Tests.debug.xcconfig @@ -0,0 +1,10 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/LWZComponents" "${PODS_ROOT}/Headers/Public/Masonry" "${PODS_ROOT}/Headers/Public/SJUIKit" "${PODS_ROOT}/Headers/Public/YYModel" +OTHER_LDFLAGS = $(inherited) -framework "CoreFoundation" -framework "Foundation" -framework "UIKit" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Example/Pods/Target Support Files/Pods-LWZComponents_Tests/Pods-LWZComponents_Tests.modulemap b/Example/Pods/Target Support Files/Pods-LWZComponents_Tests/Pods-LWZComponents_Tests.modulemap new file mode 100644 index 0000000..88853ee --- /dev/null +++ b/Example/Pods/Target Support Files/Pods-LWZComponents_Tests/Pods-LWZComponents_Tests.modulemap @@ -0,0 +1,6 @@ +framework module Pods_LWZComponents_Tests { + umbrella header "Pods-LWZComponents_Tests-umbrella.h" + + export * + module * { export * } +} diff --git a/Example/Pods/Target Support Files/Pods-LWZComponents_Tests/Pods-LWZComponents_Tests.release.xcconfig b/Example/Pods/Target Support Files/Pods-LWZComponents_Tests/Pods-LWZComponents_Tests.release.xcconfig new file mode 100644 index 0000000..adc3cc5 --- /dev/null +++ b/Example/Pods/Target Support Files/Pods-LWZComponents_Tests/Pods-LWZComponents_Tests.release.xcconfig @@ -0,0 +1,10 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/LWZComponents" "${PODS_ROOT}/Headers/Public/Masonry" "${PODS_ROOT}/Headers/Public/SJUIKit" "${PODS_ROOT}/Headers/Public/YYModel" +OTHER_LDFLAGS = $(inherited) -framework "CoreFoundation" -framework "Foundation" -framework "UIKit" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Example/Pods/Target Support Files/SJUIKit/SJUIKit-dummy.m b/Example/Pods/Target Support Files/SJUIKit/SJUIKit-dummy.m new file mode 100644 index 0000000..f418956 --- /dev/null +++ b/Example/Pods/Target Support Files/SJUIKit/SJUIKit-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_SJUIKit : NSObject +@end +@implementation PodsDummy_SJUIKit +@end diff --git a/Example/Pods/Target Support Files/SJUIKit/SJUIKit-prefix.pch b/Example/Pods/Target Support Files/SJUIKit/SJUIKit-prefix.pch new file mode 100644 index 0000000..beb2a24 --- /dev/null +++ b/Example/Pods/Target Support Files/SJUIKit/SJUIKit-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/Example/Pods/Target Support Files/SJUIKit/SJUIKit.debug.xcconfig b/Example/Pods/Target Support Files/SJUIKit/SJUIKit.debug.xcconfig new file mode 100644 index 0000000..21b1226 --- /dev/null +++ b/Example/Pods/Target Support Files/SJUIKit/SJUIKit.debug.xcconfig @@ -0,0 +1,12 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SJUIKit +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/SJUIKit" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/SJUIKit" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/SJUIKit +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Example/Pods/Target Support Files/SJUIKit/SJUIKit.release.xcconfig b/Example/Pods/Target Support Files/SJUIKit/SJUIKit.release.xcconfig new file mode 100644 index 0000000..21b1226 --- /dev/null +++ b/Example/Pods/Target Support Files/SJUIKit/SJUIKit.release.xcconfig @@ -0,0 +1,12 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SJUIKit +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/SJUIKit" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/SJUIKit" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/SJUIKit +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Example/Pods/Target Support Files/YYModel/YYModel-dummy.m b/Example/Pods/Target Support Files/YYModel/YYModel-dummy.m new file mode 100644 index 0000000..1574055 --- /dev/null +++ b/Example/Pods/Target Support Files/YYModel/YYModel-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_YYModel : NSObject +@end +@implementation PodsDummy_YYModel +@end diff --git a/Example/Pods/Target Support Files/YYModel/YYModel-prefix.pch b/Example/Pods/Target Support Files/YYModel/YYModel-prefix.pch new file mode 100644 index 0000000..beb2a24 --- /dev/null +++ b/Example/Pods/Target Support Files/YYModel/YYModel-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/Example/Pods/Target Support Files/YYModel/YYModel.debug.xcconfig b/Example/Pods/Target Support Files/YYModel/YYModel.debug.xcconfig new file mode 100644 index 0000000..2b608a9 --- /dev/null +++ b/Example/Pods/Target Support Files/YYModel/YYModel.debug.xcconfig @@ -0,0 +1,12 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/YYModel +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/YYModel" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/YYModel" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/YYModel +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Example/Pods/Target Support Files/YYModel/YYModel.release.xcconfig b/Example/Pods/Target Support Files/YYModel/YYModel.release.xcconfig new file mode 100644 index 0000000..2b608a9 --- /dev/null +++ b/Example/Pods/Target Support Files/YYModel/YYModel.release.xcconfig @@ -0,0 +1,12 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/YYModel +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/YYModel" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/YYModel" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/YYModel +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Example/Pods/YYModel/LICENSE b/Example/Pods/YYModel/LICENSE new file mode 100644 index 0000000..46be20b --- /dev/null +++ b/Example/Pods/YYModel/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 ibireme + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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 THE +AUTHORS OR COPYRIGHT HOLDERS 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. + diff --git a/Example/Pods/YYModel/README.md b/Example/Pods/YYModel/README.md new file mode 100755 index 0000000..ea38047 --- /dev/null +++ b/Example/Pods/YYModel/README.md @@ -0,0 +1,619 @@ +YYModel +============== + +[![License MIT](https://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://raw.githubusercontent.com/ibireme/YYModel/master/LICENSE)  +[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)  +[![CocoaPods](http://img.shields.io/cocoapods/v/YYModel.svg?style=flat)](http://cocoapods.org/?q= YYModel)  +[![CocoaPods](http://img.shields.io/cocoapods/p/YYModel.svg?style=flat)](http://cocoapods.org/?q= YYModel)  +[![Build Status](https://travis-ci.org/ibireme/YYModel.svg?branch=master)](https://travis-ci.org/ibireme/YYModel)  +[![codecov.io](https://codecov.io/github/ibireme/YYModel/coverage.svg?branch=master)](https://codecov.io/github/ibireme/YYModel?branch=master) + +High performance model framework for iOS/OSX.
+(It's a component of [YYKit](https://github.com/ibireme/YYKit)) + + +Performance +============== + +Time cost (process GithubUser 10000 times on iPhone 6): + +![Benchmark result](https://raw.github.com/ibireme/YYModel/master/Benchmark/Result.png +) + +See `Benchmark/ModelBenchmark.xcodeproj` for more benchmark case. + + +Features +============== +- **High performance**: The conversion performance is close to handwriting code. +- **Automatic type conversion**: The object types can be automatically converted. +- **Type Safe**: All data types will be verified to ensure type-safe during the conversion process. +- **Non-intrusive**: There is no need to make the model class inherit from other base class. +- **Lightwight**: This library contains only 5 files. +- **Docs and unit testing**: 100% docs coverage, 99.6% code coverage. + +Usage +============== + +###Simple model json convert + + // JSON: + { + "uid":123456, + "name":"Harry", + "created":"1965-07-31T00:00:00+0000" + } + + // Model: + @interface User : NSObject + @property UInt64 uid; + @property NSString *name; + @property NSDate *created; + @end + @implementation User + @end + + + // Convert json to model: + User *user = [User yy_modelWithJSON:json]; + + // Convert model to json: + NSDictionary *json = [user yy_modelToJSONObject]; + + +If the type of an object in JSON/Dictionary cannot be matched to the property of the model, the following automatic conversion is performed. If the automatic conversion failed, the value will be ignored. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
JSON/DictionaryModel
NSStringNSNumber,NSURL,SEL,Class
NSNumberNSString
NSString/NSNumberC number (BOOL,int,float,NSUInteger,UInt64,...)
+ NaN and Inf will be ignored
NSStringNSDate parsed with these formats:
+ yyyy-MM-dd
+yyyy-MM-dd HH:mm:ss
+yyyy-MM-dd'T'HH:mm:ss
+yyyy-MM-dd'T'HH:mm:ssZ
+EEE MMM dd HH:mm:ss Z yyyy +
NSDateNSString formatted with ISO8601:
+ "YYYY-MM-dd'T'HH:mm:ssZ"
NSValuestruct (CGRect,CGSize,...)
NSNullnil,0
"no","false",...@(NO),0
"yes","true",...@(YES),1
+ + + +###Match model property to different JSON key + + // JSON: + { + "n":"Harry Pottery", + "p": 256, + "ext" : { + "desc" : "A book written by J.K.Rowing." + }, + "ID" : 100010 + } + + // Model: + @interface Book : NSObject + @property NSString *name; + @property NSInteger page; + @property NSString *desc; + @property NSString *bookID; + @end + @implementation Book + + (NSDictionary *)modelCustomPropertyMapper { + return @{@"name" : @"n", + @"page" : @"p", + @"desc" : @"ext.desc", + @"bookID" : @[@"id",@"ID",@"book_id"]}; + } + @end + +You can map a json key (key path) or an array of json key (key path) to one or multiple property name. If there's no mapper for a property, it will use the property's name as default. + +###Nested model + + // JSON + { + "author":{ + "name":"J.K.Rowling", + "birthday":"1965-07-31T00:00:00+0000" + }, + "name":"Harry Potter", + "pages":256 + } + + // Model: (no need to do anything) + @interface Author : NSObject + @property NSString *name; + @property NSDate *birthday; + @end + @implementation Author + @end + + @interface Book : NSObject + @property NSString *name; + @property NSUInteger pages; + @property Author *author; + @end + @implementation Book + @end + + + +### Container property + + @class Shadow, Border, Attachment; + + @interface Attributes + @property NSString *name; + @property NSArray *shadows; //Array + @property NSSet *borders; //Set + @property NSMutableDictionary *attachments; //Dict + @end + + @implementation Attributes + + (NSDictionary *)modelContainerPropertyGenericClass { + // value should be Class or Class name. + return @{@"shadows" : [Shadow class], + @"borders" : Border.class, + @"attachments" : @"Attachment" }; + } + @end + + + + +### Whitelist and blacklist + + @interface User + @property NSString *name; + @property NSUInteger age; + @end + + @implementation Attributes + + (NSArray *)modelPropertyBlacklist { + return @[@"test1", @"test2"]; + } + + (NSArray *)modelPropertyWhitelist { + return @[@"name"]; + } + @end + +###Data validate and custom transform + + // JSON: + { + "name":"Harry", + "timestamp" : 1445534567 + } + + // Model: + @interface User + @property NSString *name; + @property NSDate *createdAt; + @end + + @implementation User + - (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic { + NSNumber *timestamp = dic[@"timestamp"]; + if (![timestamp isKindOfClass:[NSNumber class]]) return NO; + _createdAt = [NSDate dateWithTimeIntervalSince1970:timestamp.floatValue]; + return YES; + } + - (BOOL)modelCustomTransformToDictionary:(NSMutableDictionary *)dic { + if (!_createdAt) return NO; + dic[@"timestamp"] = @(n.timeIntervalSince1970); + return YES; + } + @end + +###Coding/Copying/hash/equal/description + + @interface YYShadow :NSObject + @property (nonatomic, copy) NSString *name; + @property (nonatomic, assign) CGSize size; + @end + + @implementation YYShadow + - (void)encodeWithCoder:(NSCoder *)aCoder { [self yy_modelEncodeWithCoder:aCoder]; } + - (id)initWithCoder:(NSCoder *)aDecoder { self = [super init]; return [self yy_modelInitWithCoder:aDecoder]; } + - (id)copyWithZone:(NSZone *)zone { return [self yy_modelCopy]; } + - (NSUInteger)hash { return [self yy_modelHash]; } + - (BOOL)isEqual:(id)object { return [self yy_modelIsEqual:object]; } + - (NSString *)description { return [self yy_modelDescription]; } + @end + + +Installation +============== + +### CocoaPods + +1. Add `pod 'YYModel'` to your Podfile. +2. Run `pod install` or `pod update`. +3. Import \. + + +### Carthage + +1. Add `github "ibireme/YYModel"` to your Cartfile. +2. Run `carthage update --platform ios` and add the framework to your project. +3. Import \. + + +### Manually + +1. Download all the files in the YYModel subdirectory. +2. Add the source files to your Xcode project. +3. Import `YYModel.h`. + + +Documentation +============== +Full API documentation is available on [CocoaDocs](http://cocoadocs.org/docsets/YYModel/).
+You can also install documentation locally using [appledoc](https://github.com/tomaz/appledoc). + + +Requirements +============== +This library requires `iOS 6.0+` and `Xcode 7.0+`. + + +License +============== +YYModel is provided under the MIT license. See LICENSE file for details. + + +

+--- +中文介绍 +============== +高性能 iOS/OSX 模型转换框架。
+(该项目是 [YYKit](https://github.com/ibireme/YYKit) 组件之一) + + +性能 +============== +处理 GithubUser 数据 10000 次耗时统计 (iPhone 6): + +![Benchmark result](https://raw.github.com/ibireme/YYModel/master/Benchmark/Result.png +) + +更多测试代码和用例见 `Benchmark/ModelBenchmark.xcodeproj`。 + + +特性 +============== +- **高性能**: 模型转换性能接近手写解析代码。 +- **自动类型转换**: 对象类型可以自动转换,详情见下方表格。 +- **类型安全**: 转换过程中,所有的数据类型都会被检测一遍,以保证类型安全,避免崩溃问题。 +- **无侵入性**: 模型无需继承自其他基类。 +- **轻量**: 该框架只有 5 个文件 (包括.h文件)。 +- **文档和单元测试**: 文档覆盖率100%, 代码覆盖率99.6%。 + +使用方法 +============== + +###简单的 Model 与 JSON 相互转换 + + // JSON: + { + "uid":123456, + "name":"Harry", + "created":"1965-07-31T00:00:00+0000" + } + + // Model: + @interface User : NSObject + @property UInt64 uid; + @property NSString *name; + @property NSDate *created; + @end + @implementation User + @end + + + // 将 JSON (NSData,NSString,NSDictionary) 转换为 Model: + User *user = [User yy_modelWithJSON:json]; + + // 将 Model 转换为 JSON 对象: + NSDictionary *json = [user yy_modelToJSONObject]; + +当 JSON/Dictionary 中的对象类型与 Model 属性不一致时,YYModel 将会进行如下自动转换。自动转换不支持的值将会被忽略,以避免各种潜在的崩溃问题。 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
JSON/DictionaryModel
NSStringNSNumber,NSURL,SEL,Class
NSNumberNSString
NSString/NSNumber基础类型 (BOOL,int,float,NSUInteger,UInt64,...)
+ NaN 和 Inf 会被忽略
NSStringNSDate 以下列格式解析:
+ yyyy-MM-dd
+yyyy-MM-dd HH:mm:ss
+yyyy-MM-dd'T'HH:mm:ss
+yyyy-MM-dd'T'HH:mm:ssZ
+EEE MMM dd HH:mm:ss Z yyyy +
NSDateNSString 格式化为 ISO8601:
+ "YYYY-MM-dd'T'HH:mm:ssZ"
NSValuestruct (CGRect,CGSize,...)
NSNullnil,0
"no","false",...@(NO),0
"yes","true",...@(YES),1
+ + +###Model 属性名和 JSON 中的 Key 不相同 + + // JSON: + { + "n":"Harry Pottery", + "p": 256, + "ext" : { + "desc" : "A book written by J.K.Rowing." + }, + "ID" : 100010 + } + + // Model: + @interface Book : NSObject + @property NSString *name; + @property NSInteger page; + @property NSString *desc; + @property NSString *bookID; + @end + @implementation Book + //返回一个 Dict,将 Model 属性名对映射到 JSON 的 Key。 + + (NSDictionary *)modelCustomPropertyMapper { + return @{@"name" : @"n", + @"page" : @"p", + @"desc" : @"ext.desc", + @"bookID" : @[@"id",@"ID",@"book_id"]}; + } + @end + +你可以把一个或一组 json key (key path) 映射到一个或多个属性。如果一个属性没有映射关系,那默认会使用相同属性名作为映射。 + +在 json->model 的过程中:如果一个属性对应了多个 json key,那么转换过程会按顺序查找,并使用第一个不为空的值。 + +在 model->json 的过程中:如果一个属性对应了多个 json key (key path),那么转换过程仅会处理第一个 json key (key path);如果多个属性对应了同一个 json key,则转换过过程会使用其中任意一个不为空的值。 + +###Model 包含其他 Model + + // JSON + { + "author":{ + "name":"J.K.Rowling", + "birthday":"1965-07-31T00:00:00+0000" + }, + "name":"Harry Potter", + "pages":256 + } + + // Model: 什么都不用做,转换会自动完成 + @interface Author : NSObject + @property NSString *name; + @property NSDate *birthday; + @end + @implementation Author + @end + + @interface Book : NSObject + @property NSString *name; + @property NSUInteger pages; + @property Author *author; //Book 包含 Author 属性 + @end + @implementation Book + @end + + + +###容器类属性 + + @class Shadow, Border, Attachment; + + @interface Attributes + @property NSString *name; + @property NSArray *shadows; //Array + @property NSSet *borders; //Set + @property NSMutableDictionary *attachments; //Dict + @end + + @implementation Attributes + // 返回容器类中的所需要存放的数据类型 (以 Class 或 Class Name 的形式)。 + + (NSDictionary *)modelContainerPropertyGenericClass { + return @{@"shadows" : [Shadow class], + @"borders" : Border.class, + @"attachments" : @"Attachment" }; + } + @end + + + + +###黑名单与白名单 + + @interface User + @property NSString *name; + @property NSUInteger age; + @end + + @implementation Attributes + // 如果实现了该方法,则处理过程中会忽略该列表内的所有属性 + + (NSArray *)modelPropertyBlacklist { + return @[@"test1", @"test2"]; + } + // 如果实现了该方法,则处理过程中不会处理该列表外的属性。 + + (NSArray *)modelPropertyWhitelist { + return @[@"name"]; + } + @end + +###数据校验与自定义转换 + + // JSON: + { + "name":"Harry", + "timestamp" : 1445534567 + } + + // Model: + @interface User + @property NSString *name; + @property NSDate *createdAt; + @end + + @implementation User + // 当 JSON 转为 Model 完成后,该方法会被调用。 + // 你可以在这里对数据进行校验,如果校验不通过,可以返回 NO,则该 Model 会被忽略。 + // 你也可以在这里做一些自动转换不能完成的工作。 + - (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic { + NSNumber *timestamp = dic[@"timestamp"]; + if (![timestamp isKindOfClass:[NSNumber class]]) return NO; + _createdAt = [NSDate dateWithTimeIntervalSince1970:timestamp.floatValue]; + return YES; + } + + // 当 Model 转为 JSON 完成后,该方法会被调用。 + // 你可以在这里对数据进行校验,如果校验不通过,可以返回 NO,则该 Model 会被忽略。 + // 你也可以在这里做一些自动转换不能完成的工作。 + - (BOOL)modelCustomTransformToDictionary:(NSMutableDictionary *)dic { + if (!_createdAt) return NO; + dic[@"timestamp"] = @(n.timeIntervalSince1970); + return YES; + } + @end + +###Coding/Copying/hash/equal/description + + @interface YYShadow :NSObject + @property (nonatomic, copy) NSString *name; + @property (nonatomic, assign) CGSize size; + @end + + @implementation YYShadow + // 直接添加以下代码即可自动完成 + - (void)encodeWithCoder:(NSCoder *)aCoder { [self yy_modelEncodeWithCoder:aCoder]; } + - (id)initWithCoder:(NSCoder *)aDecoder { self = [super init]; return [self yy_modelInitWithCoder:aDecoder]; } + - (id)copyWithZone:(NSZone *)zone { return [self yy_modelCopy]; } + - (NSUInteger)hash { return [self yy_modelHash]; } + - (BOOL)isEqual:(id)object { return [self yy_modelIsEqual:object]; } + - (NSString *)description { return [self yy_modelDescription]; } + @end + + +安装 +============== + +### CocoaPods + +1. 在 Podfile 中添加 `pod 'YYModel'`。 +2. 执行 `pod install` 或 `pod update`。 +3. 导入 \。 + + +### Carthage + +1. 在 Cartfile 中添加 `github "ibireme/YYModel"`。 +2. 执行 `carthage update --platform ios` 并将生成的 framework 添加到你的工程。 +3. 导入 \。 + + +### 手动安装 + +1. 下载 YYModel 文件夹内的所有内容。 +2. 将 YYModel 内的源文件添加(拖放)到你的工程。 +3. 导入 `YYModel.h`。 + + +文档 +============== +你可以在 [CocoaDocs](http://cocoadocs.org/docsets/YYModel/) 查看在线 API 文档,也可以用 [appledoc](https://github.com/tomaz/appledoc) 本地生成文档。 + + +系统要求 +============== +该项目最低支持 `iOS 6.0` 和 `Xcode 7.0`。 + + +许可证 +============== +YYModel 使用 MIT 许可证,详情见 LICENSE 文件。 + +相关链接 +============== + +[iOS JSON 模型转换库评测](http://blog.ibireme.com/2015/10/23/ios_model_framework_benchmark/) + diff --git a/Example/Pods/YYModel/YYModel/NSObject+YYModel.h b/Example/Pods/YYModel/YYModel/NSObject+YYModel.h new file mode 100644 index 0000000..82032ec --- /dev/null +++ b/Example/Pods/YYModel/YYModel/NSObject+YYModel.h @@ -0,0 +1,430 @@ +// +// NSObject+YYModel.h +// YYModel +// +// Created by ibireme on 15/5/10. +// Copyright (c) 2015 ibireme. +// +// This source code is licensed under the MIT-style license found in the +// LICENSE file in the root directory of this source tree. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + Provide some data-model method: + + * Convert json to any object, or convert any object to json. + * Set object properties with a key-value dictionary (like KVC). + * Implementations of `NSCoding`, `NSCopying`, `-hash` and `-isEqual:`. + + See `YYModel` protocol for custom methods. + + + Sample Code: + + ********************** json convertor ********************* + @interface YYAuthor : NSObject + @property (nonatomic, strong) NSString *name; + @property (nonatomic, assign) NSDate *birthday; + @end + @implementation YYAuthor + @end + + @interface YYBook : NSObject + @property (nonatomic, copy) NSString *name; + @property (nonatomic, assign) NSUInteger pages; + @property (nonatomic, strong) YYAuthor *author; + @end + @implementation YYBook + @end + + int main() { + // create model from json + YYBook *book = [YYBook yy_modelWithJSON:@"{\"name\": \"Harry Potter\", \"pages\": 256, \"author\": {\"name\": \"J.K.Rowling\", \"birthday\": \"1965-07-31\" }}"]; + + // convert model to json + NSString *json = [book yy_modelToJSONString]; + // {"author":{"name":"J.K.Rowling","birthday":"1965-07-31T00:00:00+0000"},"name":"Harry Potter","pages":256} + } + + ********************** Coding/Copying/hash/equal ********************* + @interface YYShadow :NSObject + @property (nonatomic, copy) NSString *name; + @property (nonatomic, assign) CGSize size; + @end + + @implementation YYShadow + - (void)encodeWithCoder:(NSCoder *)aCoder { [self yy_modelEncodeWithCoder:aCoder]; } + - (id)initWithCoder:(NSCoder *)aDecoder { self = [super init]; return [self yy_modelInitWithCoder:aDecoder]; } + - (id)copyWithZone:(NSZone *)zone { return [self yy_modelCopy]; } + - (NSUInteger)hash { return [self yy_modelHash]; } + - (BOOL)isEqual:(id)object { return [self yy_modelIsEqual:object]; } + @end + + */ +@interface NSObject (YYModel) + +/** + Creates and returns a new instance of the receiver from a json. + This method is thread-safe. + + @param json A json object in `NSDictionary`, `NSString` or `NSData`. + + @return A new instance created from the json, or nil if an error occurs. + */ ++ (nullable instancetype)yy_modelWithJSON:(id)json; + +/** + Creates and returns a new instance of the receiver from a key-value dictionary. + This method is thread-safe. + + @param dictionary A key-value dictionary mapped to the instance's properties. + Any invalid key-value pair in dictionary will be ignored. + + @return A new instance created from the dictionary, or nil if an error occurs. + + @discussion The key in `dictionary` will mapped to the reciever's property name, + and the value will set to the property. If the value's type does not match the + property, this method will try to convert the value based on these rules: + + `NSString` or `NSNumber` -> c number, such as BOOL, int, long, float, NSUInteger... + `NSString` -> NSDate, parsed with format "yyyy-MM-dd'T'HH:mm:ssZ", "yyyy-MM-dd HH:mm:ss" or "yyyy-MM-dd". + `NSString` -> NSURL. + `NSValue` -> struct or union, such as CGRect, CGSize, ... + `NSString` -> SEL, Class. + */ ++ (nullable instancetype)yy_modelWithDictionary:(NSDictionary *)dictionary; + +/** + Set the receiver's properties with a json object. + + @discussion Any invalid data in json will be ignored. + + @param json A json object of `NSDictionary`, `NSString` or `NSData`, mapped to the + receiver's properties. + + @return Whether succeed. + */ +- (BOOL)yy_modelSetWithJSON:(id)json; + +/** + Set the receiver's properties with a key-value dictionary. + + @param dic A key-value dictionary mapped to the receiver's properties. + Any invalid key-value pair in dictionary will be ignored. + + @discussion The key in `dictionary` will mapped to the reciever's property name, + and the value will set to the property. If the value's type doesn't match the + property, this method will try to convert the value based on these rules: + + `NSString`, `NSNumber` -> c number, such as BOOL, int, long, float, NSUInteger... + `NSString` -> NSDate, parsed with format "yyyy-MM-dd'T'HH:mm:ssZ", "yyyy-MM-dd HH:mm:ss" or "yyyy-MM-dd". + `NSString` -> NSURL. + `NSValue` -> struct or union, such as CGRect, CGSize, ... + `NSString` -> SEL, Class. + + @return Whether succeed. + */ +- (BOOL)yy_modelSetWithDictionary:(NSDictionary *)dic; + +/** + Generate a json object from the receiver's properties. + + @return A json object in `NSDictionary` or `NSArray`, or nil if an error occurs. + See [NSJSONSerialization isValidJSONObject] for more information. + + @discussion Any of the invalid property is ignored. + If the reciver is `NSArray`, `NSDictionary` or `NSSet`, it just convert + the inner object to json object. + */ +- (nullable id)yy_modelToJSONObject; + +/** + Generate a json string's data from the receiver's properties. + + @return A json string's data, or nil if an error occurs. + + @discussion Any of the invalid property is ignored. + If the reciver is `NSArray`, `NSDictionary` or `NSSet`, it will also convert the + inner object to json string. + */ +- (nullable NSData *)yy_modelToJSONData; + +/** + Generate a json string from the receiver's properties. + + @return A json string, or nil if an error occurs. + + @discussion Any of the invalid property is ignored. + If the reciver is `NSArray`, `NSDictionary` or `NSSet`, it will also convert the + inner object to json string. + */ +- (nullable NSString *)yy_modelToJSONString; + +/** + Copy a instance with the receiver's properties. + + @return A copied instance, or nil if an error occurs. + */ +- (nullable id)yy_modelCopy; + +/** + Encode the receiver's properties to a coder. + + @param aCoder An archiver object. + */ +- (void)yy_modelEncodeWithCoder:(NSCoder *)aCoder; + +/** + Decode the receiver's properties from a decoder. + + @param aDecoder An archiver object. + + @return self + */ +- (id)yy_modelInitWithCoder:(NSCoder *)aDecoder; + +/** + Get a hash code with the receiver's properties. + + @return Hash code. + */ +- (NSUInteger)yy_modelHash; + +/** + Compares the receiver with another object for equality, based on properties. + + @param model Another object. + + @return `YES` if the reciever is equal to the object, otherwise `NO`. + */ +- (BOOL)yy_modelIsEqual:(id)model; + +/** + Description method for debugging purposes based on properties. + + @return A string that describes the contents of the receiver. + */ +- (NSString *)yy_modelDescription; + +@end + + + +/** + Provide some data-model method for NSArray. + */ +@interface NSArray (YYModel) + +/** + Creates and returns an array from a json-array. + This method is thread-safe. + + @param cls The instance's class in array. + @param json A json array of `NSArray`, `NSString` or `NSData`. + Example: [{"name","Mary"},{name:"Joe"}] + + @return A array, or nil if an error occurs. + */ ++ (nullable NSArray *)yy_modelArrayWithClass:(Class)cls json:(id)json; + +@end + + + +/** + Provide some data-model method for NSDictionary. + */ +@interface NSDictionary (YYModel) + +/** + Creates and returns a dictionary from a json. + This method is thread-safe. + + @param cls The value instance's class in dictionary. + @param json A json dictionary of `NSDictionary`, `NSString` or `NSData`. + Example: {"user1":{"name","Mary"}, "user2": {name:"Joe"}} + + @return A dictionary, or nil if an error occurs. + */ ++ (nullable NSDictionary *)yy_modelDictionaryWithClass:(Class)cls json:(id)json; +@end + + + +/** + If the default model transform does not fit to your model class, implement one or + more method in this protocol to change the default key-value transform process. + There's no need to add '' to your class header. + */ +@protocol YYModel +@optional + +/** + Custom property mapper. + + @discussion If the key in JSON/Dictionary does not match to the model's property name, + implements this method and returns the additional mapper. + + Example: + + json: + { + "n":"Harry Pottery", + "p": 256, + "ext" : { + "desc" : "A book written by J.K.Rowling." + }, + "ID" : 100010 + } + + model: + @interface YYBook : NSObject + @property NSString *name; + @property NSInteger page; + @property NSString *desc; + @property NSString *bookID; + @end + + @implementation YYBook + + (NSDictionary *)modelCustomPropertyMapper { + return @{@"name" : @"n", + @"page" : @"p", + @"desc" : @"ext.desc", + @"bookID": @[@"id", @"ID", @"book_id"]}; + } + @end + + @return A custom mapper for properties. + */ ++ (nullable NSDictionary *)modelCustomPropertyMapper; + +/** + The generic class mapper for container properties. + + @discussion If the property is a container object, such as NSArray/NSSet/NSDictionary, + implements this method and returns a property->class mapper, tells which kind of + object will be add to the array/set/dictionary. + + Example: + @class YYShadow, YYBorder, YYAttachment; + + @interface YYAttributes + @property NSString *name; + @property NSArray *shadows; + @property NSSet *borders; + @property NSDictionary *attachments; + @end + + @implementation YYAttributes + + (NSDictionary *)modelContainerPropertyGenericClass { + return @{@"shadows" : [YYShadow class], + @"borders" : YYBorder.class, + @"attachments" : @"YYAttachment" }; + } + @end + + @return A class mapper. + */ ++ (nullable NSDictionary *)modelContainerPropertyGenericClass; + +/** + If you need to create instances of different classes during json->object transform, + use the method to choose custom class based on dictionary data. + + @discussion If the model implements this method, it will be called to determine resulting class + during `+modelWithJSON:`, `+modelWithDictionary:`, conveting object of properties of parent objects + (both singular and containers via `+modelContainerPropertyGenericClass`). + + Example: + @class YYCircle, YYRectangle, YYLine; + + @implementation YYShape + + + (Class)modelCustomClassForDictionary:(NSDictionary*)dictionary { + if (dictionary[@"radius"] != nil) { + return [YYCircle class]; + } else if (dictionary[@"width"] != nil) { + return [YYRectangle class]; + } else if (dictionary[@"y2"] != nil) { + return [YYLine class]; + } else { + return [self class]; + } + } + + @end + + @param dictionary The json/kv dictionary. + + @return Class to create from this dictionary, `nil` to use current class. + + */ ++ (nullable Class)modelCustomClassForDictionary:(NSDictionary *)dictionary; + +/** + All the properties in blacklist will be ignored in model transform process. + Returns nil to ignore this feature. + + @return An array of property's name. + */ ++ (nullable NSArray *)modelPropertyBlacklist; + +/** + If a property is not in the whitelist, it will be ignored in model transform process. + Returns nil to ignore this feature. + + @return An array of property's name. + */ ++ (nullable NSArray *)modelPropertyWhitelist; + +/** + This method's behavior is similar to `- (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic;`, + but be called before the model transform. + + @discussion If the model implements this method, it will be called before + `+modelWithJSON:`, `+modelWithDictionary:`, `-modelSetWithJSON:` and `-modelSetWithDictionary:`. + If this method returns nil, the transform process will ignore this model. + + @param dic The json/kv dictionary. + + @return Returns the modified dictionary, or nil to ignore this model. + */ +- (NSDictionary *)modelCustomWillTransformFromDictionary:(NSDictionary *)dic; + +/** + If the default json-to-model transform does not fit to your model object, implement + this method to do additional process. You can also use this method to validate the + model's properties. + + @discussion If the model implements this method, it will be called at the end of + `+modelWithJSON:`, `+modelWithDictionary:`, `-modelSetWithJSON:` and `-modelSetWithDictionary:`. + If this method returns NO, the transform process will ignore this model. + + @param dic The json/kv dictionary. + + @return Returns YES if the model is valid, or NO to ignore this model. + */ +- (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic; + +/** + If the default model-to-json transform does not fit to your model class, implement + this method to do additional process. You can also use this method to validate the + json dictionary. + + @discussion If the model implements this method, it will be called at the end of + `-modelToJSONObject` and `-modelToJSONString`. + If this method returns NO, the transform process will ignore this json dictionary. + + @param dic The json dictionary. + + @return Returns YES if the model is valid, or NO to ignore this model. + */ +- (BOOL)modelCustomTransformToDictionary:(NSMutableDictionary *)dic; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Example/Pods/YYModel/YYModel/NSObject+YYModel.m b/Example/Pods/YYModel/YYModel/NSObject+YYModel.m new file mode 100644 index 0000000..e92a6b1 --- /dev/null +++ b/Example/Pods/YYModel/YYModel/NSObject+YYModel.m @@ -0,0 +1,1840 @@ +// +// NSObject+YYModel.m +// YYModel +// +// Created by ibireme on 15/5/10. +// Copyright (c) 2015 ibireme. +// +// This source code is licensed under the MIT-style license found in the +// LICENSE file in the root directory of this source tree. +// + +#import "NSObject+YYModel.h" +#import "YYClassInfo.h" +#import + +#define force_inline __inline__ __attribute__((always_inline)) + +/// Foundation Class Type +typedef NS_ENUM (NSUInteger, YYEncodingNSType) { + YYEncodingTypeNSUnknown = 0, + YYEncodingTypeNSString, + YYEncodingTypeNSMutableString, + YYEncodingTypeNSValue, + YYEncodingTypeNSNumber, + YYEncodingTypeNSDecimalNumber, + YYEncodingTypeNSData, + YYEncodingTypeNSMutableData, + YYEncodingTypeNSDate, + YYEncodingTypeNSURL, + YYEncodingTypeNSArray, + YYEncodingTypeNSMutableArray, + YYEncodingTypeNSDictionary, + YYEncodingTypeNSMutableDictionary, + YYEncodingTypeNSSet, + YYEncodingTypeNSMutableSet, +}; + +/// Get the Foundation class type from property info. +static force_inline YYEncodingNSType YYClassGetNSType(Class cls) { + if (!cls) return YYEncodingTypeNSUnknown; + if ([cls isSubclassOfClass:[NSMutableString class]]) return YYEncodingTypeNSMutableString; + if ([cls isSubclassOfClass:[NSString class]]) return YYEncodingTypeNSString; + if ([cls isSubclassOfClass:[NSDecimalNumber class]]) return YYEncodingTypeNSDecimalNumber; + if ([cls isSubclassOfClass:[NSNumber class]]) return YYEncodingTypeNSNumber; + if ([cls isSubclassOfClass:[NSValue class]]) return YYEncodingTypeNSValue; + if ([cls isSubclassOfClass:[NSMutableData class]]) return YYEncodingTypeNSMutableData; + if ([cls isSubclassOfClass:[NSData class]]) return YYEncodingTypeNSData; + if ([cls isSubclassOfClass:[NSDate class]]) return YYEncodingTypeNSDate; + if ([cls isSubclassOfClass:[NSURL class]]) return YYEncodingTypeNSURL; + if ([cls isSubclassOfClass:[NSMutableArray class]]) return YYEncodingTypeNSMutableArray; + if ([cls isSubclassOfClass:[NSArray class]]) return YYEncodingTypeNSArray; + if ([cls isSubclassOfClass:[NSMutableDictionary class]]) return YYEncodingTypeNSMutableDictionary; + if ([cls isSubclassOfClass:[NSDictionary class]]) return YYEncodingTypeNSDictionary; + if ([cls isSubclassOfClass:[NSMutableSet class]]) return YYEncodingTypeNSMutableSet; + if ([cls isSubclassOfClass:[NSSet class]]) return YYEncodingTypeNSSet; + return YYEncodingTypeNSUnknown; +} + +/// Whether the type is c number. +static force_inline BOOL YYEncodingTypeIsCNumber(YYEncodingType type) { + switch (type & YYEncodingTypeMask) { + case YYEncodingTypeBool: + case YYEncodingTypeInt8: + case YYEncodingTypeUInt8: + case YYEncodingTypeInt16: + case YYEncodingTypeUInt16: + case YYEncodingTypeInt32: + case YYEncodingTypeUInt32: + case YYEncodingTypeInt64: + case YYEncodingTypeUInt64: + case YYEncodingTypeFloat: + case YYEncodingTypeDouble: + case YYEncodingTypeLongDouble: return YES; + default: return NO; + } +} + +/// Parse a number value from 'id'. +static force_inline NSNumber *YYNSNumberCreateFromID(__unsafe_unretained id value) { + static NSCharacterSet *dot; + static NSDictionary *dic; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + dot = [NSCharacterSet characterSetWithRange:NSMakeRange('.', 1)]; + dic = @{@"TRUE" : @(YES), + @"True" : @(YES), + @"true" : @(YES), + @"FALSE" : @(NO), + @"False" : @(NO), + @"false" : @(NO), + @"YES" : @(YES), + @"Yes" : @(YES), + @"yes" : @(YES), + @"NO" : @(NO), + @"No" : @(NO), + @"no" : @(NO), + @"NIL" : (id)kCFNull, + @"Nil" : (id)kCFNull, + @"nil" : (id)kCFNull, + @"NULL" : (id)kCFNull, + @"Null" : (id)kCFNull, + @"null" : (id)kCFNull, + @"(NULL)" : (id)kCFNull, + @"(Null)" : (id)kCFNull, + @"(null)" : (id)kCFNull, + @"" : (id)kCFNull, + @"" : (id)kCFNull, + @"" : (id)kCFNull}; + }); + + if (!value || value == (id)kCFNull) return nil; + if ([value isKindOfClass:[NSNumber class]]) return value; + if ([value isKindOfClass:[NSString class]]) { + NSNumber *num = dic[value]; + if (num) { + if (num == (id)kCFNull) return nil; + return num; + } + if ([(NSString *)value rangeOfCharacterFromSet:dot].location != NSNotFound) { + const char *cstring = ((NSString *)value).UTF8String; + if (!cstring) return nil; + double num = atof(cstring); + if (isnan(num) || isinf(num)) return nil; + return @(num); + } else { + const char *cstring = ((NSString *)value).UTF8String; + if (!cstring) return nil; + return @(atoll(cstring)); + } + } + return nil; +} + +/// Parse string to date. +static force_inline NSDate *YYNSDateFromString(__unsafe_unretained NSString *string) { + typedef NSDate* (^YYNSDateParseBlock)(NSString *string); + #define kParserNum 34 + static YYNSDateParseBlock blocks[kParserNum + 1] = {0}; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + { + /* + 2014-01-20 // Google + */ + NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; + formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]; + formatter.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0]; + formatter.dateFormat = @"yyyy-MM-dd"; + blocks[10] = ^(NSString *string) { return [formatter dateFromString:string]; }; + } + + { + /* + 2014-01-20 12:24:48 + 2014-01-20T12:24:48 // Google + 2014-01-20 12:24:48.000 + 2014-01-20T12:24:48.000 + */ + NSDateFormatter *formatter1 = [[NSDateFormatter alloc] init]; + formatter1.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]; + formatter1.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0]; + formatter1.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss"; + + NSDateFormatter *formatter2 = [[NSDateFormatter alloc] init]; + formatter2.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]; + formatter2.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0]; + formatter2.dateFormat = @"yyyy-MM-dd HH:mm:ss"; + + NSDateFormatter *formatter3 = [[NSDateFormatter alloc] init]; + formatter3.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]; + formatter3.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0]; + formatter3.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss.SSS"; + + NSDateFormatter *formatter4 = [[NSDateFormatter alloc] init]; + formatter4.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]; + formatter4.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0]; + formatter4.dateFormat = @"yyyy-MM-dd HH:mm:ss.SSS"; + + blocks[19] = ^(NSString *string) { + if ([string characterAtIndex:10] == 'T') { + return [formatter1 dateFromString:string]; + } else { + return [formatter2 dateFromString:string]; + } + }; + + blocks[23] = ^(NSString *string) { + if ([string characterAtIndex:10] == 'T') { + return [formatter3 dateFromString:string]; + } else { + return [formatter4 dateFromString:string]; + } + }; + } + + { + /* + 2014-01-20T12:24:48Z // Github, Apple + 2014-01-20T12:24:48+0800 // Facebook + 2014-01-20T12:24:48+12:00 // Google + 2014-01-20T12:24:48.000Z + 2014-01-20T12:24:48.000+0800 + 2014-01-20T12:24:48.000+12:00 + */ + NSDateFormatter *formatter = [NSDateFormatter new]; + formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]; + formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZ"; + + NSDateFormatter *formatter2 = [NSDateFormatter new]; + formatter2.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]; + formatter2.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss.SSSZ"; + + blocks[20] = ^(NSString *string) { return [formatter dateFromString:string]; }; + blocks[24] = ^(NSString *string) { return [formatter dateFromString:string]?: [formatter2 dateFromString:string]; }; + blocks[25] = ^(NSString *string) { return [formatter dateFromString:string]; }; + blocks[28] = ^(NSString *string) { return [formatter2 dateFromString:string]; }; + blocks[29] = ^(NSString *string) { return [formatter2 dateFromString:string]; }; + } + + { + /* + Fri Sep 04 00:12:21 +0800 2015 // Weibo, Twitter + Fri Sep 04 00:12:21.000 +0800 2015 + */ + NSDateFormatter *formatter = [NSDateFormatter new]; + formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]; + formatter.dateFormat = @"EEE MMM dd HH:mm:ss Z yyyy"; + + NSDateFormatter *formatter2 = [NSDateFormatter new]; + formatter2.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]; + formatter2.dateFormat = @"EEE MMM dd HH:mm:ss.SSS Z yyyy"; + + blocks[30] = ^(NSString *string) { return [formatter dateFromString:string]; }; + blocks[34] = ^(NSString *string) { return [formatter2 dateFromString:string]; }; + } + }); + if (!string) return nil; + if (string.length > kParserNum) return nil; + YYNSDateParseBlock parser = blocks[string.length]; + if (!parser) return nil; + return parser(string); + #undef kParserNum +} + + +/// Get the 'NSBlock' class. +static force_inline Class YYNSBlockClass() { + static Class cls; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + void (^block)(void) = ^{}; + cls = ((NSObject *)block).class; + while (class_getSuperclass(cls) != [NSObject class]) { + cls = class_getSuperclass(cls); + } + }); + return cls; // current is "NSBlock" +} + + + +/** + Get the ISO date formatter. + + ISO8601 format example: + 2010-07-09T16:13:30+12:00 + 2011-01-11T11:11:11+0000 + 2011-01-26T19:06:43Z + + length: 20/24/25 + */ +static force_inline NSDateFormatter *YYISODateFormatter() { + static NSDateFormatter *formatter = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + formatter = [[NSDateFormatter alloc] init]; + formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]; + formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZ"; + }); + return formatter; +} + +/// Get the value with key paths from dictionary +/// The dic should be NSDictionary, and the keyPath should not be nil. +static force_inline id YYValueForKeyPath(__unsafe_unretained NSDictionary *dic, __unsafe_unretained NSArray *keyPaths) { + id value = nil; + for (NSUInteger i = 0, max = keyPaths.count; i < max; i++) { + value = dic[keyPaths[i]]; + if (i + 1 < max) { + if ([value isKindOfClass:[NSDictionary class]]) { + dic = value; + } else { + return nil; + } + } + } + return value; +} + +/// Get the value with multi key (or key path) from dictionary +/// The dic should be NSDictionary +static force_inline id YYValueForMultiKeys(__unsafe_unretained NSDictionary *dic, __unsafe_unretained NSArray *multiKeys) { + id value = nil; + for (NSString *key in multiKeys) { + if ([key isKindOfClass:[NSString class]]) { + value = dic[key]; + if (value) break; + } else { + value = YYValueForKeyPath(dic, (NSArray *)key); + if (value) break; + } + } + return value; +} + + + + +/// A property info in object model. +@interface _YYModelPropertyMeta : NSObject { + @package + NSString *_name; ///< property's name + YYEncodingType _type; ///< property's type + YYEncodingNSType _nsType; ///< property's Foundation type + BOOL _isCNumber; ///< is c number type + Class _cls; ///< property's class, or nil + Class _genericCls; ///< container's generic class, or nil if threr's no generic class + SEL _getter; ///< getter, or nil if the instances cannot respond + SEL _setter; ///< setter, or nil if the instances cannot respond + BOOL _isKVCCompatible; ///< YES if it can access with key-value coding + BOOL _isStructAvailableForKeyedArchiver; ///< YES if the struct can encoded with keyed archiver/unarchiver + BOOL _hasCustomClassFromDictionary; ///< class/generic class implements +modelCustomClassForDictionary: + + /* + property->key: _mappedToKey:key _mappedToKeyPath:nil _mappedToKeyArray:nil + property->keyPath: _mappedToKey:keyPath _mappedToKeyPath:keyPath(array) _mappedToKeyArray:nil + property->keys: _mappedToKey:keys[0] _mappedToKeyPath:nil/keyPath _mappedToKeyArray:keys(array) + */ + NSString *_mappedToKey; ///< the key mapped to + NSArray *_mappedToKeyPath; ///< the key path mapped to (nil if the name is not key path) + NSArray *_mappedToKeyArray; ///< the key(NSString) or keyPath(NSArray) array (nil if not mapped to multiple keys) + YYClassPropertyInfo *_info; ///< property's info + _YYModelPropertyMeta *_next; ///< next meta if there are multiple properties mapped to the same key. +} +@end + +@implementation _YYModelPropertyMeta ++ (instancetype)metaWithClassInfo:(YYClassInfo *)classInfo propertyInfo:(YYClassPropertyInfo *)propertyInfo generic:(Class)generic { + + // support pseudo generic class with protocol name + if (!generic && propertyInfo.protocols) { + for (NSString *protocol in propertyInfo.protocols) { + Class cls = objc_getClass(protocol.UTF8String); + if (cls) { + generic = cls; + break; + } + } + } + + _YYModelPropertyMeta *meta = [self new]; + meta->_name = propertyInfo.name; + meta->_type = propertyInfo.type; + meta->_info = propertyInfo; + meta->_genericCls = generic; + + if ((meta->_type & YYEncodingTypeMask) == YYEncodingTypeObject) { + meta->_nsType = YYClassGetNSType(propertyInfo.cls); + } else { + meta->_isCNumber = YYEncodingTypeIsCNumber(meta->_type); + } + if ((meta->_type & YYEncodingTypeMask) == YYEncodingTypeStruct) { + /* + It seems that NSKeyedUnarchiver cannot decode NSValue except these structs: + */ + static NSSet *types = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSMutableSet *set = [NSMutableSet new]; + // 32 bit + [set addObject:@"{CGSize=ff}"]; + [set addObject:@"{CGPoint=ff}"]; + [set addObject:@"{CGRect={CGPoint=ff}{CGSize=ff}}"]; + [set addObject:@"{CGAffineTransform=ffffff}"]; + [set addObject:@"{UIEdgeInsets=ffff}"]; + [set addObject:@"{UIOffset=ff}"]; + // 64 bit + [set addObject:@"{CGSize=dd}"]; + [set addObject:@"{CGPoint=dd}"]; + [set addObject:@"{CGRect={CGPoint=dd}{CGSize=dd}}"]; + [set addObject:@"{CGAffineTransform=dddddd}"]; + [set addObject:@"{UIEdgeInsets=dddd}"]; + [set addObject:@"{UIOffset=dd}"]; + types = set; + }); + if ([types containsObject:propertyInfo.typeEncoding]) { + meta->_isStructAvailableForKeyedArchiver = YES; + } + } + meta->_cls = propertyInfo.cls; + + if (generic) { + meta->_hasCustomClassFromDictionary = [generic respondsToSelector:@selector(modelCustomClassForDictionary:)]; + } else if (meta->_cls && meta->_nsType == YYEncodingTypeNSUnknown) { + meta->_hasCustomClassFromDictionary = [meta->_cls respondsToSelector:@selector(modelCustomClassForDictionary:)]; + } + + if (propertyInfo.getter) { + if ([classInfo.cls instancesRespondToSelector:propertyInfo.getter]) { + meta->_getter = propertyInfo.getter; + } + } + if (propertyInfo.setter) { + if ([classInfo.cls instancesRespondToSelector:propertyInfo.setter]) { + meta->_setter = propertyInfo.setter; + } + } + + if (meta->_getter && meta->_setter) { + /* + KVC invalid type: + long double + pointer (such as SEL/CoreFoundation object) + */ + switch (meta->_type & YYEncodingTypeMask) { + case YYEncodingTypeBool: + case YYEncodingTypeInt8: + case YYEncodingTypeUInt8: + case YYEncodingTypeInt16: + case YYEncodingTypeUInt16: + case YYEncodingTypeInt32: + case YYEncodingTypeUInt32: + case YYEncodingTypeInt64: + case YYEncodingTypeUInt64: + case YYEncodingTypeFloat: + case YYEncodingTypeDouble: + case YYEncodingTypeObject: + case YYEncodingTypeClass: + case YYEncodingTypeBlock: + case YYEncodingTypeStruct: + case YYEncodingTypeUnion: { + meta->_isKVCCompatible = YES; + } break; + default: break; + } + } + + return meta; +} +@end + + +/// A class info in object model. +@interface _YYModelMeta : NSObject { + @package + YYClassInfo *_classInfo; + /// Key:mapped key and key path, Value:_YYModelPropertyMeta. + NSDictionary *_mapper; + /// Array<_YYModelPropertyMeta>, all property meta of this model. + NSArray *_allPropertyMetas; + /// Array<_YYModelPropertyMeta>, property meta which is mapped to a key path. + NSArray *_keyPathPropertyMetas; + /// Array<_YYModelPropertyMeta>, property meta which is mapped to multi keys. + NSArray *_multiKeysPropertyMetas; + /// The number of mapped key (and key path), same to _mapper.count. + NSUInteger _keyMappedCount; + /// Model class type. + YYEncodingNSType _nsType; + + BOOL _hasCustomWillTransformFromDictionary; + BOOL _hasCustomTransformFromDictionary; + BOOL _hasCustomTransformToDictionary; + BOOL _hasCustomClassFromDictionary; +} +@end + +@implementation _YYModelMeta +- (instancetype)initWithClass:(Class)cls { + YYClassInfo *classInfo = [YYClassInfo classInfoWithClass:cls]; + if (!classInfo) return nil; + self = [super init]; + + // Get black list + NSSet *blacklist = nil; + if ([cls respondsToSelector:@selector(modelPropertyBlacklist)]) { + NSArray *properties = [(id)cls modelPropertyBlacklist]; + if (properties) { + blacklist = [NSSet setWithArray:properties]; + } + } + + // Get white list + NSSet *whitelist = nil; + if ([cls respondsToSelector:@selector(modelPropertyWhitelist)]) { + NSArray *properties = [(id)cls modelPropertyWhitelist]; + if (properties) { + whitelist = [NSSet setWithArray:properties]; + } + } + + // Get container property's generic class + NSDictionary *genericMapper = nil; + if ([cls respondsToSelector:@selector(modelContainerPropertyGenericClass)]) { + genericMapper = [(id)cls modelContainerPropertyGenericClass]; + if (genericMapper) { + NSMutableDictionary *tmp = [NSMutableDictionary new]; + [genericMapper enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + if (![key isKindOfClass:[NSString class]]) return; + Class meta = object_getClass(obj); + if (!meta) return; + if (class_isMetaClass(meta)) { + tmp[key] = obj; + } else if ([obj isKindOfClass:[NSString class]]) { + Class cls = NSClassFromString(obj); + if (cls) { + tmp[key] = cls; + } + } + }]; + genericMapper = tmp; + } + } + + // Create all property metas. + NSMutableDictionary *allPropertyMetas = [NSMutableDictionary new]; + YYClassInfo *curClassInfo = classInfo; + while (curClassInfo && curClassInfo.superCls != nil) { // recursive parse super class, but ignore root class (NSObject/NSProxy) + for (YYClassPropertyInfo *propertyInfo in curClassInfo.propertyInfos.allValues) { + if (!propertyInfo.name) continue; + if (blacklist && [blacklist containsObject:propertyInfo.name]) continue; + if (whitelist && ![whitelist containsObject:propertyInfo.name]) continue; + _YYModelPropertyMeta *meta = [_YYModelPropertyMeta metaWithClassInfo:classInfo + propertyInfo:propertyInfo + generic:genericMapper[propertyInfo.name]]; + if (!meta || !meta->_name) continue; + if (!meta->_getter || !meta->_setter) continue; + if (allPropertyMetas[meta->_name]) continue; + allPropertyMetas[meta->_name] = meta; + } + curClassInfo = curClassInfo.superClassInfo; + } + if (allPropertyMetas.count) _allPropertyMetas = allPropertyMetas.allValues.copy; + + // create mapper + NSMutableDictionary *mapper = [NSMutableDictionary new]; + NSMutableArray *keyPathPropertyMetas = [NSMutableArray new]; + NSMutableArray *multiKeysPropertyMetas = [NSMutableArray new]; + + if ([cls respondsToSelector:@selector(modelCustomPropertyMapper)]) { + NSDictionary *customMapper = [(id )cls modelCustomPropertyMapper]; + [customMapper enumerateKeysAndObjectsUsingBlock:^(NSString *propertyName, NSString *mappedToKey, BOOL *stop) { + _YYModelPropertyMeta *propertyMeta = allPropertyMetas[propertyName]; + if (!propertyMeta) return; + [allPropertyMetas removeObjectForKey:propertyName]; + + if ([mappedToKey isKindOfClass:[NSString class]]) { + if (mappedToKey.length == 0) return; + + propertyMeta->_mappedToKey = mappedToKey; + NSArray *keyPath = [mappedToKey componentsSeparatedByString:@"."]; + for (NSString *onePath in keyPath) { + if (onePath.length == 0) { + NSMutableArray *tmp = keyPath.mutableCopy; + [tmp removeObject:@""]; + keyPath = tmp; + break; + } + } + if (keyPath.count > 1) { + propertyMeta->_mappedToKeyPath = keyPath; + [keyPathPropertyMetas addObject:propertyMeta]; + } + propertyMeta->_next = mapper[mappedToKey] ?: nil; + mapper[mappedToKey] = propertyMeta; + + } else if ([mappedToKey isKindOfClass:[NSArray class]]) { + + NSMutableArray *mappedToKeyArray = [NSMutableArray new]; + for (NSString *oneKey in ((NSArray *)mappedToKey)) { + if (![oneKey isKindOfClass:[NSString class]]) continue; + if (oneKey.length == 0) continue; + + NSArray *keyPath = [oneKey componentsSeparatedByString:@"."]; + if (keyPath.count > 1) { + [mappedToKeyArray addObject:keyPath]; + } else { + [mappedToKeyArray addObject:oneKey]; + } + + if (!propertyMeta->_mappedToKey) { + propertyMeta->_mappedToKey = oneKey; + propertyMeta->_mappedToKeyPath = keyPath.count > 1 ? keyPath : nil; + } + } + if (!propertyMeta->_mappedToKey) return; + + propertyMeta->_mappedToKeyArray = mappedToKeyArray; + [multiKeysPropertyMetas addObject:propertyMeta]; + + propertyMeta->_next = mapper[mappedToKey] ?: nil; + mapper[mappedToKey] = propertyMeta; + } + }]; + } + + [allPropertyMetas enumerateKeysAndObjectsUsingBlock:^(NSString *name, _YYModelPropertyMeta *propertyMeta, BOOL *stop) { + propertyMeta->_mappedToKey = name; + propertyMeta->_next = mapper[name] ?: nil; + mapper[name] = propertyMeta; + }]; + + if (mapper.count) _mapper = mapper; + if (keyPathPropertyMetas) _keyPathPropertyMetas = keyPathPropertyMetas; + if (multiKeysPropertyMetas) _multiKeysPropertyMetas = multiKeysPropertyMetas; + + _classInfo = classInfo; + _keyMappedCount = _allPropertyMetas.count; + _nsType = YYClassGetNSType(cls); + _hasCustomWillTransformFromDictionary = ([cls instancesRespondToSelector:@selector(modelCustomWillTransformFromDictionary:)]); + _hasCustomTransformFromDictionary = ([cls instancesRespondToSelector:@selector(modelCustomTransformFromDictionary:)]); + _hasCustomTransformToDictionary = ([cls instancesRespondToSelector:@selector(modelCustomTransformToDictionary:)]); + _hasCustomClassFromDictionary = ([cls respondsToSelector:@selector(modelCustomClassForDictionary:)]); + + return self; +} + +/// Returns the cached model class meta ++ (instancetype)metaWithClass:(Class)cls { + if (!cls) return nil; + static CFMutableDictionaryRef cache; + static dispatch_once_t onceToken; + static dispatch_semaphore_t lock; + dispatch_once(&onceToken, ^{ + cache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + lock = dispatch_semaphore_create(1); + }); + dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER); + _YYModelMeta *meta = CFDictionaryGetValue(cache, (__bridge const void *)(cls)); + dispatch_semaphore_signal(lock); + if (!meta || meta->_classInfo.needUpdate) { + meta = [[_YYModelMeta alloc] initWithClass:cls]; + if (meta) { + dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER); + CFDictionarySetValue(cache, (__bridge const void *)(cls), (__bridge const void *)(meta)); + dispatch_semaphore_signal(lock); + } + } + return meta; +} + +@end + + +/** + Get number from property. + @discussion Caller should hold strong reference to the parameters before this function returns. + @param model Should not be nil. + @param meta Should not be nil, meta.isCNumber should be YES, meta.getter should not be nil. + @return A number object, or nil if failed. + */ +static force_inline NSNumber *ModelCreateNumberFromProperty(__unsafe_unretained id model, + __unsafe_unretained _YYModelPropertyMeta *meta) { + switch (meta->_type & YYEncodingTypeMask) { + case YYEncodingTypeBool: { + return @(((bool (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter)); + } + case YYEncodingTypeInt8: { + return @(((int8_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter)); + } + case YYEncodingTypeUInt8: { + return @(((uint8_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter)); + } + case YYEncodingTypeInt16: { + return @(((int16_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter)); + } + case YYEncodingTypeUInt16: { + return @(((uint16_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter)); + } + case YYEncodingTypeInt32: { + return @(((int32_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter)); + } + case YYEncodingTypeUInt32: { + return @(((uint32_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter)); + } + case YYEncodingTypeInt64: { + return @(((int64_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter)); + } + case YYEncodingTypeUInt64: { + return @(((uint64_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter)); + } + case YYEncodingTypeFloat: { + float num = ((float (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter); + if (isnan(num) || isinf(num)) return nil; + return @(num); + } + case YYEncodingTypeDouble: { + double num = ((double (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter); + if (isnan(num) || isinf(num)) return nil; + return @(num); + } + case YYEncodingTypeLongDouble: { + double num = ((long double (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter); + if (isnan(num) || isinf(num)) return nil; + return @(num); + } + default: return nil; + } +} + +/** + Set number to property. + @discussion Caller should hold strong reference to the parameters before this function returns. + @param model Should not be nil. + @param num Can be nil. + @param meta Should not be nil, meta.isCNumber should be YES, meta.setter should not be nil. + */ +static force_inline void ModelSetNumberToProperty(__unsafe_unretained id model, + __unsafe_unretained NSNumber *num, + __unsafe_unretained _YYModelPropertyMeta *meta) { + switch (meta->_type & YYEncodingTypeMask) { + case YYEncodingTypeBool: { + ((void (*)(id, SEL, bool))(void *) objc_msgSend)((id)model, meta->_setter, num.boolValue); + } break; + case YYEncodingTypeInt8: { + ((void (*)(id, SEL, int8_t))(void *) objc_msgSend)((id)model, meta->_setter, (int8_t)num.charValue); + } break; + case YYEncodingTypeUInt8: { + ((void (*)(id, SEL, uint8_t))(void *) objc_msgSend)((id)model, meta->_setter, (uint8_t)num.unsignedCharValue); + } break; + case YYEncodingTypeInt16: { + ((void (*)(id, SEL, int16_t))(void *) objc_msgSend)((id)model, meta->_setter, (int16_t)num.shortValue); + } break; + case YYEncodingTypeUInt16: { + ((void (*)(id, SEL, uint16_t))(void *) objc_msgSend)((id)model, meta->_setter, (uint16_t)num.unsignedShortValue); + } break; + case YYEncodingTypeInt32: { + ((void (*)(id, SEL, int32_t))(void *) objc_msgSend)((id)model, meta->_setter, (int32_t)num.intValue); + } + case YYEncodingTypeUInt32: { + ((void (*)(id, SEL, uint32_t))(void *) objc_msgSend)((id)model, meta->_setter, (uint32_t)num.unsignedIntValue); + } break; + case YYEncodingTypeInt64: { + if ([num isKindOfClass:[NSDecimalNumber class]]) { + ((void (*)(id, SEL, int64_t))(void *) objc_msgSend)((id)model, meta->_setter, (int64_t)num.stringValue.longLongValue); + } else { + ((void (*)(id, SEL, uint64_t))(void *) objc_msgSend)((id)model, meta->_setter, (uint64_t)num.longLongValue); + } + } break; + case YYEncodingTypeUInt64: { + if ([num isKindOfClass:[NSDecimalNumber class]]) { + ((void (*)(id, SEL, int64_t))(void *) objc_msgSend)((id)model, meta->_setter, (int64_t)num.stringValue.longLongValue); + } else { + ((void (*)(id, SEL, uint64_t))(void *) objc_msgSend)((id)model, meta->_setter, (uint64_t)num.unsignedLongLongValue); + } + } break; + case YYEncodingTypeFloat: { + float f = num.floatValue; + if (isnan(f) || isinf(f)) f = 0; + ((void (*)(id, SEL, float))(void *) objc_msgSend)((id)model, meta->_setter, f); + } break; + case YYEncodingTypeDouble: { + double d = num.doubleValue; + if (isnan(d) || isinf(d)) d = 0; + ((void (*)(id, SEL, double))(void *) objc_msgSend)((id)model, meta->_setter, d); + } break; + case YYEncodingTypeLongDouble: { + long double d = num.doubleValue; + if (isnan(d) || isinf(d)) d = 0; + ((void (*)(id, SEL, long double))(void *) objc_msgSend)((id)model, meta->_setter, (long double)d); + } // break; commented for code coverage in next line + default: break; + } +} + +/** + Set value to model with a property meta. + + @discussion Caller should hold strong reference to the parameters before this function returns. + + @param model Should not be nil. + @param value Should not be nil, but can be NSNull. + @param meta Should not be nil, and meta->_setter should not be nil. + */ +static void ModelSetValueForProperty(__unsafe_unretained id model, + __unsafe_unretained id value, + __unsafe_unretained _YYModelPropertyMeta *meta) { + if (meta->_isCNumber) { + NSNumber *num = YYNSNumberCreateFromID(value); + ModelSetNumberToProperty(model, num, meta); + if (num) [num class]; // hold the number + } else if (meta->_nsType) { + if (value == (id)kCFNull) { + ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (id)nil); + } else { + switch (meta->_nsType) { + case YYEncodingTypeNSString: + case YYEncodingTypeNSMutableString: { + if ([value isKindOfClass:[NSString class]]) { + if (meta->_nsType == YYEncodingTypeNSString) { + ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value); + } else { + ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, ((NSString *)value).mutableCopy); + } + } else if ([value isKindOfClass:[NSNumber class]]) { + ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, + meta->_setter, + (meta->_nsType == YYEncodingTypeNSString) ? + ((NSNumber *)value).stringValue : + ((NSNumber *)value).stringValue.mutableCopy); + } else if ([value isKindOfClass:[NSData class]]) { + NSMutableString *string = [[NSMutableString alloc] initWithData:value encoding:NSUTF8StringEncoding]; + ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, string); + } else if ([value isKindOfClass:[NSURL class]]) { + ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, + meta->_setter, + (meta->_nsType == YYEncodingTypeNSString) ? + ((NSURL *)value).absoluteString : + ((NSURL *)value).absoluteString.mutableCopy); + } else if ([value isKindOfClass:[NSAttributedString class]]) { + ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, + meta->_setter, + (meta->_nsType == YYEncodingTypeNSString) ? + ((NSAttributedString *)value).string : + ((NSAttributedString *)value).string.mutableCopy); + } + } break; + + case YYEncodingTypeNSValue: + case YYEncodingTypeNSNumber: + case YYEncodingTypeNSDecimalNumber: { + if (meta->_nsType == YYEncodingTypeNSNumber) { + ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, YYNSNumberCreateFromID(value)); + } else if (meta->_nsType == YYEncodingTypeNSDecimalNumber) { + if ([value isKindOfClass:[NSDecimalNumber class]]) { + ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value); + } else if ([value isKindOfClass:[NSNumber class]]) { + NSDecimalNumber *decNum = [NSDecimalNumber decimalNumberWithDecimal:[((NSNumber *)value) decimalValue]]; + ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, decNum); + } else if ([value isKindOfClass:[NSString class]]) { + NSDecimalNumber *decNum = [NSDecimalNumber decimalNumberWithString:value]; + NSDecimal dec = decNum.decimalValue; + if (dec._length == 0 && dec._isNegative) { + decNum = nil; // NaN + } + ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, decNum); + } + } else { // YYEncodingTypeNSValue + if ([value isKindOfClass:[NSValue class]]) { + ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value); + } + } + } break; + + case YYEncodingTypeNSData: + case YYEncodingTypeNSMutableData: { + if ([value isKindOfClass:[NSData class]]) { + if (meta->_nsType == YYEncodingTypeNSData) { + ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value); + } else { + NSMutableData *data = ((NSData *)value).mutableCopy; + ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, data); + } + } else if ([value isKindOfClass:[NSString class]]) { + NSData *data = [(NSString *)value dataUsingEncoding:NSUTF8StringEncoding]; + if (meta->_nsType == YYEncodingTypeNSMutableData) { + data = ((NSData *)data).mutableCopy; + } + ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, data); + } + } break; + + case YYEncodingTypeNSDate: { + if ([value isKindOfClass:[NSDate class]]) { + ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value); + } else if ([value isKindOfClass:[NSString class]]) { + ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, YYNSDateFromString(value)); + } + } break; + + case YYEncodingTypeNSURL: { + if ([value isKindOfClass:[NSURL class]]) { + ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value); + } else if ([value isKindOfClass:[NSString class]]) { + NSCharacterSet *set = [NSCharacterSet whitespaceAndNewlineCharacterSet]; + NSString *str = [value stringByTrimmingCharactersInSet:set]; + if (str.length == 0) { + ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, nil); + } else { + ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, [[NSURL alloc] initWithString:str]); + } + } + } break; + + case YYEncodingTypeNSArray: + case YYEncodingTypeNSMutableArray: { + if (meta->_genericCls) { + NSArray *valueArr = nil; + if ([value isKindOfClass:[NSArray class]]) valueArr = value; + else if ([value isKindOfClass:[NSSet class]]) valueArr = ((NSSet *)value).allObjects; + if (valueArr) { + NSMutableArray *objectArr = [NSMutableArray new]; + for (id one in valueArr) { + if ([one isKindOfClass:meta->_genericCls]) { + [objectArr addObject:one]; + } else if ([one isKindOfClass:[NSDictionary class]]) { + Class cls = meta->_genericCls; + if (meta->_hasCustomClassFromDictionary) { + cls = [cls modelCustomClassForDictionary:one]; + if (!cls) cls = meta->_genericCls; // for xcode code coverage + } + NSObject *newOne = [cls new]; + [newOne yy_modelSetWithDictionary:one]; + if (newOne) [objectArr addObject:newOne]; + } + } + ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, objectArr); + } + } else { + if ([value isKindOfClass:[NSArray class]]) { + if (meta->_nsType == YYEncodingTypeNSArray) { + ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value); + } else { + ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, + meta->_setter, + ((NSArray *)value).mutableCopy); + } + } else if ([value isKindOfClass:[NSSet class]]) { + if (meta->_nsType == YYEncodingTypeNSArray) { + ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, ((NSSet *)value).allObjects); + } else { + ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, + meta->_setter, + ((NSSet *)value).allObjects.mutableCopy); + } + } + } + } break; + + case YYEncodingTypeNSDictionary: + case YYEncodingTypeNSMutableDictionary: { + if ([value isKindOfClass:[NSDictionary class]]) { + if (meta->_genericCls) { + NSMutableDictionary *dic = [NSMutableDictionary new]; + [((NSDictionary *)value) enumerateKeysAndObjectsUsingBlock:^(NSString *oneKey, id oneValue, BOOL *stop) { + if ([oneValue isKindOfClass:[NSDictionary class]]) { + Class cls = meta->_genericCls; + if (meta->_hasCustomClassFromDictionary) { + cls = [cls modelCustomClassForDictionary:oneValue]; + if (!cls) cls = meta->_genericCls; // for xcode code coverage + } + NSObject *newOne = [cls new]; + [newOne yy_modelSetWithDictionary:(id)oneValue]; + if (newOne) dic[oneKey] = newOne; + } + }]; + ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, dic); + } else { + if (meta->_nsType == YYEncodingTypeNSDictionary) { + ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value); + } else { + ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, + meta->_setter, + ((NSDictionary *)value).mutableCopy); + } + } + } + } break; + + case YYEncodingTypeNSSet: + case YYEncodingTypeNSMutableSet: { + NSSet *valueSet = nil; + if ([value isKindOfClass:[NSArray class]]) valueSet = [NSMutableSet setWithArray:value]; + else if ([value isKindOfClass:[NSSet class]]) valueSet = ((NSSet *)value); + + if (meta->_genericCls) { + NSMutableSet *set = [NSMutableSet new]; + for (id one in valueSet) { + if ([one isKindOfClass:meta->_genericCls]) { + [set addObject:one]; + } else if ([one isKindOfClass:[NSDictionary class]]) { + Class cls = meta->_genericCls; + if (meta->_hasCustomClassFromDictionary) { + cls = [cls modelCustomClassForDictionary:one]; + if (!cls) cls = meta->_genericCls; // for xcode code coverage + } + NSObject *newOne = [cls new]; + [newOne yy_modelSetWithDictionary:one]; + if (newOne) [set addObject:newOne]; + } + } + ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, set); + } else { + if (meta->_nsType == YYEncodingTypeNSSet) { + ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, valueSet); + } else { + ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, + meta->_setter, + ((NSSet *)valueSet).mutableCopy); + } + } + } // break; commented for code coverage in next line + + default: break; + } + } + } else { + BOOL isNull = (value == (id)kCFNull); + switch (meta->_type & YYEncodingTypeMask) { + case YYEncodingTypeObject: { + if (isNull) { + ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (id)nil); + } else if ([value isKindOfClass:meta->_cls] || !meta->_cls) { + ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (id)value); + } else if ([value isKindOfClass:[NSDictionary class]]) { + NSObject *one = nil; + if (meta->_getter) { + one = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter); + } + if (one) { + [one yy_modelSetWithDictionary:value]; + } else { + Class cls = meta->_cls; + if (meta->_hasCustomClassFromDictionary) { + cls = [cls modelCustomClassForDictionary:value]; + if (!cls) cls = meta->_genericCls; // for xcode code coverage + } + one = [cls new]; + [one yy_modelSetWithDictionary:value]; + ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (id)one); + } + } + } break; + + case YYEncodingTypeClass: { + if (isNull) { + ((void (*)(id, SEL, Class))(void *) objc_msgSend)((id)model, meta->_setter, (Class)NULL); + } else { + Class cls = nil; + if ([value isKindOfClass:[NSString class]]) { + cls = NSClassFromString(value); + if (cls) { + ((void (*)(id, SEL, Class))(void *) objc_msgSend)((id)model, meta->_setter, (Class)cls); + } + } else { + cls = object_getClass(value); + if (cls) { + if (class_isMetaClass(cls)) { + ((void (*)(id, SEL, Class))(void *) objc_msgSend)((id)model, meta->_setter, (Class)value); + } + } + } + } + } break; + + case YYEncodingTypeSEL: { + if (isNull) { + ((void (*)(id, SEL, SEL))(void *) objc_msgSend)((id)model, meta->_setter, (SEL)NULL); + } else if ([value isKindOfClass:[NSString class]]) { + SEL sel = NSSelectorFromString(value); + if (sel) ((void (*)(id, SEL, SEL))(void *) objc_msgSend)((id)model, meta->_setter, (SEL)sel); + } + } break; + + case YYEncodingTypeBlock: { + if (isNull) { + ((void (*)(id, SEL, void (^)()))(void *) objc_msgSend)((id)model, meta->_setter, (void (^)())NULL); + } else if ([value isKindOfClass:YYNSBlockClass()]) { + ((void (*)(id, SEL, void (^)()))(void *) objc_msgSend)((id)model, meta->_setter, (void (^)())value); + } + } break; + + case YYEncodingTypeStruct: + case YYEncodingTypeUnion: + case YYEncodingTypeCArray: { + if ([value isKindOfClass:[NSValue class]]) { + const char *valueType = ((NSValue *)value).objCType; + const char *metaType = meta->_info.typeEncoding.UTF8String; + if (valueType && metaType && strcmp(valueType, metaType) == 0) { + [model setValue:value forKey:meta->_name]; + } + } + } break; + + case YYEncodingTypePointer: + case YYEncodingTypeCString: { + if (isNull) { + ((void (*)(id, SEL, void *))(void *) objc_msgSend)((id)model, meta->_setter, (void *)NULL); + } else if ([value isKindOfClass:[NSValue class]]) { + NSValue *nsValue = value; + if (nsValue.objCType && strcmp(nsValue.objCType, "^v") == 0) { + ((void (*)(id, SEL, void *))(void *) objc_msgSend)((id)model, meta->_setter, nsValue.pointerValue); + } + } + } // break; commented for code coverage in next line + + default: break; + } + } +} + + +typedef struct { + void *modelMeta; ///< _YYModelMeta + void *model; ///< id (self) + void *dictionary; ///< NSDictionary (json) +} ModelSetContext; + +/** + Apply function for dictionary, to set the key-value pair to model. + + @param _key should not be nil, NSString. + @param _value should not be nil. + @param _context _context.modelMeta and _context.model should not be nil. + */ +static void ModelSetWithDictionaryFunction(const void *_key, const void *_value, void *_context) { + ModelSetContext *context = _context; + __unsafe_unretained _YYModelMeta *meta = (__bridge _YYModelMeta *)(context->modelMeta); + __unsafe_unretained _YYModelPropertyMeta *propertyMeta = [meta->_mapper objectForKey:(__bridge id)(_key)]; + __unsafe_unretained id model = (__bridge id)(context->model); + while (propertyMeta) { + if (propertyMeta->_setter) { + ModelSetValueForProperty(model, (__bridge __unsafe_unretained id)_value, propertyMeta); + } + propertyMeta = propertyMeta->_next; + }; +} + +/** + Apply function for model property meta, to set dictionary to model. + + @param _propertyMeta should not be nil, _YYModelPropertyMeta. + @param _context _context.model and _context.dictionary should not be nil. + */ +static void ModelSetWithPropertyMetaArrayFunction(const void *_propertyMeta, void *_context) { + ModelSetContext *context = _context; + __unsafe_unretained NSDictionary *dictionary = (__bridge NSDictionary *)(context->dictionary); + __unsafe_unretained _YYModelPropertyMeta *propertyMeta = (__bridge _YYModelPropertyMeta *)(_propertyMeta); + if (!propertyMeta->_setter) return; + id value = nil; + + if (propertyMeta->_mappedToKeyArray) { + value = YYValueForMultiKeys(dictionary, propertyMeta->_mappedToKeyArray); + } else if (propertyMeta->_mappedToKeyPath) { + value = YYValueForKeyPath(dictionary, propertyMeta->_mappedToKeyPath); + } else { + value = [dictionary objectForKey:propertyMeta->_mappedToKey]; + } + + if (value) { + __unsafe_unretained id model = (__bridge id)(context->model); + ModelSetValueForProperty(model, value, propertyMeta); + } +} + +/** + Returns a valid JSON object (NSArray/NSDictionary/NSString/NSNumber/NSNull), + or nil if an error occurs. + + @param model Model, can be nil. + @return JSON object, nil if an error occurs. + */ +static id ModelToJSONObjectRecursive(NSObject *model) { + if (!model || model == (id)kCFNull) return model; + if ([model isKindOfClass:[NSString class]]) return model; + if ([model isKindOfClass:[NSNumber class]]) return model; + if ([model isKindOfClass:[NSDictionary class]]) { + if ([NSJSONSerialization isValidJSONObject:model]) return model; + NSMutableDictionary *newDic = [NSMutableDictionary new]; + [((NSDictionary *)model) enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) { + NSString *stringKey = [key isKindOfClass:[NSString class]] ? key : key.description; + if (!stringKey) return; + id jsonObj = ModelToJSONObjectRecursive(obj); + if (!jsonObj) jsonObj = (id)kCFNull; + newDic[stringKey] = jsonObj; + }]; + return newDic; + } + if ([model isKindOfClass:[NSSet class]]) { + NSArray *array = ((NSSet *)model).allObjects; + if ([NSJSONSerialization isValidJSONObject:array]) return array; + NSMutableArray *newArray = [NSMutableArray new]; + for (id obj in array) { + if ([obj isKindOfClass:[NSString class]] || [obj isKindOfClass:[NSNumber class]]) { + [newArray addObject:obj]; + } else { + id jsonObj = ModelToJSONObjectRecursive(obj); + if (jsonObj && jsonObj != (id)kCFNull) [newArray addObject:jsonObj]; + } + } + return newArray; + } + if ([model isKindOfClass:[NSArray class]]) { + if ([NSJSONSerialization isValidJSONObject:model]) return model; + NSMutableArray *newArray = [NSMutableArray new]; + for (id obj in (NSArray *)model) { + if ([obj isKindOfClass:[NSString class]] || [obj isKindOfClass:[NSNumber class]]) { + [newArray addObject:obj]; + } else { + id jsonObj = ModelToJSONObjectRecursive(obj); + if (jsonObj && jsonObj != (id)kCFNull) [newArray addObject:jsonObj]; + } + } + return newArray; + } + if ([model isKindOfClass:[NSURL class]]) return ((NSURL *)model).absoluteString; + if ([model isKindOfClass:[NSAttributedString class]]) return ((NSAttributedString *)model).string; + if ([model isKindOfClass:[NSDate class]]) return [YYISODateFormatter() stringFromDate:(id)model]; + if ([model isKindOfClass:[NSData class]]) return nil; + + + _YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:[model class]]; + if (!modelMeta || modelMeta->_keyMappedCount == 0) return nil; + NSMutableDictionary *result = [[NSMutableDictionary alloc] initWithCapacity:64]; + __unsafe_unretained NSMutableDictionary *dic = result; // avoid retain and release in block + [modelMeta->_mapper enumerateKeysAndObjectsUsingBlock:^(NSString *propertyMappedKey, _YYModelPropertyMeta *propertyMeta, BOOL *stop) { + if (!propertyMeta->_getter) return; + + id value = nil; + if (propertyMeta->_isCNumber) { + value = ModelCreateNumberFromProperty(model, propertyMeta); + } else if (propertyMeta->_nsType) { + id v = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter); + value = ModelToJSONObjectRecursive(v); + } else { + switch (propertyMeta->_type & YYEncodingTypeMask) { + case YYEncodingTypeObject: { + id v = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter); + value = ModelToJSONObjectRecursive(v); + if (value == (id)kCFNull) value = nil; + } break; + case YYEncodingTypeClass: { + Class v = ((Class (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter); + value = v ? NSStringFromClass(v) : nil; + } break; + case YYEncodingTypeSEL: { + SEL v = ((SEL (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter); + value = v ? NSStringFromSelector(v) : nil; + } break; + default: break; + } + } + if (!value) return; + + if (propertyMeta->_mappedToKeyPath) { + NSMutableDictionary *superDic = dic; + NSMutableDictionary *subDic = nil; + for (NSUInteger i = 0, max = propertyMeta->_mappedToKeyPath.count; i < max; i++) { + NSString *key = propertyMeta->_mappedToKeyPath[i]; + if (i + 1 == max) { // end + if (!superDic[key]) superDic[key] = value; + break; + } + + subDic = superDic[key]; + if (subDic) { + if ([subDic isKindOfClass:[NSDictionary class]]) { + subDic = subDic.mutableCopy; + superDic[key] = subDic; + } else { + break; + } + } else { + subDic = [NSMutableDictionary new]; + superDic[key] = subDic; + } + superDic = subDic; + subDic = nil; + } + } else { + if (!dic[propertyMeta->_mappedToKey]) { + dic[propertyMeta->_mappedToKey] = value; + } + } + }]; + + if (modelMeta->_hasCustomTransformToDictionary) { + BOOL suc = [((id)model) modelCustomTransformToDictionary:dic]; + if (!suc) return nil; + } + return result; +} + +/// Add indent to string (exclude first line) +static NSMutableString *ModelDescriptionAddIndent(NSMutableString *desc, NSUInteger indent) { + for (NSUInteger i = 0, max = desc.length; i < max; i++) { + unichar c = [desc characterAtIndex:i]; + if (c == '\n') { + for (NSUInteger j = 0; j < indent; j++) { + [desc insertString:@" " atIndex:i + 1]; + } + i += indent * 4; + max += indent * 4; + } + } + return desc; +} + +/// Generaate a description string +static NSString *ModelDescription(NSObject *model) { + static const int kDescMaxLength = 100; + if (!model) return @""; + if (model == (id)kCFNull) return @""; + if (![model isKindOfClass:[NSObject class]]) return [NSString stringWithFormat:@"%@",model]; + + + _YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:model.class]; + switch (modelMeta->_nsType) { + case YYEncodingTypeNSString: case YYEncodingTypeNSMutableString: { + return [NSString stringWithFormat:@"\"%@\"",model]; + } + + case YYEncodingTypeNSValue: + case YYEncodingTypeNSData: case YYEncodingTypeNSMutableData: { + NSString *tmp = model.description; + if (tmp.length > kDescMaxLength) { + tmp = [tmp substringToIndex:kDescMaxLength]; + tmp = [tmp stringByAppendingString:@"..."]; + } + return tmp; + } + + case YYEncodingTypeNSNumber: + case YYEncodingTypeNSDecimalNumber: + case YYEncodingTypeNSDate: + case YYEncodingTypeNSURL: { + return [NSString stringWithFormat:@"%@",model]; + } + + case YYEncodingTypeNSSet: case YYEncodingTypeNSMutableSet: { + model = ((NSSet *)model).allObjects; + } // no break + + case YYEncodingTypeNSArray: case YYEncodingTypeNSMutableArray: { + NSArray *array = (id)model; + NSMutableString *desc = [NSMutableString new]; + if (array.count == 0) { + return [desc stringByAppendingString:@"[]"]; + } else { + [desc appendFormat:@"[\n"]; + for (NSUInteger i = 0, max = array.count; i < max; i++) { + NSObject *obj = array[i]; + [desc appendString:@" "]; + [desc appendString:ModelDescriptionAddIndent(ModelDescription(obj).mutableCopy, 1)]; + [desc appendString:(i + 1 == max) ? @"\n" : @";\n"]; + } + [desc appendString:@"]"]; + return desc; + } + } + case YYEncodingTypeNSDictionary: case YYEncodingTypeNSMutableDictionary: { + NSDictionary *dic = (id)model; + NSMutableString *desc = [NSMutableString new]; + if (dic.count == 0) { + return [desc stringByAppendingString:@"{}"]; + } else { + NSArray *keys = dic.allKeys; + + [desc appendFormat:@"{\n"]; + for (NSUInteger i = 0, max = keys.count; i < max; i++) { + NSString *key = keys[i]; + NSObject *value = dic[key]; + [desc appendString:@" "]; + [desc appendFormat:@"%@ = %@",key, ModelDescriptionAddIndent(ModelDescription(value).mutableCopy, 1)]; + [desc appendString:(i + 1 == max) ? @"\n" : @";\n"]; + } + [desc appendString:@"}"]; + } + return desc; + } + + default: { + NSMutableString *desc = [NSMutableString new]; + [desc appendFormat:@"<%@: %p>", model.class, model]; + if (modelMeta->_allPropertyMetas.count == 0) return desc; + + // sort property names + NSArray *properties = [modelMeta->_allPropertyMetas + sortedArrayUsingComparator:^NSComparisonResult(_YYModelPropertyMeta *p1, _YYModelPropertyMeta *p2) { + return [p1->_name compare:p2->_name]; + }]; + + [desc appendFormat:@" {\n"]; + for (NSUInteger i = 0, max = properties.count; i < max; i++) { + _YYModelPropertyMeta *property = properties[i]; + NSString *propertyDesc; + if (property->_isCNumber) { + NSNumber *num = ModelCreateNumberFromProperty(model, property); + propertyDesc = num.stringValue; + } else { + switch (property->_type & YYEncodingTypeMask) { + case YYEncodingTypeObject: { + id v = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, property->_getter); + propertyDesc = ModelDescription(v); + if (!propertyDesc) propertyDesc = @""; + } break; + case YYEncodingTypeClass: { + id v = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, property->_getter); + propertyDesc = ((NSObject *)v).description; + if (!propertyDesc) propertyDesc = @""; + } break; + case YYEncodingTypeSEL: { + SEL sel = ((SEL (*)(id, SEL))(void *) objc_msgSend)((id)model, property->_getter); + if (sel) propertyDesc = NSStringFromSelector(sel); + else propertyDesc = @""; + } break; + case YYEncodingTypeBlock: { + id block = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, property->_getter); + propertyDesc = block ? ((NSObject *)block).description : @""; + } break; + case YYEncodingTypeCArray: case YYEncodingTypeCString: case YYEncodingTypePointer: { + void *pointer = ((void* (*)(id, SEL))(void *) objc_msgSend)((id)model, property->_getter); + propertyDesc = [NSString stringWithFormat:@"%p",pointer]; + } break; + case YYEncodingTypeStruct: case YYEncodingTypeUnion: { + NSValue *value = [model valueForKey:property->_name]; + propertyDesc = value ? value.description : @"{unknown}"; + } break; + default: propertyDesc = @""; + } + } + + propertyDesc = ModelDescriptionAddIndent(propertyDesc.mutableCopy, 1); + [desc appendFormat:@" %@ = %@",property->_name, propertyDesc]; + [desc appendString:(i + 1 == max) ? @"\n" : @";\n"]; + } + [desc appendFormat:@"}"]; + return desc; + } + } +} + + +@implementation NSObject (YYModel) + ++ (NSDictionary *)_yy_dictionaryWithJSON:(id)json { + if (!json || json == (id)kCFNull) return nil; + NSDictionary *dic = nil; + NSData *jsonData = nil; + if ([json isKindOfClass:[NSDictionary class]]) { + dic = json; + } else if ([json isKindOfClass:[NSString class]]) { + jsonData = [(NSString *)json dataUsingEncoding : NSUTF8StringEncoding]; + } else if ([json isKindOfClass:[NSData class]]) { + jsonData = json; + } + if (jsonData) { + dic = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:NULL]; + if (![dic isKindOfClass:[NSDictionary class]]) dic = nil; + } + return dic; +} + ++ (instancetype)yy_modelWithJSON:(id)json { + NSDictionary *dic = [self _yy_dictionaryWithJSON:json]; + return [self yy_modelWithDictionary:dic]; +} + ++ (instancetype)yy_modelWithDictionary:(NSDictionary *)dictionary { + if (!dictionary || dictionary == (id)kCFNull) return nil; + if (![dictionary isKindOfClass:[NSDictionary class]]) return nil; + + Class cls = [self class]; + _YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:cls]; + if (modelMeta->_hasCustomClassFromDictionary) { + cls = [cls modelCustomClassForDictionary:dictionary] ?: cls; + } + + NSObject *one = [cls new]; + if ([one yy_modelSetWithDictionary:dictionary]) return one; + return nil; +} + +- (BOOL)yy_modelSetWithJSON:(id)json { + NSDictionary *dic = [NSObject _yy_dictionaryWithJSON:json]; + return [self yy_modelSetWithDictionary:dic]; +} + +- (BOOL)yy_modelSetWithDictionary:(NSDictionary *)dic { + if (!dic || dic == (id)kCFNull) return NO; + if (![dic isKindOfClass:[NSDictionary class]]) return NO; + + + _YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:object_getClass(self)]; + if (modelMeta->_keyMappedCount == 0) return NO; + + if (modelMeta->_hasCustomWillTransformFromDictionary) { + dic = [((id)self) modelCustomWillTransformFromDictionary:dic]; + if (![dic isKindOfClass:[NSDictionary class]]) return NO; + } + + ModelSetContext context = {0}; + context.modelMeta = (__bridge void *)(modelMeta); + context.model = (__bridge void *)(self); + context.dictionary = (__bridge void *)(dic); + + + if (modelMeta->_keyMappedCount >= CFDictionaryGetCount((CFDictionaryRef)dic)) { + CFDictionaryApplyFunction((CFDictionaryRef)dic, ModelSetWithDictionaryFunction, &context); + if (modelMeta->_keyPathPropertyMetas) { + CFArrayApplyFunction((CFArrayRef)modelMeta->_keyPathPropertyMetas, + CFRangeMake(0, CFArrayGetCount((CFArrayRef)modelMeta->_keyPathPropertyMetas)), + ModelSetWithPropertyMetaArrayFunction, + &context); + } + if (modelMeta->_multiKeysPropertyMetas) { + CFArrayApplyFunction((CFArrayRef)modelMeta->_multiKeysPropertyMetas, + CFRangeMake(0, CFArrayGetCount((CFArrayRef)modelMeta->_multiKeysPropertyMetas)), + ModelSetWithPropertyMetaArrayFunction, + &context); + } + } else { + CFArrayApplyFunction((CFArrayRef)modelMeta->_allPropertyMetas, + CFRangeMake(0, modelMeta->_keyMappedCount), + ModelSetWithPropertyMetaArrayFunction, + &context); + } + + if (modelMeta->_hasCustomTransformFromDictionary) { + return [((id)self) modelCustomTransformFromDictionary:dic]; + } + return YES; +} + +- (id)yy_modelToJSONObject { + /* + Apple said: + The top level object is an NSArray or NSDictionary. + All objects are instances of NSString, NSNumber, NSArray, NSDictionary, or NSNull. + All dictionary keys are instances of NSString. + Numbers are not NaN or infinity. + */ + id jsonObject = ModelToJSONObjectRecursive(self); + if ([jsonObject isKindOfClass:[NSArray class]]) return jsonObject; + if ([jsonObject isKindOfClass:[NSDictionary class]]) return jsonObject; + return nil; +} + +- (NSData *)yy_modelToJSONData { + id jsonObject = [self yy_modelToJSONObject]; + if (!jsonObject) return nil; + return [NSJSONSerialization dataWithJSONObject:jsonObject options:0 error:NULL]; +} + +- (NSString *)yy_modelToJSONString { + NSData *jsonData = [self yy_modelToJSONData]; + if (jsonData.length == 0) return nil; + return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; +} + +- (id)yy_modelCopy{ + if (self == (id)kCFNull) return self; + _YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:self.class]; + if (modelMeta->_nsType) return [self copy]; + + NSObject *one = [self.class new]; + for (_YYModelPropertyMeta *propertyMeta in modelMeta->_allPropertyMetas) { + if (!propertyMeta->_getter || !propertyMeta->_setter) continue; + + if (propertyMeta->_isCNumber) { + switch (propertyMeta->_type & YYEncodingTypeMask) { + case YYEncodingTypeBool: { + bool num = ((bool (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter); + ((void (*)(id, SEL, bool))(void *) objc_msgSend)((id)one, propertyMeta->_setter, num); + } break; + case YYEncodingTypeInt8: + case YYEncodingTypeUInt8: { + uint8_t num = ((bool (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter); + ((void (*)(id, SEL, uint8_t))(void *) objc_msgSend)((id)one, propertyMeta->_setter, num); + } break; + case YYEncodingTypeInt16: + case YYEncodingTypeUInt16: { + uint16_t num = ((uint16_t (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter); + ((void (*)(id, SEL, uint16_t))(void *) objc_msgSend)((id)one, propertyMeta->_setter, num); + } break; + case YYEncodingTypeInt32: + case YYEncodingTypeUInt32: { + uint32_t num = ((uint32_t (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter); + ((void (*)(id, SEL, uint32_t))(void *) objc_msgSend)((id)one, propertyMeta->_setter, num); + } break; + case YYEncodingTypeInt64: + case YYEncodingTypeUInt64: { + uint64_t num = ((uint64_t (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter); + ((void (*)(id, SEL, uint64_t))(void *) objc_msgSend)((id)one, propertyMeta->_setter, num); + } break; + case YYEncodingTypeFloat: { + float num = ((float (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter); + ((void (*)(id, SEL, float))(void *) objc_msgSend)((id)one, propertyMeta->_setter, num); + } break; + case YYEncodingTypeDouble: { + double num = ((double (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter); + ((void (*)(id, SEL, double))(void *) objc_msgSend)((id)one, propertyMeta->_setter, num); + } break; + case YYEncodingTypeLongDouble: { + long double num = ((long double (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter); + ((void (*)(id, SEL, long double))(void *) objc_msgSend)((id)one, propertyMeta->_setter, num); + } // break; commented for code coverage in next line + default: break; + } + } else { + switch (propertyMeta->_type & YYEncodingTypeMask) { + case YYEncodingTypeObject: + case YYEncodingTypeClass: + case YYEncodingTypeBlock: { + id value = ((id (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter); + ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)one, propertyMeta->_setter, value); + } break; + case YYEncodingTypeSEL: + case YYEncodingTypePointer: + case YYEncodingTypeCString: { + size_t value = ((size_t (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter); + ((void (*)(id, SEL, size_t))(void *) objc_msgSend)((id)one, propertyMeta->_setter, value); + } break; + case YYEncodingTypeStruct: + case YYEncodingTypeUnion: { + @try { + NSValue *value = [self valueForKey:NSStringFromSelector(propertyMeta->_getter)]; + if (value) { + [one setValue:value forKey:propertyMeta->_name]; + } + } @catch (NSException *exception) {} + } // break; commented for code coverage in next line + default: break; + } + } + } + return one; +} + +- (void)yy_modelEncodeWithCoder:(NSCoder *)aCoder { + if (!aCoder) return; + if (self == (id)kCFNull) { + [((id)self)encodeWithCoder:aCoder]; + return; + } + + _YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:self.class]; + if (modelMeta->_nsType) { + [((id)self)encodeWithCoder:aCoder]; + return; + } + + for (_YYModelPropertyMeta *propertyMeta in modelMeta->_allPropertyMetas) { + if (!propertyMeta->_getter) return; + + if (propertyMeta->_isCNumber) { + NSNumber *value = ModelCreateNumberFromProperty(self, propertyMeta); + if (value) [aCoder encodeObject:value forKey:propertyMeta->_name]; + } else { + switch (propertyMeta->_type & YYEncodingTypeMask) { + case YYEncodingTypeObject: { + id value = ((id (*)(id, SEL))(void *)objc_msgSend)((id)self, propertyMeta->_getter); + if (value && (propertyMeta->_nsType || [value respondsToSelector:@selector(encodeWithCoder:)])) { + if ([value isKindOfClass:[NSValue class]]) { + if ([value isKindOfClass:[NSNumber class]]) { + [aCoder encodeObject:value forKey:propertyMeta->_name]; + } + } else { + [aCoder encodeObject:value forKey:propertyMeta->_name]; + } + } + } break; + case YYEncodingTypeSEL: { + SEL value = ((SEL (*)(id, SEL))(void *)objc_msgSend)((id)self, propertyMeta->_getter); + if (value) { + NSString *str = NSStringFromSelector(value); + [aCoder encodeObject:str forKey:propertyMeta->_name]; + } + } break; + case YYEncodingTypeStruct: + case YYEncodingTypeUnion: { + if (propertyMeta->_isKVCCompatible && propertyMeta->_isStructAvailableForKeyedArchiver) { + @try { + NSValue *value = [self valueForKey:NSStringFromSelector(propertyMeta->_getter)]; + [aCoder encodeObject:value forKey:propertyMeta->_name]; + } @catch (NSException *exception) {} + } + } break; + + default: + break; + } + } + } +} + +- (id)yy_modelInitWithCoder:(NSCoder *)aDecoder { + if (!aDecoder) return self; + if (self == (id)kCFNull) return self; + _YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:self.class]; + if (modelMeta->_nsType) return self; + + for (_YYModelPropertyMeta *propertyMeta in modelMeta->_allPropertyMetas) { + if (!propertyMeta->_setter) continue; + + if (propertyMeta->_isCNumber) { + NSNumber *value = [aDecoder decodeObjectForKey:propertyMeta->_name]; + if ([value isKindOfClass:[NSNumber class]]) { + ModelSetNumberToProperty(self, value, propertyMeta); + [value class]; + } + } else { + YYEncodingType type = propertyMeta->_type & YYEncodingTypeMask; + switch (type) { + case YYEncodingTypeObject: { + id value = [aDecoder decodeObjectForKey:propertyMeta->_name]; + ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)self, propertyMeta->_setter, value); + } break; + case YYEncodingTypeSEL: { + NSString *str = [aDecoder decodeObjectForKey:propertyMeta->_name]; + if ([str isKindOfClass:[NSString class]]) { + SEL sel = NSSelectorFromString(str); + ((void (*)(id, SEL, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_setter, sel); + } + } break; + case YYEncodingTypeStruct: + case YYEncodingTypeUnion: { + if (propertyMeta->_isKVCCompatible) { + @try { + NSValue *value = [aDecoder decodeObjectForKey:propertyMeta->_name]; + if (value) [self setValue:value forKey:propertyMeta->_name]; + } @catch (NSException *exception) {} + } + } break; + + default: + break; + } + } + } + return self; +} + +- (NSUInteger)yy_modelHash { + if (self == (id)kCFNull) return [self hash]; + _YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:self.class]; + if (modelMeta->_nsType) return [self hash]; + + NSUInteger value = 0; + NSUInteger count = 0; + for (_YYModelPropertyMeta *propertyMeta in modelMeta->_allPropertyMetas) { + if (!propertyMeta->_isKVCCompatible) continue; + value ^= [[self valueForKey:NSStringFromSelector(propertyMeta->_getter)] hash]; + count++; + } + if (count == 0) value = (long)((__bridge void *)self); + return value; +} + +- (BOOL)yy_modelIsEqual:(id)model { + if (self == model) return YES; + if (![model isMemberOfClass:self.class]) return NO; + _YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:self.class]; + if (modelMeta->_nsType) return [self isEqual:model]; + if ([self hash] != [model hash]) return NO; + + for (_YYModelPropertyMeta *propertyMeta in modelMeta->_allPropertyMetas) { + if (!propertyMeta->_isKVCCompatible) continue; + id this = [self valueForKey:NSStringFromSelector(propertyMeta->_getter)]; + id that = [model valueForKey:NSStringFromSelector(propertyMeta->_getter)]; + if (this == that) continue; + if (this == nil || that == nil) return NO; + if (![this isEqual:that]) return NO; + } + return YES; +} + +- (NSString *)yy_modelDescription { + return ModelDescription(self); +} + +@end + + + +@implementation NSArray (YYModel) + ++ (NSArray *)yy_modelArrayWithClass:(Class)cls json:(id)json { + if (!json) return nil; + NSArray *arr = nil; + NSData *jsonData = nil; + if ([json isKindOfClass:[NSArray class]]) { + arr = json; + } else if ([json isKindOfClass:[NSString class]]) { + jsonData = [(NSString *)json dataUsingEncoding : NSUTF8StringEncoding]; + } else if ([json isKindOfClass:[NSData class]]) { + jsonData = json; + } + if (jsonData) { + arr = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:NULL]; + if (![arr isKindOfClass:[NSArray class]]) arr = nil; + } + return [self yy_modelArrayWithClass:cls array:arr]; +} + ++ (NSArray *)yy_modelArrayWithClass:(Class)cls array:(NSArray *)arr { + if (!cls || !arr) return nil; + NSMutableArray *result = [NSMutableArray new]; + for (NSDictionary *dic in arr) { + if (![dic isKindOfClass:[NSDictionary class]]) continue; + NSObject *obj = [cls yy_modelWithDictionary:dic]; + if (obj) [result addObject:obj]; + } + return result; +} + +@end + + +@implementation NSDictionary (YYModel) + ++ (NSDictionary *)yy_modelDictionaryWithClass:(Class)cls json:(id)json { + if (!json) return nil; + NSDictionary *dic = nil; + NSData *jsonData = nil; + if ([json isKindOfClass:[NSDictionary class]]) { + dic = json; + } else if ([json isKindOfClass:[NSString class]]) { + jsonData = [(NSString *)json dataUsingEncoding : NSUTF8StringEncoding]; + } else if ([json isKindOfClass:[NSData class]]) { + jsonData = json; + } + if (jsonData) { + dic = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:NULL]; + if (![dic isKindOfClass:[NSDictionary class]]) dic = nil; + } + return [self yy_modelDictionaryWithClass:cls dictionary:dic]; +} + ++ (NSDictionary *)yy_modelDictionaryWithClass:(Class)cls dictionary:(NSDictionary *)dic { + if (!cls || !dic) return nil; + NSMutableDictionary *result = [NSMutableDictionary new]; + for (NSString *key in dic.allKeys) { + if (![key isKindOfClass:[NSString class]]) continue; + NSObject *obj = [cls yy_modelWithDictionary:dic[key]]; + if (obj) result[key] = obj; + } + return result; +} + +@end diff --git a/Example/Pods/YYModel/YYModel/YYClassInfo.h b/Example/Pods/YYModel/YYModel/YYClassInfo.h new file mode 100644 index 0000000..6b87458 --- /dev/null +++ b/Example/Pods/YYModel/YYModel/YYClassInfo.h @@ -0,0 +1,200 @@ +// +// YYClassInfo.h +// YYModel +// +// Created by ibireme on 15/5/9. +// Copyright (c) 2015 ibireme. +// +// This source code is licensed under the MIT-style license found in the +// LICENSE file in the root directory of this source tree. +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + Type encoding's type. + */ +typedef NS_OPTIONS(NSUInteger, YYEncodingType) { + YYEncodingTypeMask = 0xFF, ///< mask of type value + YYEncodingTypeUnknown = 0, ///< unknown + YYEncodingTypeVoid = 1, ///< void + YYEncodingTypeBool = 2, ///< bool + YYEncodingTypeInt8 = 3, ///< char / BOOL + YYEncodingTypeUInt8 = 4, ///< unsigned char + YYEncodingTypeInt16 = 5, ///< short + YYEncodingTypeUInt16 = 6, ///< unsigned short + YYEncodingTypeInt32 = 7, ///< int + YYEncodingTypeUInt32 = 8, ///< unsigned int + YYEncodingTypeInt64 = 9, ///< long long + YYEncodingTypeUInt64 = 10, ///< unsigned long long + YYEncodingTypeFloat = 11, ///< float + YYEncodingTypeDouble = 12, ///< double + YYEncodingTypeLongDouble = 13, ///< long double + YYEncodingTypeObject = 14, ///< id + YYEncodingTypeClass = 15, ///< Class + YYEncodingTypeSEL = 16, ///< SEL + YYEncodingTypeBlock = 17, ///< block + YYEncodingTypePointer = 18, ///< void* + YYEncodingTypeStruct = 19, ///< struct + YYEncodingTypeUnion = 20, ///< union + YYEncodingTypeCString = 21, ///< char* + YYEncodingTypeCArray = 22, ///< char[10] (for example) + + YYEncodingTypeQualifierMask = 0xFF00, ///< mask of qualifier + YYEncodingTypeQualifierConst = 1 << 8, ///< const + YYEncodingTypeQualifierIn = 1 << 9, ///< in + YYEncodingTypeQualifierInout = 1 << 10, ///< inout + YYEncodingTypeQualifierOut = 1 << 11, ///< out + YYEncodingTypeQualifierBycopy = 1 << 12, ///< bycopy + YYEncodingTypeQualifierByref = 1 << 13, ///< byref + YYEncodingTypeQualifierOneway = 1 << 14, ///< oneway + + YYEncodingTypePropertyMask = 0xFF0000, ///< mask of property + YYEncodingTypePropertyReadonly = 1 << 16, ///< readonly + YYEncodingTypePropertyCopy = 1 << 17, ///< copy + YYEncodingTypePropertyRetain = 1 << 18, ///< retain + YYEncodingTypePropertyNonatomic = 1 << 19, ///< nonatomic + YYEncodingTypePropertyWeak = 1 << 20, ///< weak + YYEncodingTypePropertyCustomGetter = 1 << 21, ///< getter= + YYEncodingTypePropertyCustomSetter = 1 << 22, ///< setter= + YYEncodingTypePropertyDynamic = 1 << 23, ///< @dynamic +}; + +/** + Get the type from a Type-Encoding string. + + @discussion See also: + https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html + https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html + + @param typeEncoding A Type-Encoding string. + @return The encoding type. + */ +YYEncodingType YYEncodingGetType(const char *typeEncoding); + + +/** + Instance variable information. + */ +@interface YYClassIvarInfo : NSObject +@property (nonatomic, assign, readonly) Ivar ivar; ///< ivar opaque struct +@property (nonatomic, strong, readonly) NSString *name; ///< Ivar's name +@property (nonatomic, assign, readonly) ptrdiff_t offset; ///< Ivar's offset +@property (nonatomic, strong, readonly) NSString *typeEncoding; ///< Ivar's type encoding +@property (nonatomic, assign, readonly) YYEncodingType type; ///< Ivar's type + +/** + Creates and returns an ivar info object. + + @param ivar ivar opaque struct + @return A new object, or nil if an error occurs. + */ +- (instancetype)initWithIvar:(Ivar)ivar; +@end + + +/** + Method information. + */ +@interface YYClassMethodInfo : NSObject +@property (nonatomic, assign, readonly) Method method; ///< method opaque struct +@property (nonatomic, strong, readonly) NSString *name; ///< method name +@property (nonatomic, assign, readonly) SEL sel; ///< method's selector +@property (nonatomic, assign, readonly) IMP imp; ///< method's implementation +@property (nonatomic, strong, readonly) NSString *typeEncoding; ///< method's parameter and return types +@property (nonatomic, strong, readonly) NSString *returnTypeEncoding; ///< return value's type +@property (nullable, nonatomic, strong, readonly) NSArray *argumentTypeEncodings; ///< array of arguments' type + +/** + Creates and returns a method info object. + + @param method method opaque struct + @return A new object, or nil if an error occurs. + */ +- (instancetype)initWithMethod:(Method)method; +@end + + +/** + Property information. + */ +@interface YYClassPropertyInfo : NSObject +@property (nonatomic, assign, readonly) objc_property_t property; ///< property's opaque struct +@property (nonatomic, strong, readonly) NSString *name; ///< property's name +@property (nonatomic, assign, readonly) YYEncodingType type; ///< property's type +@property (nonatomic, strong, readonly) NSString *typeEncoding; ///< property's encoding value +@property (nonatomic, strong, readonly) NSString *ivarName; ///< property's ivar name +@property (nullable, nonatomic, assign, readonly) Class cls; ///< may be nil +@property (nullable, nonatomic, strong, readonly) NSArray *protocols; ///< may nil +@property (nonatomic, assign, readonly) SEL getter; ///< getter (nonnull) +@property (nonatomic, assign, readonly) SEL setter; ///< setter (nonnull) + +/** + Creates and returns a property info object. + + @param property property opaque struct + @return A new object, or nil if an error occurs. + */ +- (instancetype)initWithProperty:(objc_property_t)property; +@end + + +/** + Class information for a class. + */ +@interface YYClassInfo : NSObject +@property (nonatomic, assign, readonly) Class cls; ///< class object +@property (nullable, nonatomic, assign, readonly) Class superCls; ///< super class object +@property (nullable, nonatomic, assign, readonly) Class metaCls; ///< class's meta class object +@property (nonatomic, readonly) BOOL isMeta; ///< whether this class is meta class +@property (nonatomic, strong, readonly) NSString *name; ///< class name +@property (nullable, nonatomic, strong, readonly) YYClassInfo *superClassInfo; ///< super class's class info +@property (nullable, nonatomic, strong, readonly) NSDictionary *ivarInfos; ///< ivars +@property (nullable, nonatomic, strong, readonly) NSDictionary *methodInfos; ///< methods +@property (nullable, nonatomic, strong, readonly) NSDictionary *propertyInfos; ///< properties + +/** + If the class is changed (for example: you add a method to this class with + 'class_addMethod()'), you should call this method to refresh the class info cache. + + After called this method, `needUpdate` will returns `YES`, and you should call + 'classInfoWithClass' or 'classInfoWithClassName' to get the updated class info. + */ +- (void)setNeedUpdate; + +/** + If this method returns `YES`, you should stop using this instance and call + `classInfoWithClass` or `classInfoWithClassName` to get the updated class info. + + @return Whether this class info need update. + */ +- (BOOL)needUpdate; + +/** + Get the class info of a specified Class. + + @discussion This method will cache the class info and super-class info + at the first access to the Class. This method is thread-safe. + + @param cls A class. + @return A class info, or nil if an error occurs. + */ ++ (nullable instancetype)classInfoWithClass:(Class)cls; + +/** + Get the class info of a specified Class. + + @discussion This method will cache the class info and super-class info + at the first access to the Class. This method is thread-safe. + + @param className A class name. + @return A class info, or nil if an error occurs. + */ ++ (nullable instancetype)classInfoWithClassName:(NSString *)className; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Example/Pods/YYModel/YYModel/YYClassInfo.m b/Example/Pods/YYModel/YYModel/YYClassInfo.m new file mode 100644 index 0000000..16df6d9 --- /dev/null +++ b/Example/Pods/YYModel/YYModel/YYClassInfo.m @@ -0,0 +1,362 @@ +// +// YYClassInfo.m +// YYModel +// +// Created by ibireme on 15/5/9. +// Copyright (c) 2015 ibireme. +// +// This source code is licensed under the MIT-style license found in the +// LICENSE file in the root directory of this source tree. +// + +#import "YYClassInfo.h" +#import + +YYEncodingType YYEncodingGetType(const char *typeEncoding) { + char *type = (char *)typeEncoding; + if (!type) return YYEncodingTypeUnknown; + size_t len = strlen(type); + if (len == 0) return YYEncodingTypeUnknown; + + YYEncodingType qualifier = 0; + bool prefix = true; + while (prefix) { + switch (*type) { + case 'r': { + qualifier |= YYEncodingTypeQualifierConst; + type++; + } break; + case 'n': { + qualifier |= YYEncodingTypeQualifierIn; + type++; + } break; + case 'N': { + qualifier |= YYEncodingTypeQualifierInout; + type++; + } break; + case 'o': { + qualifier |= YYEncodingTypeQualifierOut; + type++; + } break; + case 'O': { + qualifier |= YYEncodingTypeQualifierBycopy; + type++; + } break; + case 'R': { + qualifier |= YYEncodingTypeQualifierByref; + type++; + } break; + case 'V': { + qualifier |= YYEncodingTypeQualifierOneway; + type++; + } break; + default: { prefix = false; } break; + } + } + + len = strlen(type); + if (len == 0) return YYEncodingTypeUnknown | qualifier; + + switch (*type) { + case 'v': return YYEncodingTypeVoid | qualifier; + case 'B': return YYEncodingTypeBool | qualifier; + case 'c': return YYEncodingTypeInt8 | qualifier; + case 'C': return YYEncodingTypeUInt8 | qualifier; + case 's': return YYEncodingTypeInt16 | qualifier; + case 'S': return YYEncodingTypeUInt16 | qualifier; + case 'i': return YYEncodingTypeInt32 | qualifier; + case 'I': return YYEncodingTypeUInt32 | qualifier; + case 'l': return YYEncodingTypeInt32 | qualifier; + case 'L': return YYEncodingTypeUInt32 | qualifier; + case 'q': return YYEncodingTypeInt64 | qualifier; + case 'Q': return YYEncodingTypeUInt64 | qualifier; + case 'f': return YYEncodingTypeFloat | qualifier; + case 'd': return YYEncodingTypeDouble | qualifier; + case 'D': return YYEncodingTypeLongDouble | qualifier; + case '#': return YYEncodingTypeClass | qualifier; + case ':': return YYEncodingTypeSEL | qualifier; + case '*': return YYEncodingTypeCString | qualifier; + case '^': return YYEncodingTypePointer | qualifier; + case '[': return YYEncodingTypeCArray | qualifier; + case '(': return YYEncodingTypeUnion | qualifier; + case '{': return YYEncodingTypeStruct | qualifier; + case '@': { + if (len == 2 && *(type + 1) == '?') + return YYEncodingTypeBlock | qualifier; + else + return YYEncodingTypeObject | qualifier; + } + default: return YYEncodingTypeUnknown | qualifier; + } +} + +@implementation YYClassIvarInfo + +- (instancetype)initWithIvar:(Ivar)ivar { + if (!ivar) return nil; + self = [super init]; + _ivar = ivar; + const char *name = ivar_getName(ivar); + if (name) { + _name = [NSString stringWithUTF8String:name]; + } + _offset = ivar_getOffset(ivar); + const char *typeEncoding = ivar_getTypeEncoding(ivar); + if (typeEncoding) { + _typeEncoding = [NSString stringWithUTF8String:typeEncoding]; + _type = YYEncodingGetType(typeEncoding); + } + return self; +} + +@end + +@implementation YYClassMethodInfo + +- (instancetype)initWithMethod:(Method)method { + if (!method) return nil; + self = [super init]; + _method = method; + _sel = method_getName(method); + _imp = method_getImplementation(method); + const char *name = sel_getName(_sel); + if (name) { + _name = [NSString stringWithUTF8String:name]; + } + const char *typeEncoding = method_getTypeEncoding(method); + if (typeEncoding) { + _typeEncoding = [NSString stringWithUTF8String:typeEncoding]; + } + char *returnType = method_copyReturnType(method); + if (returnType) { + _returnTypeEncoding = [NSString stringWithUTF8String:returnType]; + free(returnType); + } + unsigned int argumentCount = method_getNumberOfArguments(method); + if (argumentCount > 0) { + NSMutableArray *argumentTypes = [NSMutableArray new]; + for (unsigned int i = 0; i < argumentCount; i++) { + char *argumentType = method_copyArgumentType(method, i); + NSString *type = argumentType ? [NSString stringWithUTF8String:argumentType] : nil; + [argumentTypes addObject:type ? type : @""]; + if (argumentType) free(argumentType); + } + _argumentTypeEncodings = argumentTypes; + } + return self; +} + +@end + +@implementation YYClassPropertyInfo + +- (instancetype)initWithProperty:(objc_property_t)property { + if (!property) return nil; + self = [super init]; + _property = property; + const char *name = property_getName(property); + if (name) { + _name = [NSString stringWithUTF8String:name]; + } + + YYEncodingType type = 0; + unsigned int attrCount; + objc_property_attribute_t *attrs = property_copyAttributeList(property, &attrCount); + for (unsigned int i = 0; i < attrCount; i++) { + switch (attrs[i].name[0]) { + case 'T': { // Type encoding + if (attrs[i].value) { + _typeEncoding = [NSString stringWithUTF8String:attrs[i].value]; + type = YYEncodingGetType(attrs[i].value); + + if ((type & YYEncodingTypeMask) == YYEncodingTypeObject && _typeEncoding.length) { + NSScanner *scanner = [NSScanner scannerWithString:_typeEncoding]; + if (![scanner scanString:@"@\"" intoString:NULL]) continue; + + NSString *clsName = nil; + if ([scanner scanUpToCharactersFromSet: [NSCharacterSet characterSetWithCharactersInString:@"\"<"] intoString:&clsName]) { + if (clsName.length) _cls = objc_getClass(clsName.UTF8String); + } + + NSMutableArray *protocols = nil; + while ([scanner scanString:@"<" intoString:NULL]) { + NSString* protocol = nil; + if ([scanner scanUpToString:@">" intoString: &protocol]) { + if (protocol.length) { + if (!protocols) protocols = [NSMutableArray new]; + [protocols addObject:protocol]; + } + } + [scanner scanString:@">" intoString:NULL]; + } + _protocols = protocols; + } + } + } break; + case 'V': { // Instance variable + if (attrs[i].value) { + _ivarName = [NSString stringWithUTF8String:attrs[i].value]; + } + } break; + case 'R': { + type |= YYEncodingTypePropertyReadonly; + } break; + case 'C': { + type |= YYEncodingTypePropertyCopy; + } break; + case '&': { + type |= YYEncodingTypePropertyRetain; + } break; + case 'N': { + type |= YYEncodingTypePropertyNonatomic; + } break; + case 'D': { + type |= YYEncodingTypePropertyDynamic; + } break; + case 'W': { + type |= YYEncodingTypePropertyWeak; + } break; + case 'G': { + type |= YYEncodingTypePropertyCustomGetter; + if (attrs[i].value) { + _getter = NSSelectorFromString([NSString stringWithUTF8String:attrs[i].value]); + } + } break; + case 'S': { + type |= YYEncodingTypePropertyCustomSetter; + if (attrs[i].value) { + _setter = NSSelectorFromString([NSString stringWithUTF8String:attrs[i].value]); + } + } // break; commented for code coverage in next line + default: break; + } + } + if (attrs) { + free(attrs); + attrs = NULL; + } + + _type = type; + if (_name.length) { + if (!_getter) { + _getter = NSSelectorFromString(_name); + } + if (!_setter) { + _setter = NSSelectorFromString([NSString stringWithFormat:@"set%@%@:", [_name substringToIndex:1].uppercaseString, [_name substringFromIndex:1]]); + } + } + return self; +} + +@end + +@implementation YYClassInfo { + BOOL _needUpdate; +} + +- (instancetype)initWithClass:(Class)cls { + if (!cls) return nil; + self = [super init]; + _cls = cls; + _superCls = class_getSuperclass(cls); + _isMeta = class_isMetaClass(cls); + if (!_isMeta) { + _metaCls = objc_getMetaClass(class_getName(cls)); + } + _name = NSStringFromClass(cls); + [self _update]; + + _superClassInfo = [self.class classInfoWithClass:_superCls]; + return self; +} + +- (void)_update { + _ivarInfos = nil; + _methodInfos = nil; + _propertyInfos = nil; + + Class cls = self.cls; + unsigned int methodCount = 0; + Method *methods = class_copyMethodList(cls, &methodCount); + if (methods) { + NSMutableDictionary *methodInfos = [NSMutableDictionary new]; + _methodInfos = methodInfos; + for (unsigned int i = 0; i < methodCount; i++) { + YYClassMethodInfo *info = [[YYClassMethodInfo alloc] initWithMethod:methods[i]]; + if (info.name) methodInfos[info.name] = info; + } + free(methods); + } + unsigned int propertyCount = 0; + objc_property_t *properties = class_copyPropertyList(cls, &propertyCount); + if (properties) { + NSMutableDictionary *propertyInfos = [NSMutableDictionary new]; + _propertyInfos = propertyInfos; + for (unsigned int i = 0; i < propertyCount; i++) { + YYClassPropertyInfo *info = [[YYClassPropertyInfo alloc] initWithProperty:properties[i]]; + if (info.name) propertyInfos[info.name] = info; + } + free(properties); + } + + unsigned int ivarCount = 0; + Ivar *ivars = class_copyIvarList(cls, &ivarCount); + if (ivars) { + NSMutableDictionary *ivarInfos = [NSMutableDictionary new]; + _ivarInfos = ivarInfos; + for (unsigned int i = 0; i < ivarCount; i++) { + YYClassIvarInfo *info = [[YYClassIvarInfo alloc] initWithIvar:ivars[i]]; + if (info.name) ivarInfos[info.name] = info; + } + free(ivars); + } + + if (!_ivarInfos) _ivarInfos = @{}; + if (!_methodInfos) _methodInfos = @{}; + if (!_propertyInfos) _propertyInfos = @{}; + + _needUpdate = NO; +} + +- (void)setNeedUpdate { + _needUpdate = YES; +} + +- (BOOL)needUpdate { + return _needUpdate; +} + ++ (instancetype)classInfoWithClass:(Class)cls { + if (!cls) return nil; + static CFMutableDictionaryRef classCache; + static CFMutableDictionaryRef metaCache; + static dispatch_once_t onceToken; + static dispatch_semaphore_t lock; + dispatch_once(&onceToken, ^{ + classCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + metaCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + lock = dispatch_semaphore_create(1); + }); + dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER); + YYClassInfo *info = CFDictionaryGetValue(class_isMetaClass(cls) ? metaCache : classCache, (__bridge const void *)(cls)); + if (info && info->_needUpdate) { + [info _update]; + } + dispatch_semaphore_signal(lock); + if (!info) { + info = [[YYClassInfo alloc] initWithClass:cls]; + if (info) { + dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER); + CFDictionarySetValue(info.isMeta ? metaCache : classCache, (__bridge const void *)(cls), (__bridge const void *)(info)); + dispatch_semaphore_signal(lock); + } + } + return info; +} + ++ (instancetype)classInfoWithClassName:(NSString *)className { + Class cls = NSClassFromString(className); + return [self classInfoWithClass:cls]; +} + +@end diff --git a/Example/Pods/YYModel/YYModel/YYModel.h b/Example/Pods/YYModel/YYModel/YYModel.h new file mode 100644 index 0000000..e1154ee --- /dev/null +++ b/Example/Pods/YYModel/YYModel/YYModel.h @@ -0,0 +1,22 @@ +// +// YYModel.h +// YYModel +// +// Created by ibireme on 15/5/10. +// Copyright (c) 2015 ibireme. +// +// This source code is licensed under the MIT-style license found in the +// LICENSE file in the root directory of this source tree. +// + +#import + +#if __has_include() +FOUNDATION_EXPORT double YYModelVersionNumber; +FOUNDATION_EXPORT const unsigned char YYModelVersionString[]; +#import +#import +#else +#import "NSObject+YYModel.h" +#import "YYClassInfo.h" +#endif diff --git a/LWZComponents.podspec b/LWZComponents.podspec index 23055f4..5ce7307 100644 --- a/LWZComponents.podspec +++ b/LWZComponents.podspec @@ -8,8 +8,8 @@ Pod::Spec.new do |s| s.name = 'LWZComponents' - s.version = '0.1.0' - s.summary = 'A short description of LWZComponents.' + s.version = '1.0.0' + s.summary = '一些组件库.' # This description is used to generate tags and improve search results. # * Think: What does it do? Why did you write it? What is the focus? @@ -17,26 +17,20 @@ Pod::Spec.new do |s| # * Write the description between the DESC delimiters below. # * Finally, don't worry about the indent, CocoaPods strips it! - s.description = <<-DESC -TODO: Add long description of the pod here. - DESC + s.description = "https://github.com/changsanjiang/LWZComponents/blob/master/README.md" - s.homepage = 'https://github.com/changsanjiang@gmail.com/LWZComponents' + s.homepage = 'https://github.com/changsanjiang/LWZComponents' # s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2' s.license = { :type => 'MIT', :file => 'LICENSE' } - s.author = { 'changsanjiang@gmail.com' => 'changsanjiang@gmail.com' } - s.source = { :git => 'https://github.com/changsanjiang@gmail.com/LWZComponents.git', :tag => s.version.to_s } + s.author = { 'changsanjiang' => 'changsanjiang@gmail.com' } + s.source = { :git => 'https://github.com/changsanjiang/LWZComponents.git', :tag => s.version.to_s } # s.social_media_url = 'https://twitter.com/' s.ios.deployment_target = '9.0' - s.source_files = 'LWZComponents/Classes/**/*' + s.source_files = 'LWZComponents/*.{h,m}' - # s.resource_bundles = { - # 'LWZComponents' => ['LWZComponents/Assets/*.png'] - # } - - # s.public_header_files = 'Pod/Classes/**/*.h' - # s.frameworks = 'UIKit', 'MapKit' - # s.dependency 'AFNetworking', '~> 2.3' + s.subspec 'LWZCollectionViewComponents' do |ss| + ss.source_files = 'LWZComponents/LWZCollectionViewComponents/**/*.{h,m}' + end end diff --git a/LWZComponents/Assets/.gitkeep b/LWZComponents/Assets/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/LWZComponents/Classes/.gitkeep b/LWZComponents/Classes/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/LWZComponents/Classes/ReplaceMe.m b/LWZComponents/Classes/ReplaceMe.m deleted file mode 100644 index e69de29..0000000 diff --git a/LWZComponents/LWZCollectionViewComponents/Defines/LWZCollectionDefines.h b/LWZComponents/LWZCollectionViewComponents/Defines/LWZCollectionDefines.h new file mode 100644 index 0000000..ec2168d --- /dev/null +++ b/LWZComponents/LWZCollectionViewComponents/Defines/LWZCollectionDefines.h @@ -0,0 +1,50 @@ +// +// LWZCollectionDefines.h +// LWZCollectionViewComponents +// +// Created by changsanjiang on 2021/11/25. +// + +#import + +typedef NS_ENUM(NSUInteger, LWZCollectionLayoutType) { + /// 未指定的, 将根据collectionView的layout的类型进行布局. 例如: collectionView.layout为weightLayout, 则按照weight布局 + LWZCollectionLayoutTypeUnspecified, + LWZCollectionLayoutTypeWeight, + LWZCollectionLayoutTypeList, + LWZCollectionLayoutTypeWaterfallFlow, + LWZCollectionLayoutTypeRestrictedLayout, + LWZCollectionLayoutTypeTemplate, +}; + +typedef NS_ENUM(NSUInteger, LWZCollectionLayoutAlignment) { + LWZCollectionLayoutAlignmentStart, + LWZCollectionLayoutAlignmentEnd, + LWZCollectionLayoutAlignmentCenter, +}; + +typedef NS_ENUM(NSUInteger, LWZCollectionLayoutContentPresentationMode) { + LWZCollectionLayoutContentPresentationModeNormal, + LWZCollectionLayoutContentPresentationModeCustom, +}; + +typedef NS_ENUM(NSUInteger, LWZCollectionLayoutContentOrthogonalScrollingBehavior) { + LWZCollectionLayoutContentOrthogonalScrollingBehaviorNormal, + LWZCollectionLayoutContentOrthogonalScrollingBehaviorContinuousCentered, + LWZCollectionLayoutContentOrthogonalScrollingBehaviorPaging, +}; + +typedef NS_ENUM(NSUInteger, LWZCollectionLayoutTemplateDimensionSemantic) { + LWZCollectionLayoutTemplateDimensionSemanticFractionalWidth, + LWZCollectionLayoutTemplateDimensionSemanticFractionalHeight, + LWZCollectionLayoutTemplateDimensionSemanticAbsolute +}; + +NS_ASSUME_NONNULL_BEGIN + +FOUNDATION_EXTERN NSInteger const LWZCollectionDecorationDefaultZPosition; +FOUNDATION_EXTERN NSInteger const LWZCollectionDecorationSeparatorZPosition; + +FOUNDATION_EXTERN NSInteger const LWZCollectionOrthogonalScrollingGroupViewZPosition; + +NS_ASSUME_NONNULL_END diff --git a/LWZComponents/LWZCollectionViewComponents/Defines/LWZCollectionDefines.m b/LWZComponents/LWZCollectionViewComponents/Defines/LWZCollectionDefines.m new file mode 100644 index 0000000..02327d6 --- /dev/null +++ b/LWZComponents/LWZCollectionViewComponents/Defines/LWZCollectionDefines.m @@ -0,0 +1,13 @@ +// +// LWZCollectionDefines.m +// LWZCollectionViewComponents +// +// Created by changsanjiang on 2021/11/25. +// + +#import "LWZCollectionDefines.h" + +NSInteger const LWZCollectionDecorationDefaultZPosition = -2; +NSInteger const LWZCollectionDecorationSeparatorZPosition = 10; + +NSInteger const LWZCollectionOrthogonalScrollingGroupViewZPosition = -1; diff --git a/LWZComponents/LWZCollectionViewComponents/LWZCollectionViewComponents.h b/LWZComponents/LWZCollectionViewComponents/LWZCollectionViewComponents.h new file mode 100755 index 0000000..be661c6 --- /dev/null +++ b/LWZComponents/LWZCollectionViewComponents/LWZCollectionViewComponents.h @@ -0,0 +1,23 @@ +// +// LWZCollectionViewComponents.h +// LWZCollectionViewComponents +// +// Created by changsanjiang on 2020/11/3. +// Copyright © 2020 changsanjiang@gmail.com. All rights reserved. +// + +#ifndef LWZCollectionViewComponents_h +#define LWZCollectionViewComponents_h +#import "LWZCollectionView.h" + +#import "LWZCollectionProvider.h" +#import "LWZCollectionSection.h" +#import "LWZCollectionSectionHeaderFooter.h" +#import "LWZCollectionDecoration.h" +#import "LWZCollectionItem.h" + +#import "LWZCollectionViewPresenter.h" +#import "LWZCollectionViewRegister.h" + +#import "LWZCollectionViewLayout.h" +#endif /* LWZCollectionViewComponents_h */ diff --git a/LWZComponents/LWZCollectionViewComponents/Layout/LWZCollectionLayoutContentContainer.h b/LWZComponents/LWZCollectionViewComponents/Layout/LWZCollectionLayoutContentContainer.h new file mode 100644 index 0000000..ee52bd3 --- /dev/null +++ b/LWZComponents/LWZCollectionViewComponents/Layout/LWZCollectionLayoutContentContainer.h @@ -0,0 +1,311 @@ +// +// LWZCollectionLayoutContentContainer.h +// SJTestAutoLayout_Example +// +// Created by changsanjiang on 2021/11/9. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "LWZCollectionDefines.h" +@class LWZCollectionTemplateBuilder; + +NS_ASSUME_NONNULL_BEGIN +@interface LWZCollectionLayoutCollectionContentContainer : NSObject +@property (nonatomic, readonly) CGSize collectionSize; +@property (nonatomic, readonly) UICollectionViewScrollDirection layoutDirection; +@property (nonatomic, readonly) UIEdgeInsets layoutInsets; +@property (nonatomic, readonly) UIFloatRange layoutRange; +@property (nonatomic, readonly) CGSize layoutContainerSize; +@end + + +@interface LWZCollectionLayoutSectionContentContainer : NSObject +@property (nonatomic, readonly) UIEdgeInsets edgeSpacings; +@property (nonatomic, readonly) UIEdgeInsets contentInsets; +@property (nonatomic, strong, readonly) LWZCollectionLayoutCollectionContentContainer *collectionContentContainer; +@property (nonatomic, readonly) UICollectionViewScrollDirection layoutDirection; +@property (nonatomic, readonly) UIEdgeInsets layoutInsets; +@property (nonatomic, readonly) UIFloatRange layoutRange; + +@property (nonatomic, readonly) UIFloatRange headerFooterLayoutRange; +@property (nonatomic, readonly) UIFloatRange itemLayoutRange; +@property (nonatomic, readonly) CGSize itemLayoutContainerSize; +@end + +#pragma mark - Template + +@interface LWZCollectionLayoutTemplateDimension : NSObject +- (instancetype)initWithFractionalWidthDimension:(CGFloat)dimension; // 相对于父容器的比例 +- (instancetype)initWithFractionalHeightDimension:(CGFloat)dimension; // 相对于父容器的比例 +- (instancetype)initWithAbsoluteDimension:(CGFloat)dimension; // 固定值 +- (instancetype)initWithDimension:(CGFloat)dimension semantic:(LWZCollectionLayoutTemplateDimensionSemantic)semantic; +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; +@property (nonatomic, readonly) LWZCollectionLayoutTemplateDimensionSemantic semantic; +@property (nonatomic, readonly) CGFloat dimension; +@end + +@interface LWZCollectionLayoutTemplateSize : NSObject +- (instancetype)initWithWidthDimension:(LWZCollectionLayoutTemplateDimension *)width heightDimension:(LWZCollectionLayoutTemplateDimension *)height; +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; +@property (nonatomic, strong, readonly) LWZCollectionLayoutTemplateDimension *width; +@property (nonatomic, strong, readonly) LWZCollectionLayoutTemplateDimension *height; +@end + +@interface LWZCollectionLayoutTemplateItem : NSObject +- (instancetype)initWithSize:(LWZCollectionLayoutTemplateSize *)size; + +@property (nonatomic, strong, readonly) LWZCollectionLayoutTemplateSize *size; +@end + +/** + + 定义一个模板容器, 一个容器可以有多个item. + + \code + +-----[V]-----+ <--- Container + | | + | +-------+ <------ Item + | | | | + | | Item1 | | + | | | | + | +-------+ | + | | + | +-------+ | + | | | | + | | Item2 | | + | | | | + | +-------+ | + | | + +-------------+ + + +-------[H]----------------------+ <---- Container + | | + | +-----------+ +-----------+ <------- Item + | | | | | | + | | | | | | + | | 1 | | 2 | | + | +-----------+ +-----------+ | + | | + +--------------------------------+ + \endcode + */ +@interface LWZCollectionLayoutTemplateContainer : LWZCollectionLayoutTemplateItem +- (instancetype)initWithSize:(LWZCollectionLayoutTemplateSize *)size items:(NSArray *)items; +@property (nonatomic, copy, readonly) NSArray *items; +@end + +/** + + 定义一个模板组, 一个组可以有多个容器. + + \code + +---------------------[V]----------------------+ <--- CollectionView + | | + | +----------------------------------------+ <------ Group1 + | | | | + | | +---------------+ +---------------+ | | + | | | | | | | | + | | | Container1 | | Container2 | | | + | | | | | | | | + | | +---------------+ +---------------+ | | + | | | | + | +----------------------------------------+ | + | | + | +----------------------------------------+ <------ Group2 + | | | | + | | +----------------------------------+ | | + | | | | | | + | | | Container1 | | | + | | | | | | + | | +----------------------------------+ | | + | | | | + | +----------------------------------------+ | + | | + +----------------------------------------------+ + \endcode + */ +@interface LWZCollectionLayoutTemplateGroup : LWZCollectionLayoutTemplateItem +- (instancetype)initWithSize:(LWZCollectionLayoutTemplateSize *)size containers:(NSArray *)containers; +@property (nonatomic, strong, readonly, nullable) NSArray *containers; +@end + +@interface LWZCollectionLayoutTemplate : NSObject ++ (NSArray *)build:(void(^)(LWZCollectionTemplateBuilder *make))block; +@end + +// solver 对frame的解析. 每个result表示对应item在groups中的位置(frame) +@interface LWZCollectionTemplateLayoutSolver : NSObject +- (instancetype)initWithGroups:(NSArray *)groups scrollDirection:(UICollectionViewScrollDirection)scrollDirection numberOfItems:(NSInteger)numberOfItems lineSpacing:(CGFloat)lineSpacing itemSpacing:(CGFloat)itemSpacing containerSize:(CGSize)containerSize; +- (CGRect)itemLayoutFrameAtIndex:(NSInteger)index; +@end + +@interface LWZCollectionTemplateDimensionBuilder : NSObject +@property (nonatomic, copy, readonly) LWZCollectionTemplateDimensionBuilder *(^semantic)(LWZCollectionLayoutTemplateDimensionSemantic semantic); +@property (nonatomic, copy, readonly) LWZCollectionTemplateDimensionBuilder *(^dimension)(CGFloat dimension); + +@property (nonatomic, copy, readonly) void(^fractionalWidth)(CGFloat dimension); +@property (nonatomic, copy, readonly) void(^fractionalHeight)(CGFloat dimension); +@property (nonatomic, copy, readonly) void(^absolute)(CGFloat dimension); +@end + +@interface LWZCollectionTemplateItemBuilder : NSObject +@property (nonatomic, readonly) LWZCollectionTemplateDimensionBuilder *width; +@property (nonatomic, readonly) LWZCollectionTemplateDimensionBuilder *height; +@end + +@interface LWZCollectionTemplateContainerBuilder : LWZCollectionTemplateItemBuilder +@property (nonatomic, copy, readonly) void(^addItem)(void(^block)(LWZCollectionTemplateItemBuilder *item)); +@end + +@interface LWZCollectionTemplateGroupBuilder : LWZCollectionTemplateItemBuilder +@property (nonatomic, copy, readonly) void(^addContainer)(void(^block)(LWZCollectionTemplateContainerBuilder *container)); +@end + +@interface LWZCollectionTemplateBuilder : NSObject +@property (nonatomic, copy, readonly) void(^addGroup)(void(^block)(LWZCollectionTemplateGroupBuilder *group)); +@property (nonatomic, readonly) NSArray *groups; +@end + +/** + \code + - (NSArray *)layout:(__kindof LWZCollectionViewLayout *)layout layoutTemplateContainerGroupsInSection:(NSInteger)section { + + return [LWZCollectionLayoutTemplate build:^(LWZCollectionTemplateBuilder * _Nonnull make) { + // + // Container 1 + // | + // +---|----------------------------------+ <-- Group + // | v | + // | +--------------+ +--------------+ <----- Container 2 + // | | | | | | + // | | +--------+ | | +--------+ <-------- Item + // | | | | | | | | | | + // | | | 1 | | | | | | | + // | | +--------+ | | | | | | + // | | | | | | | | + // | | +--------+ | | | | | | + // | | | | | | | | | | + // | | | 2 | | | | 3 | | | + // | | +--------+ | | +--------+ | | + // | | | | | | + // | +--------------+ +--------------+ | + // | | + // +--------------------------------------+ + // + make.addGroup(^(LWZCollectionTemplateGroupBuilder * _Nonnull group) { + group.width.fractionalWidth(1.0); + group.height.fractionalWidth(1.0); + + // container 1 + group.addContainer(^(LWZCollectionTemplateContainerBuilder * _Nonnull container) { + container.width.fractionalWidth(0.5); + container.height.fractionalHeight(1.0); + + // items + for ( NSInteger i = 0 ; i < 2 ; ++ i ) { + container.addItem(^(LWZCollectionTemplateItemBuilder * _Nonnull item) { + item.width.fractionalWidth(1.0); + item.height.fractionalHeight(0.5); + }); + } + }); + + // container 2 + group.addContainer(^(LWZCollectionTemplateContainerBuilder * _Nonnull container) { + container.width.fractionalWidth(0.5); + container.height.fractionalHeight(1.0); + + container.addItem(^(LWZCollectionTemplateItemBuilder * _Nonnull item) { + item.width.fractionalWidth(1.0); + item.height.fractionalHeight(1.0); + }); + }); + }); + + // + // +--------------------------------------+ + // | | + // | +--------------+ +--------------+ | + // | | | | | | + // | | +--------+ | | +--------+ | | + // | | | | | | | | | | + // | | | 1 | | | | 3 | | | + // | | +--------+ | | +--------+ | | + // | | | | | | + // | | +--------+ | | +--------+ | | + // | | | | | | | | | | + // | | | 2 | | | | 4 | | | + // | | +--------+ | | +--------+ | | + // | | | | | | + // | +--------------+ +--------------+ | + // | | + // +--------------------------------------+ + // + make.addGroup(^(LWZCollectionTemplateGroupBuilder * _Nonnull group) { + group.width.fractionalWidth(1.0); + group.height.fractionalWidth(1.0); + + for ( NSInteger i = 0 ; i < 2 ; ++ i ) { + // container + group.addContainer(^(LWZCollectionTemplateContainerBuilder * _Nonnull container) { + container.width.fractionalWidth(0.5); + container.height.fractionalHeight(1.0); + + // items + for ( NSInteger i = 0 ; i < 2 ; ++ i ) { + container.addItem(^(LWZCollectionTemplateItemBuilder * _Nonnull item) { + item.width.fractionalWidth(1); + item.height.fractionalHeight(0.5); + }); + } + }); + } + }); + + // + // +--------------------+ + // | | + // | +--------------+ | + // | | | | + // | | +--------+ | | + // | | | | | | + // | | | 1 | | | + // | | +--------+ | | + // | | | | + // | | +--------+ | | + // | | | | | | + // | | | 2 | | | + // | | +--------+ | | + // | | | | + // | | +--------+ | | + // | | | | | | + // | | | 3 | | | + // | | +--------+ | | + // | | | | + // | +--------------+ | + // | | + // +--------------------+ + // + make.addGroup(^(LWZCollectionTemplateGroupBuilder * _Nonnull group) { + group.width.fractionalWidth(1.0); + group.height.fractionalWidth(1.0); + + group.addContainer(^(LWZCollectionTemplateContainerBuilder * _Nonnull container) { + container.width.fractionalWidth(1.0); + container.height.fractionalHeight(1.0); + + for ( NSInteger i = 0 ; i < 3 ; ++ i ) { + container.addItem(^(LWZCollectionTemplateItemBuilder * _Nonnull item) { + item.width.fractionalWidth(1); + item.height.fractionalHeight(1/3.0); + }); + } + }); + }); + }]; + } + \endcode + */ +NS_ASSUME_NONNULL_END diff --git a/LWZComponents/LWZCollectionViewComponents/Layout/LWZCollectionLayoutContentContainer.m b/LWZComponents/LWZCollectionViewComponents/Layout/LWZCollectionLayoutContentContainer.m new file mode 100644 index 0000000..c856964 --- /dev/null +++ b/LWZComponents/LWZCollectionViewComponents/Layout/LWZCollectionLayoutContentContainer.m @@ -0,0 +1,529 @@ +// +// LWZCollectionLayoutContentContainer.m +// SJTestAutoLayout_Example +// +// Created by changsanjiang on 2021/11/9. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "LWZCollectionLayoutContentContainer.h" + +@implementation LWZCollectionLayoutCollectionContentContainer +- (instancetype)initWithCollectionSize:(CGSize)collectionSize direction:(UICollectionViewScrollDirection)direction collectionContentInsets:(UIEdgeInsets)collectionContentInsets collectionSafeAreaInsets:(UIEdgeInsets)safeAreaInsets { + self = [super init]; + if ( self ) { + _collectionSize = collectionSize; + _layoutDirection = direction; + + CGFloat layoutRangeMin = 0; + CGFloat layoutRangeMax = 0; + switch ( direction ) { + case UICollectionViewScrollDirectionVertical: { + layoutRangeMin = 0; + layoutRangeMax = collectionSize.width - (collectionContentInsets.left + collectionContentInsets.right); + } + break; + case UICollectionViewScrollDirectionHorizontal: { + layoutRangeMin = 0; + layoutRangeMax = collectionSize.height - (collectionContentInsets.top + collectionContentInsets.bottom); + } + break; + } + _layoutRange = UIFloatRangeMake(layoutRangeMin, layoutRangeMax); + + + CGSize layoutContainerSize = CGSizeZero; + layoutContainerSize.width = collectionSize.width - (collectionContentInsets.left + collectionContentInsets.right); + layoutContainerSize.height = collectionSize.height - (collectionContentInsets.top + collectionContentInsets.bottom); + _layoutContainerSize = layoutContainerSize; + } + return self; +} + +- (instancetype)initWithCollectionSize:(CGSize)collectionSize direction:(UICollectionViewScrollDirection)direction collectionContentInsets:(UIEdgeInsets)contentInsets collectionSafeAreaInsets:(UIEdgeInsets)collectionSafeAreaInsets ignoredCollectionSafeAreaInsets:(BOOL)isIgnoredSafeAreaInsets { + self = [self initWithCollectionSize:collectionSize direction:direction collectionContentInsets:contentInsets collectionSafeAreaInsets:collectionSafeAreaInsets]; + if ( self ) { + if ( !isIgnoredSafeAreaInsets ) { + _layoutInsets = collectionSafeAreaInsets; + CGFloat layoutRangeMin = _layoutRange.minimum; + CGFloat layoutRangeMax = _layoutRange.maximum; + switch ( direction ) { + case UICollectionViewScrollDirectionVertical: { + layoutRangeMin += collectionSafeAreaInsets.left; + layoutRangeMax -= collectionSafeAreaInsets.right; + } + break; + case UICollectionViewScrollDirectionHorizontal: { + layoutRangeMin += collectionSafeAreaInsets.top; + layoutRangeMax -= collectionSafeAreaInsets.bottom; + } + break; + } + _layoutRange = UIFloatRangeMake(layoutRangeMin, layoutRangeMax); + + CGSize layoutContainerSize = _layoutContainerSize; + layoutContainerSize.width -= collectionSafeAreaInsets.left + collectionSafeAreaInsets.right; + layoutContainerSize.height -= collectionSafeAreaInsets.top + collectionSafeAreaInsets.bottom; + _layoutContainerSize = layoutContainerSize; + } + } + return self; +} +@end + + +@implementation LWZCollectionLayoutSectionContentContainer +- (instancetype)initWithCollectionContentContainer:(LWZCollectionLayoutCollectionContentContainer *)collectionContentContainer sectionEdgeSpacings:(UIEdgeInsets)edgeSpacings sectionContentInsets:(UIEdgeInsets)contentInsets { + self = [super init]; + if ( self ) { + _collectionContentContainer = collectionContentContainer; + _layoutInsets = edgeSpacings; + _contentInsets = contentInsets; + UIFloatRange collectionContentLayoutRange = collectionContentContainer.layoutRange; + CGFloat layoutRangeMin = collectionContentLayoutRange.minimum; + CGFloat layoutRangeMax = collectionContentLayoutRange.maximum; + UICollectionViewScrollDirection direction = collectionContentContainer.layoutDirection; + switch ( direction ) { + case UICollectionViewScrollDirectionVertical: { + layoutRangeMin += edgeSpacings.left; + layoutRangeMax -= edgeSpacings.right; + } + break; + case UICollectionViewScrollDirectionHorizontal: { + layoutRangeMin += edgeSpacings.top; + layoutRangeMax -= edgeSpacings.bottom; + } + break; + } + // ranges + // - section + _layoutRange = UIFloatRangeMake(layoutRangeMin, layoutRangeMax); + // - headerFooter + _headerFooterLayoutRange = _layoutRange; + // - item + CGFloat itemLayoutRangeMin = layoutRangeMin; + CGFloat itemLayoutRangeMax = layoutRangeMax; + switch ( direction ) { + case UICollectionViewScrollDirectionVertical: { + itemLayoutRangeMin += contentInsets.left; + itemLayoutRangeMax -= contentInsets.right; + } + break; + case UICollectionViewScrollDirectionHorizontal: { + itemLayoutRangeMin += contentInsets.top; + itemLayoutRangeMax -= contentInsets.bottom; + } + break; + } + _itemLayoutRange = UIFloatRangeMake(itemLayoutRangeMin, itemLayoutRangeMax); + _layoutDirection = direction; + + CGSize itemLayoutContainerSize = collectionContentContainer.layoutContainerSize; + itemLayoutContainerSize.width -= (edgeSpacings.left + edgeSpacings.right) + (contentInsets.left + contentInsets.right); + itemLayoutContainerSize.height -= (edgeSpacings.top + edgeSpacings.bottom) + (contentInsets.top + contentInsets.bottom); + _itemLayoutContainerSize = itemLayoutContainerSize; + } + return self; +} +@end + + +#pragma mark - Template + +@interface LWZCollectionLayoutTemplateDimension () +@property (nonatomic) LWZCollectionLayoutTemplateDimensionSemantic semantic; +@property (nonatomic) CGFloat dimension; +@end +@implementation LWZCollectionLayoutTemplateDimension +- (instancetype)initWithFractionalWidthDimension:(CGFloat)dimension { + return [self initWithDimension:dimension semantic:LWZCollectionLayoutTemplateDimensionSemanticFractionalWidth]; +} +- (instancetype)initWithFractionalHeightDimension:(CGFloat)dimension { + return [self initWithDimension:dimension semantic:LWZCollectionLayoutTemplateDimensionSemanticFractionalHeight]; +} +- (instancetype)initWithAbsoluteDimension:(CGFloat)dimension { + return [self initWithDimension:dimension semantic:LWZCollectionLayoutTemplateDimensionSemanticAbsolute]; +} +- (instancetype)initWithDimension:(CGFloat)dimension semantic:(LWZCollectionLayoutTemplateDimensionSemantic)semantic { + self = [super init]; + if ( self ) { + _semantic = semantic; + _dimension = dimension; + } + return self; +} +@end + + +@implementation LWZCollectionLayoutTemplateSize +- (instancetype)initWithWidthDimension:(LWZCollectionLayoutTemplateDimension *)widthDimension heightDimension:(LWZCollectionLayoutTemplateDimension *)heightDimension { + self = [super init]; + if ( self ) { + _width = widthDimension; + _height = heightDimension; + } + return self; +} +@end + + +@implementation LWZCollectionLayoutTemplateItem +- (instancetype)initWithSize:(LWZCollectionLayoutTemplateSize *)size { + self = [super init]; + if ( self ) { + _size = size; + } + return self; +} +@end + +@implementation LWZCollectionLayoutTemplateContainer +- (instancetype)initWithSize:(LWZCollectionLayoutTemplateSize *)size items:(NSArray *)items { + self = [super initWithSize:size]; + if ( self ) { + _items = items.copy; + } + return self; +} +@end + +@implementation LWZCollectionLayoutTemplateGroup +- (instancetype)initWithSize:(LWZCollectionLayoutTemplateSize *)size containers:(NSArray *)containers { + self = [super initWithSize:size]; + if ( self ) { + _containers = containers; + } + return self; +} +@end + +@implementation LWZCollectionLayoutTemplate ++ (NSArray *)build:(void(^)(LWZCollectionTemplateBuilder *make))block { + LWZCollectionTemplateBuilder *builder = [LWZCollectionTemplateBuilder.alloc init]; + block(builder); + return builder.groups; +} +@end + +UIKIT_STATIC_INLINE CGSize +LWZCollectionTemplateItemLayoutSize(LWZCollectionLayoutTemplateSize *size, CGSize containerSize) { + CGSize layoutSize = CGSizeZero; + LWZCollectionLayoutTemplateDimension *widthDimension = size.width; + LWZCollectionLayoutTemplateDimension *heightDimension = size.height; + switch ( widthDimension.semantic ) { + case LWZCollectionLayoutTemplateDimensionSemanticFractionalWidth: + layoutSize.width = widthDimension.dimension * containerSize.width; + break; + case LWZCollectionLayoutTemplateDimensionSemanticFractionalHeight: + layoutSize.width = widthDimension.dimension *containerSize.height; + break; + case LWZCollectionLayoutTemplateDimensionSemanticAbsolute: + layoutSize.width = widthDimension.dimension; + break; + } + + switch ( heightDimension.semantic ) { + case LWZCollectionLayoutTemplateDimensionSemanticFractionalWidth: + layoutSize.height = heightDimension.dimension * containerSize.width; + break; + case LWZCollectionLayoutTemplateDimensionSemanticFractionalHeight: + layoutSize.height = heightDimension.dimension * containerSize.height; + break; + case LWZCollectionLayoutTemplateDimensionSemanticAbsolute: + layoutSize.height = heightDimension.dimension; + break; + } + return layoutSize; +} + +@interface LWZCollectionTemplateLayoutSolverResult : NSObject +- (instancetype)initWithFrame:(CGRect)frame; // frame in groups? +@property (nonatomic, readonly) CGRect frame; +@end + +@implementation LWZCollectionTemplateLayoutSolverResult +- (instancetype)initWithFrame:(CGRect)frame { + self = [super init]; + if ( self ) { + _frame = frame; + } + return self; +} +@end + +@implementation LWZCollectionTemplateLayoutSolver { + NSMutableArray *mAllItemLayoutFrames; +} +- (instancetype)initWithGroups:(NSArray *)groups scrollDirection:(UICollectionViewScrollDirection)scrollDirection numberOfItems:(NSInteger)numberOfItems lineSpacing:(CGFloat)lineSpacing itemSpacing:(CGFloat)itemSpacing containerSize:(CGSize)containerSize { + self = [super init]; + if ( self ) { + NSMutableArray *results = NSMutableArray.array; + CGPoint groupOrigin = CGPointZero; + for ( LWZCollectionLayoutTemplateGroup *group in groups ) { + CGSize groupLayoutSize = LWZCollectionTemplateItemLayoutSize(group.size, containerSize); + NSArray *containers = group.containers; + NSInteger containerCount = containers.count; + CGSize containerContainerSize = groupLayoutSize; + if ( containerCount > 1 ) { + CGFloat spacings = (containerCount - 1) * itemSpacing; + switch ( scrollDirection ) { + case UICollectionViewScrollDirectionVertical: + containerContainerSize.width -= spacings; + break; + case UICollectionViewScrollDirectionHorizontal: + containerContainerSize.height -= spacings; + break; + } + } + + CGPoint containerOrigin = groupOrigin; + CGPoint itemOrigin = containerOrigin; + for ( LWZCollectionLayoutTemplateContainer *container in containers ) { + CGSize containerLayoutSize = LWZCollectionTemplateItemLayoutSize(container.size, containerContainerSize); + NSArray *items = container.items; + NSInteger itemCount = items.count; + CGSize itemContainerSize = containerLayoutSize; + if ( itemCount > 1 ) { + CGFloat spacings = (itemCount - 1) * lineSpacing; + switch ( scrollDirection ) { + case UICollectionViewScrollDirectionVertical: + itemContainerSize.height -= spacings; + break; + case UICollectionViewScrollDirectionHorizontal: + itemContainerSize.width -= spacings; + break; + } + } + + CGRect itemFrame = CGRectZero; + for ( LWZCollectionLayoutTemplateItem *item in container.items ) { + itemFrame.origin = itemOrigin; + itemFrame.size = LWZCollectionTemplateItemLayoutSize(item.size, itemContainerSize); + + [results addObject:[LWZCollectionTemplateLayoutSolverResult.alloc initWithFrame:itemFrame]]; + + switch ( scrollDirection ) { + case UICollectionViewScrollDirectionVertical: + itemOrigin.y = CGRectGetMaxY(itemFrame) + lineSpacing; + break; + case UICollectionViewScrollDirectionHorizontal: + itemOrigin.x = CGRectGetMaxX(itemFrame) + lineSpacing; + break; + } + } + + switch ( scrollDirection ) { + case UICollectionViewScrollDirectionVertical: + containerOrigin.x += containerLayoutSize.width + itemSpacing; + break; + case UICollectionViewScrollDirectionHorizontal: + containerOrigin.y += containerLayoutSize.height + itemSpacing; + break; + } + itemOrigin = containerOrigin; + } + + switch ( scrollDirection ) { + case UICollectionViewScrollDirectionVertical: + groupOrigin.y += groupLayoutSize.height + lineSpacing; + break; + case UICollectionViewScrollDirectionHorizontal: + groupOrigin.x += groupLayoutSize.width + lineSpacing; + break; + } + } + + NSInteger resultCount = results.count; + CGRect allGroupsLayoutFrame = CGRectZero; + switch ( scrollDirection ) { + case UICollectionViewScrollDirectionVertical: { + allGroupsLayoutFrame.size.height = CGRectGetMaxY(results.lastObject.frame); + allGroupsLayoutFrame.size.width = containerSize.width; + } + break; + case UICollectionViewScrollDirectionHorizontal: { + allGroupsLayoutFrame.size.width = CGRectGetMaxX(results.lastObject.frame); + allGroupsLayoutFrame.size.height = containerSize.height; + } + break; + } + + NSMutableArray *itemLayoutFrames = [NSMutableArray arrayWithCapacity:resultCount]; + CGRect itemLayoutFrame = CGRectZero; + for ( NSInteger i = 0 ; i < numberOfItems ; ++ i ) { + NSInteger idx = i % resultCount; + NSInteger mult = i / resultCount; + LWZCollectionTemplateLayoutSolverResult *result = results[idx]; + itemLayoutFrame = result.frame; + if ( mult != 0 ) { + switch ( scrollDirection ) { + case UICollectionViewScrollDirectionVertical: + itemLayoutFrame.origin.y += CGRectGetMaxY(allGroupsLayoutFrame) * mult + mult * lineSpacing; + break; + case UICollectionViewScrollDirectionHorizontal: + itemLayoutFrame.origin.x += CGRectGetMaxX(allGroupsLayoutFrame) * mult + mult * lineSpacing; + break; + } + } + [itemLayoutFrames addObject:[NSValue valueWithCGRect:itemLayoutFrame]]; + } + mAllItemLayoutFrames = itemLayoutFrames; + } + return self; +} + +- (CGRect)itemLayoutFrameAtIndex:(NSInteger)index { + return [mAllItemLayoutFrames[index] CGRectValue]; +} +@end + +@interface LWZCollectionTemplateDimensionBuilder () { + LWZCollectionLayoutTemplateDimension *mDimension; +} +- (instancetype)initWithDimension:(LWZCollectionLayoutTemplateDimension *)dimension; +@end +@implementation LWZCollectionTemplateDimensionBuilder +- (instancetype)initWithDimension:(LWZCollectionLayoutTemplateDimension *)dimension { + self = [super init]; + if ( self ) { + mDimension = dimension; + } + return self; +} +- (LWZCollectionTemplateDimensionBuilder * _Nonnull (^)(LWZCollectionLayoutTemplateDimensionSemantic))semantic { + return ^LWZCollectionTemplateDimensionBuilder *(LWZCollectionLayoutTemplateDimensionSemantic semantic){ + self->mDimension.semantic = semantic; + return self; + }; +} +- (LWZCollectionTemplateDimensionBuilder * _Nonnull (^)(CGFloat))dimension { + return ^LWZCollectionTemplateDimensionBuilder *(CGFloat dimension){ + self->mDimension.dimension = dimension; + return self; + }; +} +- (void (^)(CGFloat))fractionalWidth { + return ^(CGFloat dimension){ + self->mDimension.semantic = LWZCollectionLayoutTemplateDimensionSemanticFractionalWidth; + self->mDimension.dimension = dimension; + }; +} +- (void (^)(CGFloat))fractionalHeight { + return ^(CGFloat dimension){ + self->mDimension.semantic = LWZCollectionLayoutTemplateDimensionSemanticFractionalHeight; + self->mDimension.dimension = dimension; + }; +} +- (void (^)(CGFloat))absolute { + return ^(CGFloat dimension){ + self->mDimension.semantic = LWZCollectionLayoutTemplateDimensionSemanticAbsolute; + self->mDimension.dimension = dimension; + }; +} +@end + +@interface LWZCollectionTemplateItemBuilder () { + LWZCollectionLayoutTemplateSize *mSize; + LWZCollectionTemplateDimensionBuilder *mWidth; + LWZCollectionTemplateDimensionBuilder *mHeight; +} +@property (nonatomic, readonly) LWZCollectionLayoutTemplateSize *size; +@end +@implementation LWZCollectionTemplateItemBuilder +- (instancetype)init { + self = [super init]; + if ( self ) { + mSize = [LWZCollectionLayoutTemplateSize.alloc initWithWidthDimension:[LWZCollectionLayoutTemplateDimension.alloc initWithFractionalWidthDimension:1.0] + heightDimension:[LWZCollectionLayoutTemplateDimension.alloc initWithFractionalHeightDimension:1.0]]; + + mWidth = [LWZCollectionTemplateDimensionBuilder.alloc initWithDimension:mSize.width]; + mHeight = [LWZCollectionTemplateDimensionBuilder.alloc initWithDimension:mSize.height]; + } + return self; +} +- (LWZCollectionTemplateDimensionBuilder *)width { + return mWidth; +} +- (LWZCollectionTemplateDimensionBuilder *)height { + return mHeight; +} +- (LWZCollectionLayoutTemplateSize *)size { + return mSize; +} +@end + +@interface LWZCollectionTemplateContainerBuilder () { + NSMutableArray *mItems; +} +@property (nonatomic, readonly) NSArray *items; +@end +@implementation LWZCollectionTemplateContainerBuilder +- (instancetype)init { + self = [super init]; + if ( self ) { + mItems = NSMutableArray.array; + } + return self; +} +- (void (^)(void (^ _Nonnull)(LWZCollectionTemplateItemBuilder * _Nonnull)))addItem { + return ^(void(^block)(LWZCollectionTemplateItemBuilder *item)){ + LWZCollectionTemplateItemBuilder *builder = [LWZCollectionTemplateItemBuilder.alloc init]; + block(builder); + [self->mItems addObject:[LWZCollectionLayoutTemplateItem.alloc initWithSize:builder.size]]; + }; +} +- (NSArray *)items { + return mItems; +} +@end + +@interface LWZCollectionTemplateGroupBuilder () { + NSMutableArray *mContainers; +} +@property (nonatomic, readonly) NSArray *containers; +@end +@implementation LWZCollectionTemplateGroupBuilder +- (instancetype)init { + self = [super init]; + if ( self ) { + mContainers = NSMutableArray.array; + } + return self; +} +- (void (^)(void (^ _Nonnull)(LWZCollectionTemplateContainerBuilder * _Nonnull)))addContainer { + return ^(void(^block)(LWZCollectionTemplateContainerBuilder *container)) { + LWZCollectionTemplateContainerBuilder *builder = [LWZCollectionTemplateContainerBuilder.alloc init]; + block(builder); + [self->mContainers addObject:[LWZCollectionLayoutTemplateContainer.alloc initWithSize:builder.size items:builder.items]]; + }; +} + +- (NSArray *)containers { + return mContainers; +} +@end + + +@interface LWZCollectionTemplateBuilder () { + NSMutableArray *mGroups; +} +@end +@implementation LWZCollectionTemplateBuilder +- (instancetype)init { + self = [super init]; + if ( self ) { + mGroups = NSMutableArray.array; + } + return self; +} +- (void (^)(void (^ _Nonnull)(LWZCollectionTemplateGroupBuilder * _Nonnull)))addGroup { + return ^(void(^block)(LWZCollectionTemplateGroupBuilder *group)){ + LWZCollectionTemplateGroupBuilder *builder = [LWZCollectionTemplateGroupBuilder.alloc init]; + block(builder); + [self->mGroups addObject:[LWZCollectionLayoutTemplateGroup.alloc initWithSize:builder.size containers:builder.containers]]; + }; +} +- (NSArray *)groups { + return mGroups; +} +@end diff --git a/LWZComponents/LWZCollectionViewComponents/Layout/LWZCollectionLayoutFittingSize.h b/LWZComponents/LWZCollectionViewComponents/Layout/LWZCollectionLayoutFittingSize.h new file mode 100644 index 0000000..8919777 --- /dev/null +++ b/LWZComponents/LWZCollectionViewComponents/Layout/LWZCollectionLayoutFittingSize.h @@ -0,0 +1,22 @@ +// +// LWZCollectionLayoutFittingSize.h +// SJTestAutoLayout_Example +// +// Created by changsanjiang on 2021/11/9. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import + +#define LWZCollectionLayoutFittingSizeForWidth(__width__) [LWZCollectionLayoutFittingSize fittingSizeForWidth:__width__] +#define LWZCollectionLayoutFittingSizeForHeight(__height__) [LWZCollectionLayoutFittingSize fittingSizeForHeight:__height__] +#define LWZCollectionLayoutFittingSizeForLayoutRange(__range__, __direction__) [LWZCollectionLayoutFittingSize fittingSizeForLayoutRange:__range__ scrollDirection:__direction__] + +NS_ASSUME_NONNULL_BEGIN +@interface LWZCollectionLayoutFittingSize : NSObject ++ (CGSize)fittingSizeForWidth:(CGFloat)width; ++ (CGSize)fittingSizeForHeight:(CGFloat)height; ++ (CGSize)fittingSizeForLayoutRange:(UIFloatRange)layoutRange scrollDirection:(UICollectionViewScrollDirection)direction; ++ (BOOL)isInvalid:(CGSize)size; +@end +NS_ASSUME_NONNULL_END diff --git a/LWZComponents/LWZCollectionViewComponents/Layout/LWZCollectionLayoutFittingSize.m b/LWZComponents/LWZCollectionViewComponents/Layout/LWZCollectionLayoutFittingSize.m new file mode 100644 index 0000000..9446ecc --- /dev/null +++ b/LWZComponents/LWZCollectionViewComponents/Layout/LWZCollectionLayoutFittingSize.m @@ -0,0 +1,38 @@ +// +// LWZCollectionLayoutFittingSize.m +// SJTestAutoLayout_Example +// +// Created by changsanjiang on 2021/11/9. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "LWZCollectionLayoutFittingSize.h" + +#define _LWZ_COLLECTION_LAYOUT_MAX_BOUNDARY (4096) + +@implementation LWZCollectionLayoutFittingSize ++ (CGSize)fittingSizeForWidth:(CGFloat)width { + return CGSizeMake(width, _LWZ_COLLECTION_LAYOUT_MAX_BOUNDARY); +} + ++ (CGSize)fittingSizeForHeight:(CGFloat)height { + return CGSizeMake(_LWZ_COLLECTION_LAYOUT_MAX_BOUNDARY, height); +} + ++ (CGSize)fittingSizeForLayoutRange:(UIFloatRange)layoutRange scrollDirection:(UICollectionViewScrollDirection)direction { + CGFloat length = layoutRange.maximum - layoutRange.minimum; + switch ( direction ) { + case UICollectionViewScrollDirectionVertical: + return [self fittingSizeForWidth:length]; + case UICollectionViewScrollDirectionHorizontal: + return [self fittingSizeForHeight:length]; + } +} + ++ (BOOL)isInvalid:(CGSize)size { + return !((size.width == _LWZ_COLLECTION_LAYOUT_MAX_BOUNDARY && size.height > 0) || + (size.height == _LWZ_COLLECTION_LAYOUT_MAX_BOUNDARY && size.width > 0)); +} +@end + +#undef _LWZ_COLLECTION_LAYOUT_MAX_BOUNDARY diff --git a/LWZComponents/LWZCollectionViewComponents/Layout/LWZCollectionViewLayout.h b/LWZComponents/LWZCollectionViewComponents/Layout/LWZCollectionViewLayout.h new file mode 100755 index 0000000..5cbfed2 --- /dev/null +++ b/LWZComponents/LWZCollectionViewComponents/Layout/LWZCollectionViewLayout.h @@ -0,0 +1,582 @@ +// +// LWZCollectionViewLayout.h +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2020/11/13. +// Copyright © 2020 changsanjiang@gmail.com. All rights reserved. +// + +#import "LWZCollectionDefines.h" +#import "LWZCollectionLayoutContentContainer.h" +#import "LWZCollectionLayoutFittingSize.h" +@protocol LWZCollectionViewLayoutObserver, LWZCollectionViewLayoutDelegate; + +NS_ASSUME_NONNULL_BEGIN +@interface LWZCollectionViewLayout : UICollectionViewLayout +- (instancetype)initWithScrollDirection:(UICollectionViewScrollDirection)scrollDirection; +- (instancetype)initWithScrollDirection:(UICollectionViewScrollDirection)scrollDirection delegate:(nullable id)delegate; +@property (nonatomic, weak, nullable) id delegate; + +@property (nonatomic, readonly) UICollectionViewScrollDirection scrollDirection; + +- (void)prepareLayoutForCollectionSize:(CGSize)size contentInsets:(UIEdgeInsets)contentInsets; + +@property (nonatomic, getter=isIgnoredSafeAreaInsets) BOOL ignoredSafeAreaInsets NS_AVAILABLE_IOS(11.0); // default value is YES. +- (void)prepareLayoutForCollectionSize:(CGSize)size contentInsets:(UIEdgeInsets)contentInsets safeAreaInsets:(UIEdgeInsets)safeAreaInsets NS_AVAILABLE_IOS(11.0); + +@property (nonatomic) BOOL sectionHeadersPinToVisibleBounds; +//@property (nonatomic) BOOL sectionFootersPinToVisibleBounds; + +@property (nonatomic) UIEdgeInsets adjustedPinnedInsets; + +- (CGRect)layoutFrameForSection:(NSInteger)section; + +- (void)enumerateLayoutAttributesWithElementCategory:(UICollectionElementCategory)category usingBlock:(void(NS_NOESCAPE ^)(UICollectionViewLayoutAttributes *attributes, NSUInteger idx, BOOL *stop))block; +- (nullable NSArray *)layoutAttributesObjectsForElementCategory:(UICollectionElementCategory)category; +- (nullable NSArray *)layoutAttributesObjectsForElementCategory:(UICollectionElementCategory)category inSection:(NSInteger)section; + +- (void)registerObserver:(id)observer; +- (void)removeObserver:(id)observer; + +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; +@end + + +@interface LWZCollectionViewLayout (LWZCollectionFittingSize) +- (void)prepareLayoutFittingSize:(CGSize)fittingSize contentInsets:(UIEdgeInsets)contentInsets; +- (void)prepareLayoutFittingSize:(CGSize)fittingSize contentInsets:(UIEdgeInsets)contentInsets safeAreaInsets:(UIEdgeInsets)safeAreaInsets NS_AVAILABLE_IOS(11.0); +@end + +#pragma mark - observer methods + +@protocol LWZCollectionViewLayoutObserver +@optional +- (void)layout:(__kindof LWZCollectionViewLayout *)layout willPrepareLayoutInContainer:(LWZCollectionLayoutCollectionContentContainer *)container; + +- (void)layout:(__kindof LWZCollectionViewLayout *)layout didFinishPreparingInContainer:(LWZCollectionLayoutCollectionContentContainer *)container; +@end + + +#pragma mark - delegate methods + +@protocol LWZCollectionViewLayoutDelegate +@optional + +/** + \code + + +---------------------------+ + | | + | edgeSpacings.top | + | | + +-----------------------+---------------------------+-------------------------+ + | | | | + | | | | + | edgeSpacings.left | Section | edgeSpacings.right | + | | | | + | | | | + +-----------------------+---------------------------+-------------------------+ + | | + | edgeSpacings.bottom | + | | + +---------------------------+ + \endcode + */ +- (UIEdgeInsets)layout:(__kindof LWZCollectionViewLayout *)layout edgeSpacingsForSectionAtIndex:(NSInteger)section; + +/** + \code + +------------------------------------------------------------------------------+ + | | + | section.header | + | | + +------------------------+----------------------------+------------------------+ + | | + | contentInsets.top | + | | + +------------------------+------+---+------+---+------+------------------------+ + | | | | | | | | + | | cell | | cell | | cell | | + | | | | | | | | + | +------+ +------+ +------+ | + | contentInsets.left | | contentInset.right | + | +------+ +------+ +------+ | + | | | | | | | | + | | cell | | cell | | cell | | + | | | | | | | | + +------------------------+------+---+------+---+------+------------------------+ + | | + | contentInsets.bottom | + | | + +------------------------+----------------------------+------------------------+ + | | + | section.footer | + | | + +------------------------------------------------------------------------------+ + \endcode + */ +- (UIEdgeInsets)layout:(__kindof LWZCollectionViewLayout *)layout contentInsetsForSectionAtIndex:(NSInteger)section; + +- (BOOL)layout:(__kindof LWZCollectionViewLayout *)layout canPinToVisibleBoundsForHeaderInSection:(NSInteger)section; +- (UIEdgeInsets)layout:(__kindof LWZCollectionViewLayout *)layout adjustedPinnedInsetsForSectionAtIndex:(NSInteger)section; + +- (CGSize)layout:(__kindof LWZCollectionViewLayout *)layout layoutSizeToFit:(CGSize)fittingSize forHeaderInSection:(NSInteger)section scrollDirection:(UICollectionViewScrollDirection)scrollDirection; +- (CGSize)layout:(__kindof LWZCollectionViewLayout *)layout layoutSizeToFit:(CGSize)fittingSize forItemAtIndexPath:(NSIndexPath *)indexPath scrollDirection:(UICollectionViewScrollDirection)scrollDirection; +- (CGSize)layout:(__kindof LWZCollectionViewLayout *)layout layoutSizeToFit:(CGSize)fittingSize forFooterInSection:(NSInteger)section scrollDirection:(UICollectionViewScrollDirection)scrollDirection; + +/** + \code + +--------------------------[V]-------------------------+ + | | + | +------------------+ | + | | interitemSpacing | | + | +------------------+ | + | | | + | v | + | +----------+ +----------+ | + | | ~~~~~~~~ | | ~~~~~~~~ | | + | | ~~~~~~~~ | | ~~~~~~~~ | | + | | ~~~~~~~~ | | ~~~~~~~~ | | + | | ~~~~~~~~ | +----------+ +-------------+ | + | | ~~~~~~~~ | <----- | lineSpacing | | + | | ~~~~~~~~ | +----------+ +-------------+ | + | +----------+ | ~~~~~~~~ | | + | | ~~~~~~~~ | | + | +----------+ | ~~~~~~~~ | | + | | ~~~~~~~~ | | ~~~~~~~~ | | + | | ~~~~~~~ | | ~~~~~~~~ | | + | | ~~~~~~ | | ~~~~~~ | | + | | .... | | .... | | + | | + +------------------------------------------------------+ + + +---------------------------------[H]------------------------------+ + | | + | +-------------+ | + | | lineSpacing | | + | +-------------+ | + | | | + | v | + | +-------------+ +------------ | + | | ~~~~~~~~~~~ | | ~~~~~~~ | + | | ~~~~~~~~~~~ | | ~~~~~~~~ | + | | ~~~~~~~~~~~ | | ~~~~~~~~~~ | + | +-------------+ +------------ +------------------+ | + | <--- | interitemSpacing | | + | +--------------------+ +--------- +------------------+ | + | | ~~~~~~~~~~~~~~~~~~ | | ~~~~~ | + | | ~~~~~~~~~~~~~~~~~~ | | ~~~~~~ | + | | ~~~~~~~~~~~~~~~~~~ | | ~~~~~~~ | + | +--------------------+ +--------- | + | | + +------------------------------------------------------------------+ + \endcode + */ +- (CGFloat)layout:(__kindof LWZCollectionViewLayout *)layout minimumLineSpacingForSectionAtIndex:(NSInteger)section; +- (CGFloat)layout:(__kindof LWZCollectionViewLayout *)layout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section; + + +- (nullable NSString *)layout:(__kindof LWZCollectionViewLayout *)layout elementKindForSectionDecorationAtIndexPath:(NSIndexPath *)indexPath; +- (nullable NSString *)layout:(__kindof LWZCollectionViewLayout *)layout elementKindForHeaderDecorationAtIndexPath:(NSIndexPath *)indexPath; +- (nullable NSString *)layout:(__kindof LWZCollectionViewLayout *)layout elementKindForItemDecorationAtIndexPath:(NSIndexPath *)indexPath; +- (nullable NSString *)layout:(__kindof LWZCollectionViewLayout *)layout elementKindForFooterDecorationAtIndexPath:(NSIndexPath *)indexPath; + +- (nullable id)layout:(__kindof LWZCollectionViewLayout *)layout userInfoForSectionDecorationAtIndexPath:(NSIndexPath *)indexPath; +- (nullable id)layout:(__kindof LWZCollectionViewLayout *)layout userInfoForHeaderDecorationAtIndexPath:(NSIndexPath *)indexPath; +- (nullable id)layout:(__kindof LWZCollectionViewLayout *)layout userInfoForItemDecorationAtIndexPath:(NSIndexPath *)indexPath; +- (nullable id)layout:(__kindof LWZCollectionViewLayout *)layout userInfoForFooterDecorationAtIndexPath:(NSIndexPath *)indexPath; + +- (CGRect)layout:(__kindof LWZCollectionViewLayout *)layout relativeRectToFit:(CGRect)rect forSectionDecorationAtIndexPath:(NSIndexPath *)indexPath; +- (CGRect)layout:(__kindof LWZCollectionViewLayout *)layout relativeRectToFit:(CGRect)rect forHeaderDecorationAtIndexPath:(NSIndexPath *)indexPath; +- (CGRect)layout:(__kindof LWZCollectionViewLayout *)layout relativeRectToFit:(CGRect)rect forItemDecorationAtIndexPath:(NSIndexPath *)indexPath; +- (CGRect)layout:(__kindof LWZCollectionViewLayout *)layout relativeRectToFit:(CGRect)rect forFooterDecorationAtIndexPath:(NSIndexPath *)indexPath; + +- (NSInteger)layout:(__kindof LWZCollectionViewLayout *)layout zIndexForHeaderInSection:(NSInteger)section; +- (NSInteger)layout:(__kindof LWZCollectionViewLayout *)layout zIndexForItemAtIndexPath:(NSIndexPath *)indexPath; +- (NSInteger)layout:(__kindof LWZCollectionViewLayout *)layout zIndexForFooterInSection:(NSInteger)section; + +- (NSInteger)layout:(__kindof LWZCollectionViewLayout *)layout zIndexForSectionDecorationAtIndexPath:(NSIndexPath *)indexPath; +- (NSInteger)layout:(__kindof LWZCollectionViewLayout *)layout zIndexForHeaderDecorationAtIndexPath:(NSIndexPath *)indexPath; +- (NSInteger)layout:(__kindof LWZCollectionViewLayout *)layout zIndexForItemDecorationAtIndexPath:(NSIndexPath *)indexPath; +- (NSInteger)layout:(__kindof LWZCollectionViewLayout *)layout zIndexForFooterDecorationAtIndexPath:(NSIndexPath *)indexPath; +@end + +// [V]垂直布局 +// [H]水平布局 + +#pragma mark - weight +/** + \code + 根据比重布局 + [V] 垂直布局时, 每行根据比重分配item的宽度, 同行item的高度与行首item一致 + [H] 水平布局同理 + + + [V] 垂直布局, bounds.width * weight ==> item.maxWidth, 可以小于但不能大于. 当 item.width > maxWidth 时, 将被修正为 maxWidth + +---------------------------+ + | | + | weight = 1.0 | + | | + +-------------+-------------+ + | | | + | 0.5 | 0.5 | + | | | + +---------+---+-------------+ + | | | + | | | + +---------+-----------------+ + + [H] 水平布局, bounds.height * weight ==> item.maxHeight, 可以小于但不能大于. 当 item.height > maxHeight 时, 将被修正为 maxHeight + +--------------+-----+------+ + | | | | + | | | | + | | | | + | | 0.5 | | + | weight = 1.0 +-----+ | + | | | 0.75 | + | | +------+ + | | | | + | | 0.5 | 0.25 | + +--------------+-----+------+ + + \endcode + */ +@interface LWZCollectionWeightLayout : LWZCollectionViewLayout + +@end + +@protocol LWZCollectionWeightLayoutDelegate +@optional +/// (0, 1] +/// 如果未实现, 默认为1 +- (CGFloat)layout:(__kindof LWZCollectionViewLayout *)layout weightForItemAtIndexPath:(NSIndexPath *)indexPath; +@end + + +#pragma mark - list + +/** + 列表布局, 列数[v]为1, 行数[h]为1, interitemSpacing 将无效; + + [V] CollectionView 宽度必须设置 + [H] CollectionView 高度必须设置 + + +---------[V]-------+ + | | + | +---------+ | + | | ~~~~~~~ | <-+-- Start(左对齐) + | | ~~~~~~~ | | + | +---------+ | + | | + | +---------+ | + | | ~~~~~~~ | <-+-- Center(中对齐) + | | ~~~~~~~ | | + | +---------+ | + | | + | +---------+ | + | | ~~~~~~~ |<-+-- End(右对齐) + | | ~~~~~~~ | | + | +---------+ | + | | + +-------------------+ + + +----------------------[H]----------------------+ + | | + | +---------+ <-+-- Start(顶对齐) + | | ~~~~~~~ | +---------+ | + | | ~~~~~~~ | | ~~~~~~~ | <-+-- Center(中对齐) + | +---------+ | ~~~~~~~ | +---------+ | + | +---------+ | ~~~~~~~ |<-+-- End(底对齐) + | | ~~~~~~~ | | + | +---------+ | + | | + +-----------------------------------------------+ + */ +@interface LWZCollectionListLayout : LWZCollectionViewLayout + +@end + +@protocol LWZCollectionListLayoutDelegate +@optional +- (LWZCollectionLayoutAlignment)layout:(__kindof LWZCollectionViewLayout *)layout layoutAlignmentForItemAtIndexPath:(NSIndexPath *)indexPath; +@end + +#pragma mark - waterfall flow +/** + \code + 瀑布流布局 + [V] 垂直布局时, 每行的item平分宽度, 高度不一 + [H] 水平布局同理 + + +-----------[V]----------+ ==> numberOfArrangedItemsPerLineInSection == 2 + | | + | +-------+ +-------+ | + | | | | | | + | | | | | | + | | 1 | | | | + | +-------+ | 2 | | + | +-------+ | + | +-------+ + | | | +-------+ | + | | | | | | + | | | | | | + | | 3 | | | | + | +-------+ | | | + | | 4 | | + | +-------+ +-------+ | + | | | | + | | ... | ... | + | v v | + | | + +------------------------+ + + +-------------[H]--------------------+ ==> numberOfArrangedItemsPerLineInSection == 2 + | | + | +-------+ +-----------+ +---> | + | | | | | | | + | | | | | | ... | + | +-------+ +-----------+ +---> | + | | + | +-----------+ +----------+ | + | | | | | | + | | | | | ... | + | +-----------+ +----------+ | + | | + +------------------------------------+ + \endcode + */ +@interface LWZCollectionWaterfallFlowLayout : LWZCollectionViewLayout + +@end + +@protocol LWZCollectionWaterfallFlowLayoutDelegate +@optional +/// (0, ...) +/// 如果未实现, 默认为1 +- (NSInteger)layout:(__kindof LWZCollectionViewLayout *)layout numberOfArrangedItemsPerLineInSection:(NSInteger)section; +@end + +#pragma mark - restricted layout + +/** + \code + + 受限的布局 + [V] 垂直布局时: 每行item的高度与行首item一致, 宽度不一 + [H] 水平布局同理 + + +------------------[V]------------------------+ + | | + | +-------+ +------------+ +------------+ | + | | | | | | | | + | | 1 | | 2 | | 3 | | + | +-------+ +------------+ +------------+ | + | | + | +--------------------+ +-----+ | + | | | | | | + | | 4 | | 5 | | + | +--------------------+ +-----+ | + | | + | +---------------+ +------------+ +---+ | + | | | | | | | | + | | 6 | | 7 | | 8 | | + | +---------------+ +------------+ +---+ | + | | + +---------------------------------------------+ + + + +------------------[H]-------------------------+ + | | + | +-------+ +-------+ +-------+ +-------+ | + | | | | | | 6 | | | | + | | 1 | | | +-------+ | | | + | +-------+ | | | | | + | | | +-------+ | | | + | +-------+ | 4 | | | | 9 | | + | | | +-------+ | 7 | +-------+ | + | | 2 | +-------+ | + | +-------+ +-------+ +-------+ | + | | | +-------+ | | | + | +-------+ | | | | | 10 | | + | | | | | | 8 | +-------+ | + | | 3 | | 5 | +-------+ | + | +-------+ +-------+ | + | | + +----------------------------------------------+ + \endcode + */ +@interface LWZCollectionRestrictedLayout : LWZCollectionViewLayout + +@end + + +#pragma mark - template layout + +/** + 模板布局: 定义一套模板, cell 的位置将根据模板描述进行布局. + + \code + + Container1 + | + +--+-----------------------[V]-------------------------+ <---- Group1 + | v | + | +-------------------+ +-----------------------+ <-------- Container2 + | | | | | | + | | +-----------+ | | +---------------+ <------------ Item + | | | | | | | | | | + | | | | | | | | | | + | | | 1 | | | | | | | + | | +-----------+ | | | | | | + | | | | | | | | + | | +-----------+ | | | 4 | | | + | | | | | | +---------------+ | | + | | | | | | | | + | | | 2 | | | +---------------+ | | + | | +-----------+ | | | | | | + | | | | | | | | + | | +-----------+ | | | | | | + | | | | | | | | | | + | | | | | | | | | | + | | | 3 | | | | 5 | | | + | | +-----------+ | | +---------------+ | | + | | | | | | + | +-------------------+ +-----------------------+ | + | | + +------------------------------------------------------+ + + +--------------------[H]---------------------+ <--- Group1 + | | + | +--------------------------------------+ <------ Container1 + | | | | + | | +--------+ +--------+ +--------+ <--------- Item + | | | | | | | | | | + | | | 1 | | 2 | | 3 | | | + | | +--------+ +--------+ +--------+ | | + | | | | + | +--------------------------------------+ | + | | + | +--------------------------------------+ <------ Container2 + | | | | + | | +--------------------+ +--------+ <--------- Item + | | | | | | | | + | | | 4 | | 5 | | | + | | +--------------------+ +--------+ | | + | | | | + | +--------------------------------------+ | + | | + +--------------------------------------------+ + \endcode + */ +@interface LWZCollectionTemplateLayout : LWZCollectionViewLayout + +@end + +@protocol LWZCollectionTemplateLayoutDelegate +- (NSArray *)layout:(__kindof LWZCollectionViewLayout *)layout layoutTemplateContainerGroupsInSection:(NSInteger)section; +@end + + +#pragma mark - hybrid layout + +/** + 多种方式混合布局 + + \code + +----------------------- + | + | Weight Layout + | + | List Layout + | + | WaterfallFlow Layout + | + | Restricted Layout + | + | Template Layout + | + +----------------------- + \endcode + */ +@interface LWZCollectionHybridLayout : LWZCollectionViewLayout + +@end + +@protocol LWZCollectionHybridLayoutDelegate < + LWZCollectionWeightLayoutDelegate, + LWZCollectionListLayoutDelegate, + LWZCollectionWaterfallFlowLayoutDelegate, + LWZCollectionTemplateLayoutDelegate +> + +/// 返回指定section的布局方式 +- (LWZCollectionLayoutType)layout:(__kindof LWZCollectionViewLayout *)layout layoutTypeForItemsInSection:(NSInteger)section; +@end + + +#pragma mark - compositional layout + +/** + 这个 layout 类似于 `UICollectionViewCompositionalLayout`, 但不完全一样, 是存在缺陷的. + OrthogonalContent 区域的 cell 会在单独的 collectionView 中显示, 通过原来的 CollectionView 无法进行 cell 及 indexPath 的转换. + 缺陷如下: + + 缺陷1: [collectionView cellForItemAtIndexPath:] 可能获取到不到对应 cell + 替代方案: 使用 [layout isOrthogonalScrollingInSection:] & [layout orthogonalScrollingCellForItemAtIndexPath:]; + + 缺陷2: [collectionView indexPathForCell:] 可能获取到不到对应 indexPath + 替代方案: 使用 [(UICollectionView *)cell.superview indexPathForCell:]; + + 缺陷3: collectionView:willDisplayCell:forItemAtIndexPath: 及 collectionView:didEndDisplayingCell:forItemAtIndexPath: 这两个方法在滑动 OrthogonalContent 区域, cell 可见状态发生变化时, 对应的 delegate 方法会触发, 但在区域外滑动, 将不会被触发. + 目前未解决 + + 缺陷4: delete, move 等 updates 相关方法可能无法正常工作. + */ +@interface LWZCollectionCompositionalLayout : LWZCollectionHybridLayout +- (BOOL)isOrthogonalScrollingInSection:(NSInteger)section; +- (nullable __kindof UICollectionViewCell *)orthogonalScrollingCellForItemAtIndexPath:(NSIndexPath *)indexPath; +@end + +@protocol LWZCollectionCompositionalLayoutDelegate +@optional +- (BOOL)layout:(__kindof LWZCollectionViewLayout *)layout isOrthogonalScrollingInSection:(NSInteger)section; +/** + \code + + 水平滑动时, 需要根据 fittingSize.width 计算出合适的布局高度 + 垂直滑动时, 需要根据 fittingSize.height 计算出合适的布局宽度 + + +------------------------------------------------------------------------------+ + | | + | section.header | + | | + +------------------------+----------------------------+------------------------+ + | | + | contentInsets.top | + | | + +------------------------<===== fittingSize.width ====>---+------+---+------+-+-+------+---+------+ <---+ + | | | | | | | | | | | | | | | | | + | | cell | | cell | | cell | | cell | | cell | | | cell | | cell | | + | | | | | | | | | | | | | | | | | + | +------+ +------+ +------+ +------+ +------+ | +------+ +------+ | + | contentInsets.left | | contentInset.right | ??? (layoutSize.height) + | +------+ +------+ +------+ +------+ +------+ | +------+ +------+ | + | | | | | | | | | | | | | | | | | + | | cell | | cell | | cell | | cell | | cell | | | cell | | cell | | + | | | | | | | | | | | | | | | | | + +------------------------<======================== horizontal scroll =============================> <---+ + | | + | contentInsets.bottom | + | | + +------------------------+----------------------------+------------------------+ + | | + | section.footer | + | | + +------------------------------------------------------------------------------+ + \endcode + */ +- (CGSize)layout:(__kindof LWZCollectionViewLayout *)layout layoutSizeToFit:(CGSize)fittingSize forOrthogonalContentInSection:(NSInteger)section scrollDirection:(UICollectionViewScrollDirection)scrollDirection; +- (LWZCollectionLayoutContentOrthogonalScrollingBehavior)layout:(__kindof LWZCollectionViewLayout *)layout orthogonalContentScrollingBehaviorInSection:(NSInteger)section; +@end +NS_ASSUME_NONNULL_END diff --git a/LWZComponents/LWZCollectionViewComponents/Layout/LWZCollectionViewLayout.m b/LWZComponents/LWZCollectionViewComponents/Layout/LWZCollectionViewLayout.m new file mode 100755 index 0000000..070ba69 --- /dev/null +++ b/LWZComponents/LWZCollectionViewComponents/Layout/LWZCollectionViewLayout.m @@ -0,0 +1,2292 @@ +// +// LWZCollectionViewLayout.m +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2020/11/13. +// Copyright © 2020 changsanjiang@gmail.com. All rights reserved. +// + +#import "LWZCollectionViewLayout.h" +#import "LWZCollectionViewLayoutAttributes.h" +#import "LWZCollectionLayoutContentContainer.h" +#import "LWZCollectionViewLayoutSubclass.h" + +typedef NS_OPTIONS(NSUInteger, LWZCollectionLayoutPrepareContext) { + LWZCollectionLayoutPrepareContextNone = 0, + LWZCollectionLayoutPrepareContextInvalidateEverything = 1 << 0, + LWZCollectionLayoutPrepareContextInvalidateDataSourceCounts = 1 << 1, + LWZCollectionLayoutPrepareContextBoundaryChanging = 1 << 2, +}; + +UIKIT_STATIC_INLINE CGSize +LWZLayoutSizeItemAdjusting(CGSize size, CGSize fittingSize, UICollectionViewScrollDirection direction) { + switch ( direction ) { + case UICollectionViewScrollDirectionVertical: + if ( size.width > fittingSize.width ) size.width = fittingSize.width; + break; + case UICollectionViewScrollDirectionHorizontal: + if ( size.height > fittingSize.height ) size.height = fittingSize.height; + break; + } + return size; +} + +UIKIT_STATIC_INLINE CGSize +LWZLayoutSizeHeaderFooterAdjusting(CGSize size, CGSize fittingSize, UICollectionViewScrollDirection direction) { + switch ( direction ) { + case UICollectionViewScrollDirectionVertical: + size.width = fittingSize.width; + break; + case UICollectionViewScrollDirectionHorizontal: + size.height = fittingSize.height; + break; + } + return size; +} + +static CGFloat const LWZ_LAYOUT_MIN_VALUE = 0.1; +UIKIT_STATIC_INLINE BOOL +LWZLayoutSizeIsInvalid(CGSize size, UICollectionViewScrollDirection direction) { + switch ( direction ) { + case UICollectionViewScrollDirectionVertical: + return size.height < LWZ_LAYOUT_MIN_VALUE; + case UICollectionViewScrollDirectionHorizontal: + return size.width < LWZ_LAYOUT_MIN_VALUE; + } + return NO; +} + + +UIKIT_STATIC_INLINE CGFloat +LWZLayoutAttributesGetMaxOffset(LWZCollectionViewLayoutAttributes *attributes, UICollectionViewScrollDirection direction) { + CGFloat offset = 0; + switch ( direction ) { + case UICollectionViewScrollDirectionVertical: + offset = CGRectGetMaxY(attributes.frame); + break; + case UICollectionViewScrollDirectionHorizontal: + offset = CGRectGetMaxX(attributes.frame); + break; + } + return offset; +} + +UIKIT_STATIC_INLINE CGFloat +LWZLayoutAttributesGetMaxOffset(NSArray *attributesObjects, UICollectionViewScrollDirection direction) __attribute__((overloadable)) { + CGFloat offset = 0; + CGFloat cur = 0; + for ( LWZCollectionViewLayoutAttributes *attributes in attributesObjects ) { + cur = LWZLayoutAttributesGetMaxOffset(attributes, direction); + if ( offset < cur ) offset = cur; + } + return offset; +} + +typedef NS_ENUM(NSUInteger, LWZFloatRangeComparisonResult) { + LWZFloatRangeComparisonResultInLeft, + LWZFloatRangeComparisonResultIntersecting, + LWZFloatRangeComparisonResultInRight, +}; + +UIKIT_STATIC_INLINE LWZFloatRangeComparisonResult +LWZFloatRangeCompare(UIFloatRange range1, UIFloatRange range2) { + if ( range1.maximum < range2.minimum ) + return LWZFloatRangeComparisonResultInLeft;; + if ( range1.minimum > range2.maximum ) + return LWZFloatRangeComparisonResultInRight; + return LWZFloatRangeComparisonResultIntersecting; +} + +UIKIT_STATIC_INLINE UIFloatRange +LWZRectFloatRangeForDirection(CGRect rect, UICollectionViewScrollDirection direction) { + UIFloatRange range = UIFloatRangeZero; + switch ( direction ) { + case UICollectionViewScrollDirectionVertical: + range = UIFloatRangeMake(CGRectGetMinY(rect), CGRectGetMaxY(rect)); + break; + case UICollectionViewScrollDirectionHorizontal: + range = UIFloatRangeMake(CGRectGetMinX(rect), CGRectGetMaxX(rect)); + break; + } + return range; +} + +UIKIT_STATIC_INLINE LWZFloatRangeComparisonResult +LWZRectFloatRangeCompare(CGRect rect1, CGRect rect2, UICollectionViewScrollDirection direction) { + UIFloatRange range1 = LWZRectFloatRangeForDirection(rect1, direction); + UIFloatRange range2 = LWZRectFloatRangeForDirection(rect2, direction); + return LWZFloatRangeCompare(range1, range2); +} + +UIKIT_STATIC_INLINE BOOL +LWZRectFloatRangeIntersects(CGRect rect1, CGRect rect2, UICollectionViewScrollDirection direction) { + return LWZRectFloatRangeCompare(rect1, rect2, direction) == LWZFloatRangeComparisonResultIntersecting; +} + + +UIKIT_STATIC_INLINE NSArray *_Nullable +LWZAllHashTableObjects(NSHashTable *table) { + return table.count != 0 ? NSAllHashTableObjects(table) : nil; +} + +@interface _LWZLayoutSection : NSObject +/// 表示当前section整个的layout.frame +/// 这个属性在设置`sectionHeadersPinToVisibleBounds == YES`时会被用到, 不会再collectionView中使用 +@property (nonatomic) CGRect frame; +@property (nonatomic, strong, nullable) __kindof UIView *customView; // LWZCollectionCompositionalLayout + +@property (nonatomic) BOOL canPinToVisibleBoundsForHeader; +@property (nonatomic, strong, nullable) LWZCollectionViewLayoutAttributes *headerViewLayoutAttributes; +@property (nonatomic, strong, nullable) LWZCollectionViewLayoutAttributes *headerViewPinnedLayoutAttributes; +@property (nonatomic, strong, nullable) LWZCollectionViewLayoutAttributes *footerViewLayoutAttributes; + +@property (nonatomic, strong, nullable) LWZCollectionViewLayoutAttributes *sectionDecorationLayoutAttributes; +@property (nonatomic, strong, nullable) LWZCollectionViewLayoutAttributes *headerDecorationLayoutAttributes; +@property (nonatomic, strong, nullable) LWZCollectionViewLayoutAttributes *headerDecorationPinnedLayoutAttributes; +@property (nonatomic, strong, nullable) LWZCollectionViewLayoutAttributes *footerDecorationLayoutAttributes; + +@property (nonatomic, readonly, nullable) NSArray *cellLayoutAttributesObjects; +@property (nonatomic, readonly, nullable) NSArray *cellDecorationLayoutAttributesObjects; + +- (void)removeAllLayoutAttributes; + +- (nullable NSArray *)layoutAttributesObjectsForElementCategory:(UICollectionElementCategory)category; + +- (nullable LWZCollectionViewLayoutAttributes *)currentHeaderViewLayoutAttributes; // @note 后面如要扩展footer时也需要添加类似的方法 +- (nullable LWZCollectionViewLayoutAttributes *)currentHeaderDecorationLayoutAttributes; // @note 后面如要扩展footer时也需要添加类似的方法 +@end + +@implementation _LWZLayoutSection { + NSArray *_Nullable mCellLayoutAttributesObjects; + NSArray *_Nullable mCellDecorationLayoutAttributesObjects; +} + +- (void)dealloc { + if ( _customView != nil ) { + [_customView removeFromSuperview]; + _customView = nil; + } +} + +- (nullable NSArray *)layoutAttributesObjectsForElementCategory:(UICollectionElementCategory)category { + switch ( category ) { + case UICollectionElementCategoryCell: + return self.cellLayoutAttributesObjects; + case UICollectionElementCategorySupplementaryView: { + NSMutableArray *m = [NSMutableArray arrayWithCapacity:2]; + if ( [self currentHeaderViewLayoutAttributes] != nil ) [m addObject:[self currentHeaderViewLayoutAttributes]]; + if ( self.footerViewLayoutAttributes != nil ) [m addObject:self.footerViewLayoutAttributes]; + return m.count != 0 ? m : nil; + } + break; + case UICollectionElementCategoryDecorationView: { + NSMutableArray *m = NSMutableArray.array; + if ( [self currentHeaderDecorationLayoutAttributes] ) [m addObject:[self currentHeaderDecorationLayoutAttributes]]; + if ( self.cellDecorationLayoutAttributesObjects != nil ) [m addObjectsFromArray:self.cellDecorationLayoutAttributesObjects]; + if ( self.footerDecorationLayoutAttributes != nil ) [m addObject:self.footerDecorationLayoutAttributes]; + return m.count != 0 ? m : nil; + } + break; + } +} + +- (nullable LWZCollectionViewLayoutAttributes *)currentHeaderViewLayoutAttributes { + return _headerViewPinnedLayoutAttributes ?: _headerViewLayoutAttributes; +} + +- (nullable LWZCollectionViewLayoutAttributes *)currentHeaderDecorationLayoutAttributes { + return _headerDecorationPinnedLayoutAttributes ?: _headerDecorationLayoutAttributes; +} + +- (void)setCellLayoutAttributesObjects:(NSArray *)objects { + mCellLayoutAttributesObjects = objects; +} + +- (void)setCellDecorationLayoutAttributesObjects:(NSArray *)objects { + mCellDecorationLayoutAttributesObjects = objects; +} + +- (void)removeAllLayoutAttributes { + _frame = CGRectZero; + _footerDecorationLayoutAttributes = nil; + _headerDecorationLayoutAttributes = nil; + _sectionDecorationLayoutAttributes = nil; + mCellLayoutAttributesObjects = nil; + mCellDecorationLayoutAttributesObjects = nil; + _headerViewLayoutAttributes = nil; + _footerDecorationLayoutAttributes = nil; + if ( _customView != nil ) { + [_customView removeFromSuperview]; + _customView = nil; + } +} + +- (nullable NSArray *)cellLayoutAttributesObjects { + return [self _layoutAttributes:mCellLayoutAttributesObjects]; +} + +- (nullable NSArray *)cellDecorationLayoutAttributesObjects { + return [self _layoutAttributes:mCellDecorationLayoutAttributesObjects]; +} + +- (nullable NSArray *)_layoutAttributes:(NSArray *)array { + return array.count == 0 ? nil : array; +} + +@end + +@interface _LWZLayoutCollection : NSObject +- (instancetype)initWithScrollDirection:(UICollectionViewScrollDirection)scrollDirection; +@property (nonatomic, readonly, nullable) NSArray<_LWZLayoutSection *> *sections; + +- (nullable NSArray<_LWZLayoutSection *> *)sectionsInRect:(CGRect)rect; +- (void)addSection:(_LWZLayoutSection *)section; +- (void)removeAllSections; + +//- (void) + +- (nullable LWZCollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath; +- (nullable LWZCollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath; +- (nullable LWZCollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath; +- (nullable NSArray<__kindof LWZCollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)param; +@end + +@implementation _LWZLayoutCollection { + NSMutableArray<_LWZLayoutSection *> *mSections; + UICollectionViewScrollDirection mScrollDirection; +} + +- (instancetype)initWithScrollDirection:(UICollectionViewScrollDirection)scrollDirection { + self = [super init]; + if ( self ) { + mScrollDirection = scrollDirection; + } + return self; +} + +- (void)addSection:(_LWZLayoutSection *)section { + if ( mSections == nil ) + mSections = NSMutableArray.array; + [mSections addObject:section]; +} + +- (void)removeAllSections { + [mSections removeAllObjects]; +} + +- (nullable NSArray<_LWZLayoutSection *> *)sections { + return mSections.count != 0 ? mSections : nil; +} + +- (nullable LWZCollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath { + _LWZLayoutSection *section = [self _sectionAtIndex:indexPath.section]; + if ( section != nil ) { + if ( indexPath.item < section.cellLayoutAttributesObjects.count ) + return section.cellLayoutAttributesObjects[indexPath.item]; + } + return nil; +} + +- (nullable LWZCollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath { + _LWZLayoutSection *section = [self _sectionAtIndex:indexPath.section]; + if ( section != nil ) { + if ( [elementKind isEqualToString:UICollectionElementKindSectionHeader] ) + return [section currentHeaderViewLayoutAttributes]; + else if ( [elementKind isEqualToString:UICollectionElementKindSectionFooter ] ) + return section.footerViewLayoutAttributes; + } + return nil; +} + +- (nullable LWZCollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath { + _LWZLayoutSection *section = [self _sectionAtIndex:indexPath.section]; + if ( section != nil ) { + /// section, header, footer, cell 的`decoration` + if ( [section.sectionDecorationLayoutAttributes.representedElementKind isEqualToString:elementKind] ) + return section.sectionDecorationLayoutAttributes; + if ( [[section currentHeaderDecorationLayoutAttributes].representedElementKind isEqualToString:elementKind] ) + return [section currentHeaderDecorationLayoutAttributes]; + if ( [section.footerDecorationLayoutAttributes.representedElementKind isEqualToString:elementKind] ) + return section.footerDecorationLayoutAttributes; + /// 由于可能的情况 不是所有cell都有decoration + /// 因此这里需要遍历一下 + /// cell.decoration 需要额外的判断indexPath, 防止复用的情况 + for ( LWZCollectionViewLayoutAttributes *attr in section.cellDecorationLayoutAttributesObjects ) { + if ( [attr.representedElementKind isEqualToString:elementKind] && [attr.indexPath isEqual:indexPath] ) + return attr; + } + } + return nil; +} + +- (nullable NSArray<__kindof LWZCollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect { + UICollectionViewScrollDirection direction = mScrollDirection; + NSMutableArray *m = NSMutableArray.array; + for ( _LWZLayoutSection *section in [self sectionsInRect:rect] ) { + + if ( [section currentHeaderViewLayoutAttributes] != nil && LWZRectFloatRangeIntersects([section currentHeaderViewLayoutAttributes].frame, rect, direction) ) + [m addObject:[section currentHeaderViewLayoutAttributes]]; + if ( section.footerViewLayoutAttributes != nil && LWZRectFloatRangeIntersects(section.footerViewLayoutAttributes.frame, rect, direction) ) + [m addObject:section.footerViewLayoutAttributes]; + if ( section.sectionDecorationLayoutAttributes != nil && LWZRectFloatRangeIntersects(section.sectionDecorationLayoutAttributes.frame, rect, direction) ) + [m addObject:section.sectionDecorationLayoutAttributes]; + if ( [section currentHeaderDecorationLayoutAttributes] != nil && LWZRectFloatRangeIntersects([section currentHeaderDecorationLayoutAttributes].frame, rect, direction) ) + [m addObject:[section currentHeaderDecorationLayoutAttributes]]; + if ( section.footerDecorationLayoutAttributes != nil && LWZRectFloatRangeIntersects(section.footerDecorationLayoutAttributes.frame, rect, direction) ) + [m addObject:section.footerDecorationLayoutAttributes]; + for ( LWZCollectionViewLayoutAttributes *attributes in section.cellLayoutAttributesObjects ) { + LWZFloatRangeComparisonResult result = LWZRectFloatRangeCompare(attributes.frame, rect, direction); + /* continue */ + if ( result == LWZFloatRangeComparisonResultInLeft || result == LWZFloatRangeComparisonResultInRight ) continue; + [m addObject:attributes]; + } + for ( LWZCollectionViewLayoutAttributes *attributes in section.cellDecorationLayoutAttributesObjects ) { + LWZFloatRangeComparisonResult result = LWZRectFloatRangeCompare(attributes.frame, rect, direction); + /* continue */ + if ( result == LWZFloatRangeComparisonResultInLeft || result == LWZFloatRangeComparisonResultInRight ) continue; + [m addObject:attributes]; + } + } + return m; +} + +- (nullable _LWZLayoutSection *)_sectionAtIndex:(NSInteger)index { + if ( index < mSections.count ) { + return mSections[index]; + } + return nil; +} + +- (nullable NSArray<_LWZLayoutSection *> *)sectionsInRect:(CGRect)rect { + NSMutableArray<_LWZLayoutSection *> *m = NSMutableArray.array; + for ( _LWZLayoutSection *section in mSections ) { + __auto_type result = LWZRectFloatRangeCompare(section.frame, rect, mScrollDirection); + /* break */ + if ( result == LWZFloatRangeComparisonResultInRight ) break; + /* continue */ + if ( result == LWZFloatRangeComparisonResultInLeft ) continue; + [m addObject:section]; + } + return m; +} +@end + + +#pragma mark - mark + + +@interface LWZCollectionViewLayout () { + @protected + UICollectionViewScrollDirection _scrollDirection; + NSHashTable> *_mObservers; + _LWZLayoutCollection *_mCollection; + CGSize _mContentSize; + __kindof __weak id _delegate; + LWZCollectionLayoutPrepareContext _mPrepareContext; + CGFloat _mBoundary; + + struct { + unsigned delegateEdgeSpacingsForSection :1; + unsigned delegateContentInsetsForSection :1; + unsigned delegateAdjustedPinnedInsets :1; + unsigned delegateCanPinToVisibleBoundsForHeader :1; + unsigned delegateSizeForHeader :1; + unsigned delegateSizeForItem :1; + unsigned delegateSizeForFooter :1; + unsigned delegateLineSpacingForSection :1; + unsigned delegateInteritemSpacingForSection :1; + unsigned delegateElementKindForSectionDecoration :1; + unsigned delegateElementKindForHeaderDecoration :1; + unsigned delegateElementKindForItemDecoration :1; + unsigned delegateElementKindForFooterDecoration :1; + unsigned delegateUserInfoForSectionDecoration :1; + unsigned delegateUserInfoForHeaderDecoration :1; + unsigned delegateUserInfoForItemDecoration :1; + unsigned delegateUserInfoForFooterDecoration :1; + unsigned delegateRelativeRectForSectionDecoration :1; + unsigned delegateRelativeRectForHeaderDecoration :1; + unsigned delegateRelativeRectForItemDecoration :1; + unsigned delegateRelativeRectForFooterDecoration :1; + unsigned delegateZIndexForHeader :1; + unsigned delegateZIndexForItem :1; + unsigned delegateZIndexForFooter :1; + unsigned delegateZIndexForSectionDecoration :1; + unsigned delegateZIndexForHeaderDecoration :1; + unsigned delegateZIndexForItemDecoration :1; + unsigned delegateZIndexForFooterDecoration :1; + unsigned delegateWeightForItem :1; + unsigned delegateAlignmentForItem :1; + unsigned delegateNumberOfArrangedItemsPerLineInSection :1; + unsigned delegateWillPrepareLayoutInContainer :1; + unsigned delegateWillPrepareLayoutInContainerContentInsetsSafeAreaInsets :1 NS_AVAILABLE_IOS(11.0); + unsigned delegateDidFinishPreparingInContainer :1; + unsigned delegateDidFinishPreparingInContainerContentInsetsSafeAreaInsets :1 NS_AVAILABLE_IOS(11.0); + + unsigned isIgnoredSafeAreaInsets :1; + unsigned sectionHeadersPinToVisibleBounds :1; + } _layoutFlags; +} +@end + +@implementation LWZCollectionViewLayout ++ (Class)layoutAttributesClass { + return LWZCollectionViewLayoutAttributes.class; +} + +- (instancetype)initWithScrollDirection:(UICollectionViewScrollDirection)scrollDirection { + return [self initWithScrollDirection:scrollDirection delegate:nil]; +} + +- (instancetype)initWithScrollDirection:(UICollectionViewScrollDirection)scrollDirection delegate:(nullable id)delegate { + self = [super init]; + if ( self ) { + _mCollection = [_LWZLayoutCollection.alloc initWithScrollDirection:scrollDirection]; + _scrollDirection = scrollDirection; + _layoutFlags.isIgnoredSafeAreaInsets = YES; + self.delegate = delegate; + } + return self; +} + +@synthesize delegate = _delegate; +- (void)setDelegate:(nullable id)delegate { + if ( delegate != _delegate ) { + _delegate = delegate; + _layoutFlags.delegateEdgeSpacingsForSection = [delegate respondsToSelector:@selector(layout:edgeSpacingsForSectionAtIndex:)]; + _layoutFlags.delegateContentInsetsForSection = [delegate respondsToSelector:@selector(layout:contentInsetsForSectionAtIndex:)]; + _layoutFlags.delegateAdjustedPinnedInsets = [delegate respondsToSelector:@selector(layout:adjustedPinnedInsetsForSectionAtIndex:)]; + _layoutFlags.delegateCanPinToVisibleBoundsForHeader = [delegate respondsToSelector:@selector(layout:canPinToVisibleBoundsForHeaderInSection:)]; + _layoutFlags.delegateSizeForHeader = [delegate respondsToSelector:@selector(layout:layoutSizeToFit:forHeaderInSection:scrollDirection:)]; + _layoutFlags.delegateSizeForItem = [delegate respondsToSelector:@selector(layout:layoutSizeToFit:forItemAtIndexPath:scrollDirection:)]; + _layoutFlags.delegateSizeForFooter = [delegate respondsToSelector:@selector(layout:layoutSizeToFit:forFooterInSection:scrollDirection:)]; + _layoutFlags.delegateLineSpacingForSection = [delegate respondsToSelector:@selector(layout:minimumLineSpacingForSectionAtIndex:)]; + _layoutFlags.delegateInteritemSpacingForSection = [delegate respondsToSelector:@selector(layout:minimumInteritemSpacingForSectionAtIndex:)]; + _layoutFlags.delegateElementKindForSectionDecoration = [delegate respondsToSelector:@selector(layout:elementKindForSectionDecorationAtIndexPath:)]; + _layoutFlags.delegateElementKindForHeaderDecoration = [delegate respondsToSelector:@selector(layout:elementKindForHeaderDecorationAtIndexPath:)]; + _layoutFlags.delegateElementKindForItemDecoration = [delegate respondsToSelector:@selector(layout:elementKindForItemDecorationAtIndexPath:)]; + _layoutFlags.delegateElementKindForFooterDecoration = [delegate respondsToSelector:@selector(layout:elementKindForFooterDecorationAtIndexPath:)]; + _layoutFlags.delegateUserInfoForSectionDecoration = [delegate respondsToSelector:@selector(layout:userInfoForSectionDecorationAtIndexPath:)]; + _layoutFlags.delegateUserInfoForHeaderDecoration = [delegate respondsToSelector:@selector(layout:userInfoForHeaderDecorationAtIndexPath:)]; + _layoutFlags.delegateUserInfoForItemDecoration = [delegate respondsToSelector:@selector(layout:userInfoForItemDecorationAtIndexPath:)]; + _layoutFlags.delegateUserInfoForFooterDecoration = [delegate respondsToSelector:@selector(layout:userInfoForFooterDecorationAtIndexPath:)]; + _layoutFlags.delegateRelativeRectForSectionDecoration = [delegate respondsToSelector:@selector(layout:relativeRectToFit:forSectionDecorationAtIndexPath:)]; + _layoutFlags.delegateRelativeRectForHeaderDecoration = [delegate respondsToSelector:@selector(layout:relativeRectToFit:forHeaderDecorationAtIndexPath:)]; + _layoutFlags.delegateRelativeRectForItemDecoration = [delegate respondsToSelector:@selector(layout:relativeRectToFit:forItemDecorationAtIndexPath:)]; + _layoutFlags.delegateRelativeRectForFooterDecoration = [delegate respondsToSelector:@selector(layout:relativeRectToFit:forFooterDecorationAtIndexPath:)]; + _layoutFlags.delegateZIndexForHeader = [delegate respondsToSelector:@selector(layout:zIndexForHeaderInSection:)]; + _layoutFlags.delegateZIndexForItem = [delegate respondsToSelector:@selector(layout:zIndexForItemAtIndexPath:)]; + _layoutFlags.delegateZIndexForFooter = [delegate respondsToSelector:@selector(layout:zIndexForFooterInSection:)]; + _layoutFlags.delegateZIndexForSectionDecoration = [delegate respondsToSelector:@selector(layout:zIndexForSectionDecorationAtIndexPath:)]; + _layoutFlags.delegateZIndexForHeaderDecoration = [delegate respondsToSelector:@selector(layout:zIndexForHeaderDecorationAtIndexPath:)]; + _layoutFlags.delegateZIndexForItemDecoration = [delegate respondsToSelector:@selector(layout:zIndexForItemDecorationAtIndexPath:)]; + _layoutFlags.delegateZIndexForFooterDecoration = [delegate respondsToSelector:@selector(layout:zIndexForFooterDecorationAtIndexPath:)]; + _layoutFlags.delegateWeightForItem = [delegate respondsToSelector:@selector(layout:weightForItemAtIndexPath:)]; + _layoutFlags.delegateAlignmentForItem = [delegate respondsToSelector:@selector(layout:layoutAlignmentForItemAtIndexPath:)]; + _layoutFlags.delegateNumberOfArrangedItemsPerLineInSection = [delegate respondsToSelector:@selector(layout:numberOfArrangedItemsPerLineInSection:)]; + _layoutFlags.delegateWillPrepareLayoutInContainer = [delegate respondsToSelector:@selector(layout:willPrepareLayoutInContainer:)]; + _layoutFlags.delegateDidFinishPreparingInContainer = [delegate respondsToSelector:@selector(layout:didFinishPreparingInContainer:)]; + } +} + +- (void)setIgnoredSafeAreaInsets:(BOOL)ignoredSafeAreaInsets { + _layoutFlags.isIgnoredSafeAreaInsets = ignoredSafeAreaInsets; +} + +- (BOOL)isIgnoredSafeAreaInsets { + return _layoutFlags.isIgnoredSafeAreaInsets; +} + +- (void)setSectionHeadersPinToVisibleBounds:(BOOL)sectionHeadersPinToVisibleBounds { + _layoutFlags.sectionHeadersPinToVisibleBounds = sectionHeadersPinToVisibleBounds; +} + +- (BOOL)sectionHeadersPinToVisibleBounds { + return _layoutFlags.sectionHeadersPinToVisibleBounds; +} + +- (CGRect)layoutFrameForSection:(NSInteger)section { + if ( section >= 0 && section < _mCollection.sections.count ) { + _LWZLayoutSection *s = _mCollection.sections[section]; + return s.frame; + } + return CGRectZero; +} + +- (void)enumerateLayoutAttributesWithElementCategory:(UICollectionElementCategory)category usingBlock:(void(NS_NOESCAPE ^)(UICollectionViewLayoutAttributes *attributes, NSUInteger idx, BOOL *stop))block { + BOOL stop = NO; + NSInteger idx = 0; + for ( _LWZLayoutSection *section in _mCollection.sections ) { + for ( UICollectionViewLayoutAttributes *attributes in [section layoutAttributesObjectsForElementCategory:category] ) { + block(attributes, idx++, &stop); + if ( stop ) return; + } + } +} + +- (nullable NSArray *)layoutAttributesObjectsForElementCategory:(UICollectionElementCategory)category { + NSMutableArray *m = [NSMutableArray array]; + for ( _LWZLayoutSection *section in _mCollection.sections ) { + NSArray *array = [section layoutAttributesObjectsForElementCategory:category]; + if ( array.count != 0 ) [m addObjectsFromArray:array]; + } + return m.count > 0 ? m.copy : nil; +} + +- (nullable NSArray *)layoutAttributesObjectsForElementCategory:(UICollectionElementCategory)category inSection:(NSInteger)section { + if ( section < _mCollection.sections.count ) { + return [_mCollection.sections[section] layoutAttributesObjectsForElementCategory:category]; + } + return nil; +} + +- (void)registerObserver:(id)observer { + if ( _mObservers == nil ) { + _mObservers = [NSHashTable weakObjectsHashTable]; + } + [_mObservers addObject:observer]; +} + +- (void)removeObserver:(id)observer { + [_mObservers removeObject:observer]; +} + +#pragma mark - mark + +- (void)invalidateLayoutWithContext:(UICollectionViewLayoutInvalidationContext *)context { +#ifdef LWZ_DEBUG + printf("%d : %s\n", __LINE__, sel_getName(_cmd)); + + NSString *log = [NSString stringWithFormat:@"\ninvalidateEverything: %d, \ninvalidateDataSourceCounts: %d, \ninvalidatedItemIndexPaths: %@, \ninvalidatedSupplementaryIndexPaths: %@, \ninvalidatedDecorationIndexPaths: %@, \ncontentOffsetAdjustment: %@, \ncontentSizeAdjustment: %@, \npreviousIndexPathsForInteractivelyMovingItems: %@, \ntargetIndexPathsForInteractivelyMovingItems: %@, \ninteractiveMovementTarget: %@", + context.invalidateEverything, + context.invalidateDataSourceCounts, + context.invalidatedItemIndexPaths, + context.invalidatedSupplementaryIndexPaths, + context.invalidatedDecorationIndexPaths, + NSStringFromCGPoint(context.contentOffsetAdjustment), + NSStringFromCGSize(context.contentSizeAdjustment), + context.previousIndexPathsForInteractivelyMovingItems, + context.targetIndexPathsForInteractivelyMovingItems, + NSStringFromCGPoint(context.interactiveMovementTarget)]; + printf("%s\n", log.UTF8String); + + printf("%s\n\n", self.collectionView.description.UTF8String); +#endif + + LWZCollectionLayoutPrepareContext prepareContext = _mPrepareContext; + if ( context.invalidateEverything ) { + prepareContext |= LWZCollectionLayoutPrepareContextInvalidateEverything; + } + + if ( context.invalidateDataSourceCounts ) { + prepareContext |= LWZCollectionLayoutPrepareContextInvalidateDataSourceCounts; + } + + UICollectionView *collectionView = self.collectionView; + CGRect bounds = collectionView.bounds; + CGFloat boundary = 0; + + CGSize contentSizeAdjustment = context.contentSizeAdjustment; + if ( !CGSizeEqualToSize(CGSizeZero, contentSizeAdjustment) ) { + switch ( _scrollDirection ) { + case UICollectionViewScrollDirectionVertical: + boundary = bounds.size.width - contentSizeAdjustment.width; + break; + case UICollectionViewScrollDirectionHorizontal: + boundary = bounds.size.height - contentSizeAdjustment.height; + break; + } + } + else { + switch ( _scrollDirection ) { + case UICollectionViewScrollDirectionVertical: + boundary = bounds.size.width; + break; + case UICollectionViewScrollDirectionHorizontal: + boundary = bounds.size.height; + break; + } + } + + CGFloat oldBoundary = _mBoundary; + if ( boundary != oldBoundary ) { + prepareContext |= LWZCollectionLayoutPrepareContextBoundaryChanging; + _mBoundary = boundary; + } + + _mPrepareContext = prepareContext; + + if ( oldBoundary != 0 && prepareContext & LWZCollectionLayoutPrepareContextBoundaryChanging ) { + CGPoint oldContentOffset = collectionView.contentOffset; + CGPoint contentOffsetAdjustment = context.contentOffsetAdjustment; + // 调整contentOffset, 按照boundary变化比例, 增加或减少相应比例的offset + if ( CGPointEqualToPoint(CGPointZero, contentOffsetAdjustment) ) { + switch ( _scrollDirection ) { + case UICollectionViewScrollDirectionVertical: { + CGFloat newOffsetY = boundary * oldContentOffset.y / oldBoundary; + contentOffsetAdjustment.y = newOffsetY - oldContentOffset.y; + } + break; + case UICollectionViewScrollDirectionHorizontal: { + CGFloat newOffsetX = boundary * oldContentOffset.x / oldBoundary; + contentOffsetAdjustment.x = newOffsetX - oldContentOffset.x; + } + break; + } + context.contentOffsetAdjustment = contentOffsetAdjustment; + } + } + + [super invalidateLayoutWithContext:context]; +} + +- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds { +#ifdef LWZ_DEBUG + printf("%d : %s\n", __LINE__, sel_getName(_cmd)); +#endif + + + BOOL isBoundaryChanged = NO; + UICollectionView *collectionView = self.collectionView; + switch ( _scrollDirection ) { + case UICollectionViewScrollDirectionVertical: + isBoundaryChanged = newBounds.size.width != collectionView.bounds.size.width; + break; + case UICollectionViewScrollDirectionHorizontal: + isBoundaryChanged = newBounds.size.height != collectionView.bounds.size.height; + break; + } + + if ( isBoundaryChanged ) + return YES; + + if ( _layoutFlags.sectionHeadersPinToVisibleBounds /*|| _sectionFootersPinToVisibleBounds*/ ) { + NSArray<_LWZLayoutSection *> *sections = [_mCollection sectionsInRect:newBounds]; + for ( _LWZLayoutSection *section in sections ) { + // 目前仅处理header, 后续如有footer的需要再扩展footer了 + // + if ( section.headerViewLayoutAttributes != nil && section.canPinToVisibleBoundsForHeader ) + return YES; +// if ( section.footerViewLayoutAttributes != nil ) +// return YES; + } + } + return [super shouldInvalidateLayoutForBoundsChange:newBounds]; +} + +// 目前仅处理header, 后续如有footer的需要再扩展footer了 +- (UICollectionViewLayoutInvalidationContext *)invalidationContextForBoundsChange:(CGRect)newBounds { +#ifdef LWZ_DEBUG + printf("%d : %s\n", __LINE__, sel_getName(_cmd)); +#endif + + UICollectionViewLayoutInvalidationContext *context = [super invalidationContextForBoundsChange:newBounds]; + if ( context == nil ) context = [UICollectionViewLayoutInvalidationContext.alloc init]; + + UICollectionView *collectionView = self.collectionView; + CGRect curBounds = collectionView.bounds; + CGSize contentSizeAdjustment = CGSizeMake(newBounds.size.width - curBounds.size.width, newBounds.size.height - curBounds.size.height); + if ( !CGSizeEqualToSize(CGSizeZero, contentSizeAdjustment) ) { + context.contentSizeAdjustment = contentSizeAdjustment; + } + else { + NSArray<_LWZLayoutSection *> *sections = [_mCollection sectionsInRect:newBounds]; + if ( sections.count != 0 ) { + for ( _LWZLayoutSection *section in sections ) { + if ( section.canPinToVisibleBoundsForHeader ) { + LWZCollectionViewLayoutAttributes *header = section.headerViewLayoutAttributes.copy; + NSArray *indexPaths = @[header.indexPath]; + [context invalidateSupplementaryElementsOfKind:header.representedElementKind atIndexPaths:indexPaths]; + + // decoration + LWZCollectionViewLayoutAttributes *decoration = section.headerDecorationLayoutAttributes.copy; + if ( decoration != nil ) { + [context invalidateDecorationElementsOfKind:decoration.representedElementKind atIndexPaths:indexPaths]; + } + } + } + } + } + return context; +} + +- (void)prepareLayout { +#ifdef LWZ_DEBUG + printf("%d : %s\n", __LINE__, sel_getName(_cmd)); +#endif + + if ( _mPrepareContext != LWZCollectionLayoutPrepareContextNone ) { + UICollectionView *collectionView = self.collectionView; + CGSize collectionSize = collectionView.frame.size; + if ( @available(iOS 11.0, *) ) { + [self prepareLayoutForCollectionSize:collectionSize contentInsets:collectionView.contentInset safeAreaInsets:collectionView.safeAreaInsets]; + } + else { + [self prepareLayoutForCollectionSize:collectionSize contentInsets:collectionView.contentInset]; + } + } + [super prepareLayout]; +} + +- (void)prepareLayoutForCollectionSize:(CGSize)size contentInsets:(UIEdgeInsets)contentInsets { + [self _prepareLayoutForCollectionSize:size contentInsets:contentInsets safeAreaInsets:UIEdgeInsetsZero]; +} + +- (void)prepareLayoutForCollectionSize:(CGSize)size contentInsets:(UIEdgeInsets)contentInsets safeAreaInsets:(UIEdgeInsets)safeAreaInsets NS_AVAILABLE_IOS(11.0) { + [self _prepareLayoutForCollectionSize:size contentInsets:contentInsets safeAreaInsets:safeAreaInsets]; +} + +- (nullable LWZCollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath { + return [_mCollection layoutAttributesForItemAtIndexPath:indexPath]; +} + +- (nullable LWZCollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath { + if ( _layoutFlags.sectionHeadersPinToVisibleBounds ) { + NSInteger sIdx = indexPath.section; + if ( elementKind == UICollectionElementKindSectionHeader ) { + _LWZLayoutSection *section = [_mCollection _sectionAtIndex:sIdx]; + LWZCollectionViewLayoutAttributes *header = section.headerViewLayoutAttributes; + if ( header != nil && section.canPinToVisibleBoundsForHeader ) { + CGRect headerPinnedFrame = [self _headerPinnedFrameForSectionFrame:section.frame headerFrame:header.frame adjustedPinnedInsets:[self adjustedPinnedInsetsForSectionAtIndex:sIdx]]; + [section.headerViewPinnedLayoutAttributes setFrame:headerPinnedFrame]; + } + } + } + return [_mCollection layoutAttributesForSupplementaryViewOfKind:elementKind atIndexPath:indexPath]; +} + +- (nullable LWZCollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath { + if ( _layoutFlags.sectionHeadersPinToVisibleBounds ) { + NSInteger sIdx = indexPath.section; + _LWZLayoutSection *section = [_mCollection _sectionAtIndex:sIdx]; + LWZCollectionViewLayoutAttributes *decoration = section.headerDecorationLayoutAttributes; + if ( decoration != nil && decoration.representedElementKind == elementKind && section.canPinToVisibleBoundsForHeader ) { + LWZCollectionViewLayoutAttributes *header = section.headerViewLayoutAttributes; + CGRect headerFrame = header.frame; + LWZCollectionViewLayoutAttributes *headerPinnedAttributes = section.headerViewPinnedLayoutAttributes; + CGRect headerPinnedFrame = headerPinnedAttributes.frame; + CGRect decorationPinnedFrame = [self _headerDecorationPinnedFrameForHeaderFrame:headerFrame headerPinnedFrame:headerPinnedFrame headerDecorationFrame:decoration.frame]; + [section.headerDecorationPinnedLayoutAttributes setFrame:decorationPinnedFrame]; + } + } + return [_mCollection layoutAttributesForDecorationViewOfKind:elementKind atIndexPath:indexPath]; +} + +- (nullable NSArray<__kindof LWZCollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect { + return [_mCollection layoutAttributesForElementsInRect:rect]; +} + +- (CGSize)collectionViewContentSize { + return _mContentSize; +} + +#pragma mark - mark + +- (void)_prepareLayoutForCollectionSize:(CGSize)collectionSize contentInsets:(UIEdgeInsets)contentInsets safeAreaInsets:(UIEdgeInsets)safeAreaInsets { +#ifdef LWZ_DEBUG + printf("%s\n", [NSString stringWithFormat:@"%d - %s - %@ - %@ - %@", (int)__LINE__, __func__, NSStringFromCGSize(collectionSize), NSStringFromUIEdgeInsets(contentInsets), NSStringFromUIEdgeInsets(safeAreaInsets)].UTF8String); + printf("%s\n\n", [NSString stringWithFormat:@"prepareContext { invalidateEverything: %d, invalidateDataSourceCounts: %d, boundaryChanging: %d }", (BOOL)(_mPrepareContext & LWZCollectionLayoutPrepareContextInvalidateEverything), (BOOL)(_mPrepareContext & LWZCollectionLayoutPrepareContextInvalidateDataSourceCounts), (BOOL)(_mPrepareContext & LWZCollectionLayoutPrepareContextBoundaryChanging)].UTF8String); +#endif + + [_mCollection removeAllSections]; + _mContentSize = CGSizeZero; + _mPrepareContext = LWZCollectionLayoutPrepareContextNone; + + if ( _delegate == nil ) { +#if DEBUG + NSLog(@"⚠️ %@<%p>: `layout.delegate`未设置!!!", NSStringFromClass(self.class), self); + NSLog(@"⚠️ %@<%p>: `layout.delegate`未设置!!!", NSStringFromClass(self.class), self); + NSLog(@"⚠️ %@<%p>: `layout.delegate`未设置!!!", NSStringFromClass(self.class), self); +#endif + return; + } + + UICollectionView *collectionView = self.collectionView; + NSInteger numberOfSections = collectionView.numberOfSections; + if ( numberOfSections == 0 ) + return; + + CGRect collectionBounds = (CGRect){0, 0, collectionSize}; + if ( CGRectIsInfinite(collectionBounds) || CGRectIsNull(collectionBounds) || CGRectIsEmpty(collectionBounds) ) + return; + + LWZCollectionLayoutCollectionContentContainer *collectionContentContainer = [LWZCollectionLayoutCollectionContentContainer.alloc initWithCollectionSize:collectionSize direction:_scrollDirection collectionContentInsets:contentInsets collectionSafeAreaInsets:safeAreaInsets ignoredCollectionSafeAreaInsets:_layoutFlags.isIgnoredSafeAreaInsets]; + + [self willPrepareLayoutInContainer:collectionContentContainer]; + + CGFloat offset = 0; + switch ( _scrollDirection ) { + case UICollectionViewScrollDirectionVertical: { + offset = collectionContentContainer.layoutInsets.top; + } + break; + case UICollectionViewScrollDirectionHorizontal: { + offset = collectionContentContainer.layoutInsets.left; + } + break; + } + + CGRect sectionFrame = CGRectZero; + for ( NSInteger sIdx = 0 ; sIdx < numberOfSections; ++ sIdx ) { + if ( ![self shouldProcessSectionLayoutAtIndex:sIdx] ) + continue; + + UIEdgeInsets sectionEdgeSpacings = [self edgeSpacingsForSectionAtIndex:sIdx]; + UIEdgeInsets sectionContentInsets = [self contentInsetsForSectionAtIndex:sIdx]; + + LWZCollectionLayoutSectionContentContainer *sectionContentContainer = [LWZCollectionLayoutSectionContentContainer.alloc initWithCollectionContentContainer:collectionContentContainer sectionEdgeSpacings:sectionEdgeSpacings sectionContentInsets:sectionContentInsets]; + + _LWZLayoutSection *sectionLayout = [_LWZLayoutSection.alloc init]; + + switch ( _scrollDirection ) { + case UICollectionViewScrollDirectionVertical: { + offset += sectionContentContainer.layoutInsets.top; + + sectionFrame.origin.y = offset; + sectionFrame.origin.x = collectionContentContainer.layoutInsets.left + sectionContentContainer.layoutInsets.left; + sectionFrame.size.width = sectionContentContainer.layoutRange.maximum - sectionContentContainer.layoutRange.minimum; + } + break; + case UICollectionViewScrollDirectionHorizontal: { + offset += sectionContentContainer.layoutInsets.left; + + sectionFrame.origin.x = offset; + sectionFrame.origin.y = collectionContentContainer.layoutInsets.top + sectionContentContainer.layoutInsets.top; + sectionFrame.size.height = sectionContentContainer.layoutRange.maximum - sectionContentContainer.layoutRange.minimum; + } + break; + } + + NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:sIdx]; + // header + LWZCollectionViewLayoutAttributes *_Nullable header = [self layoutAttributesForSupplementaryViewWithElementKind:UICollectionElementKindSectionHeader indexPath:indexPath offset:offset container:sectionContentContainer]; + LWZCollectionViewLayoutAttributes *_Nullable headerDecoration = nil; + if ( header != nil ) { + sectionLayout.headerViewLayoutAttributes = header; + offset = LWZLayoutAttributesGetMaxOffset(header, _scrollDirection); + + // header decoration + headerDecoration = [self layoutAttributesForHeaderDecorationViewWithIndexPath:indexPath inRect:header.frame]; + if ( headerDecoration != nil ) sectionLayout.headerDecorationLayoutAttributes = headerDecoration; + } + + // cells + switch ( _scrollDirection ) { + case UICollectionViewScrollDirectionVertical: + offset += sectionContentInsets.top; + break; + case UICollectionViewScrollDirectionHorizontal: + offset += sectionContentInsets.left; + break; + } + + switch ( [self layoutContentPresentationModeForCellsInSection:sIdx] ) { + case LWZCollectionLayoutContentPresentationModeNormal: { + NSArray *_Nullable cells = [self layoutAttributesObjectsForCellsWithSection:sIdx offset:offset container:sectionContentContainer]; + if ( cells.count != 0 ) { + [sectionLayout setCellLayoutAttributesObjects:cells]; + offset = LWZLayoutAttributesGetMaxOffset(cells, _scrollDirection); + + // cell decoration + NSArray *_Nullable cellDecorations = [self _cellDecorationAttributesObjectsWithCellAttributesArray:cells]; + if ( cellDecorations.count != 0 ) [sectionLayout setCellDecorationLayoutAttributesObjects:cellDecorations]; + } + } + break; + case LWZCollectionLayoutContentPresentationModeCustom: { + UIView *customView = [self layoutCustomViewForCellsWithSection:sIdx offset:offset container:sectionContentContainer]; + sectionLayout.customView = customView; + [collectionView addSubview:customView]; + + CGRect groupFrame = customView.frame; + switch ( _scrollDirection ) { + case UICollectionViewScrollDirectionVertical: + offset = CGRectGetMaxY(groupFrame); + break; + case UICollectionViewScrollDirectionHorizontal: + offset = CGRectGetMaxX(groupFrame); + break; + } + } + break; + } + + switch ( _scrollDirection ) { + case UICollectionViewScrollDirectionVertical: + offset += sectionContentInsets.bottom; + break; + case UICollectionViewScrollDirectionHorizontal: + offset += sectionContentInsets.right; + break; + } + + // footer + LWZCollectionViewLayoutAttributes *_Nullable footer = [self layoutAttributesForSupplementaryViewWithElementKind:UICollectionElementKindSectionFooter indexPath:indexPath offset:offset container:sectionContentContainer]; + + if ( footer != nil ) { + sectionLayout.footerViewLayoutAttributes = footer; + offset = LWZLayoutAttributesGetMaxOffset(footer, _scrollDirection); + + // footer decoration + LWZCollectionViewLayoutAttributes *_Nullable footerDecoration = [self layoutAttributesForFooterDecorationViewWithIndexPath:indexPath inRect:footer.frame]; + if ( footerDecoration != nil ) sectionLayout.footerDecorationLayoutAttributes = footerDecoration; + } + + switch ( _scrollDirection ) { + case UICollectionViewScrollDirectionVertical: + sectionFrame.size.height = offset - sectionFrame.origin.y; + break; + case UICollectionViewScrollDirectionHorizontal: + sectionFrame.size.width = offset - sectionFrame.origin.x; + break; + } + + // section decoration + LWZCollectionViewLayoutAttributes *_Nullable sectionDecoration = [self layoutAttributesForSectionDecorationViewWithIndexPath:indexPath inRect:sectionFrame]; + if ( sectionDecoration != nil ) sectionLayout.sectionDecorationLayoutAttributes = sectionDecoration; + + // pinned + if ( header != nil && _layoutFlags.sectionHeadersPinToVisibleBounds && [self canPinToVisibleBoundsForHeaderInSection:sIdx] ) { + sectionLayout.canPinToVisibleBoundsForHeader = YES; + + CGRect headerFrame = header.frame; + CGRect headerPinnedFrame = [self _headerPinnedFrameForSectionFrame:sectionFrame headerFrame:headerFrame adjustedPinnedInsets:[self adjustedPinnedInsetsForSectionAtIndex:sIdx]]; + LWZCollectionViewLayoutAttributes *headerPinnedAttributes = header.copy; + headerPinnedAttributes.zIndex = header.zIndex + 10; + headerPinnedAttributes.frame = headerPinnedFrame; + sectionLayout.headerViewPinnedLayoutAttributes = headerPinnedAttributes; + + if ( headerDecoration != nil ) { + CGRect headerDecorationFrame = headerDecoration.frame; + CGRect headerDecorationPinnedFrame = [self _headerDecorationPinnedFrameForHeaderFrame:headerFrame headerPinnedFrame:headerPinnedFrame headerDecorationFrame:headerDecorationFrame]; + + LWZCollectionViewLayoutAttributes *headerDecorationPinnedAttributes = headerDecoration.copy; + headerDecorationPinnedAttributes.zIndex = headerDecoration.zIndex + 10; + headerDecorationPinnedAttributes.frame = headerDecorationPinnedFrame; + sectionLayout.headerDecorationPinnedLayoutAttributes = headerDecorationPinnedAttributes; + } + } + + sectionLayout.frame = sectionFrame; + [_mCollection addSection:sectionLayout]; + + switch ( _scrollDirection ) { + case UICollectionViewScrollDirectionVertical: + offset += sectionEdgeSpacings.bottom; + break; + case UICollectionViewScrollDirectionHorizontal: + offset += sectionEdgeSpacings.right; + break; + } + } + + CGFloat contentWidth = 0; + CGFloat contentHeight = 0; + switch ( _scrollDirection ) { + case UICollectionViewScrollDirectionVertical: { + offset += collectionContentContainer.layoutInsets.bottom; + + contentHeight = offset; + contentWidth = collectionSize.width; + } + break; + case UICollectionViewScrollDirectionHorizontal: { + offset += collectionContentContainer.layoutInsets.right; + + contentWidth = offset; + contentHeight = collectionSize.height; + } + break; + } + + _mContentSize = CGSizeMake(contentWidth, contentHeight); + + [self didFinishPreparingInContainer:collectionContentContainer]; +} + +- (void)willPrepareLayoutInContainer:(LWZCollectionLayoutCollectionContentContainer *)container { + if ( _layoutFlags.delegateWillPrepareLayoutInContainer ) { + [_delegate layout:self willPrepareLayoutInContainer:container]; + } + + for ( id observer in LWZAllHashTableObjects(_mObservers) ) { + if ( [observer respondsToSelector:@selector(layout:willPrepareLayoutInContainer:)] ) { + [observer layout:self willPrepareLayoutInContainer:container]; + } + } +} + +- (void)didFinishPreparingInContainer:(LWZCollectionLayoutCollectionContentContainer *)container { + if ( _layoutFlags.delegateDidFinishPreparingInContainer ) { + [_delegate layout:self didFinishPreparingInContainer:container]; + } + + for ( id observer in LWZAllHashTableObjects(_mObservers) ) { + if ( [observer respondsToSelector:@selector(layout:didFinishPreparingInContainer:)] ) { + [observer layout:self didFinishPreparingInContainer:container]; + } + } +} + +- (BOOL)shouldProcessSectionLayoutAtIndex:(NSInteger)index { + return YES; +} + +- (nullable LWZCollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewWithElementKind:(NSString *)kind indexPath:(NSIndexPath *)indexPath offset:(CGFloat)offset container:(LWZCollectionLayoutSectionContentContainer *)container { + UIFloatRange layoutRange = container.headerFooterLayoutRange; + if ( layoutRange.maximum <= layoutRange.minimum ) return nil; + + NSInteger section = indexPath.section; + UICollectionViewScrollDirection direction = _scrollDirection; + CGSize fittingSize = LWZCollectionLayoutFittingSizeForLayoutRange(layoutRange, direction); + CGSize layoutSize = kind == UICollectionElementKindSectionHeader ? [self layoutSizeToFit:fittingSize forHeaderInSection:section scrollDirection:direction] : [self layoutSizeToFit:fittingSize forFooterInSection:section scrollDirection:direction]; + if ( LWZLayoutSizeIsInvalid(layoutSize, _scrollDirection) ) return nil; + + LWZCollectionViewLayoutAttributes *attributes = [LWZCollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:kind withIndexPath:indexPath]; + attributes.zIndex = [self zIndexForHeaderInSection:section]; + switch ( direction ) { + case UICollectionViewScrollDirectionVertical: + attributes.frame = (CGRect){layoutRange.minimum, offset, layoutSize}; + break; + case UICollectionViewScrollDirectionHorizontal: + attributes.frame = (CGRect){offset, layoutRange.minimum, layoutSize};; + break; + } + return attributes; +} + +- (LWZCollectionLayoutContentPresentationMode)layoutContentPresentationModeForCellsInSection:(NSInteger)index { + return LWZCollectionLayoutContentPresentationModeNormal; +} + +- (nullable UIView *)layoutCustomViewForCellsWithSection:(NSInteger)section offset:(CGFloat)offset container:(LWZCollectionLayoutSectionContentContainer *)container { + @throw [NSException exceptionWithName:NSInternalInconsistencyException + reason:[NSString stringWithFormat:@"You must override %@ in a subclass.", NSStringFromSelector(_cmd)] + userInfo:nil]; +} + +/// offset => 首个cell开始的位置, 例如: 垂直布局时, offset = preHeader.maxY + sectionContentInsets.top +- (nullable NSArray *)layoutAttributesObjectsForCellsWithSection:(NSInteger)section offset:(CGFloat)offset container:(LWZCollectionLayoutSectionContentContainer *)container { + @throw [NSException exceptionWithName:NSInternalInconsistencyException + reason:[NSString stringWithFormat:@"You must override %@ in a subclass.", NSStringFromSelector(_cmd)] + userInfo:nil]; +} + +- (nullable LWZCollectionViewLayoutAttributes *)layoutAttributesForSectionDecorationViewWithIndexPath:(NSIndexPath *)indexPath inRect:(CGRect)rect { + NSString *kind = [self _elementKindForSectionDecorationAtIndexPath:indexPath]; + if ( kind.length != 0 ) { + CGRect fitsRect = (CGRect){0, 0, rect.size}; + CGRect frame = [self _relativeRectToFit:fitsRect forSectionDecorationAtIndexPath:indexPath]; + if ( LWZLayoutSizeIsInvalid(frame.size, _scrollDirection) ) return nil; + frame.origin.x += rect.origin.x; + frame.origin.y += rect.origin.y; + LWZCollectionViewLayoutAttributes *attr = [LWZCollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:kind withIndexPath:indexPath]; + attr.zIndex = [self _zIndexForSectionDecorationAtIndexPath:indexPath]; + attr.frame = frame; + attr.decorationUserInfo = [self _userInfoForSectionDecorationAtIndexPath:indexPath]; + return attr; + } + return nil; +} + +- (nullable LWZCollectionViewLayoutAttributes *)layoutAttributesForHeaderDecorationViewWithIndexPath:(NSIndexPath *)indexPath inRect:(CGRect)rect { + NSString *kind = [self _elementKindForHeaderDecorationAtIndexPath:indexPath]; + if ( kind.length != 0 ) { + CGRect fitsRect = (CGRect){0, 0, rect.size}; + CGRect frame = [self _relativeRectToFit:fitsRect forHeaderDecorationAtIndexPath:indexPath]; + if ( LWZLayoutSizeIsInvalid(frame.size, _scrollDirection) ) return nil; + frame.origin.x += rect.origin.x; + frame.origin.y += rect.origin.y; + LWZCollectionViewLayoutAttributes *attr = [LWZCollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:kind withIndexPath:indexPath]; + attr.zIndex = [self _zIndexForHeaderDecorationAtIndexPath:indexPath]; + attr.frame = frame; + attr.decorationUserInfo = [self _userInfoForHeaderDecorationAtIndexPath:indexPath]; + return attr; + } + return nil; +} + +- (nullable LWZCollectionViewLayoutAttributes *)layoutAttributesForCellDecorationViewWithIndexPath:(NSIndexPath *)indexPath inRect:(CGRect)rect { + NSString *kind = [self _elementKindForItemDecorationAtIndexPath:indexPath]; + if ( kind.length != 0 ) { + CGRect fitsRect = (CGRect){0, 0, rect.size}; + CGRect frame = [self _relativeRectToFit:fitsRect forItemDecorationAtIndexPath:indexPath]; + if ( LWZLayoutSizeIsInvalid(frame.size, _scrollDirection) ) return nil; + frame.origin.x += rect.origin.x; + frame.origin.y += rect.origin.y; + LWZCollectionViewLayoutAttributes *attr = [LWZCollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:kind withIndexPath:indexPath]; + attr.zIndex = [self _zIndexForItemDecorationAtIndexPath:indexPath]; + attr.frame = frame; + attr.decorationUserInfo = [self _userInfoForItemDecorationAtIndexPath:indexPath]; + return attr; + } + return nil; +} + +- (nullable LWZCollectionViewLayoutAttributes *)layoutAttributesForFooterDecorationViewWithIndexPath:(NSIndexPath *)indexPath inRect:(CGRect)rect { + NSString *kind = [self _elementKindForFooterDecorationAtIndexPath:indexPath]; + if ( kind.length != 0 ) { + CGRect fitsRect = (CGRect){0, 0, rect.size}; + CGRect frame = [self _relativeRectToFit:fitsRect forFooterDecorationAtIndexPath:indexPath]; + if ( LWZLayoutSizeIsInvalid(frame.size, _scrollDirection) ) return nil; + frame.origin.x += rect.origin.x; + frame.origin.y += rect.origin.y; + LWZCollectionViewLayoutAttributes *attr = [LWZCollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:kind withIndexPath:indexPath]; + attr.zIndex = [self _zIndexForFooterDecorationAtIndexPath:indexPath]; + attr.frame = frame; + attr.decorationUserInfo = [self _userInfoForFooterDecorationAtIndexPath:indexPath]; + return attr; + } + return nil; +} + +- (nullable NSArray *)_cellDecorationAttributesObjectsWithCellAttributesArray:(NSArray *)cellAttributes { + NSMutableArray *m = [[NSMutableArray alloc] initWithCapacity:cellAttributes.count]; + for ( LWZCollectionViewLayoutAttributes *cell in cellAttributes ) { + LWZCollectionViewLayoutAttributes *attr = [self layoutAttributesForCellDecorationViewWithIndexPath:cell.indexPath inRect:cell.frame]; + if ( attr == nil ) continue;; + [m addObject:attr]; + } + return m.count != 0 ? m.copy : nil; +} + +#pragma mark - mark + + +- (UIEdgeInsets)edgeSpacingsForSectionAtIndex:(NSInteger)section { + if ( _layoutFlags.delegateEdgeSpacingsForSection ) { + return [_delegate layout:self edgeSpacingsForSectionAtIndex:section]; + } + return UIEdgeInsetsZero; +} + +- (UIEdgeInsets)contentInsetsForSectionAtIndex:(NSInteger)section { + if ( _layoutFlags.delegateContentInsetsForSection ) { + return [_delegate layout:self contentInsetsForSectionAtIndex:section]; + } + return UIEdgeInsetsZero; +} + +- (UIEdgeInsets)adjustedPinnedInsetsForSectionAtIndex:(NSInteger)section { + if ( _layoutFlags.delegateAdjustedPinnedInsets ) { + return [_delegate layout:self adjustedPinnedInsetsForSectionAtIndex:section]; + } + return _adjustedPinnedInsets; +} + +- (BOOL)canPinToVisibleBoundsForHeaderInSection:(NSInteger)section { + if ( _layoutFlags.sectionHeadersPinToVisibleBounds && _layoutFlags.delegateCanPinToVisibleBoundsForHeader ) { + return [_delegate layout:self canPinToVisibleBoundsForHeaderInSection:section]; + } + return _layoutFlags.sectionHeadersPinToVisibleBounds; +} + +- (CGSize)layoutSizeToFit:(CGSize)fittingSize forHeaderInSection:(NSInteger)section scrollDirection:(UICollectionViewScrollDirection)scrollDirection { + if ( _layoutFlags.delegateSizeForHeader ) { + CGSize size = [_delegate layout:self layoutSizeToFit:fittingSize forHeaderInSection:section scrollDirection:scrollDirection]; + return LWZLayoutSizeHeaderFooterAdjusting(size, fittingSize, scrollDirection); + } + return CGSizeZero; +} + +- (CGSize)layoutSizeToFit:(CGSize)fittingSize forItemAtIndexPath:(NSIndexPath *)indexPath scrollDirection:(UICollectionViewScrollDirection)scrollDirection { + if ( _layoutFlags.delegateSizeForItem ) { + CGSize size = [_delegate layout:self layoutSizeToFit:fittingSize forItemAtIndexPath:indexPath scrollDirection:scrollDirection]; + return LWZLayoutSizeItemAdjusting(size, fittingSize, scrollDirection); + } + return CGSizeZero; +} + +- (CGSize)layoutSizeToFit:(CGSize)fittingSize forFooterInSection:(NSInteger)section scrollDirection:(UICollectionViewScrollDirection)scrollDirection { + if ( _layoutFlags.delegateSizeForFooter ) { + CGSize size = [_delegate layout:self layoutSizeToFit:fittingSize forFooterInSection:section scrollDirection:scrollDirection]; + return LWZLayoutSizeHeaderFooterAdjusting(size, fittingSize, scrollDirection); + } + return CGSizeZero; +} + +- (CGFloat)minimumLineSpacingForSectionAtIndex:(NSInteger)section { + if ( _layoutFlags.delegateLineSpacingForSection ) { + return [_delegate layout:self minimumLineSpacingForSectionAtIndex:section]; + } + return 0; +} + +- (CGFloat)minimumInteritemSpacingForSectionAtIndex:(NSInteger)section { + if ( _layoutFlags.delegateInteritemSpacingForSection ) { + return [_delegate layout:self minimumInteritemSpacingForSectionAtIndex:section]; + } + return 0; +} + +- (nullable NSString *)_elementKindForSectionDecorationAtIndexPath:(NSIndexPath *)indexPath { + if ( _layoutFlags.delegateElementKindForSectionDecoration ) { + return [_delegate layout:self elementKindForSectionDecorationAtIndexPath:indexPath]; + } + return nil; +} + +- (CGRect)_relativeRectToFit:(CGRect)rect forSectionDecorationAtIndexPath:(NSIndexPath *)indexPath { + if ( _layoutFlags.delegateRelativeRectForSectionDecoration ) { + return [_delegate layout:self relativeRectToFit:rect forSectionDecorationAtIndexPath:indexPath]; + } + return CGRectZero; +} + +- (nullable NSString *)_elementKindForHeaderDecorationAtIndexPath:(NSIndexPath *)indexPath { + if ( _layoutFlags.delegateElementKindForHeaderDecoration ) { + return [_delegate layout:self elementKindForHeaderDecorationAtIndexPath:indexPath]; + } + return nil; +} + +- (CGRect)_relativeRectToFit:(CGRect)rect forHeaderDecorationAtIndexPath:(NSIndexPath *)indexPath { + if ( _layoutFlags.delegateRelativeRectForHeaderDecoration ) { + return [_delegate layout:self relativeRectToFit:rect forHeaderDecorationAtIndexPath:indexPath]; + } + return CGRectZero; +} + +- (nullable NSString *)_elementKindForItemDecorationAtIndexPath:(NSIndexPath *)indexPath { + if ( _layoutFlags.delegateElementKindForItemDecoration ) { + return [_delegate layout:self elementKindForItemDecorationAtIndexPath:indexPath]; + } + return nil; +} + +- (CGRect)_relativeRectToFit:(CGRect)rect forItemDecorationAtIndexPath:(NSIndexPath *)indexPath { + if ( _layoutFlags.delegateRelativeRectForItemDecoration ) { + return [_delegate layout:self relativeRectToFit:rect forItemDecorationAtIndexPath:indexPath]; + } + return CGRectZero; +} + +- (nullable NSString *)_elementKindForFooterDecorationAtIndexPath:(NSIndexPath *)indexPath { + if ( _layoutFlags.delegateElementKindForFooterDecoration ) { + return [_delegate layout:self elementKindForFooterDecorationAtIndexPath:indexPath]; + } + return nil; +} + +- (CGRect)_relativeRectToFit:(CGRect)rect forFooterDecorationAtIndexPath:(NSIndexPath *)indexPath { + if ( _layoutFlags.delegateRelativeRectForFooterDecoration ) { + return [_delegate layout:self relativeRectToFit:rect forFooterDecorationAtIndexPath:indexPath]; + } + return CGRectZero; +} + +- (CGFloat)zIndexForHeaderInSection:(NSInteger)section { + if ( _layoutFlags.delegateZIndexForHeader ) { + return [_delegate layout:self zIndexForHeaderInSection:section]; + } + return 0; +} + +- (CGFloat)zIndexForItemAtIndexPath:(NSIndexPath *)indexPath { + if ( _layoutFlags.delegateZIndexForItem ) { + return [_delegate layout:self zIndexForItemAtIndexPath:indexPath]; + } + return 0; +} + +- (CGFloat)zIndexForFooterInSection:(NSInteger)section { + if ( _layoutFlags.delegateZIndexForFooter ) { + return [_delegate layout:self zIndexForFooterInSection:section]; + } + return 0; +} + +- (CGFloat)_zIndexForSectionDecorationAtIndexPath:(NSIndexPath *)indexPath { + if ( _layoutFlags.delegateZIndexForSectionDecoration ) { + return [_delegate layout:self zIndexForSectionDecorationAtIndexPath:indexPath]; + } + return 0; +} + +- (CGFloat)_zIndexForHeaderDecorationAtIndexPath:(NSIndexPath *)indexPath { + if ( _layoutFlags.delegateZIndexForHeaderDecoration ) { + return [_delegate layout:self zIndexForHeaderDecorationAtIndexPath:indexPath]; + } + return 0; +} + +- (CGFloat)_zIndexForItemDecorationAtIndexPath:(NSIndexPath *)indexPath { + if ( _layoutFlags.delegateZIndexForItemDecoration ) { + return [_delegate layout:self zIndexForItemDecorationAtIndexPath:indexPath]; + } + return 0; +} + +- (CGFloat)_zIndexForFooterDecorationAtIndexPath:(NSIndexPath *)indexPath { + if ( _layoutFlags.delegateZIndexForFooterDecoration ) { + return [_delegate layout:self zIndexForFooterDecorationAtIndexPath:indexPath]; + } + return 0; +} + +- (nullable id)_userInfoForSectionDecorationAtIndexPath:(NSIndexPath *)indexPath { + if ( _layoutFlags.delegateUserInfoForSectionDecoration ) { + return [_delegate layout:self userInfoForSectionDecorationAtIndexPath:indexPath]; + } + return nil; +} + +- (nullable id)_userInfoForHeaderDecorationAtIndexPath:(NSIndexPath *)indexPath { + if ( _layoutFlags.delegateUserInfoForHeaderDecoration ) { + return [_delegate layout:self userInfoForHeaderDecorationAtIndexPath:indexPath]; + } + return nil; +} + +- (nullable id)_userInfoForItemDecorationAtIndexPath:(NSIndexPath *)indexPath { + if ( _layoutFlags.delegateUserInfoForItemDecoration ) { + return [_delegate layout:self userInfoForItemDecorationAtIndexPath:indexPath]; + } + return nil; +} + +- (nullable id)_userInfoForFooterDecorationAtIndexPath:(NSIndexPath *)indexPath { + if ( _layoutFlags.delegateUserInfoForFooterDecoration ) { + return [_delegate layout:self userInfoForFooterDecorationAtIndexPath:indexPath]; + } + return nil; +} + +- (CGRect)_headerPinnedFrameForSectionFrame:(CGRect)sectionFrame headerFrame:(CGRect)headerFrame adjustedPinnedInsets:(UIEdgeInsets)adjustedPinnedInsets { + UICollectionView *collectionView = self.collectionView; + CGPoint contentOffset = collectionView.contentOffset; + CGFloat pinnedX = headerFrame.origin.x; + CGFloat pinnedY = headerFrame.origin.y; + /// collectionView的偏移量(需要根据滚动方向设置值`switch{}`中设置) + CGFloat offset = 0; + switch ( _scrollDirection ) { + case UICollectionViewScrollDirectionVertical: { + if (@available(iOS 11.0, *)) { + offset = contentOffset.y + collectionView.adjustedContentInset.top; + } else { + offset = contentOffset.y + collectionView.contentInset.top; + } + offset += adjustedPinnedInsets.top; + if ( pinnedY < offset ) { + if ( offset < 0 ) offset = 0; + CGFloat maxY = CGRectGetMaxY(sectionFrame) - headerFrame.size.height; + /// 不足以显示header + /// 则需要跟随collectionView移动 + /// 到底maxY之后, 则跟随collectionView移动 + pinnedY = offset > maxY ? maxY : offset; + } + } + break; + case UICollectionViewScrollDirectionHorizontal: { + if (@available(iOS 11.0, *)) { + offset = contentOffset.x + collectionView.adjustedContentInset.left; + } else { + offset = contentOffset.x + collectionView.contentInset.left; + } + offset += adjustedPinnedInsets.top; + if ( pinnedX < offset ) { + if ( offset < 0 ) offset = 0; + CGFloat maxX = CGRectGetMaxX(sectionFrame) - headerFrame.size.width; + /// 不足以显示header + /// 则需要跟随collectionView移动 + /// 到底maxX之后, 则跟随collectionView移动 + pinnedX = offset > maxX ? maxX : offset; + } + } + break; + } + + CGRect pinnedFrame = headerFrame; + pinnedFrame.origin.x = pinnedX; + pinnedFrame.origin.y = pinnedY; + return pinnedFrame; +} + +- (CGRect)_headerDecorationPinnedFrameForHeaderFrame:(CGRect)headerFrame headerPinnedFrame:(CGRect)headerPinnedFrame headerDecorationFrame:(CGRect)headerDecorationFrame { + CGRect decorationPinnedFrame = headerDecorationFrame; + switch ( _scrollDirection ) { + case UICollectionViewScrollDirectionVertical: { + decorationPinnedFrame.origin.y += headerPinnedFrame.origin.y - headerFrame.origin.y; + } + break; + case UICollectionViewScrollDirectionHorizontal: { + decorationPinnedFrame.origin.x += headerPinnedFrame.origin.x - headerFrame.origin.x; + } + break; + } + return decorationPinnedFrame; +} +@end + + +@implementation LWZCollectionViewLayout (LWZCollectionFittingSize) +- (void)prepareLayoutFittingSize:(CGSize)fittingSize contentInsets:(UIEdgeInsets)contentInsets { + [self prepareLayoutForCollectionSize:fittingSize contentInsets:contentInsets]; +} +- (void)prepareLayoutFittingSize:(CGSize)fittingSize contentInsets:(UIEdgeInsets)contentInsets safeAreaInsets:(UIEdgeInsets)safeAreaInsets { + [self prepareLayoutForCollectionSize:fittingSize contentInsets:contentInsets safeAreaInsets:safeAreaInsets]; +} +@end + + + +#pragma mark - WeightLayout + +@implementation LWZCollectionViewLayout (WeightLayout) + +UIKIT_STATIC_INLINE CGSize +_LWZCollectionWeightLayoutFittingSize(CGFloat weight, CGSize fittingSize, NSInteger arranged, CGFloat itemSpacing, UICollectionViewScrollDirection direction) { + switch ( direction ) { + case UICollectionViewScrollDirectionVertical: + return CGSizeMake((fittingSize.width - ((arranged - 1) * itemSpacing)) * weight, fittingSize.height); + case UICollectionViewScrollDirectionHorizontal: + return CGSizeMake(fittingSize.width, (fittingSize.height - ((arranged - 1) * itemSpacing)) * weight); + } +} + +- (id)weight_layout_delegate { + return (id)_delegate; +} + +- (CGFloat)_weightForItemAtIndexPath:(NSIndexPath *)indexPath { + if ( _layoutFlags.delegateWeightForItem ) { + return [self.weight_layout_delegate layout:(id)self weightForItemAtIndexPath:indexPath]; + } + return 1; +} + +// offset => 首个cell开始的位置, 例如: 垂直布局时, offset = preHeader.maxY + section.top +- (nullable NSArray *)_weight_layout_layoutAttributesObjectsForCellsWithSection:(NSInteger)section offset:(CGFloat)offset container:(LWZCollectionLayoutSectionContentContainer *)container { + NSInteger numberOfItems = [self.collectionView numberOfItemsInSection:section]; + if ( numberOfItems == 0 ) return nil; + UIFloatRange layoutRange = container.itemLayoutRange; + if ( layoutRange.maximum <= layoutRange.minimum ) return nil; + UICollectionViewScrollDirection scrollDirection = container.layoutDirection; + + /* + + item layouts + |-----------| + |-----|-----| + |---|---|---| + + */ + CGSize fittingSize = LWZCollectionLayoutFittingSizeForLayoutRange(layoutRange, scrollDirection); + CGFloat lineSpacing = [self minimumLineSpacingForSectionAtIndex:section]; + CGFloat itemSpacing = [self minimumInteritemSpacingForSectionAtIndex:section]; + NSMutableArray *attributesArray = [NSMutableArray.alloc initWithCapacity:numberOfItems]; + LWZCollectionViewLayoutAttributes *previousItemAttributes = nil; + LWZCollectionViewLayoutAttributes *firstItemAttributes = nil; + CGFloat progress = 0; + for ( NSInteger i = 0 ; i < numberOfItems ; ++ i ) { + NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:section]; + CGFloat weight = [self _weightForItemAtIndexPath:indexPath]; + NSParameterAssert(weight > 0 && weight <= 1); + + // newline + CGFloat cur = progress + weight; + BOOL isFirstItem = firstItemAttributes == nil || cur > 1; + BOOL isNewline = isFirstItem && firstItemAttributes != nil; + progress = isFirstItem ? weight : cur; + + if ( isNewline ) { + switch ( scrollDirection ) { + case UICollectionViewScrollDirectionVertical: + offset = CGRectGetMaxY(previousItemAttributes.frame) + lineSpacing; + break; + case UICollectionViewScrollDirectionHorizontal: + offset = CGRectGetMaxX(previousItemAttributes.frame) + lineSpacing; + break; + } + } + + NSInteger arranges = 1.0 / weight; + CGSize itemFittingSize = _LWZCollectionWeightLayoutFittingSize(weight, fittingSize, arranges, itemSpacing, scrollDirection); + CGSize layoutSize = [self layoutSizeToFit:itemFittingSize forItemAtIndexPath:indexPath scrollDirection:scrollDirection]; + + CGRect frame = (CGRect){0, 0, layoutSize}; + switch ( scrollDirection ) { + case UICollectionViewScrollDirectionVertical: { + frame.origin.x = isFirstItem ? layoutRange.minimum : CGRectGetMaxX(previousItemAttributes.frame) + itemSpacing; + frame.origin.y = offset; + // fix size + // 同行item的高度与行首item一致 + if ( !isFirstItem ) frame.size.height = firstItemAttributes.frame.size.height; + } + break; + case UICollectionViewScrollDirectionHorizontal: { + frame.origin.x = offset; + frame.origin.y = isFirstItem ? layoutRange.minimum : CGRectGetMaxY(previousItemAttributes.frame) + itemSpacing; + // fix size + // 同行item的宽度与行首item一致 + if ( !isFirstItem ) frame.size.width = firstItemAttributes.frame.size.width; + } + break; + } + + LWZCollectionViewLayoutAttributes *attributes = [LWZCollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath]; + attributes.zIndex = [self zIndexForItemAtIndexPath:indexPath]; + attributes.frame = frame; + [attributesArray addObject:attributes]; + previousItemAttributes = attributes; + if ( isFirstItem ) firstItemAttributes = attributes; + } + + return attributesArray; +} +@end + +@implementation LWZCollectionWeightLayout +- (nullable NSArray *)layoutAttributesObjectsForCellsWithSection:(NSInteger)section offset:(CGFloat)offset container:(LWZCollectionLayoutSectionContentContainer *)container { + return [self _weight_layout_layoutAttributesObjectsForCellsWithSection:section offset:offset container:container]; +} +@end + +#pragma mark - ListLayout + +@implementation LWZCollectionViewLayout (ListLayout) +- (id)list_layout_delegate { + return (id)_delegate; +} + +- (LWZCollectionLayoutAlignment)layoutAlignmentForItemAtIndexPath:(NSIndexPath *)indexPath { + if ( _layoutFlags.delegateAlignmentForItem ) { + return [self.list_layout_delegate layout:self layoutAlignmentForItemAtIndexPath:indexPath]; + } + return LWZCollectionLayoutAlignmentCenter; +} + +// offset => 首个cell开始的位置, 例如: 垂直布局时, offset = preHeader.maxY + section.top +- (nullable NSArray *)_list_layout_layoutAttributesObjectsForCellsWithSection:(NSInteger)section offset:(CGFloat)offset container:(LWZCollectionLayoutSectionContentContainer *)container { + NSInteger numberOfItems = [self.collectionView numberOfItemsInSection:section]; + if ( numberOfItems == 0 ) return nil; + UIFloatRange layoutRange = container.itemLayoutRange; + if ( layoutRange.maximum <= layoutRange.minimum ) return nil; + UICollectionViewScrollDirection scrollDirection = container.layoutDirection; + + CGFloat lineSpacing = [self minimumLineSpacingForSectionAtIndex:section]; + CGSize fittingSize = LWZCollectionLayoutFittingSizeForLayoutRange(layoutRange, scrollDirection); + NSMutableArray *attributesArray = [NSMutableArray.alloc initWithCapacity:numberOfItems]; + CGRect previousFrame = CGRectZero; + switch ( scrollDirection ) { + case UICollectionViewScrollDirectionVertical: { + previousFrame.origin.x = layoutRange.minimum; + previousFrame.origin.y = offset - lineSpacing; + } + break; + case UICollectionViewScrollDirectionHorizontal: { + previousFrame.origin.x = offset - lineSpacing; + previousFrame.origin.y = layoutRange.minimum; + } + break; + } + + CGFloat length = layoutRange.maximum - layoutRange.minimum; + for ( NSInteger curIdx = 0 ; curIdx < numberOfItems ; ++ curIdx ) { + NSIndexPath *indexPath = [NSIndexPath indexPathForItem:curIdx inSection:section]; + CGSize layoutSize = [self layoutSizeToFit:fittingSize forItemAtIndexPath:indexPath scrollDirection:scrollDirection]; + + CGRect frame = (CGRect){0, 0, layoutSize}; + LWZCollectionLayoutAlignment alignment = [self layoutAlignmentForItemAtIndexPath:indexPath]; + switch ( scrollDirection ) { + case UICollectionViewScrollDirectionVertical: { + frame.origin.y = CGRectGetMaxY(previousFrame) + lineSpacing; + switch ( alignment ) { + // 左对齐 + case LWZCollectionLayoutAlignmentStart: { + frame.origin.x = layoutRange.minimum; + } + break; + // 右对齐 + case LWZCollectionLayoutAlignmentEnd: { + frame.origin.x = layoutRange.minimum + length - frame.size.width; + } + break; + // 中对齐 + case LWZCollectionLayoutAlignmentCenter: { + frame.origin.x = layoutRange.minimum + (length - frame.size.width) * 0.5; + } + break; + } + } + break; + case UICollectionViewScrollDirectionHorizontal: { + frame.origin.x = CGRectGetMaxX(previousFrame) + lineSpacing; + switch ( alignment ) { + // 顶对齐 + case LWZCollectionLayoutAlignmentStart: { + frame.origin.y = layoutRange.minimum; + } + break; + // 底对齐 + case LWZCollectionLayoutAlignmentEnd: { + frame.origin.y = layoutRange.minimum + length - frame.size.height; + } + break; + // 中对齐 + case LWZCollectionLayoutAlignmentCenter: { + frame.origin.y = layoutRange.minimum + (length - frame.size.height) * 0.5; + } + break; + } + } + break; + } + LWZCollectionViewLayoutAttributes *attributes = [LWZCollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath]; + attributes.zIndex = [self zIndexForItemAtIndexPath:indexPath]; + attributes.frame = frame; + [attributesArray addObject:attributes]; + + previousFrame = frame; + } + return attributesArray; +} +@end + +@implementation LWZCollectionListLayout +- (nullable NSArray *)layoutAttributesObjectsForCellsWithSection:(NSInteger)section offset:(CGFloat)offset container:(LWZCollectionLayoutSectionContentContainer *)container { + return [self _list_layout_layoutAttributesObjectsForCellsWithSection:section offset:offset container:container]; +} +@end + +#pragma mark - WaterfallFlowLayout + +@implementation LWZCollectionViewLayout(WaterfallFlowLayout) +- (id)waterfall_flow_layout_delegate { + return (id)_delegate; +} + +- (NSInteger)_numberOfArrangedItemsPerLineInSection:(NSInteger)section { + if ( _layoutFlags.delegateNumberOfArrangedItemsPerLineInSection ) { + return [self.waterfall_flow_layout_delegate layout:self numberOfArrangedItemsPerLineInSection:section]; + } + return 1; +} + +// offset => 首个cell开始的位置, 例如: 垂直布局时, offset = preHeader.maxY + section.top +- (nullable NSArray *)_waterfall_flow_layout_layoutAttributesObjectsForCellsWithSection:(NSInteger)section offset:(CGFloat)offset container:(LWZCollectionLayoutSectionContentContainer *)container { + /* + + item layouts + | | |__| + |__| | | + | |__| | + | | |__| + |__|__| | + */ + NSInteger numberOfItems = [self.collectionView numberOfItemsInSection:section]; + if ( numberOfItems == 0 ) return nil; + UIFloatRange layoutRange = container.itemLayoutRange; + if ( layoutRange.maximum <= layoutRange.minimum ) return nil; + UICollectionViewScrollDirection scrollDirection = container.layoutDirection; + + CGFloat lineSpacing = [self minimumLineSpacingForSectionAtIndex:section]; + CGFloat itemSpacing = [self minimumInteritemSpacingForSectionAtIndex:section]; + NSInteger arrangements = [self _numberOfArrangedItemsPerLineInSection:section]; + NSParameterAssert(arrangements > 0); + CGSize fittingSize = _LWZCollectionWaterfallFlowLayoutFittingSize(LWZCollectionLayoutFittingSizeForLayoutRange(layoutRange, scrollDirection), arrangements, itemSpacing, scrollDirection); + NSMutableArray *attributesArray = [NSMutableArray.alloc initWithCapacity:numberOfItems]; + CGFloat columns[arrangements]; + _LWZCollectionWaterfallFlowLayoutInitColumns(columns, arrangements, offset - lineSpacing); + for ( NSInteger curIdx = 0 ; curIdx < numberOfItems ; ++ curIdx ) { + NSIndexPath *indexPath = [NSIndexPath indexPathForItem:curIdx inSection:section]; + CGSize layoutSize = [self layoutSizeToFit:fittingSize forItemAtIndexPath:indexPath scrollDirection:scrollDirection]; + + NSInteger minOffsetIndex = _LWZCollectionWaterfallFlowLayoutGetMinOffsetColumnIndex(columns, arrangements); + offset = columns[minOffsetIndex]; + CGRect frame = (CGRect){0, 0, layoutSize}; + switch ( scrollDirection ) { + case UICollectionViewScrollDirectionVertical: { + frame.origin.x = layoutRange.minimum + minOffsetIndex * (fittingSize.width + itemSpacing); + frame.origin.y = offset + lineSpacing; + } + break; + case UICollectionViewScrollDirectionHorizontal: { + frame.origin.x = offset + lineSpacing; + frame.origin.y = layoutRange.minimum + minOffsetIndex * (fittingSize.height + itemSpacing); + } + break; + } + _LWZCollectionWaterfallFlowLayoutSetColumnOffset(columns, minOffsetIndex, frame, scrollDirection); + LWZCollectionViewLayoutAttributes *attributes = [LWZCollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath]; + attributes.zIndex = [self zIndexForItemAtIndexPath:indexPath]; + attributes.frame = frame; + [attributesArray addObject:attributes]; + } + return attributesArray; +} + +UIKIT_STATIC_INLINE CGSize +_LWZCollectionWaterfallFlowLayoutFittingSize(CGSize bounds, NSInteger arrangements, CGFloat itemSpacing, UICollectionViewScrollDirection direction) { + switch ( direction ) { + case UICollectionViewScrollDirectionVertical: + return CGSizeMake((bounds.width - (arrangements - 1) * itemSpacing) / arrangements, bounds.height); + case UICollectionViewScrollDirectionHorizontal: + return CGSizeMake(bounds.width, (bounds.height - (arrangements - 1) * itemSpacing) / arrangements); + } +} + +UIKIT_STATIC_INLINE void +_LWZCollectionWaterfallFlowLayoutInitColumns(CGFloat *columns, NSInteger arrangements, CGFloat offset) { + for ( NSInteger i = 0 ; i < arrangements ; ++ i ) columns[i] = offset; +} + +UIKIT_STATIC_INLINE NSInteger +_LWZCollectionWaterfallFlowLayoutGetMinOffsetColumnIndex(CGFloat *columns, NSInteger arrangements) { + NSInteger index = 0; + CGFloat min = CGFLOAT_MAX; + for ( NSInteger i = 0 ; i < arrangements ; ++ i ) { + if ( min > columns[i] ) { + min = columns[i]; + index = i; + } + } + return index; +} + +UIKIT_STATIC_INLINE void +_LWZCollectionWaterfallFlowLayoutSetColumnOffset(CGFloat *columns, NSInteger index, CGRect itemFrame, UICollectionViewScrollDirection direction) { + switch ( direction ) { + case UICollectionViewScrollDirectionVertical: + columns[index] = CGRectGetMaxY(itemFrame); + break; + case UICollectionViewScrollDirectionHorizontal: + columns[index] = CGRectGetMaxX(itemFrame); + break; + } +} +@end + +@implementation LWZCollectionWaterfallFlowLayout +- (nullable NSArray *)layoutAttributesObjectsForCellsWithSection:(NSInteger)section offset:(CGFloat)offset container:(LWZCollectionLayoutSectionContentContainer *)container { + return [self _waterfall_flow_layout_layoutAttributesObjectsForCellsWithSection:section offset:offset container:container]; +} +@end + +#pragma mark - RestrictedLayout + +@implementation LWZCollectionViewLayout(RestrictedLayout) +// offset => 首个cell开始的位置, 例如: 垂直布局时, offset = preHeader.maxY + section.top +- (nullable NSArray *)_restricted_layout_layoutAttributesObjectsForCellsWithSection:(NSInteger)section offset:(CGFloat)offset container:(LWZCollectionLayoutSectionContentContainer *)container { + NSInteger numberOfItems = [self.collectionView numberOfItemsInSection:section]; + if ( numberOfItems == 0 ) return nil; + UIFloatRange layoutRange = container.itemLayoutRange; + if ( layoutRange.maximum <= layoutRange.minimum ) return nil; + UICollectionViewScrollDirection scrollDirection = container.layoutDirection; + /* + + item layouts + |-----------| + |-----|-|--| + |---|---|---| + |--|-|----| + + */ + CGFloat lineSpacing = [self minimumLineSpacingForSectionAtIndex:section]; + CGFloat itemSpacing = [self minimumInteritemSpacingForSectionAtIndex:section]; + CGSize fittingSize = LWZCollectionLayoutFittingSizeForLayoutRange(layoutRange, scrollDirection); + NSMutableArray *attributesArray = [NSMutableArray.alloc initWithCapacity:numberOfItems]; + CGRect previousFrame = CGRectZero; + switch ( scrollDirection ) { + case UICollectionViewScrollDirectionVertical: { + previousFrame.origin.x = layoutRange.minimum - itemSpacing; + previousFrame.origin.y = offset - lineSpacing; + } + break; + case UICollectionViewScrollDirectionHorizontal: { + previousFrame.origin.x = offset - lineSpacing; + previousFrame.origin.y = layoutRange.minimum - itemSpacing; + } + break; + } + + // 每行的第一个item + LWZCollectionViewLayoutAttributes *firstItemAttributes = nil; + for ( NSInteger curIdx = 0 ; curIdx < numberOfItems ; ++ curIdx ) { + NSIndexPath *indexPath = [NSIndexPath indexPathForItem:curIdx inSection:section]; + CGSize layoutSize = [self layoutSizeToFit:fittingSize forItemAtIndexPath:indexPath scrollDirection:scrollDirection]; + + CGRect frame = (CGRect){0, 0, layoutSize}; + BOOL isFirstItem = NO; + switch ( scrollDirection ) { + case UICollectionViewScrollDirectionVertical: { + CGFloat left = CGRectGetMaxX(previousFrame) + itemSpacing; + CGFloat maxX = left + layoutSize.width; + if ( firstItemAttributes == nil || maxX > layoutRange.maximum ) { + // new line + frame.origin.x = layoutRange.minimum; + frame.origin.y = CGRectGetMaxY(previousFrame) + lineSpacing; + isFirstItem = YES; + } + else { + // current line + frame.origin.x = left; + frame.origin.y = CGRectGetMinY(previousFrame); + // fix size + // 每行item的高度与行首item一致 + frame.size.height = firstItemAttributes.frame.size.height; + } + } + break; + case UICollectionViewScrollDirectionHorizontal: { + CGFloat top = CGRectGetMaxY(previousFrame) + itemSpacing; + CGFloat maxY = top + layoutSize.height; + // new line + if ( firstItemAttributes == nil || maxY > layoutRange.maximum ) { + frame.origin.x = CGRectGetMaxX(previousFrame) + lineSpacing; + frame.origin.y = layoutRange.minimum; + isFirstItem = YES; + } + else { + // current line + frame.origin.x = CGRectGetMinX(previousFrame); + frame.origin.y = top; + // fix size + // 每行item的宽度度与行首item一致 + frame.size.width = firstItemAttributes.frame.size.width; + } + } + break; + } + LWZCollectionViewLayoutAttributes *attributes = [LWZCollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath]; + attributes.zIndex = [self zIndexForItemAtIndexPath:indexPath]; + attributes.frame = frame; + [attributesArray addObject:attributes]; + + previousFrame = frame; + if ( isFirstItem ) firstItemAttributes = attributes; + } + return attributesArray; +} +@end + +@implementation LWZCollectionRestrictedLayout +- (nullable NSArray *)layoutAttributesObjectsForCellsWithSection:(NSInteger)section offset:(CGFloat)offset container:(LWZCollectionLayoutSectionContentContainer *)container { + return [self _restricted_layout_layoutAttributesObjectsForCellsWithSection:section offset:offset container:container]; +} +@end + + +#pragma mark - template layout + +@implementation LWZCollectionViewLayout(TemplateLayout) +- (id)template_layout_delegate { + return (id)_delegate; +} + +- (NSArray *)_layoutTemplateGroupsInSection:(NSInteger)section { + return [self.template_layout_delegate layout:self layoutTemplateContainerGroupsInSection:section]; +} + +// offset => 首个cell开始的位置, 例如: 垂直布局时, offset = preHeader.maxY + section.top +- (nullable NSArray *)_template_layout_layoutAttributesObjectsForCellsWithSection:(NSInteger)section offset:(CGFloat)offset container:(LWZCollectionLayoutSectionContentContainer *)container { + NSInteger numberOfItems = [self.collectionView numberOfItemsInSection:section]; + if ( numberOfItems == 0 ) return nil; + UIFloatRange layoutRange = container.itemLayoutRange; + if ( layoutRange.maximum <= layoutRange.minimum ) return nil; + UICollectionViewScrollDirection scrollDirection = container.layoutDirection; + + NSArray *groups = [self _layoutTemplateGroupsInSection:section]; + NSAssert(groups != nil, @"The template groups can't be nil!"); + + CGFloat lineSpacing = [self minimumLineSpacingForSectionAtIndex:section]; + CGFloat itemSpacing = [self minimumInteritemSpacingForSectionAtIndex:section]; + + LWZCollectionTemplateLayoutSolver *solver = [LWZCollectionTemplateLayoutSolver.alloc initWithGroups:groups scrollDirection:scrollDirection numberOfItems:numberOfItems lineSpacing:lineSpacing itemSpacing:itemSpacing containerSize:container.itemLayoutContainerSize]; + NSMutableArray *attributesArray = [NSMutableArray.alloc initWithCapacity:numberOfItems]; + // 将cell填充到模板中 + for ( NSInteger i = 0 ; i < numberOfItems ; ++ i ) { + CGRect frame = [solver itemLayoutFrameAtIndex:i]; + switch ( scrollDirection ) { + case UICollectionViewScrollDirectionVertical: { + frame.origin.y += offset; + frame.origin.x += layoutRange.minimum; + } + break; + case UICollectionViewScrollDirectionHorizontal: { + frame.origin.x += offset; + frame.origin.y += layoutRange.minimum; + } + break; + } + NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:section]; + LWZCollectionViewLayoutAttributes *attributes = [LWZCollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath]; + attributes.zIndex = [self zIndexForItemAtIndexPath:indexPath]; + attributes.frame = frame; + [attributesArray addObject:attributes]; + } + return attributesArray; +} +@end + +@implementation LWZCollectionTemplateLayout +- (nullable NSArray *)layoutAttributesObjectsForCellsWithSection:(NSInteger)section offset:(CGFloat)offset container:(LWZCollectionLayoutSectionContentContainer *)container { + return [self _template_layout_layoutAttributesObjectsForCellsWithSection:section offset:offset container:container]; +} +@end + +#pragma mark - hybrid layout + +@implementation LWZCollectionHybridLayout + +- (id)hybrid_layout_delegate { + return (id)self.delegate; +} + +- (LWZCollectionLayoutType)_layoutTypeForItemsInSection:(NSInteger)section { + return [self.hybrid_layout_delegate layout:self layoutTypeForItemsInSection:section]; +} + +- (nullable NSArray *)layoutAttributesObjectsForCellsWithSection:(NSInteger)section offset:(CGFloat)offset container:(LWZCollectionLayoutSectionContentContainer *)container { + switch ( [self _layoutTypeForItemsInSection:section] ) { + case LWZCollectionLayoutTypeUnspecified: + @throw [NSException exceptionWithName:NSInternalInconsistencyException + reason:@"You must specify a layout!" + userInfo:nil]; + case LWZCollectionLayoutTypeWeight: + return [self _weight_layout_layoutAttributesObjectsForCellsWithSection:section offset:offset container:container]; + case LWZCollectionLayoutTypeList: + return [self _list_layout_layoutAttributesObjectsForCellsWithSection:section offset:offset container:container]; + case LWZCollectionLayoutTypeWaterfallFlow: + return [self _waterfall_flow_layout_layoutAttributesObjectsForCellsWithSection:section offset:offset container:container]; + case LWZCollectionLayoutTypeRestrictedLayout: + return [self _restricted_layout_layoutAttributesObjectsForCellsWithSection:section offset:offset container:container]; + case LWZCollectionLayoutTypeTemplate: + return [self _template_layout_layoutAttributesObjectsForCellsWithSection:section offset:offset container:container]; + } +} + +@end + +#pragma mark - compositional layout + +@interface LWZCollectionNestedGroupLayout : LWZCollectionViewLayout +- (instancetype)initWithParentLayout:(__weak LWZCollectionCompositionalLayout *)parentLayout inSection:(NSInteger)idx scrollDirection:(UICollectionViewScrollDirection)scrollDirection orthogonalScrollingBehavior:(LWZCollectionLayoutContentOrthogonalScrollingBehavior)behavior; +@end + +@implementation LWZCollectionNestedGroupLayout { + NSInteger mSectionIdx; + + __weak LWZCollectionCompositionalLayout *mParentLayout; + LWZCollectionLayoutContentOrthogonalScrollingBehavior mBehavior; +} + +- (instancetype)initWithParentLayout:(__weak LWZCollectionCompositionalLayout *)parentLayout inSection:(NSInteger)idx scrollDirection:(UICollectionViewScrollDirection)scrollDirection orthogonalScrollingBehavior:(LWZCollectionLayoutContentOrthogonalScrollingBehavior)behavior { + self = [super initWithScrollDirection:scrollDirection delegate:parentLayout.delegate]; + if ( self ) { + mSectionIdx = idx; + mParentLayout = parentLayout; + mBehavior = behavior; + if (@available(iOS 11.0, *)) { + self.ignoredSafeAreaInsets = YES; + } + } + return self; +} + +- (void)willPrepareLayoutInContainer:(LWZCollectionLayoutCollectionContentContainer *)container { } + +- (void)didFinishPreparingInContainer:(LWZCollectionLayoutCollectionContentContainer *)container { } + +- (BOOL)shouldProcessSectionLayoutAtIndex:(NSInteger)index { + return index == mSectionIdx; +} + +- (UIEdgeInsets)edgeSpacingsForSectionAtIndex:(NSInteger)section { + return UIEdgeInsetsZero; +} + +- (UIEdgeInsets)contentInsetsForSectionAtIndex:(NSInteger)section { + return UIEdgeInsetsZero; +} + +- (nullable LWZCollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewWithElementKind:(NSString *)kind indexPath:(NSIndexPath *)indexPath offset:(CGFloat)offset container:(LWZCollectionLayoutSectionContentContainer *)container { + return nil; +} + +- (nullable NSArray *)layoutAttributesObjectsForCellsWithSection:(NSInteger)section offset:(CGFloat)offset container:(LWZCollectionLayoutSectionContentContainer *)container { + if ( section == mSectionIdx ) { + return [mParentLayout layoutAttributesObjectsForCellsWithSection:section offset:offset container:container]; + } + return nil; +} + +- (nullable LWZCollectionViewLayoutAttributes *)layoutAttributesForSectionDecorationViewWithIndexPath:(NSIndexPath *)indexPath inRect:(CGRect)rect { return nil; } +- (nullable LWZCollectionViewLayoutAttributes *)layoutAttributesForHeaderDecorationViewWithIndexPath:(NSIndexPath *)indexPath inRect:(CGRect)rect { return nil; } +- (nullable LWZCollectionViewLayoutAttributes *)layoutAttributesForFooterDecorationViewWithIndexPath:(NSIndexPath *)indexPath inRect:(CGRect)rect { return nil; } + +- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity { + switch ( mBehavior ) { + case LWZCollectionLayoutContentOrthogonalScrollingBehaviorNormal: + return [super targetContentOffsetForProposedContentOffset:proposedContentOffset withScrollingVelocity:velocity]; + case LWZCollectionLayoutContentOrthogonalScrollingBehaviorContinuousCentered: + case LWZCollectionLayoutContentOrthogonalScrollingBehaviorPaging: { + UICollectionView *collectionView = self.collectionView; + CGRect bounds = collectionView.bounds; + CGPoint contentOffset = mBehavior == LWZCollectionLayoutContentOrthogonalScrollingBehaviorContinuousCentered ? proposedContentOffset : collectionView.contentOffset; + // 计算参考线 + CGFloat refLine = 0; + switch ( _scrollDirection ) { + case UICollectionViewScrollDirectionVertical: { + if ( fabs(velocity.y) < 0.35 ) { + refLine = contentOffset.y + bounds.size.height * 0.5; + } + // 向上拖拽 + else if ( velocity.y > 0 ) { + refLine = contentOffset.y + bounds.size.height; + } + // 向下拖拽 + else { + refLine = contentOffset.y; + } + } + break; + case UICollectionViewScrollDirectionHorizontal: { + if ( fabs(velocity.x) < 0.35 ) { + refLine = contentOffset.x + bounds.size.width * 0.5; + } + // 向左拖拽 + else if ( velocity.x > 0 ) { + refLine = contentOffset.x + bounds.size.width; + } + // 向右拖拽 + else { + refLine = contentOffset.x; + } + } + break; + } + return [self _targetContentOffsetForRefLine:refLine proposedContentOffset:proposedContentOffset]; + } + break; + } +} + +- (CGPoint)_targetContentOffsetForRefLine:(CGFloat)refLine proposedContentOffset:(CGPoint)proposedContentOffset { + // 查找距离 refline 最近的 cell + CGFloat curMinOffset = CGFLOAT_MAX; + LWZCollectionViewLayoutAttributes *finalAttributes = nil; + for ( LWZCollectionViewLayoutAttributes *attributes in _mCollection.sections.firstObject.cellLayoutAttributesObjects ) { + CGRect frame = attributes.frame; + CGFloat midOffset = 0; + switch ( _scrollDirection ) { + case UICollectionViewScrollDirectionVertical: + midOffset = CGRectGetMidY(frame); + break; + case UICollectionViewScrollDirectionHorizontal: + midOffset = CGRectGetMidX(frame); + break; + } + CGFloat sub = floor(ABS(refLine - midOffset)); + if ( sub < curMinOffset ) { + finalAttributes = attributes; + curMinOffset = sub; + } + } + + UICollectionView *collectionView = self.collectionView; + UIEdgeInsets contentInset = collectionView.contentInset; + CGSize contentSize = _mContentSize; + CGRect bounds = collectionView.bounds; + switch ( _scrollDirection ) { + case UICollectionViewScrollDirectionVertical: { + CGFloat offsetY = CGRectGetMidY(finalAttributes.frame) - bounds.size.height * 0.5; + CGFloat minY = -contentInset.top; + CGFloat maxY = contentSize.height - bounds.size.height * 0.5; + if ( offsetY < minY ) + offsetY = minY; + else if ( offsetY > maxY ) + offsetY = maxY; + proposedContentOffset.y = offsetY; + return proposedContentOffset; + } + case UICollectionViewScrollDirectionHorizontal: { + CGFloat offsetX = CGRectGetMidX(finalAttributes.frame) - bounds.size.width * 0.5; + CGFloat minX = -contentInset.left; + CGFloat maxX = contentSize.width - bounds.size.width * 0.5; + if ( offsetX < minX ) + offsetX = minX; + else if ( offsetX > maxX ) + offsetX = maxX; + proposedContentOffset.x = offsetX; + return proposedContentOffset; + } + } +} +@end + +UIKIT_STATIC_INLINE CGSize +LWZLayoutSizeGroupAdjusting(CGSize size, CGSize fittingSize, UICollectionViewScrollDirection direction) { + switch ( direction ) { + case UICollectionViewScrollDirectionVertical: + size.width = fittingSize.width; + break; + case UICollectionViewScrollDirectionHorizontal: + size.height = fittingSize.height; + break; + } + return size; +} + +@implementation LWZCollectionCompositionalLayout { + struct { + unsigned delegateIsOrthogonalScrollingInSection :1; + unsigned delegateOrthogonalContentScrollingBehaviorInSection :1; + } _compositionalLayoutFlags; +} + +- (void)setDelegate:(id)delegate { + if ( delegate != self.delegate ) { + [super setDelegate:delegate]; + _compositionalLayoutFlags.delegateIsOrthogonalScrollingInSection = [delegate respondsToSelector:@selector(layout:isOrthogonalScrollingInSection:)]; + _compositionalLayoutFlags.delegateOrthogonalContentScrollingBehaviorInSection = [delegate respondsToSelector:@selector(layout:orthogonalContentScrollingBehaviorInSection:)]; + } +} + +- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds { + for ( _LWZLayoutSection *section in _mCollection.sections ) { + UICollectionView *groupView = section.customView; + if ( groupView != nil ) { + BOOL needsHidden = LWZRectFloatRangeCompare(groupView.frame, newBounds, _scrollDirection) != LWZFloatRangeComparisonResultIntersecting; + groupView.hidden = needsHidden; + } + } + return [super shouldInvalidateLayoutForBoundsChange:newBounds]; +} + +- (id)compositional_layout_delegate { + return (id)self.delegate; +} + +- (BOOL)isOrthogonalScrollingInSection:(NSInteger)section { + if ( _compositionalLayoutFlags.delegateIsOrthogonalScrollingInSection ) { + return [self.compositional_layout_delegate layout:self isOrthogonalScrollingInSection:section]; + } + return NO; +} + +- (nullable __kindof UICollectionViewCell *)orthogonalScrollingCellForItemAtIndexPath:(NSIndexPath *)indexPath { + if ( indexPath.section < _mCollection.sections.count ) { + _LWZLayoutSection *section = _mCollection.sections[indexPath.section]; + return section.customView != nil ? [section.customView cellForItemAtIndexPath:indexPath] : nil; + } + return nil; +} + +- (CGSize)layoutSizeToFit:(CGSize)fittingSize forOrthogonalContentInSection:(NSInteger)section scrollDirection:(UICollectionViewScrollDirection)scrollDirection { + return [self.compositional_layout_delegate layout:self layoutSizeToFit:fittingSize forOrthogonalContentInSection:section scrollDirection:scrollDirection]; +} + +- (LWZCollectionLayoutContentOrthogonalScrollingBehavior)_orthogonalContentScrollingBehaviorInSection:(NSInteger)section { + if ( _compositionalLayoutFlags.delegateOrthogonalContentScrollingBehaviorInSection ) { + return [self.compositional_layout_delegate layout:self orthogonalContentScrollingBehaviorInSection:section]; + } + return LWZCollectionLayoutContentOrthogonalScrollingBehaviorNormal; +} + +- (LWZCollectionLayoutContentPresentationMode)layoutContentPresentationModeForCellsInSection:(NSInteger)index { + return [self isOrthogonalScrollingInSection:index] ? LWZCollectionLayoutContentPresentationModeCustom : LWZCollectionLayoutContentPresentationModeNormal; +} +// 包裹所有 cell 的容器. +- (nullable UIView *)layoutCustomViewForCellsWithSection:(NSInteger)sIdx offset:(CGFloat)offset container:(LWZCollectionLayoutSectionContentContainer *)container { + LWZCollectionLayoutCollectionContentContainer *collectionContentContainer = container.collectionContentContainer; + LWZCollectionLayoutSectionContentContainer *sectionContentContainer = container; + UICollectionView *collectionView = self.collectionView; + UIFloatRange layoutRange = sectionContentContainer.itemLayoutRange; + NSInteger numberOfItems = [collectionView numberOfItemsInSection:sIdx]; + if ( numberOfItems != 0 && layoutRange.maximum > layoutRange.minimum ) { + UICollectionViewScrollDirection groupViewScrollDirection = _scrollDirection == UICollectionViewScrollDirectionVertical ? UICollectionViewScrollDirectionHorizontal : UICollectionViewScrollDirectionVertical; + + CGSize collectionSize = collectionContentContainer.collectionSize; + CGSize contentFittingSize = LWZCollectionLayoutFittingSizeForLayoutRange(layoutRange, _scrollDirection); + CGSize contentLayoutSize = [self layoutSizeToFit:contentFittingSize forOrthogonalContentInSection:sIdx scrollDirection:groupViewScrollDirection]; + contentLayoutSize = LWZLayoutSizeGroupAdjusting(contentLayoutSize, contentFittingSize, _scrollDirection); + UIEdgeInsets contentInsets = container.contentInsets; + UIEdgeInsets groupInsets = UIEdgeInsetsZero; + CGRect groupFrame = CGRectZero; + switch ( _scrollDirection ) { + case UICollectionViewScrollDirectionVertical: { + groupFrame.origin.y = offset; + groupFrame.origin.x = 0; + groupFrame.size.width = collectionSize.width; + groupFrame.size.height = contentLayoutSize.height; + groupInsets.left = collectionContentContainer.layoutInsets.left + sectionContentContainer.layoutInsets.left + contentInsets.left; + groupInsets.right = collectionContentContainer.layoutInsets.right + sectionContentContainer.layoutInsets.right + contentInsets.right; + } + break; + case UICollectionViewScrollDirectionHorizontal: { + groupFrame.origin.x = offset; + groupFrame.origin.y = 0; + groupFrame.size.height = collectionSize.height; + groupFrame.size.width = contentLayoutSize.width; + groupInsets.top = collectionContentContainer.layoutInsets.top + sectionContentContainer.layoutInsets.top + contentInsets.top; + groupInsets.bottom = collectionContentContainer.layoutInsets.bottom + sectionContentContainer.layoutInsets.bottom + contentInsets.bottom; + } + break; + } + + LWZCollectionLayoutContentOrthogonalScrollingBehavior behavior = [self _orthogonalContentScrollingBehaviorInSection:sIdx]; + UICollectionView *groupView = [UICollectionView.alloc initWithFrame:groupFrame collectionViewLayout:[LWZCollectionNestedGroupLayout.alloc initWithParentLayout:self inSection:sIdx scrollDirection:groupViewScrollDirection orthogonalScrollingBehavior:behavior]]; + groupView.contentInset = groupInsets; + groupView.backgroundColor = UIColor.clearColor; + groupView.dataSource = collectionView.dataSource; + groupView.delegate = collectionView.delegate; + groupView.hidden = LWZRectFloatRangeCompare(groupFrame, collectionView.bounds, _scrollDirection) != LWZFloatRangeComparisonResultIntersecting; + groupView.showsHorizontalScrollIndicator = NO; + groupView.showsVerticalScrollIndicator = NO; + groupView.layer.zPosition = LWZCollectionOrthogonalScrollingGroupViewZPosition; + if ( behavior == LWZCollectionLayoutContentOrthogonalScrollingBehaviorPaging ) { + groupView.decelerationRate = UIScrollViewDecelerationRateFast; + } + if (@available(iOS 11.0, *)) { + groupView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; + } + return groupView; + } + return nil; +} +@end + +/** + + 关于Decoration, Section, Header, Item, Footer 不能是同一个类型的`Decoration`对象, 需要分开创建. + + Decoration 自身分辨的依据是 ElementKind 以及 IndexPath, 因此如果Section, Header... 采用相同类型的Decoration(ElementKind一致)时, 仅通过IndexPath是无法确定谁的Decoration的. + + */ + +/** + + Thread 1: "layout attributes for supplementary item at index path ( {length = 2, path = 0 - 0}) changed + + from index path: ( {length = 2, path = 0 - 0}); element kind: (UICollectionElementKindSectionHeader); frame = (0 210.5; 375 48); zIndex = 10; + + to index path: ( {length = 2, path = 0 - 0}); element kind: (UICollectionElementKindSectionHeader); frame = (0 0; 375 48); without invalidating the layout" + + + + Thread 1: "layout attributes for supplementary item at index path ( {length = 2, path = 0 - 0}) changed + + from index path: ( {length = 2, path = 0 - 0}); element kind: (UICollectionElementKindSectionHeader); frame = (0 0; 375 48); zIndex = 10; + + to index path: ( {length = 2, path = 0 - 0}); element kind: (UICollectionElementKindSectionHeader); frame = (0 0; 375 48); without invalidating the layout" + + + Thread 1: "layout attributes for supplementary item at index path (0 - 0) changed + + from + + to + + without invalidating the layout" + */ +#ifdef LWZ_DEBUG + + +/* + + + layoutInsets: 内容布局边缘间距 + layoutRange: 内容布局范围限制 + + */ +#endif diff --git a/LWZComponents/LWZCollectionViewComponents/Layout/LWZCollectionViewLayoutAttributes.h b/LWZComponents/LWZCollectionViewComponents/Layout/LWZCollectionViewLayoutAttributes.h new file mode 100644 index 0000000..3dc646b --- /dev/null +++ b/LWZComponents/LWZCollectionViewComponents/Layout/LWZCollectionViewLayoutAttributes.h @@ -0,0 +1,14 @@ +// +// LWZCollectionViewLayoutAttributes.h +// LWZAppComponents +// +// Created by changsanjiang on 2021/8/25. +// + +#import + +NS_ASSUME_NONNULL_BEGIN +@interface LWZCollectionViewLayoutAttributes : UICollectionViewLayoutAttributes +@property (nonatomic, strong, nullable) id decorationUserInfo; +@end +NS_ASSUME_NONNULL_END diff --git a/LWZComponents/LWZCollectionViewComponents/Layout/LWZCollectionViewLayoutAttributes.m b/LWZComponents/LWZCollectionViewComponents/Layout/LWZCollectionViewLayoutAttributes.m new file mode 100644 index 0000000..34e927f --- /dev/null +++ b/LWZComponents/LWZCollectionViewComponents/Layout/LWZCollectionViewLayoutAttributes.m @@ -0,0 +1,19 @@ +// +// LWZCollectionViewLayoutAttributes.m +// LWZAppComponents +// +// Created by changsanjiang on 2021/8/25. +// + +#import "LWZCollectionViewLayoutAttributes.h" + +@implementation LWZCollectionViewLayoutAttributes +- (id)copyWithZone:(nullable NSZone *)zone { + LWZCollectionViewLayoutAttributes *attr = [super copyWithZone:zone]; + id userInfo = self.decorationUserInfo; + if ( userInfo != nil ) { + attr.decorationUserInfo = userInfo; + } + return attr; +} +@end diff --git a/LWZComponents/LWZCollectionViewComponents/Layout/LWZCollectionViewLayoutSubclass.h b/LWZComponents/LWZCollectionViewComponents/Layout/LWZCollectionViewLayoutSubclass.h new file mode 100644 index 0000000..311be8e --- /dev/null +++ b/LWZComponents/LWZCollectionViewComponents/Layout/LWZCollectionViewLayoutSubclass.h @@ -0,0 +1,66 @@ +// +// LWZCollectionViewLayoutSubclass.h +// SJTestAutoLayout_Example +// +// Created by changsanjiang on 2021/11/10. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "LWZCollectionViewLayout.h" + +NS_ASSUME_NONNULL_BEGIN +@interface LWZCollectionViewLayout (Subclass) + +- (void)willPrepareLayoutInContainer:(LWZCollectionLayoutCollectionContentContainer *)container; + +- (void)didFinishPreparingInContainer:(LWZCollectionLayoutCollectionContentContainer *)container; + +#pragma mark - section + +- (BOOL)shouldProcessSectionLayoutAtIndex:(NSInteger)index; + +#pragma mark - header footer + +- (nullable LWZCollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewWithElementKind:(NSString *)kind indexPath:(NSIndexPath *)indexPath offset:(CGFloat)offset container:(LWZCollectionLayoutSectionContentContainer *)container; + +#pragma mark - cells + +- (LWZCollectionLayoutContentPresentationMode)layoutContentPresentationModeForCellsInSection:(NSInteger)index; + +- (nullable UIView *)layoutCustomViewForCellsWithSection:(NSInteger)section offset:(CGFloat)offset container:(LWZCollectionLayoutSectionContentContainer *)container; + +- (nullable NSArray *)layoutAttributesObjectsForCellsWithSection:(NSInteger)section offset:(CGFloat)offset container:(LWZCollectionLayoutSectionContentContainer *)container; + +#pragma mark - decorations + +- (nullable LWZCollectionViewLayoutAttributes *)layoutAttributesForSectionDecorationViewWithIndexPath:(NSIndexPath *)indexPath inRect:(CGRect)rect; +- (nullable LWZCollectionViewLayoutAttributes *)layoutAttributesForHeaderDecorationViewWithIndexPath:(NSIndexPath *)indexPath inRect:(CGRect)rect; +- (nullable LWZCollectionViewLayoutAttributes *)layoutAttributesForCellDecorationViewWithIndexPath:(NSIndexPath *)indexPath inRect:(CGRect)rect; +- (nullable LWZCollectionViewLayoutAttributes *)layoutAttributesForFooterDecorationViewWithIndexPath:(NSIndexPath *)indexPath inRect:(CGRect)rect; + +#pragma mark - + +- (UIEdgeInsets)edgeSpacingsForSectionAtIndex:(NSInteger)section; +- (UIEdgeInsets)contentInsetsForSectionAtIndex:(NSInteger)section; +- (UIEdgeInsets)adjustedPinnedInsetsForSectionAtIndex:(NSInteger)section; + +- (CGSize)layoutSizeToFit:(CGSize)fittingSize forHeaderInSection:(NSInteger)section scrollDirection:(UICollectionViewScrollDirection)scrollDirection; +- (CGSize)layoutSizeToFit:(CGSize)fittingSize forItemAtIndexPath:(NSIndexPath *)indexPath scrollDirection:(UICollectionViewScrollDirection)scrollDirection; +- (CGSize)layoutSizeToFit:(CGSize)fittingSize forFooterInSection:(NSInteger)section scrollDirection:(UICollectionViewScrollDirection)scrollDirection; + +- (CGFloat)minimumLineSpacingForSectionAtIndex:(NSInteger)section; +- (CGFloat)minimumInteritemSpacingForSectionAtIndex:(NSInteger)section; + +- (CGFloat)zIndexForHeaderInSection:(NSInteger)section; +- (CGFloat)zIndexForItemAtIndexPath:(NSIndexPath *)indexPath; +- (CGFloat)zIndexForFooterInSection:(NSInteger)section; +@end + +@interface LWZCollectionLayoutCollectionContentContainer (Internal) +- (instancetype)initWithCollectionSize:(CGSize)collectionSize direction:(UICollectionViewScrollDirection)direction collectionContentInsets:(UIEdgeInsets)contentInsets collectionSafeAreaInsets:(UIEdgeInsets)safeAreaInsets ignoredCollectionSafeAreaInsets:(BOOL)isIgnoredSafeAreaInsets; +@end + +@interface LWZCollectionLayoutSectionContentContainer (Internal) +- (instancetype)initWithCollectionContentContainer:(LWZCollectionLayoutCollectionContentContainer *)collectionContentContainer sectionEdgeSpacings:(UIEdgeInsets)edgeSpacings sectionContentInsets:(UIEdgeInsets)contentInsets; +@end +NS_ASSUME_NONNULL_END diff --git a/LWZComponents/LWZCollectionViewComponents/Presenter/LWZCollectionViewPresenter.h b/LWZComponents/LWZCollectionViewComponents/Presenter/LWZCollectionViewPresenter.h new file mode 100755 index 0000000..05e63c3 --- /dev/null +++ b/LWZComponents/LWZCollectionViewComponents/Presenter/LWZCollectionViewPresenter.h @@ -0,0 +1,59 @@ +// +// LWZCollectionViewPresenter.h +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2020/11/16. +// Copyright © 2020 changsanjiang@gmail.com. All rights reserved. +// + +#import "LWZCollectionProvider.h" +#import "LWZCollectionViewLayout.h" +@protocol LWZCollectionViewPresenterDelegate; + +/// 数据呈现器 +/// +/// 1. 实现了 `layout.delegate, collection.dataSource` +/// 2. 对各个视图高度做了缓存处理, 必要时可以调用`invalidateAllPresentationSizes`进行刷新 +/// +NS_ASSUME_NONNULL_BEGIN +@interface LWZCollectionViewPresenter : NSObject +- (instancetype)initWithProvider:(LWZCollectionProvider *)provider; + +@property (nonatomic, strong, nullable) __kindof LWZCollectionProvider *provider; +@property (nonatomic, weak, nullable) id delegate; +- (void)invalidateAllPresentationSizes; // 使所有视图的size在下次显示时重新计算 +- (void)refreshVisibleItemsForCollectionView:(UICollectionView *)collectionView; +@end + +@protocol LWZCollectionViewPresenterDelegate +- (UIEdgeInsets)presenter:(LWZCollectionViewPresenter *)presenter adjustedPinnedInsetsForSectionAtIndex:(NSInteger)section; +@end + +#pragma mark - hooks + +@protocol LWZCollectionViewCellHooks +@optional +- (void)willDisplayAtIndexPath:(NSIndexPath *)indexPath; +- (void)didEndDisplayingAtIndexPath:(NSIndexPath *)indexPath; +@end + + +@interface LWZCollectionViewPresenter (LWZCollectionViewDelegateProxyHookedMethods) +- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath; +- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath; +- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath; +- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath; +- (void)collectionView:(UICollectionView *)collectionView willDisplaySupplementaryView:(UICollectionReusableView *)view forElementKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath; +- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingSupplementaryView:(UICollectionReusableView *)view forElementOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath; +@end + + +@interface LWZCollectionViewPresenter (LWZCollectionUpdates) +- (void)didSelectItemAtIndexPath:(NSIndexPath *)indexPath; +- (void)didDeselectItemAtIndexPath:(NSIndexPath *)indexPath; +- (void)willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath; +- (void)didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath; +- (void)willDisplaySupplementaryView:(UICollectionReusableView *)view forElementKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath; +- (void)didEndDisplayingSupplementaryView:(UICollectionReusableView *)view forElementOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath; +@end +NS_ASSUME_NONNULL_END diff --git a/LWZComponents/LWZCollectionViewComponents/Presenter/LWZCollectionViewPresenter.m b/LWZComponents/LWZCollectionViewComponents/Presenter/LWZCollectionViewPresenter.m new file mode 100755 index 0000000..12e2c34 --- /dev/null +++ b/LWZComponents/LWZCollectionViewComponents/Presenter/LWZCollectionViewPresenter.m @@ -0,0 +1,508 @@ +// +// LWZCollectionViewPresenter.m +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2020/11/16. +// Copyright © 2020 changsanjiang@gmail.com. All rights reserved. +// + +#import "LWZCollectionViewPresenter.h" +#import "LWZCollectionViewRegister.h" +#import "LWZCollectionInternals.h" +#import + +UIKIT_STATIC_INLINE CGSize +LWZCollectionSectionHeaderFooterLayoutSize(LWZCollectionSectionHeaderFooter *_Nullable headerFooter, + LWZCollectionSection *section, + CGSize fittingSize, + NSInteger index, + UICollectionViewScrollDirection scrollDirection) { + if ( headerFooter == nil ) { + return CGSizeZero; + } + + if ( headerFooter.needsLayout ) { + headerFooter.layoutSize = [headerFooter layoutSizeThatFits:fittingSize inSection:section atIndex:index scrollDirection:scrollDirection]; +//#ifdef DEBUG +// NSLog(@"headerFooter<%p>.size: %@, .index: %ld", headerFooter, NSStringFromCGSize(headerFooter.layoutSize), (long)index); +//#endif + } + return headerFooter.layoutSize; +} + +UIKIT_STATIC_INLINE CGSize +LWZCollectionItemLayoutSize(LWZCollectionItem *item, + LWZCollectionSection *section, + CGSize fittingSize, + NSIndexPath *indexPath, + UICollectionViewScrollDirection scrollDirection) { + if ( item.needsLayout ) { + item.layoutSize = [item layoutSizeThatFits:fittingSize inSection:section atIndexPath:indexPath scrollDirection:scrollDirection]; +//#ifdef DEBUG +// NSLog(@"item<%p>.size: %@, indexPath: {%ld, %ld}", item, NSStringFromCGSize(item.layoutSize), (long)indexPath.section, (long)indexPath.item); +//#endif + } + return item.layoutSize; +} + +UIKIT_STATIC_INLINE CGRect +LWZCollectionDecorationLayoutFrame(LWZCollectionDecoration *_Nullable decoration, + CGRect rect, + NSIndexPath *indexPath) { + if ( decoration == nil ) + return CGRectZero; + + if ( decoration.needsLayout ) { + decoration.relativeRect = [decoration relativeRectToFit:rect atIndexPath:indexPath]; +//#ifdef DEBUG +// NSLog(@"decoration<%p>.frame: %@, indexPath: {%ld, %ld}", decoration, NSStringFromCGRect(decoration.relativeRect), (long)indexPath.section, (long)indexPath.item); +//#endif + } + return decoration.relativeRect; +} + +@implementation UICollectionViewCell (LWZCollectionInternalAdditions) +static void *kBindingCollectionItem = &kBindingCollectionItem; +- (void)setLwz_bindingCollectionItem:(__kindof LWZCollectionItem *)lwz_bindingCollectionItem { + objc_setAssociatedObject(self, kBindingCollectionItem, lwz_bindingCollectionItem, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} +- (nullable __kindof LWZCollectionItem *)lwz_bindingCollectionItem { + return objc_getAssociatedObject(self, kBindingCollectionItem); +} + +- (BOOL)lwz_respondsToWillDisplay { + static void *key = &key; + NSNumber *rev = objc_getAssociatedObject(self, key); + if ( rev == nil ) { + rev = @([self respondsToSelector:@selector(willDisplayAtIndexPath:)]); + objc_setAssociatedObject(self, key, rev, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + } + return [rev boolValue]; +} +- (BOOL)lwz_respondsToDidEndDisplaying { + static void *key = &key; + NSNumber *rev = objc_getAssociatedObject(self, key); + if ( rev == nil ) { + rev = @([self respondsToSelector:@selector(didEndDisplayingAtIndexPath:)]); + objc_setAssociatedObject(self, key, rev, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + } + return [rev boolValue]; +} +@end + +@implementation UICollectionReusableView (LWZCollectionInternalAdditions) +static void *kBindingHeaderFooter = &kBindingHeaderFooter; +- (void)setLwz_bindingHeaderFooter:(nullable __kindof LWZCollectionSectionHeaderFooter *)lwz_bindingHeaderFooter { + objc_setAssociatedObject(self, kBindingHeaderFooter, lwz_bindingHeaderFooter, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (nullable __kindof LWZCollectionSectionHeaderFooter *)lwz_bindingHeaderFooter { + return objc_getAssociatedObject(self, kBindingHeaderFooter); +} +@end + + +@implementation LWZCollectionViewPresenter { + LWZCollectionViewRegister *mRegister; + + struct { + unsigned adjustedPinnedInsetsForSectionAtIndex :1; + + // 当collectionView的视图信息例如frame, bounds等发生改变时, 在布局准备完成后, 需要及时的刷新当前显示的cell + // + unsigned needsRefreshVisibleItems :1; + } _mFlags; + + struct { + UIFloatRange layoutRange; + } _mBoundary; +} + +- (instancetype)initWithProvider:(LWZCollectionProvider *)provider { + self = [self init]; + _provider = provider; + return self; +} + +- (instancetype)init { + self = [super init]; + if ( self ) { + mRegister = [LWZCollectionViewRegister.alloc init]; + } + return self; +} + +- (void)setDelegate:(nullable id)delegate { + if ( delegate != _delegate ) { + _delegate = delegate; + _mFlags.adjustedPinnedInsetsForSectionAtIndex = delegate != nil && [delegate respondsToSelector:@selector(presenter:adjustedPinnedInsetsForSectionAtIndex:)]; + } +} + +- (void)invalidateAllPresentationSizes { + [_provider enumerateSectionsUsingBlock:^(__kindof LWZCollectionSection * _Nonnull section, NSInteger idx, BOOL * _Nonnull stop) { + [section setNeedsLayout]; + }]; +} + +- (void)refreshVisibleItemsForCollectionView:(UICollectionView *)collectionView { + LWZCollectionProvider *provider = _provider; + for ( NSIndexPath *indexPath in collectionView.indexPathsForVisibleItems ) { + LWZCollectionItem *item = [provider itemAtIndexPath:indexPath]; + if ( item.needsLayout ) { + UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath]; + if ( item == cell.lwz_bindingCollectionItem ) { + [self willDisplayCell:[collectionView cellForItemAtIndexPath:indexPath] forItemAtIndexPath:indexPath]; + } + } + + LWZCollectionSection *section = [provider sectionAtIndex:indexPath.section]; + LWZCollectionSectionHeaderFooter *header = section.header; + if ( header.needsLayout ) { + NSIndexPath *headerIndexPath = [NSIndexPath indexPathForItem:0 inSection:indexPath.section]; + UICollectionReusableView *view = [collectionView supplementaryViewForElementKind:UICollectionElementKindSectionHeader atIndexPath:headerIndexPath]; + if ( view != nil && header == view.lwz_bindingHeaderFooter ) { + [self willDisplaySupplementaryView:view forElementKind:UICollectionElementKindSectionHeader atIndexPath:headerIndexPath]; + } + } + + LWZCollectionSectionHeaderFooter *footer = section.footer; + if ( footer.needsLayout ) { + NSIndexPath *footerIndexPath = [NSIndexPath indexPathForItem:0 inSection:indexPath.section]; + UICollectionReusableView *view = [collectionView supplementaryViewForElementKind:UICollectionElementKindSectionFooter atIndexPath:footerIndexPath]; + if ( view != nil && footer == view.lwz_bindingHeaderFooter ) { + [self willDisplaySupplementaryView:view forElementKind:UICollectionElementKindSectionFooter atIndexPath:footerIndexPath]; + } + } + } +} + +#pragma mark - mark + +- (void)didSelectItemAtIndexPath:(NSIndexPath *)indexPath { + LWZCollectionItem *item = [_provider itemAtIndexPath:indexPath]; + if ( item.tapHandler != nil ) { + item.tapHandler(item, indexPath); + } + + [item didSelectAtIndexPath:indexPath]; + + LWZCollectionSection *section = [_provider sectionAtIndex:indexPath.section]; + [section didSelectItemAtIndexPath:indexPath]; +} +- (void)didDeselectItemAtIndexPath:(NSIndexPath *)indexPath { + LWZCollectionItem *item = [_provider itemAtIndexPath:indexPath]; + [item didDeselectAtIndexPath:indexPath]; + + LWZCollectionSection *section = [_provider sectionAtIndex:indexPath.section]; + [section didDeselectItemAtIndexPath:indexPath]; +} +- (void)willDisplayCell:(__kindof UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath { + LWZCollectionItem *item = [_provider itemAtIndexPath:indexPath]; + /// 为防止数据已发生改变, 此处需要做一层相同判断 + if ( cell.class == item.cellClass ) { + cell.lwz_bindingCollectionItem = item; + [item willDisplayCell:cell forItemAtIndexPath:indexPath]; + + if ( [cell lwz_respondsToWillDisplay] ) { + [(id)cell willDisplayAtIndexPath:indexPath]; + } + + LWZCollectionSection *section = [_provider sectionAtIndex:indexPath.section]; + [section didBindCellForItemAtIndexPath:indexPath]; + } +} +- (void)didEndDisplayingCell:(__kindof UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath { + LWZCollectionItem *item = [_provider itemAtIndexPath:indexPath]; + /// dataSource 的数据随时可能发生变化(这会出当前 item 与 cell绑定的 item 不一致的情况), 为防止数据已发生改变, 此处需要做一层相同判断 + /// + if ( cell.lwz_bindingCollectionItem == item ) { + [item didEndDisplayingCell:cell forItemAtIndexPath:indexPath]; + + if ( [cell lwz_respondsToDidEndDisplaying] ) { + [(id)cell didEndDisplayingAtIndexPath:indexPath]; + } + + LWZCollectionSection *section = [_provider sectionAtIndex:indexPath.section]; + [section didUnbindCellForItemAtIndexPath:indexPath]; + + cell.lwz_bindingCollectionItem = nil; + } +} +- (void)willDisplaySupplementaryView:(__kindof UICollectionReusableView *)view forElementKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath { + LWZCollectionSectionHeaderFooter *headerFooter = nil; + if ( elementKind == UICollectionElementKindSectionHeader ) { + headerFooter = [_provider headerForSectionAtIndex:indexPath.section]; + } + else if ( elementKind == UICollectionElementKindSectionFooter ) { + headerFooter = [_provider footerForSectionAtIndex:indexPath.section]; + } + /// 为防止数据已发生改变, 此处需要做一层相同判断 + if ( headerFooter != nil && view.class == headerFooter.viewClass ) { + view.lwz_bindingHeaderFooter = headerFooter; + [headerFooter willDisplaySupplementaryView:view forElementKind:elementKind atIndexPath:indexPath]; + } +} +- (void)didEndDisplayingSupplementaryView:(__kindof UICollectionReusableView *)view forElementOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath { + LWZCollectionSectionHeaderFooter *headerFooter = nil; + if ( elementKind == UICollectionElementKindSectionHeader ) { + headerFooter = [_provider headerForSectionAtIndex:indexPath.section]; + } + else if ( elementKind == UICollectionElementKindSectionFooter ) { + headerFooter = [_provider footerForSectionAtIndex:indexPath.section]; + } + + /// 为防止数据已发生改变, 此处需要做一层相同判断 + if ( headerFooter != nil && view.lwz_bindingHeaderFooter == headerFooter ) { + [headerFooter didEndDisplayingSupplementaryView:view forElementOfKind:elementKind atIndexPath:indexPath]; + view.lwz_bindingHeaderFooter = nil; + } +} + +#pragma mark - UICollectionViewDataSource + +#ifdef DEBUG + +//- (BOOL)collectionView:(UICollectionView *)collectionView canMoveItemAtIndexPath:(NSIndexPath *)indexPath { +// return YES; +//} +// +//- (void)collectionView:(UICollectionView *)collectionView moveItemAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath { +//#ifdef DEBUG +// NSLog(@"%d : %s", __LINE__, sel_getName(_cmd)); +//#endif +// +//} + +#endif + +- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { + return _provider.numberOfSections; +} + +- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { + return [_provider isSectionHiddenAtIndex:section] ? 0 : [_provider sectionAtIndex:section].numberOfItems; +} + +- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { + LWZCollectionSectionHeaderFooter *headerFooter = kind == UICollectionElementKindSectionHeader ? [_provider headerForSectionAtIndex:indexPath.section] : [_provider footerForSectionAtIndex:indexPath.section]; + return [mRegister collectionView:collectionView dequeueReusableHeaderFooterViewWithHeaderFooter:headerFooter kind:kind indexPath:indexPath]; +} + +- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { + return [mRegister collectionView:collectionView dequeueReusableCellWithItem:[_provider itemAtIndexPath:indexPath] forIndexPath:indexPath]; +} + +#pragma mark - LWZCollectionViewDelegate + +- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { + [self didSelectItemAtIndexPath:indexPath]; +} + +- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath { + [self didDeselectItemAtIndexPath:indexPath]; +} + +- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath { + [self willDisplayCell:cell forItemAtIndexPath:indexPath]; +} + +- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath { + [self didEndDisplayingCell:cell forItemAtIndexPath:indexPath]; +} + +- (void)collectionView:(UICollectionView *)collectionView willDisplaySupplementaryView:(UICollectionReusableView *)view forElementKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath { + [self willDisplaySupplementaryView:view forElementKind:elementKind atIndexPath:indexPath]; +} + +- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingSupplementaryView:(UICollectionReusableView *)view forElementOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath { + [self didEndDisplayingSupplementaryView:view forElementOfKind:elementKind atIndexPath:indexPath]; +} + +#pragma mark - LWZCollectionViewLayoutDelegate + +- (void)layout:(__kindof LWZCollectionViewLayout *)layout willPrepareLayoutInContainer:(LWZCollectionLayoutCollectionContentContainer *)container { + UIFloatRange layoutRange = container.layoutRange; + if ( !UIFloatRangeIsEqualToRange(layoutRange, _mBoundary.layoutRange) ) { + _mFlags.needsRefreshVisibleItems = YES; + _mBoundary.layoutRange = layoutRange; + [self invalidateAllPresentationSizes]; + } +} + +- (void)layout:(__kindof LWZCollectionViewLayout *)layout didFinishPreparingInContainer:(LWZCollectionLayoutCollectionContentContainer *)container { + if ( _mFlags.needsRefreshVisibleItems ) { + _mFlags.needsRefreshVisibleItems = NO; + [self refreshVisibleItemsForCollectionView:layout.collectionView]; + } +} + +- (UIEdgeInsets)layout:(__kindof LWZCollectionViewLayout *)layout edgeSpacingsForSectionAtIndex:(NSInteger)section { + return [_provider isSectionHiddenAtIndex:section] ? UIEdgeInsetsZero : [_provider sectionAtIndex:section].edgeSpacings; +} + +- (UIEdgeInsets)layout:(__kindof LWZCollectionViewLayout *)layout contentInsetsForSectionAtIndex:(NSInteger)section { + return [_provider isSectionHiddenAtIndex:section] ? UIEdgeInsetsZero : [_provider sectionAtIndex:section].contentInsets; +} + +- (BOOL)layout:(__kindof LWZCollectionViewLayout *)layout canPinToVisibleBoundsForHeaderInSection:(NSInteger)section { + return [_provider sectionAtIndex:section].canPinToVisibleBoundsForHeader; +} + +- (UIEdgeInsets)layout:(__kindof LWZCollectionViewLayout *)layout adjustedPinnedInsetsForSectionAtIndex:(NSInteger)section { + if ( _mFlags.adjustedPinnedInsetsForSectionAtIndex ) { + return [_delegate presenter:self adjustedPinnedInsetsForSectionAtIndex:section]; + } + return layout.adjustedPinnedInsets; +} + +- (CGSize)layout:(__kindof LWZCollectionViewLayout *)layout layoutSizeToFit:(CGSize)fittingSize forHeaderInSection:(NSInteger)index scrollDirection:(UICollectionViewScrollDirection)scrollDirection { + return [_provider isSectionHiddenAtIndex:index] ? CGSizeZero : LWZCollectionSectionHeaderFooterLayoutSize([_provider headerForSectionAtIndex:index], [_provider sectionAtIndex:index], fittingSize, index, scrollDirection); +} +- (CGSize)layout:(__kindof LWZCollectionViewLayout *)layout layoutSizeToFit:(CGSize)fittingSize forItemAtIndexPath:(NSIndexPath *)indexPath scrollDirection:(UICollectionViewScrollDirection)scrollDirection { + return [_provider isSectionHiddenAtIndex:indexPath.section] ? CGSizeZero : LWZCollectionItemLayoutSize([_provider itemAtIndexPath:indexPath], [_provider sectionAtIndex:indexPath.section], fittingSize, indexPath, scrollDirection); +} +- (CGSize)layout:(__kindof LWZCollectionViewLayout *)layout layoutSizeToFit:(CGSize)fittingSize forFooterInSection:(NSInteger)index scrollDirection:(UICollectionViewScrollDirection)scrollDirection { + return [_provider isSectionHiddenAtIndex:index] ? CGSizeZero : LWZCollectionSectionHeaderFooterLayoutSize([_provider footerForSectionAtIndex:index], [_provider sectionAtIndex:index], fittingSize, index, scrollDirection); +} + +- (CGFloat)layout:(__kindof LWZCollectionViewLayout *)layout minimumLineSpacingForSectionAtIndex:(NSInteger)section { + return [_provider sectionAtIndex:section].minimumLineSpacing; +} +- (CGFloat)layout:(__kindof LWZCollectionViewLayout *)layout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section { + return [_provider sectionAtIndex:section].minimumInteritemSpacing; +} + +- (nullable NSString *)layout:(__kindof LWZCollectionViewLayout *)layout elementKindForSectionDecorationAtIndexPath:(NSIndexPath *)indexPath { + if ( [_provider isSectionHiddenAtIndex:indexPath.section] ) return nil; + LWZCollectionDecoration *decoration = [self _sectionDecorationAtIndex:indexPath.section]; + return [mRegister layout:layout registerDecoration:decoration]; +} +- (nullable NSString *)layout:(__kindof LWZCollectionViewLayout *)layout elementKindForHeaderDecorationAtIndexPath:(NSIndexPath *)indexPath { + if ( [_provider isSectionHiddenAtIndex:indexPath.section] ) return nil; + LWZCollectionDecoration *decoration = [self _sectionHeaderDecorationAtIndex:indexPath.section]; + return [mRegister layout:layout registerDecoration:decoration]; +} +- (nullable NSString *)layout:(__kindof LWZCollectionViewLayout *)layout elementKindForItemDecorationAtIndexPath:(NSIndexPath *)indexPath { + if ( [_provider isSectionHiddenAtIndex:indexPath.section] ) return nil; + LWZCollectionDecoration *decoration = [self _itemDecorationAtIndexPath:indexPath]; + return [mRegister layout:layout registerDecoration:decoration]; +} +- (nullable NSString *)layout:(__kindof LWZCollectionViewLayout *)layout elementKindForFooterDecorationAtIndexPath:(NSIndexPath *)indexPath { + if ( [_provider isSectionHiddenAtIndex:indexPath.section] ) return nil; + LWZCollectionDecoration *decoration = [self _sectionFooterDecorationAtIndex:indexPath.section]; + return [mRegister layout:layout registerDecoration:decoration]; +} + +- (nullable id)layout:(__kindof LWZCollectionViewLayout *)layout userInfoForSectionDecorationAtIndexPath:(NSIndexPath *)indexPath { + return [self _sectionDecorationAtIndex:indexPath.section].userInfo; +} +- (nullable id)layout:(__kindof LWZCollectionViewLayout *)layout userInfoForHeaderDecorationAtIndexPath:(NSIndexPath *)indexPath { + return [self _sectionHeaderDecorationAtIndex:indexPath.section].userInfo; +} +- (nullable id)layout:(__kindof LWZCollectionViewLayout *)layout userInfoForItemDecorationAtIndexPath:(NSIndexPath *)indexPath { + return [self _itemDecorationAtIndexPath:indexPath].userInfo; +} +- (nullable id)layout:(__kindof LWZCollectionViewLayout *)layout userInfoForFooterDecorationAtIndexPath:(NSIndexPath *)indexPath { + return [self _sectionFooterDecorationAtIndex:indexPath.section].userInfo; +} + +- (CGRect)layout:(__kindof LWZCollectionViewLayout *)layout relativeRectToFit:(CGRect)rect forSectionDecorationAtIndexPath:(NSIndexPath *)indexPath { + return LWZCollectionDecorationLayoutFrame([self _sectionDecorationAtIndex:indexPath.section], rect, indexPath); +} +- (CGRect)layout:(__kindof LWZCollectionViewLayout *)layout relativeRectToFit:(CGRect)rect forHeaderDecorationAtIndexPath:(NSIndexPath *)indexPath { + return LWZCollectionDecorationLayoutFrame([self _sectionHeaderDecorationAtIndex:indexPath.section], rect, indexPath); +} +- (CGRect)layout:(__kindof LWZCollectionViewLayout *)layout relativeRectToFit:(CGRect)rect forItemDecorationAtIndexPath:(NSIndexPath *)indexPath { + return LWZCollectionDecorationLayoutFrame([self _itemDecorationAtIndexPath:indexPath], rect, indexPath); +} +- (CGRect)layout:(__kindof LWZCollectionViewLayout *)layout relativeRectToFit:(CGRect)rect forFooterDecorationAtIndexPath:(NSIndexPath *)indexPath { + return LWZCollectionDecorationLayoutFrame([self _sectionFooterDecorationAtIndex:indexPath.section], rect, indexPath); +} + +- (NSInteger)layout:(__kindof LWZCollectionViewLayout *)layout zIndexForHeaderInSection:(NSInteger)section { + return [_provider headerForSectionAtIndex:section].zPosition; +} +- (NSInteger)layout:(__kindof LWZCollectionViewLayout *)layout zIndexForItemAtIndexPath:(NSIndexPath *)indexPath { + return [_provider itemAtIndexPath:indexPath].zPosition; +} +- (NSInteger)layout:(__kindof LWZCollectionViewLayout *)layout zIndexForFooterInSection:(NSInteger)section { + return [_provider footerForSectionAtIndex:section].zPosition; +} + +- (NSInteger)layout:(__kindof LWZCollectionViewLayout *)layout zIndexForSectionDecorationAtIndexPath:(NSIndexPath *)indexPath { + return [self _sectionDecorationAtIndex:indexPath.section].zPosition; +} +- (NSInteger)layout:(__kindof LWZCollectionViewLayout *)layout zIndexForHeaderDecorationAtIndexPath:(NSIndexPath *)indexPath { + return [self _sectionHeaderDecorationAtIndex:indexPath.section].zPosition; +} +- (NSInteger)layout:(__kindof LWZCollectionViewLayout *)layout zIndexForItemDecorationAtIndexPath:(NSIndexPath *)indexPath { + return [self _itemDecorationAtIndexPath:indexPath].zPosition; +} +- (NSInteger)layout:(__kindof LWZCollectionViewLayout *)layout zIndexForFooterDecorationAtIndexPath:(NSIndexPath *)indexPath { + return [self _sectionFooterDecorationAtIndex:indexPath.section].zPosition; +} + +#pragma mark - LWZCollectionWeightLayoutDelegate + +- (CGFloat)layout:(LWZCollectionWeightLayout *)layout weightForItemAtIndexPath:(NSIndexPath *)indexPath { + return [_provider itemAtIndexPath:indexPath].weight; +} + +#pragma mark - LWZCollectionListLayoutDelegate + +- (LWZCollectionLayoutAlignment)layout:(__kindof LWZCollectionViewLayout *)layout layoutAlignmentForItemAtIndexPath:(NSIndexPath *)indexPath { + return [_provider itemAtIndexPath:indexPath].layoutAlignment; +} + +#pragma mark - LWZCollectionWaterfallFlowLayoutDelegate + +- (NSInteger)layout:(LWZCollectionWaterfallFlowLayout *)layout numberOfArrangedItemsPerLineInSection:(NSInteger)index { + return [_provider sectionAtIndex:index].numberOfArrangedItemsPerLine; +} + +#pragma mark - LWZCollectionTemplateLayoutDelegate + +- (NSArray *)layout:(__kindof LWZCollectionViewLayout *)layout layoutTemplateContainerGroupsInSection:(NSInteger)index { + return [_provider sectionAtIndex:index].layoutTemplateContainerGroups; +} + +#pragma mark - LWZCollectionHybridLayoutDelegate + +- (LWZCollectionLayoutType)layout:(__kindof LWZCollectionViewLayout *)layout layoutTypeForItemsInSection:(NSInteger)index { + return [_provider sectionAtIndex:index].layoutType; +} + +#pragma mark - LWZCollectionCompositionalLayoutDelegate + +- (BOOL)layout:(__kindof LWZCollectionViewLayout *)layout isOrthogonalScrollingInSection:(NSInteger)index { + return [_provider sectionAtIndex:index].isOrthogonalScrolling; +} + +- (CGSize)layout:(__kindof LWZCollectionViewLayout *)layout layoutSizeToFit:(CGSize)fittingSize forOrthogonalContentInSection:(NSInteger)index scrollDirection:(UICollectionViewScrollDirection)scrollDirection { + return [[_provider sectionAtIndex:index] layoutSizeThatFits:fittingSize forOrthogonalContentAtIndex:index scrollDirection:scrollDirection]; +} + +- (LWZCollectionLayoutContentOrthogonalScrollingBehavior)layout:(__kindof LWZCollectionViewLayout *)layout orthogonalContentScrollingBehaviorInSection:(NSInteger)index { + return [_provider sectionAtIndex:index].orthogonalScrollingBehavior; +} + +#pragma mark - mark + +- (nullable LWZCollectionDecoration *)_sectionDecorationAtIndex:(NSInteger)index { + return [_provider sectionAtIndex:index].decoration; +} + +- (nullable LWZCollectionDecoration *)_sectionHeaderDecorationAtIndex:(NSInteger)index { + return [_provider headerForSectionAtIndex:index].decoration; +} + +- (nullable LWZCollectionDecoration *)_itemDecorationAtIndexPath:(NSIndexPath *)indexPath { + return [_provider itemAtIndexPath:indexPath].decoration; +} + +- (nullable LWZCollectionDecoration *)_sectionFooterDecorationAtIndex:(NSInteger)index { + return [_provider footerForSectionAtIndex:index].decoration; +} + +@end diff --git a/LWZComponents/LWZCollectionViewComponents/Presenter/LWZCollectionViewRegister.h b/LWZComponents/LWZCollectionViewComponents/Presenter/LWZCollectionViewRegister.h new file mode 100755 index 0000000..f93b3db --- /dev/null +++ b/LWZComponents/LWZCollectionViewComponents/Presenter/LWZCollectionViewRegister.h @@ -0,0 +1,24 @@ +// +// LWZCollectionViewRegister.h +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2020/12/7. +// Copyright © 2020 changsanjiang@gmail.com. All rights reserved. +// + +#import "LWZCollectionSectionHeaderFooter.h" +#import "LWZCollectionDecoration.h" +#import "LWZCollectionItem.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface LWZCollectionViewRegister : NSObject + +- (nullable __kindof UICollectionReusableView *)collectionView:(UICollectionView *)collectionView dequeueReusableHeaderFooterViewWithHeaderFooter:(LWZCollectionSectionHeaderFooter *)headerFooter kind:(NSString *)kind indexPath:(NSIndexPath *)indexPath; + +- (nullable __kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView dequeueReusableCellWithItem:(LWZCollectionItem *)item forIndexPath:(NSIndexPath *)indexPath; + +- (nullable NSString *)layout:(UICollectionViewLayout *)layout registerDecoration:(LWZCollectionDecoration *)decoration; +@end + +NS_ASSUME_NONNULL_END diff --git a/LWZComponents/LWZCollectionViewComponents/Presenter/LWZCollectionViewRegister.m b/LWZComponents/LWZCollectionViewComponents/Presenter/LWZCollectionViewRegister.m new file mode 100755 index 0000000..ee6fac6 --- /dev/null +++ b/LWZComponents/LWZCollectionViewComponents/Presenter/LWZCollectionViewRegister.m @@ -0,0 +1,136 @@ +// +// LWZCollectionViewRegister.m +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2020/12/7. +// Copyright © 2020 changsanjiang@gmail.com. All rights reserved. +// + +#import "LWZCollectionViewRegister.h" +#import + +@interface UICollectionView (LWZCollectionViewRegisterAdditions) +@property (nonatomic, strong, readonly) NSMutableSet *lwz_registeredClasses; +@property (nonatomic, strong, readonly) NSMutableSet *lwz_registeredClassesForFooter; +@end + +@implementation UICollectionView (LWZCollectionViewRegisterAdditions) +// header or cell +- (NSMutableSet *)lwz_registeredClasses { + NSMutableSet *set = objc_getAssociatedObject(self, _cmd); + if ( set == nil ) { + set = NSMutableSet.set; + objc_setAssociatedObject(self, _cmd, set, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + } + return set; +} + +// footer +- (NSMutableSet *)lwz_registeredClassesForFooter { + NSMutableSet *set = objc_getAssociatedObject(self, _cmd); + if ( set == nil ) { + set = NSMutableSet.set; + objc_setAssociatedObject(self, _cmd, set, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + } + return set; +} + +@end + +@interface UICollectionViewLayout (LWZCollectionViewRegisterAdditions) + +@end + +@implementation UICollectionViewLayout (LWZCollectionViewRegisterAdditions) +// decoration +- (NSMutableSet *)lwz_registeredDecorations { + NSMutableSet *set = objc_getAssociatedObject(self, _cmd); + if ( set == nil ) { + set = NSMutableSet.set; + objc_setAssociatedObject(self, _cmd, set, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + } + return set; +} +@end + +@interface LWZCollectionViewRegister () + +@end + +@implementation LWZCollectionViewRegister + +- (nullable __kindof UICollectionReusableView *)collectionView:(UICollectionView *)collectionView dequeueReusableHeaderFooterViewWithHeaderFooter:(LWZCollectionSectionHeaderFooter *)headerFooter kind:(NSString *)kind indexPath:(NSIndexPath *)indexPath { + if ( headerFooter == nil ) + return nil; + + Class cls = headerFooter.viewClass; + NSParameterAssert([(id)cls isKindOfClass:object_getClass(UICollectionReusableView.class)]); + + // register if needed + NSString *name = NSStringFromClass(cls); + NSString *identifier = name; + NSMutableSet *classes = kind == UICollectionElementKindSectionHeader ? [collectionView lwz_registeredClasses] : [collectionView lwz_registeredClassesForFooter]; + if ( ![classes containsObject:cls] ) { + [classes addObject:cls]; + + NSBundle *nibBundle = [headerFooter respondsToSelector:@selector(viewNibBundle)] ? [headerFooter viewNibBundle] : nil; + UINib *nib = nibBundle != nil ? [UINib nibWithNibName:name bundle:nibBundle] : ([headerFooter respondsToSelector:@selector(viewNib)] ? headerFooter.viewNib : nil); + + nib == nil ? [collectionView registerClass:cls forSupplementaryViewOfKind:kind withReuseIdentifier:identifier] : + [collectionView registerNib:nib forSupplementaryViewOfKind:kind withReuseIdentifier:identifier]; + } + + // dequeue + return [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:identifier forIndexPath:indexPath]; +} + +- (nullable __kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView dequeueReusableCellWithItem:(LWZCollectionItem *)item forIndexPath:(NSIndexPath *)indexPath { + if ( item == nil ) { + NSParameterAssert(item != nil); + return nil; + } + + Class cls = item.cellClass; + NSParameterAssert([(id)cls isKindOfClass:object_getClass(UICollectionViewCell.class)]); + + // register if needed + NSString *name = NSStringFromClass(cls); + NSString *identifier = name; + if ( ![collectionView.lwz_registeredClasses containsObject:cls] ) { + [collectionView.lwz_registeredClasses addObject:cls]; + NSBundle *nibBundle = [item respondsToSelector:@selector(cellNibBundle)] ? item.cellNibBundle : nil; + UINib *nib = nibBundle != nil ? [UINib nibWithNibName:name bundle:nibBundle] : ([item respondsToSelector:@selector(cellNib)] ? item.cellNib : nil); + nib == nil ? [collectionView registerClass:cls forCellWithReuseIdentifier:identifier] : + [collectionView registerNib:nib forCellWithReuseIdentifier:identifier]; + } + + // dequeue + return [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath]; +} + +/// 返回 reuseIdentifier +/// +- (nullable NSString *)layout:(UICollectionViewLayout *)layout registerDecoration:(LWZCollectionDecoration *)decoration { + if ( decoration == nil ) + return nil; + + Class viewClass = decoration.viewClass; + NSParameterAssert([(id)viewClass isKindOfClass:object_getClass(UICollectionReusableView.class)]); + + // register if needed + Class cls = decoration.class; + NSString *identifier = NSStringFromClass(cls); + NSMutableSet *classes = [layout lwz_registeredDecorations]; + if ( ![classes containsObject:cls] ) { + [classes addObject:cls]; + + NSString *viewName = NSStringFromClass(viewClass); + NSBundle *nibBundle = [decoration respondsToSelector:@selector(viewNibBundle)] ? [decoration viewNibBundle] : nil; + UINib *viewNib = nibBundle != nil ? [UINib nibWithNibName:viewName bundle:nibBundle] : ([decoration respondsToSelector:@selector(viewNib)] ? decoration.viewNib : nil); + + viewNib == nil ? [layout registerClass:viewClass forDecorationViewOfKind:identifier] : + [layout registerNib:viewNib forDecorationViewOfKind:identifier]; + } + return identifier; +} +@end diff --git a/LWZComponents/LWZCollectionViewComponents/Provider/LWZCollectionDecoration.h b/LWZComponents/LWZCollectionViewComponents/Provider/LWZCollectionDecoration.h new file mode 100755 index 0000000..1bce324 --- /dev/null +++ b/LWZComponents/LWZCollectionViewComponents/Provider/LWZCollectionDecoration.h @@ -0,0 +1,98 @@ +// +// LWZCollectionDecoration.h +// LWZFoundation +// +// Created by 畅三江 on 2021/2/22. +// + +#import + +NS_ASSUME_NONNULL_BEGIN +@interface LWZCollectionDecoration : NSObject +@property (nonatomic, readonly) Class viewClass; +@property (nonatomic, readonly, nullable) NSBundle *viewNibBundle; +@property (nonatomic, readonly, nullable) UINib *viewNib; +@property (nonatomic, readonly) BOOL needsLayout; + +- (void)setNeedsLayout NS_REQUIRES_SUPER; +- (CGRect)relativeRectThatFits:(CGRect)rect atIndexPath:(NSIndexPath *)indexPath; + +@property (nonatomic) NSInteger zPosition; +@end + +#pragma mark - separator + +typedef NS_ENUM(NSUInteger, LWZCollectionSeparatorLayoutPosition) { + LWZCollectionSeparatorLayoutPositionBottom, + LWZCollectionSeparatorLayoutPositionTop, +}; + +/// 分割线装饰. +/// +/// - section: LWZCollectionSectionSeparatorDecoration +/// - header: LWZCollectionHeaderSeparatorDecoration +/// - item: LWZCollectionItemSeparatorDecoration +/// - footer: LWZCollectionFooterSeparatorDecoration +/// +@interface LWZCollectionSeparatorDecoration : LWZCollectionDecoration +- (instancetype)initWithColor:(UIColor *)color height:(CGFloat)height; +@property (nonatomic) NSInteger zPosition; // default is 10; + +@property (nonatomic) LWZCollectionSeparatorLayoutPosition position; // .bottom +@property (nonatomic, strong, nullable) UIColor *color; +@property (nonatomic) UIEdgeInsets contentInsets; +@property (nonatomic) CGFloat height; +@end + +/// Section +@interface LWZCollectionSectionSeparatorDecoration : LWZCollectionSeparatorDecoration + +@end + +/// Header +@interface LWZCollectionHeaderSeparatorDecoration : LWZCollectionSeparatorDecoration + +@end + +/// Item +@interface LWZCollectionItemSeparatorDecoration : LWZCollectionSeparatorDecoration + +@end + +/// Footer +@interface LWZCollectionFooterSeparatorDecoration : LWZCollectionSeparatorDecoration + +@end + +#pragma mark - background + +@interface LWZCollectionBackgroundDecoration : LWZCollectionDecoration +- (instancetype)initWithBackgroundColor:(UIColor *)backgroundColor; +@property (nonatomic, strong, nullable) UIColor *backgroundColor; +@property (nonatomic) NSInteger zPosition; // default is -2; +@property (nonatomic) UIEdgeInsets contentInsets; +@property (nonatomic) CGFloat cornerRadius; +@property (nonatomic, strong, nullable) UIColor *borderColor; +@property (nonatomic) CGFloat borderWidth; +@end + +/// Section +@interface LWZCollectionSectionBackgroundDecoration : LWZCollectionBackgroundDecoration + +@end + +/// Header +@interface LWZCollectionHeaderBackgroundDecoration : LWZCollectionBackgroundDecoration + +@end + +/// Item +@interface LWZCollectionItemBackgroundDecoration : LWZCollectionBackgroundDecoration + +@end + +/// Footer +@interface LWZCollectionFooterBackgroundDecoration : LWZCollectionBackgroundDecoration + +@end +NS_ASSUME_NONNULL_END diff --git a/LWZComponents/LWZCollectionViewComponents/Provider/LWZCollectionDecoration.m b/LWZComponents/LWZCollectionViewComponents/Provider/LWZCollectionDecoration.m new file mode 100755 index 0000000..ab1fb87 --- /dev/null +++ b/LWZComponents/LWZCollectionViewComponents/Provider/LWZCollectionDecoration.m @@ -0,0 +1,236 @@ +// +// LWZCollectionDecoration.m +// LWZFoundation +// +// Created by 畅三江 on 2021/2/22. +// + +#import "LWZCollectionDecoration.h" +#import "LWZCollectionDefines.h" + +@interface LWZCollectionDecoration () +@property (nonatomic, readonly) id userInfo; +@property (nonatomic) CGRect relativeRect; +@end + +@implementation LWZCollectionDecoration { + BOOL _needsLayout; +} + +- (instancetype)init { + self = [super init]; + if (self) { + _zPosition = LWZCollectionDecorationDefaultZPosition; + _needsLayout = YES; + } + return self; +} + +- (Class)viewClass { + @throw [NSException exceptionWithName:NSInternalInconsistencyException + reason:[NSString stringWithFormat:@"You must override %@ in a subclass.", NSStringFromSelector(_cmd)] + userInfo:nil]; +} + +- (BOOL)needsLayout { + return _needsLayout; +} + +- (void)setNeedsLayout { + _needsLayout = YES; +} + +- (CGRect)relativeRectThatFits:(CGRect)rect atIndexPath:(NSIndexPath *)indexPath { + @throw [NSException exceptionWithName:NSInternalInconsistencyException + reason:[NSString stringWithFormat:@"You must override %@ in a subclass.", NSStringFromSelector(_cmd)] + userInfo:nil]; +} + +- (CGRect)relativeRectToFit:(CGRect)rect atIndexPath:(NSIndexPath *)indexPath { + _needsLayout = NO; + return [self relativeRectThatFits:rect atIndexPath:indexPath]; +} + +// LWZCollectionViewLayoutAttributes.decorationUserInfo +- (id)userInfo { + return self; +} +@end + + +#import "LWZCollectionViewLayoutAttributes.h" + +@interface LWZCollectionSeparatorDecorationView : UICollectionReusableView +@property (nonatomic, strong) UIView *contentView; +@end + +@implementation LWZCollectionSeparatorDecorationView +- (instancetype)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if ( self ) { + _contentView = [UIView.alloc initWithFrame:CGRectZero]; + [self addSubview:_contentView]; + _contentView.backgroundColor = UIColor.whiteColor; + } + return self; +} + +- (void)layoutSubviews { + [super layoutSubviews]; + _contentView.frame = self.bounds; +} + +- (void)applyLayoutAttributes:(LWZCollectionViewLayoutAttributes *)layoutAttributes { + [super applyLayoutAttributes:layoutAttributes]; + + LWZCollectionSeparatorDecoration *separator = layoutAttributes.decorationUserInfo; + if ( separator == nil ) return; + + _contentView.backgroundColor = separator.color; + self.layer.zPosition = separator.zPosition; +} +@end + +@implementation LWZCollectionSeparatorDecoration +@dynamic zPosition; + +- (instancetype)initWithColor:(UIColor *)color height:(CGFloat)height { + self = [self init]; + _color = color; + _height = height; + return self; +} + +- (instancetype)init { + self = [super init]; + if (self) { + self.zPosition = LWZCollectionDecorationSeparatorZPosition; + } + return self; +} + +- (Class)viewClass { + return LWZCollectionSeparatorDecorationView.class; +} + +- (CGRect)relativeRectThatFits:(CGRect)rect atIndexPath:(NSIndexPath *)indexPath { + CGRect frame = rect; + frame.size.height = _height; + frame.origin.x += _contentInsets.left; + frame.size.width -= _contentInsets.left + _contentInsets.right; + switch ( _position ) { + case LWZCollectionSeparatorLayoutPositionBottom: { + frame.origin.y = CGRectGetMaxY(rect) - _height - _contentInsets.bottom; + } + break; + case LWZCollectionSeparatorLayoutPositionTop: { + frame.origin.y = CGRectGetMinY(rect) + _contentInsets.top; + } + break; + } + return frame; +} +@end + +@implementation LWZCollectionSectionSeparatorDecoration + +@end + +@implementation LWZCollectionHeaderSeparatorDecoration + +@end + +@implementation LWZCollectionItemSeparatorDecoration + +@end + +@implementation LWZCollectionFooterSeparatorDecoration + +@end + + +#pragma mark - mark + +@interface LWZCollectionBackgroundDecorationView : UICollectionReusableView +@property (nonatomic, strong) UIView *contentView; +@end + +@implementation LWZCollectionBackgroundDecorationView +- (instancetype)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if ( self ) { + _contentView = [UIView.alloc initWithFrame:CGRectZero]; + _contentView.clipsToBounds = YES; + [self addSubview:_contentView]; + } + return self; +} + +- (void)layoutSubviews { + [super layoutSubviews]; + _contentView.frame = self.bounds; +} + +- (void)applyLayoutAttributes:(LWZCollectionViewLayoutAttributes *)layoutAttributes { + [super applyLayoutAttributes:layoutAttributes]; + + LWZCollectionBackgroundDecoration *decoration = layoutAttributes.decorationUserInfo; + if ( decoration == nil ) return; + + _contentView.backgroundColor = decoration.backgroundColor; + _contentView.layer.cornerRadius = decoration.cornerRadius; + _contentView.layer.borderColor = decoration.borderColor.CGColor; + _contentView.layer.borderWidth = decoration.borderWidth; + + self.layer.zPosition = decoration.zPosition; +} +@end + + +@interface LWZCollectionBackgroundDecoration () + +@end + +@implementation LWZCollectionBackgroundDecoration +@dynamic zPosition; + + +- (instancetype)initWithBackgroundColor:(UIColor *)backgroundColor { + self = [self init]; + _backgroundColor = backgroundColor; + return self; +} + +- (instancetype)init { + self = [super init]; + if (self) { + self.zPosition = LWZCollectionDecorationDefaultZPosition; + } + return self; +} + +- (Class)viewClass { + return LWZCollectionBackgroundDecorationView.class; +} + +- (CGRect)relativeRectThatFits:(CGRect)rect atIndexPath:(NSIndexPath *)indexPath { + return UIEdgeInsetsInsetRect(rect, _contentInsets); +} +@end + +@implementation LWZCollectionSectionBackgroundDecoration + +@end + +@implementation LWZCollectionHeaderBackgroundDecoration + +@end + +@implementation LWZCollectionItemBackgroundDecoration + +@end + +@implementation LWZCollectionFooterBackgroundDecoration + +@end + diff --git a/LWZComponents/LWZCollectionViewComponents/Provider/LWZCollectionInternals.h b/LWZComponents/LWZCollectionViewComponents/Provider/LWZCollectionInternals.h new file mode 100644 index 0000000..d6658a1 --- /dev/null +++ b/LWZComponents/LWZCollectionViewComponents/Provider/LWZCollectionInternals.h @@ -0,0 +1,42 @@ +// +// LWZCollectionInternals.h +// LWZAppComponents +// +// Created by changsanjiang on 2021/8/25. +// +#import "LWZCollectionSection.h" +#import "LWZCollectionSectionHeaderFooter.h" +#import "LWZCollectionDecoration.h" +#import "LWZCollectionItem.h" + +NS_ASSUME_NONNULL_BEGIN +@interface LWZCollectionDecoration (LWZCollectionInternalAdditions) +@property (nonatomic, readonly) id userInfo; +@property (nonatomic) CGRect relativeRect; // 由presenter维护 +- (CGRect)relativeRectToFit:(CGRect)rect atIndexPath:(NSIndexPath *)indexPath; // calls relativeRectThatFits:; +@end + +@interface LWZCollectionItem (LWZCollectionInternalAdditions) +@property (nonatomic) CGSize layoutSize; // 由presenter维护 + +- (void)willDisplayCell:(__kindof UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath; +- (void)didEndDisplayingCell:(__kindof UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath; +@end + +@interface LWZCollectionSectionHeaderFooter (LWZCollectionInternalAdditions) +@property (nonatomic) CGSize layoutSize; // 由presenter维护 + +- (void)willDisplaySupplementaryView:(__kindof UICollectionReusableView *)view forElementKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath; +- (void)didEndDisplayingSupplementaryView:(__kindof UICollectionReusableView *)view forElementOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath; +@end + +@interface UICollectionViewCell (LWZCollectionInternalAdditions) +@property (nonatomic, strong, nullable) __kindof LWZCollectionItem *lwz_bindingCollectionItem; +@property (nonatomic, readonly) BOOL lwz_respondsToWillDisplay; +@property (nonatomic, readonly) BOOL lwz_respondsToDidEndDisplaying; +@end + +@interface UICollectionReusableView (LWZCollectionInternalAdditions) +@property (nonatomic, strong, nullable) __kindof LWZCollectionSectionHeaderFooter *lwz_bindingHeaderFooter; +@end +NS_ASSUME_NONNULL_END diff --git a/LWZComponents/LWZCollectionViewComponents/Provider/LWZCollectionItem.h b/LWZComponents/LWZCollectionViewComponents/Provider/LWZCollectionItem.h new file mode 100644 index 0000000..a6f601a --- /dev/null +++ b/LWZComponents/LWZCollectionViewComponents/Provider/LWZCollectionItem.h @@ -0,0 +1,51 @@ +// +// LWZCollectionItem.h +// LWZAppComponents +// +// Created by changsanjiang on 2021/8/25. +// + +#import "LWZCollectionDefines.h" +@class LWZCollectionSection, LWZCollectionDecoration; + +NS_ASSUME_NONNULL_BEGIN +/** + LWZCollectionItem 主要职责: 根据模型层对`视图配置`及`视图交互事件传递`等均可在此部分中实现. + + 视图层不宜添加复杂的业务逻辑, 为了使视图达到最大程度的复用, + 可以改变一下方式, 采用 item 的形式, + 对于同一个cell, 不同复用场景 可以这样 不同业务可以做不同的item, 但对应的可以是同一个 cell. + + 视图只管显示对应数据或传递触发事件, + */ +@interface LWZCollectionItem : NSObject +@property (nonatomic, readonly) Class cellClass; +@property (nonatomic, readonly, nullable) NSBundle *cellNibBundle; +@property (nonatomic, readonly, nullable) UINib *cellNib; +@property (nonatomic, readonly) BOOL needsLayout; + +@property (nonatomic) NSInteger zPosition; +@property (nonatomic, strong, nullable) LWZCollectionDecoration *decoration; + +- (void)setNeedsLayout NS_REQUIRES_SUPER; +- (CGSize)layoutSizeThatFits:(CGSize)size inSection:(nullable LWZCollectionSection *)section atIndexPath:(nullable NSIndexPath *)indexPath scrollDirection:(UICollectionViewScrollDirection)scrollDirection; + +- (void)bindCell:(__kindof UICollectionViewCell *)cell atIndexPath:(NSIndexPath *)indexPath; +- (void)unbindCell:(__kindof UICollectionViewCell *)cell atIndexPath:(NSIndexPath *)indexPath; + +@property (nonatomic, copy, nullable) void(^tapHandler)(__kindof LWZCollectionItem *item, NSIndexPath *indexPath); +@end + +@interface LWZCollectionItem (LWZCollectionWeightLayoutAdditions) +@property (nonatomic) CGFloat weight; +@end + +@interface LWZCollectionItem (LWZCollectionListLayoutAdditions) +@property (nonatomic) LWZCollectionLayoutAlignment layoutAlignment; +@end + +@interface LWZCollectionItem (LWZCollectionSubclassHooks) +- (void)didSelectAtIndexPath:(NSIndexPath *)indexPath; +- (void)didDeselectAtIndexPath:(NSIndexPath *)indexPath; +@end +NS_ASSUME_NONNULL_END diff --git a/LWZComponents/LWZCollectionViewComponents/Provider/LWZCollectionItem.m b/LWZComponents/LWZCollectionViewComponents/Provider/LWZCollectionItem.m new file mode 100644 index 0000000..1312c3f --- /dev/null +++ b/LWZComponents/LWZCollectionViewComponents/Provider/LWZCollectionItem.m @@ -0,0 +1,75 @@ +// +// LWZCollectionItem.m +// LWZAppComponents +// +// Created by changsanjiang on 2021/8/25. +// + +#import "LWZCollectionItem.h" +#import "LWZCollectionDecoration.h" + +@interface LWZCollectionItem () +@property (nonatomic) CGSize layoutSize; +@property (nonatomic) CGFloat weight; +@property (nonatomic) LWZCollectionLayoutAlignment layoutAlignment; +@end + +@implementation LWZCollectionItem { + BOOL _needsLayout; +} +- (instancetype)init { + self = [super init]; + if (self) { + _weight = 1; + _needsLayout = YES; + } + return self; +} + +- (Class)cellClass { + @throw [NSException exceptionWithName:NSInternalInconsistencyException + reason:[NSString stringWithFormat:@"You must override %@ in a subclass.", NSStringFromSelector(_cmd)] + userInfo:nil]; +} + +- (BOOL)needsLayout { + return _needsLayout; +} + +/** + 标记是否需要重新计算布局, LWZCollectionPresenter 将会在合适时机调用`layoutSizeThatFits:inSection:` + */ +- (void)setNeedsLayout { + _needsLayout = YES; + if ( _decoration != nil ) [_decoration setNeedsLayout]; +} + +/** + 返回布局. 当`needsLayout == YES`时, LWZCollectionPresenter 将会调用此方法. + + 为避免频繁调用, 此 size 将会被 LWZCollectionPresenter 缓存, 调用`setNeedsLayout`来标记需要刷新. + */ +- (CGSize)layoutSizeThatFits:(CGSize)size inSection:(nullable LWZCollectionSection *)section atIndexPath:(nullable NSIndexPath *)indexPath scrollDirection:(UICollectionViewScrollDirection)scrollDirection { + @throw [NSException exceptionWithName:NSInternalInconsistencyException + reason:[NSString stringWithFormat:@"You must override %@ in a subclass.", NSStringFromSelector(_cmd)] + userInfo:nil]; +} + +- (void)willDisplayCell:(__kindof UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath { + [self bindCell:cell atIndexPath:indexPath]; + _needsLayout = NO; +} + +- (void)didEndDisplayingCell:(__kindof UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath { + [self unbindCell:cell atIndexPath:indexPath]; +} + +/// 绑定cell +- (void)bindCell:(__kindof UICollectionViewCell *)cell atIndexPath:(NSIndexPath *)indexPath { } +/// 解绑cell +- (void)unbindCell:(__kindof UICollectionViewCell *)cell atIndexPath:(NSIndexPath *)indexPath { } + +- (void)didSelectAtIndexPath:(NSIndexPath *)indexPath {} + +- (void)didDeselectAtIndexPath:(NSIndexPath *)indexPath {} +@end diff --git a/LWZComponents/LWZCollectionViewComponents/Provider/LWZCollectionProvider.h b/LWZComponents/LWZCollectionViewComponents/Provider/LWZCollectionProvider.h new file mode 100755 index 0000000..aaacacd --- /dev/null +++ b/LWZComponents/LWZCollectionViewComponents/Provider/LWZCollectionProvider.h @@ -0,0 +1,153 @@ +// +// LWZCollectionProvider.h +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2020/11/16. +// Copyright © 2020 changsanjiang@gmail.com. All rights reserved. +// + +#import "LWZCollectionSection.h" +#import "LWZCollectionSectionHeaderFooter.h" +#import "LWZCollectionDecoration.h" +#import "LWZCollectionItem.h" + +/// 数据供应器, 数据组合 +/// +NS_ASSUME_NONNULL_BEGIN +@interface LWZCollectionProvider : NSObject +- (void)addSectionWithBlock:(void(^NS_NOESCAPE)(__kindof LWZCollectionSection *make))block; + +@property (nonatomic, readonly) NSInteger numberOfSections; +@property (nonatomic, readonly, nullable) __kindof LWZCollectionSection *firstSection; +@property (nonatomic, readonly, nullable) __kindof LWZCollectionSection *lastSection; + +- (void)enumerateSectionsUsingBlock:(void(NS_NOESCAPE ^)(__kindof LWZCollectionSection *section, NSInteger idx, BOOL *stop))block; +- (void)enumerateSectionHeadersUsingBlock:(void(NS_NOESCAPE ^)(__kindof LWZCollectionSectionHeaderFooter *header, NSInteger idx, BOOL *stop))block; +- (void)enumerateItemIndexPathsUsingBlock:(void(NS_NOESCAPE ^)(NSIndexPath *indexPath, BOOL *stop))block; +- (void)enumerateSectionFootersUsingBlock:(void(NS_NOESCAPE ^)(__kindof LWZCollectionSectionHeaderFooter *footer, NSInteger idx, BOOL *stop))block; + +- (void)enumerateSectionsWithOptions:(NSEnumerationOptions)opts usingBlock:(void(NS_NOESCAPE ^)(__kindof LWZCollectionSection *section, NSInteger idx, BOOL *stop))block; +- (void)enumerateSectionHeadersWithOptions:(NSEnumerationOptions)opts usingBlock:(void(NS_NOESCAPE ^)(__kindof LWZCollectionSectionHeaderFooter *header, NSInteger idx, BOOL *stop))block; +- (void)enumerateItemIndexPathsWithOptions:(NSEnumerationOptions)opts usingBlock:(void(NS_NOESCAPE ^)(NSIndexPath *indexPath, BOOL *stop))block; +- (void)enumerateSectionFootersWithOptions:(NSEnumerationOptions)opts usingBlock:(void(NS_NOESCAPE ^)(__kindof LWZCollectionSectionHeaderFooter *footer, NSInteger idx, BOOL *stop))block; + +- (NSInteger)addSection:(LWZCollectionSection *)section; +- (nullable NSIndexSet *)addSections:(NSArray *)sections; +- (NSInteger)insertSection:(LWZCollectionSection *)section atIndex:(NSInteger)index; +- (nullable NSIndexSet *)insertSections:(NSArray *)sections withPreviousSection:(LWZCollectionSection *)section; +- (nullable NSIndexSet *)insertSections:(NSArray *)sections withNextSection:(LWZCollectionSection *)section; +- (void)replaceSectionAtIndex:(NSInteger)index withSection:(LWZCollectionSection *)section; + +- (nullable __kindof LWZCollectionSection *)sectionAtIndex:(NSInteger)index; +- (nullable __kindof LWZCollectionSectionHeaderFooter *)headerForSectionAtIndex:(NSInteger)index; +- (nullable __kindof LWZCollectionSectionHeaderFooter *)footerForSectionAtIndex:(NSInteger)index; +- (nullable __kindof LWZCollectionItem *)itemAtIndexPath:(NSIndexPath *)indexPath; + +- (BOOL)containsSection:(LWZCollectionSection *)section; +- (BOOL)containsHeader:(LWZCollectionSectionHeaderFooter *)header; +- (BOOL)containsFooter:(LWZCollectionSectionHeaderFooter *)footer; +- (BOOL)containsItem:(LWZCollectionItem *)item; + +- (NSInteger)indexOfSection:(LWZCollectionSection *)section; +- (NSInteger)indexOfSectionForHeader:(LWZCollectionSectionHeaderFooter *)header; +- (NSInteger)indexOfSectionForFooter:(LWZCollectionSectionHeaderFooter *)footer; +- (nullable NSIndexSet *)indexesForSectionsInArray:(NSArray *)sections; +- (nullable NSIndexPath *)indexPathOfItem:(LWZCollectionItem *)item; +- (nullable NSArray *)indexPathsForItemsInArray:(NSArray *)items; + +- (nullable LWZCollectionSection *)sectionOfItem:(LWZCollectionItem *)item; +- (nullable LWZCollectionSection *)sectionOfHeader:(LWZCollectionSectionHeaderFooter *)header; +- (nullable LWZCollectionSection *)sectionOfFooter:(LWZCollectionSectionHeaderFooter *)footer; + +/// +/// 设置section隐藏. +/// +/// @note 设置 hidden, 不会移除 section, 只是控制界面上是否显示. +/// +- (void)setHidden:(BOOL)isHidden forSectionAtIndex:(NSInteger)index; +- (void)setHidden:(BOOL)isHidden forSection:(LWZCollectionSection *)section; +- (BOOL)isSectionHiddenAtIndex:(NSInteger)index; +- (BOOL)isSectionHidden:(LWZCollectionSection *)section; + +/// +/// 将`item`移动到previousItem之后. +/// +/// @return 返回新位置的索引. `item`为空或当前section不包含`previousItem & item`, 将返回 nil. +/// +- (nullable NSIndexPath *)moveItemAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath; +- (nullable NSIndexPath *)moveItem:(LWZCollectionItem *)item withPreviousItem:(LWZCollectionItem *)previousItem; +- (nullable NSArray *)moveItemsInArray:(NSArray *)items withPreviousItem:(LWZCollectionItem *)previousItem; + +/// +/// 将`item`移动到nextItem之前. +/// +/// @return 返回新位置的索引. `item`为空或当前section不包含`nextItem & item`, 将返回 nil. +/// +- (nullable NSIndexPath *)moveItem:(LWZCollectionItem *)item withNextItem:(LWZCollectionItem *)nextItem; +- (nullable NSArray *)moveItemsInArray:(NSArray *)items withNextItem:(LWZCollectionItem *)nextItem; + +/// +/// 将item插入到previousItem后面. +/// +/// @return 返回插入位置的索引. `item`为空或`previousItem`未添加到某个section中, 将返回 nil. +/// +- (nullable NSIndexPath *)insertItem:(LWZCollectionItem *)item atIndexPath:(NSIndexPath *)indexPath; +- (nullable NSIndexPath *)insertItem:(LWZCollectionItem *)item withPreviousItem:(LWZCollectionItem *)previousItem; +- (nullable NSArray *)insertItems:(NSArray *)items withPreviousItem:(LWZCollectionItem *)previousItem; + +/// +/// 将item插入到nextItem前面. +/// +/// @return 返回插入位置的索引. `item`为空或`nextItem`未添加到某个section中, 将返回 nil. +/// +- (nullable NSIndexPath *)insertItem:(LWZCollectionItem *)item withNextItem:(LWZCollectionItem *)nextItem; +- (nullable NSArray *)insertItems:(NSArray *)items withNextItem:(LWZCollectionItem *)nextItem; + +/// +/// 在顶部插入item +/// +- (nullable NSIndexPath *)insertItemToTop:(LWZCollectionItem *)item inSection:(LWZCollectionSection *)section; + +/// +/// 移除整个section. 包括decoration +/// +/// @return 返回section的索引. 索引越界将返回 NSNotFound. +/// +- (NSInteger)removeSectionAtIndex:(NSInteger)index; +- (NSInteger)removeSection:(LWZCollectionSection *)section; +- (nullable NSIndexSet *)removeSections:(NSArray *)sections; +- (void)removeAllSections; + +/// +/// 移除section的header. 包括decoration +/// +/// @return 返回section的索引. 索引越界将返回 NSNotFound. +/// +- (NSInteger)removeHeaderForSectionAtIndex:(NSInteger)index; +- (NSInteger)removeHeaderForSection:(LWZCollectionSection *)section; + +/// +/// 移除section的footer. 包括decoration +/// +/// @return 返回section的索引. 索引越界将返回 NSNotFound. +/// +- (NSInteger)removeFooterForSectionAtIndex:(NSInteger)index; +- (NSInteger)removeFooterForSection:(LWZCollectionSection *)section; + +/// +/// 移除item包括decoration +/// +- (nullable NSIndexPath *)removeItemAtIndexPath:(NSIndexPath *)indexPath; +- (nullable NSIndexPath *)removeItem:(LWZCollectionItem *)item; +- (nullable NSArray *)removeItemsInArray:(NSArray *)items; +- (nullable NSArray *)removeItemsAtIndexPaths:(NSArray *)indexPaths; + +/// +/// 移除decoration +/// +- (NSInteger)removeSectionDecorationAtIndex:(NSInteger)index; +- (NSInteger)removeSectionHeaderDecorationAtIndex:(NSInteger)index; +- (nullable NSIndexPath *)removeItemDecorationAtIndexPath:(NSIndexPath *)indexPath; +- (NSInteger)removeSectionFooterDecorationAtIndex:(NSInteger)index; +@end +NS_ASSUME_NONNULL_END diff --git a/LWZComponents/LWZCollectionViewComponents/Provider/LWZCollectionProvider.m b/LWZComponents/LWZCollectionViewComponents/Provider/LWZCollectionProvider.m new file mode 100755 index 0000000..afad114 --- /dev/null +++ b/LWZComponents/LWZCollectionViewComponents/Provider/LWZCollectionProvider.m @@ -0,0 +1,593 @@ +// +// LWZCollectionProvider.m +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2020/11/16. +// Copyright © 2020 changsanjiang@gmail.com. All rights reserved. +// + +#import "LWZCollectionProvider.h" + +@interface LWZCollectionProvider () +@property (nonatomic, strong, readonly) NSMutableArray *sections; +@end + +@implementation LWZCollectionProvider + +- (void)addSectionWithBlock:(void(^NS_NOESCAPE)(__kindof LWZCollectionSection *make))block { + LWZCollectionSection *section = [LWZCollectionSection.alloc init]; + block(section); + [self addSection:section]; +} + +- (void)enumerateSectionsUsingBlock:(void(NS_NOESCAPE ^)(__kindof LWZCollectionSection *section, NSInteger idx, BOOL *stop))block { + [_sections enumerateObjectsUsingBlock:^(LWZCollectionSection * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + block(obj, idx, stop); + }]; +} + +- (void)enumerateSectionHeadersUsingBlock:(void(NS_NOESCAPE ^)(__kindof LWZCollectionSectionHeaderFooter *header, NSInteger idx, BOOL *stop))block { + BOOL stop = NO; + for ( NSInteger s = 0 ; s < self.numberOfSections ; ++ s ) { + LWZCollectionSection *section = [self sectionAtIndex:s]; + if ( section.header != nil ) { + block(section.header, s, &stop); + if ( stop ) return; + } + } +} + +- (void)enumerateItemIndexPathsUsingBlock:(void(NS_NOESCAPE ^)(NSIndexPath *indexPath, BOOL *stop))block { + BOOL stop = NO; + for ( NSInteger s = 0 ; s < self.numberOfSections ; ++ s ) { + LWZCollectionSection *section = [self sectionAtIndex:s]; + for ( NSInteger i = 0 ; i < section.numberOfItems ; ++ i ) { + NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:s]; + block(indexPath, &stop); + if ( stop ) + return; + } + } +} + +- (void)enumerateSectionFootersUsingBlock:(void(NS_NOESCAPE ^)(__kindof LWZCollectionSectionHeaderFooter *footer, NSInteger idx, BOOL *stop))block { + BOOL stop = NO; + for ( NSInteger s = 0 ; s < self.numberOfSections ; ++ s ) { + LWZCollectionSection *section = [self sectionAtIndex:s]; + if ( section.footer != nil ) { + block(section.footer, s, &stop); + if ( stop ) return; + } + } +} + +- (void)enumerateSectionsWithOptions:(NSEnumerationOptions)opts usingBlock:(void(NS_NOESCAPE ^)(__kindof LWZCollectionSection *section, NSInteger idx, BOOL *stop))block { + [_sections enumerateObjectsWithOptions:opts usingBlock:^(LWZCollectionSection * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + block(obj, idx, stop); + }]; +} +- (void)enumerateSectionHeadersWithOptions:(NSEnumerationOptions)opts usingBlock:(void(NS_NOESCAPE ^)(__kindof LWZCollectionSectionHeaderFooter *header, NSInteger idx, BOOL *stop))block { + [_sections enumerateObjectsWithOptions:opts usingBlock:^(LWZCollectionSection * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + LWZCollectionSectionHeaderFooter *header = obj.header; + if ( header != nil ) { + block(header, idx, stop); + } + }]; +} +- (void)enumerateItemIndexPathsWithOptions:(NSEnumerationOptions)opts usingBlock:(void(NS_NOESCAPE ^)(NSIndexPath *indexPath, BOOL *stop))block { + [_sections enumerateObjectsWithOptions:opts usingBlock:^(LWZCollectionSection * _Nonnull obj, NSUInteger sIdx, BOOL * _Nonnull sStop) { + [obj enumerateItemsWithOptions:opts usingBlock:^(__kindof LWZCollectionItem * _Nonnull item, NSInteger iIdx, BOOL * _Nonnull iStop) { + NSIndexPath *indexPath = [NSIndexPath indexPathForItem:iIdx inSection:sIdx]; + block(indexPath, iStop); + if ( *iStop ) *sStop = YES; + }]; + }]; +} +- (void)enumerateSectionFootersWithOptions:(NSEnumerationOptions)opts usingBlock:(void(NS_NOESCAPE ^)(__kindof LWZCollectionSectionHeaderFooter *footer, NSInteger idx, BOOL *stop))block { + [_sections enumerateObjectsWithOptions:opts usingBlock:^(LWZCollectionSection * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + LWZCollectionSectionHeaderFooter *footer = obj.footer; + if ( footer != nil ) { + block(footer, idx, stop); + } + }]; +} + +#pragma mark - mark + +- (NSInteger)numberOfSections { + return _sections.count; +} + +- (nullable __kindof LWZCollectionSection *)firstSection { + return _sections.firstObject; +} + +- (nullable __kindof LWZCollectionSection *)lastSection { + return _sections.lastObject; +} + +- (NSInteger)addSection:(LWZCollectionSection *)section { + if ( section != nil ) { + NSInteger location = _sections.count; + [self.sections addObject:section]; + return location; + } + return NSNotFound; +} + +- (nullable NSIndexSet *)addSections:(NSArray *)sections { + if ( sections.count != 0 ) { + NSInteger location = _sections.count; + [self.sections addObjectsFromArray:sections]; + return [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(location, sections.count)]; + } + return nil; +} + +- (NSInteger)insertSection:(LWZCollectionSection *)section atIndex:(NSInteger)index { + if ( [self _isSafeIndexForInserting:index] ) { + [self.sections insertObject:section atIndex:index]; + return index; + } + return NSNotFound; +} + +- (nullable NSIndexSet *)insertSections:(NSArray *)sections withPreviousSection:(LWZCollectionSection *)section { + NSInteger index = [self indexOfSection:section] + 1; + if ( [self _isSafeIndexForInserting:index] ) { + NSIndexSet *set = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(index, sections.count)]; + [_sections insertObjects:sections atIndexes:set]; + return set; + } + return nil; +} + +- (nullable NSIndexSet *)insertSections:(NSArray *)sections withNextSection:(LWZCollectionSection *)section { + NSInteger index = [self indexOfSection:section]; + if ( [self _isSafeIndexForInserting:index] ) { + NSIndexSet *set = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(index, sections.count)]; + [_sections insertObjects:sections atIndexes:set]; + return set; + } + return nil; +} + + +- (void)replaceSectionAtIndex:(NSInteger)index withSection:(LWZCollectionSection *)section { + if ( [self _isSafeIndexForGetting:index] ) { + [self.sections replaceObjectAtIndex:index withObject:section]; + } +} + +- (nullable __kindof LWZCollectionSection *)sectionAtIndex:(NSInteger)index { + return [self _isSafeIndexForGetting:index] ? _sections[index] : nil; +} + +- (nullable __kindof LWZCollectionSectionHeaderFooter *)headerForSectionAtIndex:(NSInteger)index { + return [self sectionAtIndex:index].header; +} + +- (nullable __kindof LWZCollectionSectionHeaderFooter *)footerForSectionAtIndex:(NSInteger)index { + return [self sectionAtIndex:index].footer; +} + +- (nullable __kindof LWZCollectionItem *)itemAtIndexPath:(NSIndexPath *)indexPath { + return [[self sectionAtIndex:indexPath.section] itemAtIndex:indexPath.item]; +} + +- (BOOL)containsSection:(LWZCollectionSection *)section { + return [self indexOfSection:section] != NSNotFound; +} + +- (BOOL)containsHeader:(LWZCollectionSectionHeaderFooter *)header { + return [self indexOfSectionForHeader:header] != NSNotFound; +} + +- (BOOL)containsFooter:(LWZCollectionSectionHeaderFooter *)footer { + return [self indexOfSectionForFooter:footer] != NSNotFound; +} + +- (BOOL)containsItem:(LWZCollectionItem *)item { + return [self indexPathOfItem:item] != nil; +} + +- (NSInteger)indexOfSection:(LWZCollectionSection *)section { + return section != nil ? [_sections indexOfObject:section] : NSNotFound; +} + +- (NSInteger)indexOfSectionForHeader:(LWZCollectionSectionHeaderFooter *)header { + if ( header != nil ) { + return [self _indexOfSectionWithCondition:^BOOL(LWZCollectionSection *section) { + return section.header == header; + }]; + } + return NSNotFound; +} + +- (NSInteger)indexOfSectionForFooter:(LWZCollectionSectionHeaderFooter *)footer { + if ( footer != nil ) { + return [self _indexOfSectionWithCondition:^BOOL(LWZCollectionSection *section) { + return section.footer == footer; + }]; + } + return NSNotFound; +} + +- (nullable NSIndexSet *)indexesForSectionsInArray:(NSArray *)sections { + NSMutableIndexSet *set = [NSMutableIndexSet.alloc init]; + for ( LWZCollectionSection *section in sections ) { + NSInteger index = [self indexOfSection:section]; + if ( index == NSNotFound ) + return nil; + [set addIndex:index]; + } + return set.copy; +} + +- (nullable NSIndexPath *)indexPathOfItem:(LWZCollectionItem *)item { + if ( item != nil ) { + return [self _indexPathOfItemWithCondition:^BOOL(LWZCollectionSection *section, LWZCollectionItem * i) { + return i == item; + }]; + } + return nil; +} + +- (nullable NSArray *)indexPathsForItemsInArray:(NSArray *)items { + if ( items.count != 0 ) { + NSMutableArray *m = [NSMutableArray arrayWithCapacity:items.count]; + for ( LWZCollectionItem * item in items ) { + NSIndexPath *indexPath = [self indexPathOfItem:item]; + if ( indexPath == nil ) return nil; + [m addObject:indexPath]; + } + return m.copy; + } + return nil; +} + +- (nullable LWZCollectionSection *)sectionOfItem:(LWZCollectionItem *)item { + return item != nil ? [self sectionAtIndex:[self indexPathOfItem:item].section] : nil; +} + +- (nullable LWZCollectionSection *)sectionOfHeader:(LWZCollectionSectionHeaderFooter *)header { + return header != nil ? [self sectionAtIndex:[self indexOfSectionForHeader:header]] : nil; +} + +- (nullable LWZCollectionSection *)sectionOfFooter:(LWZCollectionSectionHeaderFooter *)footer { + return footer != nil ? [self sectionAtIndex:[self indexOfSectionForFooter:footer]] : nil; +} + +- (void)setHidden:(BOOL)isHidden forSectionAtIndex:(NSInteger)index { + [[self sectionAtIndex:index] setHidden:isHidden]; +} + +- (void)setHidden:(BOOL)isHidden forSection:(LWZCollectionSection *)section { + [self setHidden:isHidden forSectionAtIndex:[self indexOfSection:section]]; +} + +- (BOOL)isSectionHiddenAtIndex:(NSInteger)index { + return [[self sectionAtIndex:index] isHidden]; +} + +- (BOOL)isSectionHidden:(LWZCollectionSection *)section { + return [self isSectionHiddenAtIndex:[self indexOfSection:section]]; +} + +- (nullable NSIndexPath *)moveItemAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath { + LWZCollectionItem *destinatioItem = [self itemAtIndexPath:destinationIndexPath]; + LWZCollectionItem *sourceItem = [self itemAtIndexPath:sourceIndexPath]; + NSComparisonResult result = [sourceIndexPath compare:destinationIndexPath]; + BOOL isMoveDown = NO; + switch ( result ) { + case NSOrderedSame: + /* return */ + return nil; + case NSOrderedAscending: + isMoveDown = YES; + break; + case NSOrderedDescending: + break; + } + return isMoveDown ? [self moveItem:sourceItem withPreviousItem:destinatioItem] : [self moveItem:sourceItem withNextItem:destinatioItem]; +} + +- (nullable NSIndexPath *)moveItem:(LWZCollectionItem *)i1 withPreviousItem:(LWZCollectionItem *)i2 { + if ( i1 != i2 && [self containsItem:i1] && [self containsItem:i2] ) { + LWZCollectionSection *s1 = [self sectionOfItem:i1]; + LWZCollectionSection *s2 = [self sectionOfItem:i2]; + [s1 removeItem:i1]; + return [NSIndexPath indexPathForItem:[s2 insertItem:i1 withPreviousItem:i2] inSection:[self indexOfSection:s2]]; + } + return nil; +} + +- (nullable NSArray *)moveItemsInArray:(NSArray *)items withPreviousItem:(LWZCollectionItem *)previousItem { +#ifdef DEBUG + for ( LWZCollectionItem * item in items ) { + NSAssert([self containsItem:item], @"必须是已添加到列表中的item!"); + } +#endif + + if ( items.count != 0 && [self containsItem:previousItem] ) { + for ( NSInteger i = items.count - 1 ; i >= 0 ; -- i ) { + [self moveItem:items[i] withPreviousItem:previousItem]; + } + return [self indexPathsForItemsInArray:items]; + } + return nil; +} + +- (nullable NSIndexPath *)moveItem:(LWZCollectionItem *)i1 withNextItem:(LWZCollectionItem *)i2 { + if ( i1 != i2 && [self containsItem:i1] && [self containsItem:i2] ) { + LWZCollectionSection *s1 = [self sectionOfItem:i1]; + LWZCollectionSection *s2 = [self sectionOfItem:i2]; + [s1 removeItem:i1]; + return [NSIndexPath indexPathForItem:[s2 insertItem:i1 withNextItem:i2] inSection:[self indexOfSection:s2]]; + } + return nil; +} + +- (nullable NSArray *)moveItemsInArray:(NSArray *)items withNextItem:(LWZCollectionItem *)nextItem { +#ifdef DEBUG + for ( LWZCollectionItem * item in items ) { + NSAssert([self containsItem:item], @"必须是已添加到列表中的item!"); + } +#endif + + if ( items.count != 0 && [self containsItem:nextItem] ) { + for ( NSInteger i = 0 ; i < items.count ; ++ i ) { + [self moveItem:items[i] withNextItem:nextItem]; + } + return [self indexPathsForItemsInArray:items]; + } + return nil; +} + +- (nullable NSIndexPath *)insertItem:(LWZCollectionItem *)item atIndexPath:(NSIndexPath *)indexPath { + LWZCollectionItem *sourceItem = [self itemAtIndexPath:indexPath]; + return [self insertItem:item withNextItem:sourceItem]; +} + +- (nullable NSIndexPath *)insertItem:(LWZCollectionItem *)item withPreviousItem:(LWZCollectionItem *)previousItem { + if ( item != nil && [self containsItem:previousItem] ) { + [[self sectionOfItem:previousItem] insertItem:item withPreviousItem:previousItem]; + return [self indexPathOfItem:item]; + } + return nil; +} + +- (nullable NSArray *)insertItems:(NSArray *)items withPreviousItem:(LWZCollectionItem *)previousItem { + if ( items.count != 0 && [self containsItem:previousItem] ) { + for ( NSInteger i = items.count - 1 ; i >= 0 ; -- i ) { + [self insertItem:items[i] withPreviousItem:previousItem]; + } + return [self indexPathsForItemsInArray:items]; + } + return nil; +} + +- (nullable NSIndexPath *)insertItem:(LWZCollectionItem *)item withNextItem:(LWZCollectionItem *)nextItem { + if ( item != nil && [self containsItem:nextItem] ) { + [[self sectionOfItem:nextItem] insertItem:item withNextItem:nextItem]; + return [self indexPathOfItem:item]; + } + return nil; +} + +- (nullable NSArray *)insertItems:(NSArray *)items withNextItem:(LWZCollectionItem *)nextItem { + if ( items.count != 0 && [self containsItem:nextItem] ) { + for ( NSInteger i = 0 ; i < items.count ; ++ i ) { + [self insertItem:items[i] withNextItem:nextItem]; + } + return [self indexPathsForItemsInArray:items]; + } + return nil; +} + +- (nullable NSIndexPath *)insertItemToTop:(LWZCollectionItem *)item inSection:(LWZCollectionSection *)section { + if ( item != nil && [self containsSection:section] ) { + [section insertItemToTop:item]; + return [self indexPathOfItem:item]; + } + return nil; +} + +- (NSInteger)removeSectionAtIndex:(NSInteger)index { + if ( [self _isSafeIndexForGetting:index] ) { + [_sections removeObjectAtIndex:index]; + return index; + } + return NSNotFound; +} + +- (NSInteger)removeSection:(LWZCollectionSection *)section { + return [self removeSectionAtIndex:[self indexOfSection:section]]; +} + +- (nullable NSIndexSet *)removeSections:(NSArray *)sections { + NSIndexSet *set = [self indexesForSectionsInArray:sections]; + if ( set != nil ) { + NSUInteger currentIndex = set.lastIndex; + do { + [self removeSectionAtIndex:currentIndex]; + currentIndex = [set indexLessThanIndex:currentIndex]; + } while ( currentIndex != NSNotFound ); + } + return set; +} + +- (void)removeAllSections { + [_sections removeAllObjects]; +} + +- (NSInteger)removeHeaderForSectionAtIndex:(NSInteger)index { + if ( [self _isSafeIndexForGetting:index] && [self sectionAtIndex:index].header != nil ) { + [self sectionAtIndex:index].header = nil; + return index; + } + return NSNotFound; +} + +- (NSInteger)removeHeaderForSection:(LWZCollectionSection *)section { + return [self removeHeaderForSectionAtIndex:[self indexOfSection:section]]; +} + +- (NSInteger)removeFooterForSectionAtIndex:(NSInteger)index { + if ( [self _isSafeIndexForGetting:index] && [self sectionAtIndex:index].footer != nil ) { + [self sectionAtIndex:index].footer = nil; + return index; + } + return NSNotFound; +} + +- (NSInteger)removeFooterForSection:(LWZCollectionSection *)section { + return [self removeFooterForSectionAtIndex:[self indexOfSection:section]]; +} + +- (nullable NSIndexPath *)removeItemAtIndexPath:(NSIndexPath *)indexPath { + LWZCollectionItem * item = [self itemAtIndexPath:indexPath]; + if ( item != nil ) { + LWZCollectionSection *section = [self sectionAtIndex:indexPath.section]; + [section removeItem:item]; + return indexPath; + } + return nil; +} + +- (nullable NSIndexPath *)removeItem:(LWZCollectionItem *)item { + return [self removeItemAtIndexPath:[self indexPathOfItem:item]]; +} + +- (nullable NSArray *)removeItemsInArray:(NSArray *)items { + if ( items.count != 0 ) { + NSMutableArray *m = [NSMutableArray arrayWithCapacity:items.count]; + [[self _sortedArrayForItems:items] enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(LWZCollectionItem * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + [m addObject:[self removeItem:obj]]; + }]; + return m.copy; + } + return nil; +} + +- (nullable NSArray *)removeItemsAtIndexPaths:(NSArray *)indexPaths { + if ( indexPaths.count != 0 ) { + NSMutableArray *m = [NSMutableArray arrayWithCapacity:indexPaths.count]; + [[self _sortedArrayForIndexPaths:indexPaths] enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(NSIndexPath * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + [m addObject:[self removeItemAtIndexPath:obj]]; + }]; + return m.copy; + } + return nil; +} + +/// +/// 移除decoration +/// +- (NSInteger)removeSectionDecorationAtIndex:(NSInteger)index { + LWZCollectionSection *section = [self sectionAtIndex:index]; + if ( section.decoration != nil ) { + section.decoration = nil; + return index; + } + return NSNotFound; +} + +- (NSInteger)removeSectionHeaderDecorationAtIndex:(NSInteger)index { + LWZCollectionSectionHeaderFooter * header = [self headerForSectionAtIndex:index]; + if ( header != nil ) { + header.decoration = nil; + return index; + } + return NSNotFound; +} + +- (nullable NSIndexPath *)removeItemDecorationAtIndexPath:(NSIndexPath *)indexPath { + LWZCollectionItem * item = [self itemAtIndexPath:indexPath]; + if ( item != nil ) { + item.decoration = nil; + return indexPath; + } + return nil; +} + +- (NSInteger)removeSectionFooterDecorationAtIndex:(NSInteger)index { + LWZCollectionSectionHeaderFooter * footer = [self footerForSectionAtIndex:index]; + if ( footer != nil ) { + footer.decoration = nil; + return index; + } + return NSNotFound; +} + +#pragma mark - mark +@synthesize sections = _sections; +- (NSMutableArray *)sections { + if ( _sections == nil ) { + _sections = NSMutableArray.array; + } + return _sections; +} + +- (BOOL)_isSafeIndexForGetting:(NSInteger)index { + return index >= 0 && index < _sections.count; +} + +- (BOOL)_isSafeIndexForInserting:(NSInteger)index { + return index >= 0 && index <= _sections.count; +} + +- (BOOL)_isSafeIndexPathForGetting:(NSIndexPath *)indexPath { + LWZCollectionSection *section = [self sectionAtIndex:indexPath.section]; + return indexPath.item < section.numberOfItems; +} + +- (BOOL)_isSafeIndexPathForInserting:(NSIndexPath *)indexPath { + LWZCollectionSection *section = [self sectionAtIndex:indexPath.section]; + return section.numberOfItems > indexPath.item; +} + +- (nullable NSArray *)_indexPathsWithIndexes:(NSIndexSet *)set inSection:(NSInteger)section { + if ( set.count != 0 ) { + NSMutableArray *indexPaths = [NSMutableArray arrayWithCapacity:set.count]; + NSInteger currentIndex = set.firstIndex; + do { + [indexPaths addObject:[NSIndexPath indexPathForItem:currentIndex inSection:section]]; + currentIndex = [set indexGreaterThanIndex:currentIndex]; + } while ( currentIndex != NSNotFound ); + return indexPaths; + } + return nil; +} + +- (nullable NSIndexPath *)_indexPathOfItemWithCondition:(BOOL(^)(LWZCollectionSection *section, LWZCollectionItem * item))condition { + for ( NSInteger s = 0 ; s < _sections.count ; ++ s ) { + for ( NSInteger r = 0 ; r < _sections[s].numberOfItems ; ++ r ) { + if ( condition(_sections[s], [_sections[s] itemAtIndex:r]) ) return [NSIndexPath indexPathForItem:r inSection:s]; + } + } + return nil; +} + +- (NSInteger)_indexOfSectionWithCondition:(BOOL(^)(LWZCollectionSection *section))condition { + for ( NSInteger s = 0 ; s < _sections.count ; ++ s ) { + if ( condition(_sections[s]) ) return s; + } + return NSNotFound; +} + +- (NSArray *)_sortedArrayForItems:(NSArray *)rows { + return [rows sortedArrayUsingComparator:^NSComparisonResult(LWZCollectionItem * _Nonnull r1, LWZCollectionItem * _Nonnull r2) { + NSIndexPath *i1 = [self indexPathOfItem:r1]; + NSIndexPath *i2 = [self indexPathOfItem:r2]; + NSParameterAssert(i1 != nil && i2 != nil); + return [i1 compare:i2]; + }]; +} + +- (NSArray *)_sortedArrayForIndexPaths:(NSArray *)indexPaths { + return [indexPaths sortedArrayUsingComparator:^NSComparisonResult(NSIndexPath *_Nonnull obj1, NSIndexPath *_Nonnull obj2) { + return [obj1 compare:obj2]; + }]; +} +@end diff --git a/LWZComponents/LWZCollectionViewComponents/Provider/LWZCollectionSection.h b/LWZComponents/LWZCollectionViewComponents/Provider/LWZCollectionSection.h new file mode 100755 index 0000000..9812344 --- /dev/null +++ b/LWZComponents/LWZCollectionViewComponents/Provider/LWZCollectionSection.h @@ -0,0 +1,95 @@ +// +// LWZCollectionSection.h +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2020/11/16. +// Copyright © 2020 changsanjiang@gmail.com. All rights reserved. +// + +#import "LWZCollectionDefines.h" +#import "LWZCollectionSectionHeaderFooter.h" +#import "LWZCollectionDecoration.h" +#import "LWZCollectionItem.h" +@class LWZCollectionLayoutTemplateGroup; + +NS_ASSUME_NONNULL_BEGIN +@interface LWZCollectionSection : NSObject +@property (nonatomic) CGFloat minimumInteritemSpacing; +@property (nonatomic) CGFloat minimumLineSpacing; +@property (nonatomic) UIEdgeInsets contentInsets; // 内间距. +@property (nonatomic) UIEdgeInsets edgeSpacings; // 外间距. + +@property (nonatomic, strong, nullable) __kindof LWZCollectionSectionHeaderFooter *header; +@property (nonatomic, strong, nullable) __kindof LWZCollectionSectionHeaderFooter *footer; +@property (nonatomic, strong, nullable) __kindof LWZCollectionDecoration *decoration; + +@property (nonatomic) BOOL canPinToVisibleBoundsForHeader; // default value is YES. + +@property (nonatomic, getter=isHidden) BOOL hidden; +@property (nonatomic) BOOL hidesAutomatically; // 当`items.count == 0`时, 是否自动隐藏 + +@property (nonatomic, readonly) NSInteger numberOfItems; +@property (nonatomic, readonly, nullable) __kindof LWZCollectionItem *firstItem; +@property (nonatomic, readonly, nullable) __kindof LWZCollectionItem *lastItem; +- (BOOL)containsItem:(LWZCollectionItem *)item; +- (NSInteger)indexForItem:(LWZCollectionItem *)item; +- (nullable NSIndexSet *)indexesForItemsInArray:(NSArray *)items; +- (nullable __kindof LWZCollectionItem *)itemAtIndex:(NSInteger)index; +- (void)enumerateItemsUsingBlock:(void(NS_NOESCAPE ^)(__kindof LWZCollectionItem *item, NSInteger index, BOOL *stop))block; +- (void)enumerateItemsWithOptions:(NSEnumerationOptions)opts usingBlock:(void(NS_NOESCAPE ^)(__kindof LWZCollectionItem *item, NSInteger index, BOOL *stop))block; + +- (NSInteger)addItem:(LWZCollectionItem *)item; +- (nullable NSIndexSet *)addItemsFromArray:(NSArray *)items; + +- (NSInteger)insertItem:(LWZCollectionItem *)item atIndex:(NSInteger)index; +- (NSInteger)insertItem:(LWZCollectionItem *)item withPreviousItem:(LWZCollectionItem *)previousItem; +- (NSInteger)insertItem:(LWZCollectionItem *)item withNextItem:(LWZCollectionItem *)nextItem; +- (NSInteger)insertItemToTop:(LWZCollectionItem *)item; +- (nullable NSIndexSet *)insertItems:(NSArray *)items withPreviousItem:(LWZCollectionItem *)previousItem; +- (nullable NSIndexSet *)insertItems:(NSArray *)items withNextItem:(LWZCollectionItem *)nextItem; + +- (NSInteger)moveItem:(LWZCollectionItem *)item toIndex:(NSInteger)index; +- (NSInteger)moveItem:(LWZCollectionItem *)item withPreviousItem:(LWZCollectionItem *)previousItem; +- (NSInteger)moveItem:(LWZCollectionItem *)item withNextItem:(LWZCollectionItem *)nextItem; +- (nullable NSIndexSet *)moveItemsInArray:(NSArray *)items withPreviousItem:(LWZCollectionItem *)previousItem; +- (nullable NSIndexSet *)moveItemsInArray:(NSArray *)items withNextItem:(LWZCollectionItem *)nextItem; + +- (NSInteger)removeItemAtIndex:(NSInteger)index; +- (NSInteger)removeItem:(LWZCollectionItem *)item; +- (nullable NSIndexSet *)removeItemsInArray:(NSArray *)items; +- (void)removeAllItems; + +- (void)setNeedsLayout; +@end + + +@interface LWZCollectionSection (LWZCollectionWaterfallFlowLayoutAdditions) +@property (nonatomic) NSInteger numberOfArrangedItemsPerLine; +@end + + +@interface LWZCollectionSection (LWZCollectionTemplateLayoutAdditions) +@property (nonatomic, strong, nullable) NSArray *layoutTemplateContainerGroups; +@end + +@interface LWZCollectionSection (LWZCollectionHybridLayoutAdditions) +@property (nonatomic) LWZCollectionLayoutType layoutType; +@end + + +@interface LWZCollectionSection (LWZCollectionCompositionalLayoutAdditions) +@property (nonatomic, getter=isOrthogonalScrolling) BOOL orthogonalScrolling; +- (CGSize)layoutSizeThatFits:(CGSize)size forOrthogonalContentAtIndex:(NSInteger)index scrollDirection:(UICollectionViewScrollDirection)scrollDirection; + +@property (nonatomic, copy, nullable) CGSize(^orthogonalContentLayoutSizeCalculator)(LWZCollectionSection *section, CGSize fittingSize, NSInteger index, UICollectionViewScrollDirection scrollDirection); +@property (nonatomic) LWZCollectionLayoutContentOrthogonalScrollingBehavior orthogonalScrollingBehavior; +@end + + +@interface LWZCollectionSection (LWZCollectionSubclassHooks) +- (void)didSelectItemAtIndexPath:(NSIndexPath *)indexPath; +- (void)didDeselectItemAtIndexPath:(NSIndexPath *)indexPath; +- (void)didBindCellForItemAtIndexPath:(NSIndexPath *)indexPath; +- (void)didUnbindCellForItemAtIndexPath:(NSIndexPath *)indexPath; +@end +NS_ASSUME_NONNULL_END diff --git a/LWZComponents/LWZCollectionViewComponents/Provider/LWZCollectionSection.m b/LWZComponents/LWZCollectionViewComponents/Provider/LWZCollectionSection.m new file mode 100755 index 0000000..40b86df --- /dev/null +++ b/LWZComponents/LWZCollectionViewComponents/Provider/LWZCollectionSection.m @@ -0,0 +1,298 @@ +// +// LWZCollectionSection.m +// LWZCollectionViewComponents_Example +// +// Created by changsanjiang on 2020/11/16. +// Copyright © 2020 changsanjiang@gmail.com. All rights reserved. +// + +#import "LWZCollectionSection.h" + +@interface LWZCollectionSection () +@property (nonatomic, strong, readonly) NSMutableArray *items; +@property (nonatomic) NSInteger numberOfArrangedItemsPerLine; // LWZCollectionWaterfallFlowLayout +@property (nonatomic) LWZCollectionLayoutType layoutType; // LWZCollectionHybridLayout +@property (nonatomic, getter=isOrthogonalScrolling) BOOL orthogonalScrolling; // LWZCollectionCompositionalLayout +- (CGSize)layoutSizeThatFits:(CGSize)size forOrthogonalContentAtIndex:(NSInteger)index scrollDirection:(UICollectionViewScrollDirection)scrollDirection; // LWZCollectionCompositionalLayout + +@property (nonatomic, copy, nullable) CGSize(^orthogonalContentLayoutSizeCalculator)(LWZCollectionSection *section, CGSize fittingSize, NSInteger index, UICollectionViewScrollDirection scrollDirection); // LWZCollectionCompositionalLayout +@property (nonatomic) LWZCollectionLayoutContentOrthogonalScrollingBehavior orthogonalScrollingBehavior; // LWZCollectionCompositionalLayout +@property (nonatomic, strong, nullable) NSArray *layoutTemplateContainerGroups; // LWZCollectionTemplateLayout +@end + +@implementation LWZCollectionSection +@synthesize hidden = _hidden; +@synthesize minimumInteritemSpacing = _minimumInteritemSpacing; +@synthesize minimumLineSpacing = _minimumLineSpacing; +@synthesize contentInsets = _contentInsets; + +- (instancetype)init { + self = [super init]; + if ( self ) { + _numberOfArrangedItemsPerLine = 1; + /// 当开启 layout.sectionHeadersPinToVisibleBounds 时, + /// layout 会询问当前 section.header 是否可以固定到顶部 + /// 请设置该值以确定是否可以固定 + _canPinToVisibleBoundsForHeader = YES; + } + return self; +} + +- (void)enumerateItemsUsingBlock:(void(NS_NOESCAPE ^)(__kindof LWZCollectionItem *item, NSInteger index, BOOL *stop))block { + [_items enumerateObjectsUsingBlock:^(LWZCollectionItem * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + block(obj, idx, stop); + }]; +} + +- (void)enumerateItemsWithOptions:(NSEnumerationOptions)opts usingBlock:(void(NS_NOESCAPE ^)(__kindof LWZCollectionItem *item, NSInteger index, BOOL *stop))block { + [_items enumerateObjectsWithOptions:opts usingBlock:^(LWZCollectionItem * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + block(obj, idx, stop); + }]; +} + +#pragma mark - + +- (BOOL)isHidden { + return _hidesAutomatically ? self.numberOfItems == 0 : _hidden; +} + +- (NSInteger)numberOfItems { + return _items.count; +} + +- (nullable __kindof LWZCollectionItem *)firstItem { + return _items.firstObject; +} + +- (nullable __kindof LWZCollectionItem *)lastItem { + return _items.lastObject; +} + +- (BOOL)containsItem:(LWZCollectionItem *)item { + return item != nil ? [_items containsObject:item] : NO; +} + +- (NSInteger)indexForItem:(LWZCollectionItem *)item { + return item != nil ? [_items indexOfObject:item] : NSNotFound; +} + +- (nullable NSIndexSet *)indexesForItemsInArray:(NSArray *)items { + if ( items.count != 0 ) { + NSMutableIndexSet *set = [NSMutableIndexSet.alloc init]; + for ( LWZCollectionItem * item in _items ) { + NSInteger index = [self indexForItem:item]; + if ( index == NSNotFound ) + return nil; + [set addIndex:index]; + } + return set.copy; + } + return nil; +} + +- (nullable __kindof LWZCollectionItem *)itemAtIndex:(NSInteger)index { + return [self _isSafeIndexForGetting:index] ? _items[index] : nil; +} + +- (NSInteger)addItem:(LWZCollectionItem *)item { + if ( item != nil ) { + NSInteger location = _items.count; + [self.items addObject:item]; + return location; + } + return NSNotFound; +} + +- (nullable NSIndexSet *)addItemsFromArray:(NSArray *)items { + if ( items.count != 0 ) { + NSInteger location = _items.count; + [self.items addObjectsFromArray:items]; + return [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(location, items.count)]; + } + return nil; +} + +- (NSInteger)insertItem:(LWZCollectionItem *)item atIndex:(NSInteger)index { + if ( item != nil && [self _isSafeIndexForInserting:index] ) { + [_items insertObject:item atIndex:index]; + return index; + } + return NSNotFound; +} + +- (NSInteger)insertItem:(LWZCollectionItem *)item withPreviousItem:(LWZCollectionItem *)previousItem { + if ( item != nil && [self containsItem:previousItem] ) { + NSInteger index = [self indexForItem:previousItem] + 1; + [self.items insertObject:item atIndex:index]; + return index; + } + return NSNotFound; +} + +- (nullable NSIndexSet *)insertItems:(NSArray *)items withPreviousItem:(LWZCollectionItem *)previousItem { + if ( items.count != 0 ) { + NSMutableIndexSet *set = [NSMutableIndexSet.alloc init]; + for ( NSInteger i = items.count - 1 ; i >= 0 ; -- i ) { + [set addIndex:[self insertItem:items[i] withPreviousItem:previousItem]]; + } + return set.copy; + } + return nil; +} + +- (NSInteger)insertItem:(LWZCollectionItem *)item withNextItem:(LWZCollectionItem *)nextItem { + if ( item != nil && [self containsItem:nextItem] ) { + NSInteger index = [self indexForItem:nextItem]; + [self.items insertObject:item atIndex:index]; + return index; + } + return NSNotFound; +} + +- (nullable NSIndexSet *)insertItems:(NSArray *)items withNextItem:(LWZCollectionItem *)nextItem { + if ( items.count != 0 ) { + NSMutableIndexSet *set = [NSMutableIndexSet.alloc init]; + for ( NSInteger i = 0 ; i < items.count ; ++ i ) { + [set addIndex:[self insertItem:items[i] withNextItem:nextItem]]; + } + return set.copy; + } + return nil; +} + +- (NSInteger)insertItemToTop:(LWZCollectionItem *)item { + if ( item != nil ) { + [self.items insertObject:item atIndex:0]; + return 0; + } + return NSNotFound; +} + +- (NSInteger)moveItem:(LWZCollectionItem *)item toIndex:(NSInteger)index { + if ( [self containsItem:item] ) { + LWZCollectionItem *sourceItem = [self itemAtIndex:index]; + if ( sourceItem != nil ) { + [self removeItem:item]; + return [self insertItem:item withNextItem:sourceItem]; + } + } + return NSNotFound; +} + +- (NSInteger)moveItem:(LWZCollectionItem *)i1 withPreviousItem:(LWZCollectionItem *)i2 { + if ( i1 != i2 && [self containsItem:i1] && [self containsItem:i2] ) { + [self removeItem:i1]; + [self insertItem:i1 withPreviousItem:i2]; + return [self indexForItem:i1]; + } + return NSNotFound; +} + +- (nullable NSIndexSet *)moveItemsInArray:(NSArray *)items withPreviousItem:(LWZCollectionItem *)previousItem { + if ( items.count != 0 && [self containsItem:previousItem] ) { + NSMutableIndexSet *set = [NSMutableIndexSet.alloc init]; + for ( NSInteger i = items.count - 1 ; i >= 0 ; -- i ) { + [set addIndex:[self moveItem:items[i] withPreviousItem:previousItem]]; + } + return set.copy; + } + return nil; +} + +- (NSInteger)moveItem:(LWZCollectionItem *)i1 withNextItem:(LWZCollectionItem *)i2 { + if ( i1 != i2 && [self containsItem:i1] && [self containsItem:i2] ) { + [self removeItem:i1]; + [self insertItem:i1 withNextItem:i2]; + return [self indexForItem:i1]; + } + return NSNotFound; +} + +- (nullable NSIndexSet *)moveItemsInArray:(NSArray *)items withNextItem:(LWZCollectionItem *)nextItem { + if ( items.count != 0 && [self containsItem:nextItem] ) { + NSMutableIndexSet *set = [NSMutableIndexSet.alloc init]; + for ( NSInteger i = 0 ; i < items.count ; ++ i ) { + [set addIndex:[self moveItem:items[i] withNextItem:nextItem]]; + } + return set.copy; + } + return nil; +} + +- (NSInteger)removeItemAtIndex:(NSInteger)index { + if ( [self _isSafeIndexForGetting:index] ) { + [_items removeObjectAtIndex:index]; + return index; + } + return NSNotFound; +} + +- (NSInteger)removeItem:(LWZCollectionItem *)item { + NSInteger index = [self indexForItem:item]; + if ( index != NSNotFound ) { + [_items removeObjectAtIndex:index]; + } + return index; +} + +- (nullable NSIndexSet *)removeItemsInArray:(NSArray *)items { + NSIndexSet *set = [self indexesForItemsInArray:items]; + if ( set != nil ) { + NSUInteger currentIndex = set.lastIndex; + do { + [_items removeObjectAtIndex:currentIndex]; + currentIndex = [set indexLessThanIndex:currentIndex]; + } while ( currentIndex != NSNotFound ); + } + return nil; +} + +- (void)removeAllItems { + [_items removeAllObjects]; +} + +- (void)setNeedsLayout { + [_decoration setNeedsLayout]; + [_header setNeedsLayout]; + + for ( LWZCollectionItem * item in _items ) { + [item setNeedsLayout]; + } + + [_footer setNeedsLayout]; +} + +- (CGSize)layoutSizeThatFits:(CGSize)size forOrthogonalContentAtIndex:(NSInteger)index scrollDirection:(UICollectionViewScrollDirection)scrollDirection { + if ( _orthogonalContentLayoutSizeCalculator != nil ) + return _orthogonalContentLayoutSizeCalculator(self, size, index, scrollDirection); + + @throw [NSException exceptionWithName:NSInternalInconsistencyException + reason:[NSString stringWithFormat:@"You must override %@ in a subclass.", NSStringFromSelector(_cmd)] + userInfo:nil]; +} + +#pragma mark - + +- (void)didSelectItemAtIndexPath:(NSIndexPath *)indexPath {} +- (void)didDeselectItemAtIndexPath:(NSIndexPath *)indexPath {} +- (void)didBindCellForItemAtIndexPath:(NSIndexPath *)indexPath {} +- (void)didUnbindCellForItemAtIndexPath:(NSIndexPath *)indexPath {} + +#pragma mark - + +@synthesize items = _items; +- (NSMutableArray *)items { + if ( _items == nil ) { + _items = NSMutableArray.array; + } + return _items; +} + +- (BOOL)_isSafeIndexForGetting:(NSInteger)index { + return index > -1 && index < _items.count; +} + +- (BOOL)_isSafeIndexForInserting:(NSInteger)index { + return index > -1 && index <= _items.count; +} +@end diff --git a/LWZComponents/LWZCollectionViewComponents/Provider/LWZCollectionSectionHeaderFooter.h b/LWZComponents/LWZCollectionViewComponents/Provider/LWZCollectionSectionHeaderFooter.h new file mode 100644 index 0000000..ac8dd7d --- /dev/null +++ b/LWZComponents/LWZCollectionViewComponents/Provider/LWZCollectionSectionHeaderFooter.h @@ -0,0 +1,27 @@ +// +// LWZCollectionSectionHeaderFooter.h +// LWZAppComponents +// +// Created by changsanjiang on 2021/8/25. +// + +#import +@class LWZCollectionSection, LWZCollectionDecoration; + +NS_ASSUME_NONNULL_BEGIN +@interface LWZCollectionSectionHeaderFooter : NSObject +@property (nonatomic, readonly) Class viewClass; +@property (nonatomic, readonly, nullable) NSBundle *viewNibBundle; +@property (nonatomic, readonly, nullable) UINib *viewNib; +@property (nonatomic, readonly) BOOL needsLayout; + +- (void)setNeedsLayout NS_REQUIRES_SUPER; +- (CGSize)layoutSizeThatFits:(CGSize)size inSection:(__kindof LWZCollectionSection *)section atIndex:(NSInteger)index scrollDirection:(UICollectionViewScrollDirection)scrollDirection; + +@property (nonatomic) NSInteger zPosition; +@property (nonatomic, strong, nullable) LWZCollectionDecoration *decoration; + +- (void)bindView:(__kindof UICollectionReusableView *)view inSection:(NSInteger)section; +- (void)unbindView:(__kindof UICollectionReusableView *)view inSection:(NSInteger)section; +@end +NS_ASSUME_NONNULL_END diff --git a/LWZComponents/LWZCollectionViewComponents/Provider/LWZCollectionSectionHeaderFooter.m b/LWZComponents/LWZCollectionViewComponents/Provider/LWZCollectionSectionHeaderFooter.m new file mode 100644 index 0000000..ccb7007 --- /dev/null +++ b/LWZComponents/LWZCollectionViewComponents/Provider/LWZCollectionSectionHeaderFooter.m @@ -0,0 +1,63 @@ +// +// LWZCollectionSectionHeaderFooter.m +// LWZAppComponents +// +// Created by changsanjiang on 2021/8/25. +// + +#import "LWZCollectionSectionHeaderFooter.h" +#import "LWZCollectionDecoration.h" + +@interface LWZCollectionSectionHeaderFooter () +@property (nonatomic) CGSize layoutSize; +@end + +@implementation LWZCollectionSectionHeaderFooter { + BOOL _needsLayout; +} + +- (instancetype)init { + self = [super init]; + if ( self ) { + _needsLayout = YES; + } + return self; +} + +- (Class)viewClass { + @throw [NSException exceptionWithName:NSInternalInconsistencyException + reason:[NSString stringWithFormat:@"You must override %@ in a subclass.", NSStringFromSelector(_cmd)] + userInfo:nil]; +} + +- (BOOL)needsLayout { + return _needsLayout; +} + +- (void)setNeedsLayout { + _needsLayout = YES; + if ( _decoration != nil ) [_decoration setNeedsLayout]; +} + +- (CGSize)layoutSizeThatFits:(CGSize)size inSection:(__kindof LWZCollectionSection *)section atIndex:(NSInteger)index scrollDirection:(UICollectionViewScrollDirection)scrollDirection { + @throw [NSException exceptionWithName:NSInternalInconsistencyException + reason:[NSString stringWithFormat:@"You must override %@ in a subclass.", NSStringFromSelector(_cmd)] + userInfo:nil]; +} + +- (void)willDisplaySupplementaryView:(__kindof UICollectionReusableView *)view forElementKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath { + [self bindView:view inSection:indexPath.section]; + _needsLayout = NO; +} + +- (void)didEndDisplayingSupplementaryView:(__kindof UICollectionReusableView *)view forElementOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath { + [self unbindView:view inSection:indexPath.section]; +} + +- (void)removeDecoration { + _decoration = nil; +} + +- (void)bindView:(__kindof UICollectionReusableView *)view inSection:(NSInteger)section { } +- (void)unbindView:(__kindof UICollectionReusableView *)view inSection:(NSInteger)section { } +@end diff --git a/LWZComponents/LWZCollectionViewComponents/View/LWZCollectionView.h b/LWZComponents/LWZCollectionViewComponents/View/LWZCollectionView.h new file mode 100755 index 0000000..e4fd7b8 --- /dev/null +++ b/LWZComponents/LWZCollectionViewComponents/View/LWZCollectionView.h @@ -0,0 +1,16 @@ +// +// LWZCollectionView.h +// LWZFoundation +// +// Created by changsanjiang on 2020/12/22. +// + +#import "LWZCollectionViewPresenter.h" + +NS_ASSUME_NONNULL_BEGIN +@interface LWZCollectionView : UICollectionView + +@property (nonatomic, weak, nullable) LWZCollectionViewPresenter *dataSource; + +@end +NS_ASSUME_NONNULL_END diff --git a/LWZComponents/LWZCollectionViewComponents/View/LWZCollectionView.m b/LWZComponents/LWZCollectionViewComponents/View/LWZCollectionView.m new file mode 100755 index 0000000..92bb0d6 --- /dev/null +++ b/LWZComponents/LWZCollectionViewComponents/View/LWZCollectionView.m @@ -0,0 +1,55 @@ +// +// LWZCollectionView.m +// LWZFoundation +// +// Created by changsanjiang on 2020/12/22. +// + +#import "LWZCollectionView.h" +#import "LWZCollectionViewDelegateProxy.h" + +@implementation LWZCollectionView { + LWZCollectionViewDelegateProxy *_mDelegateProxy2; +} +@dynamic dataSource; + +- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout { + self = [super initWithFrame:frame collectionViewLayout:layout]; + if ( self ) { + self.backgroundColor = UIColor.whiteColor; + if (@available(iOS 11.0, *)) { + self.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; + } + + _mDelegateProxy2 = [LWZCollectionViewDelegateProxy.alloc initWithCollectionView:self]; + } + return self; +} + +- (void)setDataSource:(nullable LWZCollectionViewPresenter *)dataSource { + [super setDataSource:dataSource]; + _mDelegateProxy2.presenter = dataSource; + if ( dataSource != nil && self.delegate == nil ) self.delegate = dataSource; +} + +/// delegate 将增加一层proxy, 使得 presenter 能够 hook UICollectionViewDelegate 相关方法 +- (void)setDelegate:(nullable id)delegate { + _mDelegateProxy2.delegate = delegate; + [super setDelegate:delegate]; +} + +#ifdef DEBUG + +- (void)reloadData { + [super reloadData]; +} + +- (void)setContentOffset:(CGPoint)contentOffset { + [super setContentOffset:contentOffset]; +} + +- (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated { + [super setContentOffset:contentOffset animated:animated]; +} +#endif +@end diff --git a/LWZComponents/LWZCollectionViewComponents/View/LWZCollectionViewDelegateProxy.h b/LWZComponents/LWZCollectionViewComponents/View/LWZCollectionViewDelegateProxy.h new file mode 100644 index 0000000..040c855 --- /dev/null +++ b/LWZComponents/LWZCollectionViewComponents/View/LWZCollectionViewDelegateProxy.h @@ -0,0 +1,19 @@ +// +// LWZCollectionViewDelegateProxy.h +// SJTestAutoLayout_Example +// +// Created by changsanjiang on 2021/11/13. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import +@class LWZCollectionViewPresenter; + +NS_ASSUME_NONNULL_BEGIN +@interface LWZCollectionViewDelegateProxy : NSObject +- (instancetype)initWithCollectionView:(UICollectionView *)collectionView; +@property (nonatomic, weak, readonly, nullable) UICollectionView *collectionView; +@property (nonatomic, weak, nullable) id delegate; +@property (nonatomic, weak, nullable) LWZCollectionViewPresenter *presenter; +@end +NS_ASSUME_NONNULL_END diff --git a/LWZComponents/LWZCollectionViewComponents/View/LWZCollectionViewDelegateProxy.m b/LWZComponents/LWZCollectionViewComponents/View/LWZCollectionViewDelegateProxy.m new file mode 100644 index 0000000..fc0fac7 --- /dev/null +++ b/LWZComponents/LWZCollectionViewComponents/View/LWZCollectionViewDelegateProxy.m @@ -0,0 +1,141 @@ +// +// LWZCollectionViewDelegateProxy.m +// SJTestAutoLayout_Example +// +// Created by changsanjiang on 2021/11/13. +// Copyright © 2021 changsanjiang@gmail.com. All rights reserved. +// + +#import "LWZCollectionViewDelegateProxy.h" +#import "LWZCollectionViewPresenter.h" +#import + +/// +/// 这里个协议列出来 presenter 需要 hook 的代理方法 +/// 当以后 presenter 有新的 hook 需要时, 不要忘记修改此处及proxy的处理 +/// +@protocol LWZCollectionViewDelegateProxyMethods +- (void)cvd_proxy_collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath; +- (void)cvd_proxy_collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath; +- (void)cvd_proxy_collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath; +- (void)cvd_proxy_collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath; +- (void)cvd_proxy_collectionView:(UICollectionView *)collectionView willDisplaySupplementaryView:(UICollectionReusableView *)view forElementKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath; +- (void)cvd_proxy_collectionView:(UICollectionView *)collectionView didEndDisplayingSupplementaryView:(UICollectionReusableView *)view forElementOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath; +@end + +@implementation LWZCollectionViewDelegateProxy +static void *kProxy = &kProxy; + +static inline Class +cvd_proxy_get_method_defined_root_class(Class aClass, SEL originalSelector) { + Class currentClass = aClass; + Class rootClass = currentClass; + while ( currentClass != NULL ) { + if ( class_getInstanceMethod(currentClass, originalSelector) != NULL ) { + rootClass = currentClass; + } + currentClass = class_getSuperclass(currentClass); + } + return rootClass; +} + +static void cvd_proxy_empty_impl() { } + +static void cvd_proxy_swizzleSelector(Class aClass, SEL originalSelector, SEL swizzledSelector) { + Method proxyMethod = class_getInstanceMethod(LWZCollectionViewDelegateProxy.class, swizzledSelector); + const char *methodType = method_getTypeEncoding(proxyMethod); + IMP proxyIMP = method_getImplementation(proxyMethod); + + Class rootClass = cvd_proxy_get_method_defined_root_class(aClass, originalSelector); + if ( class_getInstanceMethod(rootClass, originalSelector) == NULL ) { + class_addMethod(rootClass, originalSelector, (IMP)cvd_proxy_empty_impl, methodType); + } + + Class currentClass = aClass; + while ( class_getInstanceMethod(currentClass, originalSelector) ) { + Class superClass = class_getSuperclass(currentClass); + Method currentMethod = class_getInstanceMethod(currentClass, originalSelector); + Method superMethod = class_getInstanceMethod(superClass, originalSelector); + IMP currentIMP = method_getImplementation(currentMethod); + IMP superIMP = method_getImplementation(superMethod); + if ( currentIMP != superIMP && currentIMP != proxyIMP ) { + if ( class_addMethod(currentClass, swizzledSelector, proxyIMP, methodType) ) { + method_exchangeImplementations(currentMethod, class_getInstanceMethod(currentClass, swizzledSelector)); + } + } + currentClass = superClass; + } +} + +static void cvd_proxy_init(Class aClass) { + static void *kFlag = &kFlag; + if ( objc_getAssociatedObject(aClass, kFlag) != nil ) return; + static NSString *kValue = @"a"; + objc_setAssociatedObject(aClass, kFlag, kValue, OBJC_ASSOCIATION_ASSIGN); + + cvd_proxy_swizzleSelector(aClass, @selector(collectionView:didSelectItemAtIndexPath:), @selector(cvd_proxy_collectionView:didSelectItemAtIndexPath:)); + cvd_proxy_swizzleSelector(aClass, @selector(collectionView:didDeselectItemAtIndexPath:), @selector(cvd_proxy_collectionView:didDeselectItemAtIndexPath:)); + cvd_proxy_swizzleSelector(aClass, @selector(collectionView:willDisplayCell:forItemAtIndexPath:), @selector(cvd_proxy_collectionView:willDisplayCell:forItemAtIndexPath:)); + cvd_proxy_swizzleSelector(aClass, @selector(collectionView:didEndDisplayingCell:forItemAtIndexPath:), @selector(cvd_proxy_collectionView:didEndDisplayingCell:forItemAtIndexPath:)); + cvd_proxy_swizzleSelector(aClass, @selector(collectionView:willDisplaySupplementaryView:forElementKind:atIndexPath:), @selector(cvd_proxy_collectionView:willDisplaySupplementaryView:forElementKind:atIndexPath:)); + cvd_proxy_swizzleSelector(aClass, @selector(collectionView:didEndDisplayingSupplementaryView:forElementOfKind:atIndexPath:), @selector(cvd_proxy_collectionView:didEndDisplayingSupplementaryView:forElementOfKind:atIndexPath:)); +} + +- (instancetype)initWithCollectionView:(UICollectionView *)collectionView { + self = [super init]; + if ( self ) { + _collectionView = collectionView; + } + return self; +} + +- (void)setDelegate:(nullable id)delegate { + if ( delegate != _delegate ) { + if ( delegate == _presenter || [delegate isKindOfClass:LWZCollectionViewPresenter.class] ) return; + + _delegate = delegate; + if ( delegate != nil ) { + objc_setAssociatedObject(delegate, kProxy, self, OBJC_ASSOCIATION_ASSIGN); + cvd_proxy_init(delegate.class); + } + } +} + +#pragma mark - UICollectionViewDelegate + +- (void)cvd_proxy_collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { + LWZCollectionViewDelegateProxy *proxy = objc_getAssociatedObject(self, kProxy); + [proxy.presenter collectionView:collectionView didSelectItemAtIndexPath:indexPath]; + [self cvd_proxy_collectionView:collectionView didSelectItemAtIndexPath:indexPath]; +} + +- (void)cvd_proxy_collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath { + LWZCollectionViewDelegateProxy *proxy = objc_getAssociatedObject(self, kProxy); + [proxy.presenter collectionView:collectionView didDeselectItemAtIndexPath:indexPath]; + [self cvd_proxy_collectionView:collectionView didDeselectItemAtIndexPath:indexPath]; +} + +- (void)cvd_proxy_collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath { + LWZCollectionViewDelegateProxy *proxy = objc_getAssociatedObject(self, kProxy); + [proxy.presenter collectionView:collectionView willDisplayCell:cell forItemAtIndexPath:indexPath]; + [self cvd_proxy_collectionView:collectionView willDisplayCell:cell forItemAtIndexPath:indexPath]; +} + +- (void)cvd_proxy_collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath { + LWZCollectionViewDelegateProxy *proxy = objc_getAssociatedObject(self, kProxy); + [proxy.presenter collectionView:collectionView didEndDisplayingCell:cell forItemAtIndexPath:indexPath]; + [self cvd_proxy_collectionView:collectionView didEndDisplayingCell:cell forItemAtIndexPath:indexPath]; +} + +- (void)cvd_proxy_collectionView:(UICollectionView *)collectionView willDisplaySupplementaryView:(UICollectionReusableView *)view forElementKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath { + LWZCollectionViewDelegateProxy *proxy = objc_getAssociatedObject(self, kProxy); + [proxy.presenter collectionView:collectionView willDisplaySupplementaryView:view forElementKind:elementKind atIndexPath:indexPath]; + [self cvd_proxy_collectionView:collectionView willDisplaySupplementaryView:view forElementKind:elementKind atIndexPath:indexPath]; +} + +- (void)cvd_proxy_collectionView:(UICollectionView *)collectionView didEndDisplayingSupplementaryView:(UICollectionReusableView *)view forElementOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath { + LWZCollectionViewDelegateProxy *proxy = objc_getAssociatedObject(self, kProxy); + [proxy.presenter collectionView:collectionView didEndDisplayingSupplementaryView:view forElementOfKind:elementKind atIndexPath:indexPath]; + [self cvd_proxy_collectionView:collectionView didEndDisplayingSupplementaryView:view forElementOfKind:elementKind atIndexPath:indexPath]; +} +@end diff --git a/LWZComponents/LWZComponents.h b/LWZComponents/LWZComponents.h new file mode 100644 index 0000000..2b40f69 --- /dev/null +++ b/LWZComponents/LWZComponents.h @@ -0,0 +1,13 @@ +// +// LWZComponents.h +// Pods +// +// Created by 蓝舞者 on 2021/12/25. +// + +#ifndef LWZComponents_h +#define LWZComponents_h + +#import "LWZCollectionViewComponents.h" + +#endif /* LWZComponents_h */