diff --git a/CHANGELOG.md b/CHANGELOG.md index 2942825..9eaed7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,37 @@ # 版本变更日志 **MyLayout**的所有版本的变更日志都将会在这里记录. +## [V2.0.0](https://github.com/youngsoft/MyLinearLayout/releases/tag/2.0.0)(2026/03/15) +### Add +1. 为流式布局MyFlowLayout添加新的`maxLines`属性和`arrangedLines`属性。前者用来设置最大可显示的行列数,后者用来获取当前的行列数,具体例子详见[FLLTest10ViewController](https://github.com/youngsoft/MyLinearLayout/blob/master/MyLayoutDemo/FLLTest10ViewController.m)中的demo. +2. 添加弹性布局MyFlexLayout对`space-evenly`属性值的支持。 + +### Update + 1. 系统大重构,对布局特性做进一步的抽象。将原先的fetchLayoutSizeClass方法改名为fetchLayoutTraitsInSizeClass,并且返回为一个id 或者派生接口。布局中的所有属性都可以用id中的属性来描述和设置。 + 2. 优化MyLayoutDragger的实现,添加了一个MyLayoutDraggerDelegate协议接口,以便支持实际的拖放操作逻辑处理。 + 3. 删除对系统过期对象id 的支持,在MyLayoutPos中的此种位置类型值不再被支持!! 请用 @(MyLayoutPos.safeAreaMargin) + 4. 对布局中的关于间距的命名由space统一改名为spacing。这里可能会造成一些错误编译提示,请正确命名: + + 旧命名|新命名 + -------|--------------- + subviewSpace | subviewSpacing + subviewHSpace|subviewHSpacing + subviewVSpace|subviewVSpacing + MyPathSpaceType|MyPathSpacingType + MyPathSpace|MyPathSpacing + MyPathLayout.spaceType|MyPathLayout.spacingType + kMyGridSpace|kMyGridSpacing + MySubviewsShrinkType.MySubviewsShrink_Space | MySubviewsShrinkType.MySubviewsShrink_spacing + MySubviewsShrinkType.MySubviewsShrink_SizeAndSpace | MySubviewsShrinkType.MySubviewsShrink_SizeAndSpacing + + 5. 将MyFlexLayout中的horz_space和vert_space名字改为horz_spacing和vert_spacing + 6. 将MyGridLayout中的space改为spacing + + ### Fixed + 1. 修复相对布局MyRelativeLayout中子视图设置visibility为Gone时的尺寸评估错误的缺陷。 + + + ## [V1.9.9](https://github.com/youngsoft/MyLinearLayout/releases/tag/1.9.9)(2022/12/22) ### Add 1. 扩充了栅格布局MyGridLayout中行列的自适应尺寸计算的能力。在老版本中如果某个格子的行高为自适应,那么这个格子的子格子只能是行。新版本中自适应行高的格子可以拆分为列子格子,同样自适应列宽的格子可以拆分为行子格子。 diff --git a/MyLayout.podspec b/MyLayout.podspec index 6af7531..ce7faf4 100644 --- a/MyLayout.podspec +++ b/MyLayout.podspec @@ -16,7 +16,7 @@ Pod::Spec.new do |s| # s.name = "MyLayout" - s.version = "1.9.10" + s.version = "2.0.0" s.summary = "MyLayout is an iOS UI framework integrates the functions with Android,AutoLayout,SizeClass,HTML CSS float and flexbox,UIView UITableView." s.description = <<-DESC @@ -90,7 +90,7 @@ Pod::Spec.new do |s| #s.exclude_files = "Classes/Exclude" s.public_header_files = "MyLayout/Lib/*.h" - s.private_header_files = "MyLayout/Lib/{*Inner.h,MyLayoutDelegate.h,MyLayoutMath.h,MyGridNode.h,MyLayoutSizeClass.h}" + s.private_header_files = "MyLayout/Lib/{*Inner.h,MyLayoutDelegate.h,MyLayoutMath.h,MyGridNode.h,MyLayoutTraitsImpl.h}" end diff --git a/MyLayout.xcodeproj/project.pbxproj b/MyLayout.xcodeproj/project.pbxproj index ffe2c57..0c24a1d 100644 --- a/MyLayout.xcodeproj/project.pbxproj +++ b/MyLayout.xcodeproj/project.pbxproj @@ -20,7 +20,7 @@ 182225CE1E77E5930081AA4B /* MyFrameLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 182225B71E77E5930081AA4B /* MyFrameLayout.m */; }; 182225CF1E77E5930081AA4B /* MyFloatLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 182225B91E77E5930081AA4B /* MyFloatLayout.m */; }; 182225D01E77E5930081AA4B /* MyLayoutPos.m in Sources */ = {isa = PBXBuildFile; fileRef = 182225BB1E77E5930081AA4B /* MyLayoutPos.m */; }; - 182225D11E77E5930081AA4B /* MyLayoutSizeClass.m in Sources */ = {isa = PBXBuildFile; fileRef = 182225BF1E77E5930081AA4B /* MyLayoutSizeClass.m */; }; + 182225D11E77E5930081AA4B /* MyLayoutTraitsImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = 182225BF1E77E5930081AA4B /* MyLayoutTraitsImpl.m */; }; 182225D21E77E5930081AA4B /* MyMaker.m in Sources */ = {isa = PBXBuildFile; fileRef = 182225C11E77E5930081AA4B /* MyMaker.m */; }; 182225D31E77E5930081AA4B /* MyTableLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 182225C31E77E5930081AA4B /* MyTableLayout.m */; }; 182225D41E77E5930081AA4B /* MyDimeScale.m in Sources */ = {isa = PBXBuildFile; fileRef = 182225C41E77E5930081AA4B /* MyDimeScale.m */; }; @@ -44,7 +44,7 @@ 18C15AF41EDF140400AADEAC /* MyBaseLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 182225AF1E77E5930081AA4B /* MyBaseLayout.h */; settings = {ATTRIBUTES = (Public, ); }; }; 18C15AF51EDF140D00AADEAC /* MyLayoutPos.h in Headers */ = {isa = PBXBuildFile; fileRef = 182225B01E77E5930081AA4B /* MyLayoutPos.h */; settings = {ATTRIBUTES = (Public, ); }; }; 18C15AF61EDF141000AADEAC /* MyLayoutSize.h in Headers */ = {isa = PBXBuildFile; fileRef = 182225BC1E77E5930081AA4B /* MyLayoutSize.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 18C15AF71EDF141600AADEAC /* MyLayoutSizeClass.h in Headers */ = {isa = PBXBuildFile; fileRef = 182225C01E77E5930081AA4B /* MyLayoutSizeClass.h */; }; + 18C15AF71EDF141600AADEAC /* MyLayoutTraitsImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 182225C01E77E5930081AA4B /* MyLayoutTraitsImpl.h */; }; 18C15AF81EDF141A00AADEAC /* MyMaker.h in Headers */ = {isa = PBXBuildFile; fileRef = 182225C21E77E5930081AA4B /* MyMaker.h */; settings = {ATTRIBUTES = (Public, ); }; }; 18C15AF91EDF142000AADEAC /* MyDimeScale.h in Headers */ = {isa = PBXBuildFile; fileRef = 182225C71E77E5930081AA4B /* MyDimeScale.h */; settings = {ATTRIBUTES = (Public, ); }; }; 18C15AFA1EDF142300AADEAC /* MyLayoutDef.h in Headers */ = {isa = PBXBuildFile; fileRef = 182225B31E77E5930081AA4B /* MyLayoutDef.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -58,7 +58,7 @@ 18C15B021EDF144B00AADEAC /* MyBaseLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 182225B11E77E5930081AA4B /* MyBaseLayout.m */; }; 18C15B031EDF144B00AADEAC /* MyLayoutPos.m in Sources */ = {isa = PBXBuildFile; fileRef = 182225BB1E77E5930081AA4B /* MyLayoutPos.m */; }; 18C15B041EDF144B00AADEAC /* MyLayoutSize.m in Sources */ = {isa = PBXBuildFile; fileRef = 182225C51E77E5930081AA4B /* MyLayoutSize.m */; }; - 18C15B051EDF144B00AADEAC /* MyLayoutSizeClass.m in Sources */ = {isa = PBXBuildFile; fileRef = 182225BF1E77E5930081AA4B /* MyLayoutSizeClass.m */; }; + 18C15B051EDF144B00AADEAC /* MyLayoutTraitsImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = 182225BF1E77E5930081AA4B /* MyLayoutTraitsImpl.m */; }; 18C15B061EDF144B00AADEAC /* MyMaker.m in Sources */ = {isa = PBXBuildFile; fileRef = 182225C11E77E5930081AA4B /* MyMaker.m */; }; 18C15B071EDF144B00AADEAC /* MyDimeScale.m in Sources */ = {isa = PBXBuildFile; fileRef = 182225C41E77E5930081AA4B /* MyDimeScale.m */; }; 18D3C9111EDF074900D3DE43 /* LLTest1ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 18D3C9041EDF074900D3DE43 /* LLTest1ViewController.m */; }; @@ -139,6 +139,7 @@ 200A26D91FA8A90300B06E7B /* AllTest9ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 200A26D81FA8A90300B06E7B /* AllTest9ViewController.m */; }; 200A26DC1FA96B2400B06E7B /* AllTest9CollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 200A26DB1FA96B2400B06E7B /* AllTest9CollectionViewCell.m */; }; 202A5A0D2950A1E60076DD27 /* AllTest9WaterFlowLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 202A5A0C2950A1E60076DD27 /* AllTest9WaterFlowLayout.m */; }; + 2040F5342CBC0CA800C85B1C /* FLLTest10ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2040F5332CBC0CA800C85B1C /* FLLTest10ViewController.m */; }; 204DC321209D5A8700F6CB57 /* TLTest4ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 204DC320209D5A8600F6CB57 /* TLTest4ViewController.m */; }; 205642861F4CFF9B00E8BDDE /* MyBorderline.h in Headers */ = {isa = PBXBuildFile; fileRef = 205642841F4CFF9B00E8BDDE /* MyBorderline.h */; settings = {ATTRIBUTES = (Public, ); }; }; 205642871F4CFF9B00E8BDDE /* MyBorderline.m in Sources */ = {isa = PBXBuildFile; fileRef = 205642851F4CFF9B00E8BDDE /* MyBorderline.m */; }; @@ -163,6 +164,7 @@ 208157BD1F586AA700E945B6 /* DetailViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 208157BC1F586AA700E945B6 /* DetailViewController.m */; }; 208157C31F5A886400E945B6 /* MyLayoutDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 208157C11F5A886300E945B6 /* MyLayoutDelegate.m */; }; 208157C41F5A886400E945B6 /* MyLayoutDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 208157C11F5A886300E945B6 /* MyLayoutDelegate.m */; }; + 2089F6352CDA68C5000C4A9A /* MyLayoutTraits.h in Headers */ = {isa = PBXBuildFile; fileRef = 2089F6342CDA68C5000C4A9A /* MyLayoutTraits.h */; settings = {ATTRIBUTES = (Public, ); }; }; 2094619E2341FD6F005F5C8E /* AllTestExampleViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2094619D2341FD6F005F5C8E /* AllTestExampleViewController.m */; }; 20A45EC52318FCA50026A18C /* MyFlexLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 20DF42402318F4EA0078CAA0 /* MyFlexLayout.m */; }; 20B46A8D1F8A79C300826372 /* layoutdemo8.gif in Resources */ = {isa = PBXBuildFile; fileRef = 20B46A8C1F8A79C300826372 /* layoutdemo8.gif */; }; @@ -235,8 +237,8 @@ 182225BC1E77E5930081AA4B /* MyLayoutSize.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MyLayoutSize.h; sourceTree = ""; }; 182225BD1E77E5930081AA4B /* MyPathLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MyPathLayout.h; sourceTree = ""; }; 182225BE1E77E5930081AA4B /* MyRelativeLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MyRelativeLayout.h; sourceTree = ""; }; - 182225BF1E77E5930081AA4B /* MyLayoutSizeClass.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = MyLayoutSizeClass.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; - 182225C01E77E5930081AA4B /* MyLayoutSizeClass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MyLayoutSizeClass.h; sourceTree = ""; }; + 182225BF1E77E5930081AA4B /* MyLayoutTraitsImpl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = MyLayoutTraitsImpl.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; + 182225C01E77E5930081AA4B /* MyLayoutTraitsImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MyLayoutTraitsImpl.h; sourceTree = ""; }; 182225C11E77E5930081AA4B /* MyMaker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MyMaker.m; sourceTree = ""; }; 182225C21E77E5930081AA4B /* MyMaker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MyMaker.h; sourceTree = ""; }; 182225C31E77E5930081AA4B /* MyTableLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MyTableLayout.m; sourceTree = ""; }; @@ -393,6 +395,8 @@ 200A26DB1FA96B2400B06E7B /* AllTest9CollectionViewCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AllTest9CollectionViewCell.m; sourceTree = ""; }; 202A5A0B2950A1E60076DD27 /* AllTest9WaterFlowLayout.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AllTest9WaterFlowLayout.h; sourceTree = ""; }; 202A5A0C2950A1E60076DD27 /* AllTest9WaterFlowLayout.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AllTest9WaterFlowLayout.m; sourceTree = ""; }; + 2040F5322CBC0C9400C85B1C /* FLLTest10ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLLTest10ViewController.h; sourceTree = ""; }; + 2040F5332CBC0CA800C85B1C /* FLLTest10ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLLTest10ViewController.m; sourceTree = ""; }; 2043D5DB20995DF800276C68 /* CoreBluetooth.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreBluetooth.framework; path = System/Library/Frameworks/CoreBluetooth.framework; sourceTree = SDKROOT; }; 204DC31F209D5A7C00F6CB57 /* TLTest4ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TLTest4ViewController.h; sourceTree = ""; }; 204DC320209D5A8600F6CB57 /* TLTest4ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TLTest4ViewController.m; sourceTree = ""; }; @@ -416,6 +420,7 @@ 208157BC1F586AA700E945B6 /* DetailViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DetailViewController.m; sourceTree = ""; }; 208157C01F5A886300E945B6 /* MyLayoutDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MyLayoutDelegate.h; sourceTree = ""; }; 208157C11F5A886300E945B6 /* MyLayoutDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MyLayoutDelegate.m; sourceTree = ""; }; + 2089F6342CDA68C5000C4A9A /* MyLayoutTraits.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MyLayoutTraits.h; sourceTree = ""; }; 2094619C2341FD5E005F5C8E /* AllTestExampleViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AllTestExampleViewController.h; sourceTree = ""; }; 2094619D2341FD6F005F5C8E /* AllTestExampleViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AllTestExampleViewController.m; sourceTree = ""; }; 20B46A8C1F8A79C300826372 /* layoutdemo8.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = layoutdemo8.gif; sourceTree = ""; }; @@ -624,6 +629,8 @@ 20F0DFFF21120A8D00CFCE8C /* FLLTest8ViewController.m */, 468988452367339C00BFE829 /* FLLTest9ViewController.h */, 46898846236733AC00BFE829 /* FLLTest9ViewController.m */, + 2040F5322CBC0C9400C85B1C /* FLLTest10ViewController.h */, + 2040F5332CBC0CA800C85B1C /* FLLTest10ViewController.m */, ); name = FlowLayoutDemo; sourceTree = ""; @@ -834,14 +841,16 @@ 182225B71E77E5930081AA4B /* MyFrameLayout.m */, 182225AF1E77E5930081AA4B /* MyBaseLayout.h */, 182225B11E77E5930081AA4B /* MyBaseLayout.m */, + 182DB6401EF7876200E2D65D /* MyGridLayout.h */, + 182DB6411EF7876200E2D65D /* MyGridLayout.m */, 182225B01E77E5930081AA4B /* MyLayoutPos.h */, 182225C61E77E5930081AA4B /* MyLayoutPosInner.h */, 182225BB1E77E5930081AA4B /* MyLayoutPos.m */, 182225BC1E77E5930081AA4B /* MyLayoutSize.h */, 182225C81E77E5930081AA4B /* MyLayoutSizeInner.h */, 182225C51E77E5930081AA4B /* MyLayoutSize.m */, - 182225C01E77E5930081AA4B /* MyLayoutSizeClass.h */, - 182225BF1E77E5930081AA4B /* MyLayoutSizeClass.m */, + 182225C01E77E5930081AA4B /* MyLayoutTraitsImpl.h */, + 182225BF1E77E5930081AA4B /* MyLayoutTraitsImpl.m */, 182225C21E77E5930081AA4B /* MyMaker.h */, 182225C11E77E5930081AA4B /* MyMaker.m */, 182225C71E77E5930081AA4B /* MyDimeScale.h */, @@ -849,8 +858,6 @@ 182225B81E77E5930081AA4B /* MyLayoutInner.h */, 182225B31E77E5930081AA4B /* MyLayoutDef.h */, 205642891F4D013D00E8BDDE /* MyGrid.h */, - 182DB6401EF7876200E2D65D /* MyGridLayout.h */, - 182DB6411EF7876200E2D65D /* MyGridLayout.m */, 205642841F4CFF9B00E8BDDE /* MyBorderline.h */, 205642851F4CFF9B00E8BDDE /* MyBorderline.m */, 205643081F4E53CD00E8BDDE /* MyGridNode.h */, @@ -859,6 +866,7 @@ 205643151F55A03000E8BDDE /* MyLayoutMath.m */, 208157C01F5A886300E945B6 /* MyLayoutDelegate.h */, 208157C11F5A886300E945B6 /* MyLayoutDelegate.m */, + 2089F6342CDA68C5000C4A9A /* MyLayoutTraits.h */, ); path = Lib; sourceTree = ""; @@ -940,9 +948,10 @@ 205E0C5F20B90347001A8B99 /* MyGrid.h in Headers */, 182DB6421EF7876200E2D65D /* MyGridLayout.h in Headers */, 205642861F4CFF9B00E8BDDE /* MyBorderline.h in Headers */, + 2089F6352CDA68C5000C4A9A /* MyLayoutTraits.h in Headers */, 20DF42412318F4EA0078CAA0 /* MyFlexLayout.h in Headers */, 18C15AF81EDF141A00AADEAC /* MyMaker.h in Headers */, - 18C15AF71EDF141600AADEAC /* MyLayoutSizeClass.h in Headers */, + 18C15AF71EDF141600AADEAC /* MyLayoutTraitsImpl.h in Headers */, 18C15AF91EDF142000AADEAC /* MyDimeScale.h in Headers */, 18C15AFA1EDF142300AADEAC /* MyLayoutDef.h in Headers */, 205E0C6020B907CC001A8B99 /* MyLayoutPosInner.h in Headers */, @@ -1042,7 +1051,7 @@ }; 1840828C1B2C46E8003F378B = { CreatedOnToolsVersion = 6.3.2; - DevelopmentTeam = BGQ8WB468K; + DevelopmentTeam = Y87SGSP84T; ProvisioningStyle = Automatic; }; 184082A51B2C46E8003F378B = { @@ -1161,7 +1170,7 @@ 18C15B031EDF144B00AADEAC /* MyLayoutPos.m in Sources */, 18C15B041EDF144B00AADEAC /* MyLayoutSize.m in Sources */, 208157C41F5A886400E945B6 /* MyLayoutDelegate.m in Sources */, - 18C15B051EDF144B00AADEAC /* MyLayoutSizeClass.m in Sources */, + 18C15B051EDF144B00AADEAC /* MyLayoutTraitsImpl.m in Sources */, 18C15B061EDF144B00AADEAC /* MyMaker.m in Sources */, 18C15B071EDF144B00AADEAC /* MyDimeScale.m in Sources */, ); @@ -1189,6 +1198,7 @@ 182225D51E77E5930081AA4B /* MyLayoutSize.m in Sources */, 6740E20120A52DC100AFBC5A /* AllTest10Cell.m in Sources */, 18D3C93A1EDF07C000D3DE43 /* FLLTest2ViewController.m in Sources */, + 2040F5342CBC0CA800C85B1C /* FLLTest10ViewController.m in Sources */, 467E63CF228CDA660065D080 /* AllTest1TableViewCellForAutoLayout.m in Sources */, 18D3C95A1EDF07F800D3DE43 /* FOLTest1ViewController.m in Sources */, 182225D01E77E5930081AA4B /* MyLayoutPos.m in Sources */, @@ -1232,7 +1242,7 @@ 202A5A0D2950A1E60076DD27 /* AllTest9WaterFlowLayout.m in Sources */, 20A45EC52318FCA50026A18C /* MyFlexLayout.m in Sources */, 2056430C1F4E8B1E00E8BDDE /* MyGridNode.m in Sources */, - 182225D11E77E5930081AA4B /* MyLayoutSizeClass.m in Sources */, + 182225D11E77E5930081AA4B /* MyLayoutTraitsImpl.m in Sources */, 444B71911F6A3E3F00331872 /* GLTest5ViewController.m in Sources */, 18D3C9741EDF082800D3DE43 /* AllTestDataModel.m in Sources */, 205CFA8E1F49BD1C00355489 /* GLTest1ViewController.m in Sources */, @@ -1537,7 +1547,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = BGQ8WB468K; + DEVELOPMENT_TEAM = Y87SGSP84T; GCC_PREPROCESSOR_DEFINITIONS = "DEBUG=1"; INFOPLIST_FILE = "$(SRCROOT)/MyLayoutDemo/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; @@ -1559,7 +1569,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = BGQ8WB468K; + DEVELOPMENT_TEAM = Y87SGSP84T; GCC_PREPROCESSOR_DEFINITIONS = ""; INFOPLIST_FILE = "$(SRCROOT)/MyLayoutDemo/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; diff --git a/MyLayout.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/MyLayout.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..0c67376 --- /dev/null +++ b/MyLayout.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,5 @@ + + + + + diff --git a/MyLayout/Lib/MyBaseLayout.h b/MyLayout/Lib/MyBaseLayout.h index db54adf..2aa0ea5 100644 --- a/MyLayout/Lib/MyBaseLayout.h +++ b/MyLayout/Lib/MyBaseLayout.h @@ -7,9 +7,7 @@ // #import "MyBorderline.h" -#import "MyLayoutDef.h" -#import "MyLayoutPos.h" -#import "MyLayoutSize.h" +#import "MyLayoutTraits.h" /* 几种视图类型的定义: @@ -21,9 +19,7 @@ 6.非布局子视图:如果某个视图的子视图不是一个布局视图,那么这个子视图就是非布局子视图。 */ -@class MyBaseLayout; - -@interface UIView (MyLayoutExt) +@interface UIView (MyLayoutExt) /* 视图的布局位置对象属性,用来指定视图水平布局位置和垂直布局位置。对于一个视图来说我们可以使用frame属性中的origin部分来确定一个视图的左上方位在父视图的位置。这种方法的缺点是需要明确的指定一个常数值,以及需要进行位置的计算,缺乏可扩展性以及可维护性。因此对于布局视图里面的子视图来说我们将不再通过设置frame属性中的origin部分来确定位置,而是通过视图扩展的布局位置对象属性来设置视图的布局位置。子视图的布局位置对象在不同类型的布局视图里和不同的场景下所表达的意义以及能设置的值类型将会有一定的差异性。下面的表格将列出这些差异性: @@ -80,7 +76,7 @@ Scene7:当同时设置了topPos和bottomPos上下边距后就能确定出视图的布局高度了,这样就不需要为子视图指定布局高度值了。需要注意的是只有同时设置了上下边距才能确定视图的高度,而设置上下间距是则不能。 比如:某个布局布局视图高度是100,而某个子视图的topPos.equalTo(@10),bottomPos.equalTo(@20).则这个子视图的高度=70(100-10-20) - 另外如果某个布局视图是视图控制器的根视图,那么你可以将布局视图里面的第一个子视图的topPos设置为: topPos.equalTo(vc.topLayoutGuide) 或者最后一个子视图的bottomPos设置为bottomPos.equalTo(vc.bottomLayoutGuide) 这样不管视图控制器所在的导航条是否半透明,总是会显示在导航条下面的位置。。 + 另外如果某个布局视图是视图控制器的根视图,那么你可以将布局视图里面的第一个子视图的topPos设置为: topPos.equalTo(@(MyLayoutPos.safeAreaMargin)) 或者最后一个子视图的bottomPos设置为bottomPos.equalTo(@(MyLayoutPos.safeAreaMargin)) 这样不管视图控制器所在的导航条是否半透明,总是会显示在导航条下面的位置。。 */ @@ -519,6 +515,69 @@ */ @property (nonatomic, assign) MyGravity alignment; +/** + 下面两个属性:reverseFloat和clearFloat 仅用在浮动布局MyFloatLayout下面的子视图中使用。 + */ + +/** + 是否反方向浮动,默认是NO表示正向浮动。正向浮动和反向浮动的意义根据所在的父浮动布局视图的方向的不同而不同: + + 1. 如果父视图是垂直浮动布局则默认正向浮动是向左浮动的,而反向浮动则是向右浮动。 + + @code + 下面是垂直浮动布局中的正向浮动和反向浮动的效果图(正向浮动:A,B,D; 反向浮动:C,E,F): + + |<--A-- <---B--- -C->| + |<-----D---- -F-> --E-->| + @endcode + + 2. 如果父视图是水平浮动布局则默认正向浮动是向上浮动的,而反向浮动则是向下浮动。 + + @code + 下面是水平浮动布局中的正向浮动和反向浮动的效果图(正向浮动:A,B,D; 反向浮动:C,E,F): + + ----------- + ↑ ↑ + | | + A | + | D + | + ↑ | + B + | F + ↓ + | | + C E + ↓ ↓ + ------------ + @endcode + + @note 这个属性的定义是完全参考CSS样式表中float属性的定义 + */ +@property (nonatomic, assign, getter=isReverseFloat) BOOL reverseFloat; + +/** + 清除浮动,默认是NO。这个属性的意义也跟父浮动布局视图的方向相关。如果设置为了清除浮动属性则表示本子视图不会在浮动方向上紧跟在前一个浮动子视图的后面,而是会另外新起一行或者一列来重新排列。 + + @code + 垂直浮动布局下的浮动和清除浮动 + + |<--A-- <---B--- <-C--| + |<----D--- | + |<--E-- <---F-- | + |<-----G---- | + | ---I---> --H-->| + | -J-> | + + A(正向浮动);B(正向浮动);C(正向浮动);D(正向浮动);E(正向浮动);F(正向浮动);G(正向浮动,清除浮动);H(反向浮动);I(反向浮动);J(反向浮动,清除浮动) + @endcode + + @note 这个属性的定义是完全参考CSS样式表中clear属性的定义。 + + */ +@property (nonatomic, assign) BOOL clearFloat; + + /** 视图在父布局视图中布局完成后也就是视图的frame更新完成后执行的block,执行完block后会被重置为nil。通过在viewLayoutCompleteBlock中我们可以得到这个视图真实的frame值,当然您也可以在里面进行其他业务逻辑的操作和属性的获取和更新。block方法中layout参数就是父布局视图,而v就是视图本身,block中这两个参数目的是为了防止循环引用的问题。 @@ -536,32 +595,33 @@ - (CGRect)estimatedRect; /** - *清除视图所有为布局而设置的扩展属性值。如果是布局视图调用这个方法则同时会清除布局视图中所有关于布局设置的属性值。 + *清除视图所有为布局而设置的布局特性值。如果是布局视图调用这个方法则同时会清除布局视图中所有关于布局设置的属性值。 */ -- (void)resetMyLayoutSetting; -- (void)resetMyLayoutSettingInSizeClass:(MySizeClass)sizeClass; +- (void)clearLayoutTraits; +- (void)clearLayoutTraitsInSizeClass:(MySizeClass)sizeClass; /** - 获取视图在某个Size Classes下的MyLayoutSizeClass对象。视图可以通过得到的MyLayoutSizeClass对象来设置视图在对应Size Classes下的各种布局约束属性。 + 获取视图在某个SizeClass下的布局特性对象。视图可以通过得到的布局特性对象来设置视图在对应SizeClass下的各种布局约束属性。如果指定的sizeClass之前不存在则会从默认sizeClass下拷贝一份。 @param sizeClass 指定获取某种SizeClass - @return 返回指定的SizeClass + @return 返回指定的布局特性对象 */ -- (instancetype)fetchLayoutSizeClass:(MySizeClass)sizeClass; +- (__kindof id)fetchLayoutTraitsInSizeClass:(MySizeClass)sizeClass; /** - 获取视图在某个Size Classes下的MyLayoutSizeClass对象,如果对象不存在则会新建立一个MyLayoutSizeClass对象,并且其布局约束属性都拷贝自srcSizeClass中的属性,如果对象已经存在则srcSizeClass不起作用,如果srcSizeClass本来就不存在则也不会起作用。 + 获取视图在某个SizeClass下的布局特性对象,如果对象不存在则会新建立一个新的特性对象,并且其布局约束属性都拷贝自srcSizeClass中的属性,如果对象已经存在则srcSizeClass不起作用,如果srcSizeClass本来就不存在则也不会起作用。 @param sizeClass 指定获取某个SizeClass @param srcSizeClass 指定从哪种SizeClass中拷贝 - @return 返回指定的SizeClass + @return 返回指定的布局特性对象 */ -- (instancetype)fetchLayoutSizeClass:(MySizeClass)sizeClass copyFrom:(MySizeClass)srcSizeClass; +- (__kindof id)fetchLayoutTraitsInSizeClass:(MySizeClass)sizeClass copyFrom:(MySizeClass)srcSizeClass; -/// 为某个sizeClass设置新的布局特性值,新的布局特性值一般来源于其他视图或者其他sizeClass,从而实现布局特征属性的拷贝功能。 +/// 为某个sizeClass设置新的布局特性对象,新的布局特性对象一般来源于其他视图或者其他sizeClass,从而实现布局特性属性的拷贝功能。 +/// @param value 新的值,这个值必须是上述两个fetchLayoutTraits方法返回值的copy结果。 /// @param sizeClass 具体的sizeClass类型 -/// @param value 新的值,这个值必须是上述两个fetchLayoutSizeClass方法返回值的copy结果。 -- (void)setLayoutSizeClass:(MySizeClass)sizeClass withValue:(id)value; +- (void)setLayoutTraits:(__kindof id)value inSizeClass:(MySizeClass)sizeClass; + @end @@ -573,7 +633,7 @@ MyLayout布局库根据实际中常见的场景实现了8种不同的布局视图派生类他们分别是:线性布局、表格布局、相对布局、框架布局、流式布局、浮动布局、路径布局、栅格布局。 */ -@interface MyBaseLayout : UIView +@interface MyBaseLayout < __covariant MyLayoutTraitsT> : UIView< MyLayoutTraits > /** 用于实现对阿拉伯国家的布局适配。对于非阿拉伯国家来说,界面布局都是默认从左到右排列。而对于阿拉伯国家来说界面布局则默认是从右往左排列。默认这个属性是NO,您可以将这个属性设置为YES,这样布局里面的所有视图都将从右到左进行排列布局。如果您需要考虑国际化布局的问题,那么您应该用leadingPos来表示头部的位置,而用trailingPos来表示尾部的位置,这样当布局方向是LTR时那么leadingPos就表示的是左边而trailingPos则表示的是右边;而当布局方向是RTL时那么leadingPos表示的是右边而trailingPos则表示的是左边。如果您的界面布局不会考虑到国际化以及不需要考虑RTL时那么您可以用leftPos和rightPos来表示左右而不需要用leadingPos和trailingPos。 @@ -670,15 +730,15 @@ /** *设置布局视图里面子视图之间的垂直间距。如果布局内每个子视图的垂直间距都相等则可以用这个属性来设置。这个属性不支持框架布局、相对布局、路径布局。 */ -@property (nonatomic, assign) CGFloat subviewVSpace; +@property (nonatomic, assign) CGFloat subviewVSpacing; /** *设置布局视图里面子视图之间的水平间距。如果布局内每个子视图的水平间距都相等则可以用这个属性来设置。这个属性不支持框架布局、相对布局、路径布局。 */ -@property (nonatomic, assign) CGFloat subviewHSpace; +@property (nonatomic, assign) CGFloat subviewHSpacing; /** *设置布局视图里面子视图之间的间距。如果布局内每个子视图的间距都相等则可以用这个属性来设置。这个属性不支持框架布局、相对布局、路径布局。 */ -@property (nonatomic, assign) CGFloat subviewSpace; +@property (nonatomic, assign) CGFloat subviewSpacing; /** 布局里面的所有子视图的整体停靠方向以及填充,所谓停靠是指布局视图里面的所有子视图整体在布局视图中的位置,系统默认的停靠是在布局视图的左上角。 @@ -931,51 +991,94 @@ */ - (CGRect)subview:(UIView *)subview estimatedRectInLayoutSize:(CGSize)size; + +- (MyLayoutTraitsT)fetchLayoutTraitsInSizeClass:(MySizeClass)sizeClass; +- (MyLayoutTraitsT)fetchLayoutTraitsInSizeClass:(MySizeClass)sizeClass copyFrom:(MySizeClass)srcSizeClass; + +- (void)setLayoutTraits:(MyLayoutTraitsT)value inSizeClass:(MySizeClass)sizeClass; + + @end #pragma mark-- MyLayoutDragger @class MyLayoutDragger; + +@protocol MyLayoutDraggerDelegate + +@optional + +/// 拖动的视图开始悬停在某个视图之上通知 +/// - Parameters: +/// - dragger: 拖动管理器 +/// - draggingView: 拖动的视图 +/// - hoveringView: 悬停的视图 +- (void)myLayoutDragger:(MyLayoutDragger *)dragger draggingView:(UIView *)draggingView enterInHoveringView:(UIView *)hoveringView; + +/// 拖动的视图从悬停的视图上离开通知 +/// - Parameters: +/// - dragger: 拖动管理器 +/// - draggingView: 拖动的视图 +/// - hoveringView: 悬停的视图 +- (void)myLayoutDragger:(MyLayoutDragger *)dragger draggingView:(UIView *)draggingView leaveFromHoveringView:(UIView *)hoveringView; + +/// 开始拖动通知 +/// - Parameters: +/// - dragger: 推动管理器 +/// - dragView: 开始拖动的视图 +- (void)myLayoutDragger:(MyLayoutDragger *)dragger startDragView:(UIView *)dragView; + +/// 结束拖动通知 +/// - Parameters: +/// - dragger: 拖动管理器 +/// - dropView: 结束拖动的视图 +- (void)myLayoutDragger:(MyLayoutDragger *)dragger endDropView:(UIView *)dropView; + +@end + + /** - 布局视图拖动器类,用来实现布局内的视图的拖动封装。用于实现布局子视图的拖放处理。 -布局视图的拖动器类,只支持那些按顺序添加的布局视图,不支持相对布局、框架布局、栅格布局、路径布局。 + 布局视图拖动管理器类,用来实现布局内的视图的拖动封装。用于实现布局子视图的拖放处理。 +布局视图的拖动管理器类,只支持那些按顺序添加的布局视图,不支持相对布局、框架布局、栅格布局、路径布局。 一般情况下我们要实现布局内子视图的: 1.UIControlEventTouchDown 事件来处理拖动开始 2.UIControlEventTouchDragInside UIControlEventTouchDragOutside 事件来处理拖动进行中 3. UIControlEventTouchUpInside UIControlEventTouchCancel 事件来处理拖动结束。 - 如果您的布局下的子视图不是UIControl类的派生类的话,请分别重载如下事件: + 如果您添加到布局下的子视图不是UIControl类的派生类的话,请分别重载如下事件: - - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 在这个方法中调用dragView:withEvent: - - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event; 在这个方法中调用dragginView:withEvent + - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 在这个方法内部调用dragView:withEvent:方法 + - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event; 在这个方法内部调用dragginView:withEvent方法 - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event; - - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event 在上述两个方法中调用dropView:withEvent - - 无论何种方式,最终都是在调用完dropView:withEvent方法后,通过读取currentIndex和oldIndex的值来获取位置信息的改变。 + - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event 在上述两个方法内部调用dropView:withEvent方法 */ @interface MyLayoutDragger : NSObject -//子视图拖动时,拖动区域当前所归属的子视图位置索引。 -@property (nonatomic, assign, readonly) NSUInteger currentIndex; - -//子视图拖动时,拖动视图的老的位置索引。 -@property (nonatomic, assign, readonly) NSUInteger oldIndex; - -//当前是否正在拖动中。 -@property (nonatomic, assign, readonly) BOOL hasDragging; +//当拖动的视图和其他视图区域重叠时拖动视图否可以悬停在其他视图上。默认为NO,表明当拖动的视图与其他视图重叠后其他视图会移动,如果为YES则重叠时其他视图会保持不动。 +@property (nonatomic, assign) BOOL canHover; -//设置拖动时不会调整的子视图列表。也就是说数组中指定的子视图在拖动时不会被移动而是总是固定在原有的位置。 +//设置拖动时不会移动位置的子视图列表。也就是说数组中指定的子视图在和拖动的视图重叠时总是固定在原有的位置不会变动。 @property (nonatomic, strong) NSArray *exclusiveViews; +//设定在拖动时拖动视图进入目标视图尺寸的比例就会触发目标视图移开或者触发悬停回调通知,取值为[0,1] 默认值为0.25. +@property (nonatomic, assign) CGFloat overlapRatio; + //设置拖动时位置调整的动画时长,默认是0秒,设置为0时拖动不产生动画效果。设置为0.2效果较好。 @property (nonatomic, assign) NSTimeInterval animateDuration; -//当拖动的视图和现有视图重叠时是否支持悬停功能,默认为NO。当开启开关后,并且oldIndex和currentIndex相等时则处于悬停状态。开启悬停功能的目的是为了支持一些替换或者更新的能力。 -@property (nonatomic, assign) BOOL canHover; +@property (nonatomic, weak) id delegate; + + +//拖动管理器所属的布局视图 +@property (nonatomic, weak, readonly) MyBaseLayout *layoutView; +//正在执行拖动的视图,可能为空 +@property (nonatomic, weak, readonly) UIView *draggingView; +//拖动时,拖动视图所悬停的视图,可能为空。注意悬停的视图也有可能是exclusiveViews中指定的视图。 +@property (nonatomic, weak, readonly) UIView *hoveringView; + + -//当前是否正在悬停中。我们可以借助这个属性值判断来做一些操作处理。 -@property (nonatomic, assign, readonly) BOOL isHovering; //下列方法请在子视图的相应事件处理中调用。 @@ -984,7 +1087,7 @@ - (void)dragView:(UIView *)view withEvent:(UIEvent *)event; //拖动中,请在子视图view的拖动过程事件中调用这个方法,其中的view指定拖动中的视图。 - (void)dragginView:(UIView *)view withEvent:(UIEvent *)event; -//结束拖动,请在子视图view的结束拖动事件中调用这个方法,其中的view指定要结束拖动的视图。 +//结束拖动,请在子视图view的结束或者终止拖动事件中调用这个方法,其中的view指定要结束拖动的视图。 - (void)dropView:(UIView *)view withEvent:(UIEvent *)event; @end @@ -1023,15 +1126,21 @@ */ @property (nonatomic, assign) BOOL wrapContentSize; + +- (void)resetMyLayoutSetting MYDEPRECATED("use clearLayoutTraits instead"); +- (void)resetMyLayoutSettingInSizeClass:(MySizeClass)sizeClass MYDEPRECATED("use clearLayoutTraitsInSizeClass: instead"); + +- (instancetype)fetchLayoutSizeClass:(MySizeClass)sizeClass MYDEPRECATED("use fetchLayoutTraitsInSizeClass: instead"); + +- (instancetype)fetchLayoutSizeClass:(MySizeClass)sizeClass copyFrom:(MySizeClass)srcSizeClass MYDEPRECATED("use fetchLayoutTraitsInSizeClass:copyFrom: instead"); + + +- (void)setLayoutSizeClass:(MySizeClass)sizeClass withValue:(id)value MYDEPRECATED("use setLayoutTraits:inSizeClass: instead"); + + +@property (nonatomic, assign) CGFloat subviewVSpace MYDEPRECATED("use subviewVSpacing instead"); +@property (nonatomic, assign) CGFloat subviewHSpace MYDEPRECATED("use subviewHSpacing instead"); +@property (nonatomic, assign) CGFloat subviewSpace MYDEPRECATED("use subviewSpacing instead"); + @end -//@interface MyBaseLayout(MyDeprecated) -// -//@property (nonatomic, assign) CGFloat topPadding MYDEPRECATED("use paddingTop instead"); -//@property (nonatomic, assign) CGFloat leadingPadding MYDEPRECATED("use paddingLeading instead"); -//@property (nonatomic, assign) CGFloat bottomPadding MYDEPRECATED("use paddingBottom instead"); -//@property (nonatomic, assign) CGFloat trailingPadding MYDEPRECATED("use paddingTrailing instead"); -//@property (nonatomic, assign) CGFloat leftPadding MYDEPRECATED("use paddingLeft instead"); -//@property (nonatomic, assign) CGFloat rightPadding MYDEPRECATED("use paddingRight instead"); -// -//@end diff --git a/MyLayout/Lib/MyBaseLayout.m b/MyLayout/Lib/MyBaseLayout.m index 93f9b6a..c5e1237 100644 --- a/MyLayout/Lib/MyBaseLayout.m +++ b/MyLayout/Lib/MyBaseLayout.m @@ -21,8 +21,14 @@ @interface MyLayoutDragger () @property (nonatomic, assign) NSUInteger currentIndex; @property (nonatomic, assign) NSUInteger oldIndex; -@property (nonatomic, assign) BOOL hasDragging; -@property (nonatomic, weak) MyBaseLayout *layout; + +//布局视图 +@property (nonatomic, weak, readwrite) MyBaseLayout *layoutView; +//正在执行拖动的视图,可能为空 +@property (nonatomic, weak, readwrite) UIView *draggingView; +//拖动时悬停的视图,可能为空 +@property (nonatomic, weak, readwrite) UIView *hoveringView; + @end @@ -38,299 +44,53 @@ - (void)myUpdateRTL:(BOOL)isRTL; @implementation UIView (MyLayoutExt) -- (MyLayoutPos *)topPos { - return self.myDefaultSizeClass.topPos; -} - -- (MyLayoutPos *)leadingPos { - return self.myDefaultSizeClass.leadingPos; -} - -- (MyLayoutPos *)bottomPos { - return self.myDefaultSizeClass.bottomPos; -} - -- (MyLayoutPos *)trailingPos { - return self.myDefaultSizeClass.trailingPos; -} - -- (MyLayoutPos *)centerXPos { - return self.myDefaultSizeClass.centerXPos; -} - -- (MyLayoutPos *)centerYPos { - return self.myDefaultSizeClass.centerYPos; -} - -- (MyLayoutPos *)leftPos { - return self.myDefaultSizeClass.leftPos; -} - -- (MyLayoutPos *)rightPos { - return self.myDefaultSizeClass.rightPos; -} - -- (MyLayoutPos *)baselinePos { - return self.myDefaultSizeClass.baselinePos; -} - -- (CGFloat)myTop { -#if DEBUG - NSLog(@"%s 一般只用于设置,而不能用于获取!!", sel_getName(_cmd)); -#endif - return self.myDefaultSizeClassInner.myTop; -} - -- (void)setMyTop:(CGFloat)myTop { - self.myDefaultSizeClass.myTop = myTop; -} - -- (CGFloat)myLeading { -#if DEBUG - NSLog(@"%s 一般只用于设置,而不能用于获取!!", sel_getName(_cmd)); -#endif - return self.myDefaultSizeClassInner.myLeading; -} - -- (void)setMyLeading:(CGFloat)myLeading { - self.myDefaultSizeClass.myLeading = myLeading; -} - -- (CGFloat)myBottom { -#if DEBUG - NSLog(@"%s 一般只用于设置,而不能用于获取!!", sel_getName(_cmd)); -#endif - return self.myDefaultSizeClassInner.myBottom; -} - -- (void)setMyBottom:(CGFloat)myBottom { - self.myDefaultSizeClass.myBottom = myBottom; -} - -- (CGFloat)myTrailing { -#if DEBUG - NSLog(@"%s 一般只用于设置,而不能用于获取!!", sel_getName(_cmd)); -#endif - return self.myDefaultSizeClassInner.myTrailing; -} - -- (void)setMyTrailing:(CGFloat)myTrailing { - self.myDefaultSizeClass.myTrailing = myTrailing; -} - -- (CGFloat)myCenterX { -#if DEBUG - NSLog(@"%s 一般只用于设置,而不能用于获取!!", sel_getName(_cmd)); -#endif - return self.myDefaultSizeClassInner.myCenterX; -} - -- (void)setMyCenterX:(CGFloat)myCenterX { - self.myDefaultSizeClass.myCenterX = myCenterX; -} - -- (CGFloat)myCenterY { -#if DEBUG - NSLog(@"%s 一般只用于设置,而不能用于获取!!", sel_getName(_cmd)); -#endif - return self.myDefaultSizeClassInner.myCenterY; -} - -- (void)setMyCenterY:(CGFloat)myCenterY { - self.myDefaultSizeClass.myCenterY = myCenterY; -} - -- (CGPoint)myCenter { -#if DEBUG - NSLog(@"%s 一般只用于设置,而不能用于获取!!", sel_getName(_cmd)); -#endif - return self.myDefaultSizeClassInner.myCenter; -} - -- (void)setMyCenter:(CGPoint)myCenter { - self.myDefaultSizeClass.myCenter = myCenter; -} - -- (CGFloat)myLeft { -#if DEBUG - NSLog(@"%s 一般只用于设置,而不能用于获取!!", sel_getName(_cmd)); -#endif - return self.myDefaultSizeClassInner.myLeft; -} - -- (void)setMyLeft:(CGFloat)myLeft { - self.myDefaultSizeClass.myLeft = myLeft; -} - -- (CGFloat)myRight { -#if DEBUG - NSLog(@"%s 一般只用于设置,而不能用于获取!!", sel_getName(_cmd)); -#endif - return self.myDefaultSizeClassInner.myRight; -} - -- (void)setMyRight:(CGFloat)myRight { - self.myDefaultSizeClass.myRight = myRight; -} - -- (CGFloat)myMargin { -#if DEBUG - NSLog(@"%s 一般只用于设置,而不能用于获取!!", sel_getName(_cmd)); -#endif - return self.myDefaultSizeClassInner.myMargin; -} - -- (void)setMyMargin:(CGFloat)myMargin { - self.myDefaultSizeClass.myMargin = myMargin; -} - -- (CGFloat)myHorzMargin { -#if DEBUG - NSLog(@"%s 一般只用于设置,而不能用于获取!!", sel_getName(_cmd)); -#endif - return self.myDefaultSizeClassInner.myHorzMargin; -} - -- (void)setMyHorzMargin:(CGFloat)myHorzMargin { - self.myDefaultSizeClass.myHorzMargin = myHorzMargin; -} - -- (CGFloat)myVertMargin { -#if DEBUG - NSLog(@"%s 一般只用于设置,而不能用于获取!!", sel_getName(_cmd)); -#endif - return self.myDefaultSizeClassInner.myVertMargin; -} - -- (void)setMyVertMargin:(CGFloat)myVertMargin { - self.myDefaultSizeClass.myVertMargin = myVertMargin; -} - -- (MyLayoutSize *)widthSize { - return self.myDefaultSizeClass.widthSize; -} - -- (MyLayoutSize *)heightSize { - return self.myDefaultSizeClass.heightSize; -} - -- (CGFloat)myWidth { - return self.myDefaultSizeClassInner.myWidth; -} - -- (void)setMyWidth:(CGFloat)myWidth { - self.myDefaultSizeClass.myWidth = myWidth; -} - -- (CGFloat)myHeight { - return self.myDefaultSizeClassInner.myHeight; -} - -- (void)setMyHeight:(CGFloat)myHeight { - self.myDefaultSizeClass.myHeight = myHeight; -} +_MYLAYOUT_PROPERTY_VIEW_GET3(MyLayoutPos *, topPos) +_MYLAYOUT_PROPERTY_VIEW_GET3(MyLayoutPos *, leadingPos) +_MYLAYOUT_PROPERTY_VIEW_GET3(MyLayoutPos *, bottomPos) +_MYLAYOUT_PROPERTY_VIEW_GET3(MyLayoutPos *, trailingPos) +_MYLAYOUT_PROPERTY_VIEW_GET3(MyLayoutPos *, centerXPos) +_MYLAYOUT_PROPERTY_VIEW_GET3(MyLayoutPos *, centerYPos) +_MYLAYOUT_PROPERTY_VIEW_GET3(MyLayoutPos *, leftPos) +_MYLAYOUT_PROPERTY_VIEW_GET3(MyLayoutPos *, rightPos) +_MYLAYOUT_PROPERTY_VIEW_GET3(MyLayoutPos *, baselinePos) -- (CGSize)mySize { -#if DEBUG - NSLog(@"%s 一般只用于设置,而不能用于获取!!", sel_getName(_cmd)); -#endif - return self.myDefaultSizeClassInner.mySize; -} +_MYLAYOUT_PROPERTY_VIEW_OVERRIDE1(CGFloat, MyTop, myTop) +_MYLAYOUT_PROPERTY_VIEW_OVERRIDE1(CGFloat, MyLeading, myLeading) +_MYLAYOUT_PROPERTY_VIEW_OVERRIDE1(CGFloat, MyBottom, myBottom) +_MYLAYOUT_PROPERTY_VIEW_OVERRIDE1(CGFloat, MyTrailing, myTrailing) +_MYLAYOUT_PROPERTY_VIEW_OVERRIDE1(CGFloat, MyCenterX, myCenterX) +_MYLAYOUT_PROPERTY_VIEW_OVERRIDE1(CGFloat, MyCenterY, myCenterY) +_MYLAYOUT_PROPERTY_VIEW_OVERRIDE1(CGPoint, MyCenter, myCenter) +_MYLAYOUT_PROPERTY_VIEW_OVERRIDE1(CGFloat, MyLeft, myLeft) +_MYLAYOUT_PROPERTY_VIEW_OVERRIDE1(CGFloat, MyRight, myRight) +_MYLAYOUT_PROPERTY_VIEW_OVERRIDE1(CGFloat, MyMargin, myMargin) +_MYLAYOUT_PROPERTY_VIEW_OVERRIDE1(CGFloat, MyHorzMargin, myHorzMargin) +_MYLAYOUT_PROPERTY_VIEW_OVERRIDE1(CGFloat, MyVertMargin, myVertMargin) -- (void)setMySize:(CGSize)mySize { - self.myDefaultSizeClass.mySize = mySize; -} +_MYLAYOUT_PROPERTY_VIEW_GET3(MyLayoutSize *, widthSize) +_MYLAYOUT_PROPERTY_VIEW_GET3(MyLayoutSize *, heightSize) -- (void)setWrapContentHeight:(BOOL)wrapContentHeight { - MyViewTraits *viewTraits = (MyViewTraits*)self.myDefaultSizeClass; - if (viewTraits.wrapContentHeight != wrapContentHeight) { - viewTraits.wrapContentHeight = wrapContentHeight; - if (self.superview != nil) { - [self.superview setNeedsLayout]; - } - } -} +_MYLAYOUT_PROPERTY_VIEW_OVERRIDE1(CGFloat, MyWidth, myWidth) +_MYLAYOUT_PROPERTY_VIEW_OVERRIDE1(CGFloat, MyHeight, myHeight) +_MYLAYOUT_PROPERTY_VIEW_OVERRIDE1(CGSize, MySize, mySize) -- (BOOL)wrapContentHeight { - //特殊处理,减少不必要的对象创建 - return self.myDefaultSizeClassInner.wrapContentHeight; -} -- (void)setWrapContentWidth:(BOOL)wrapContentWidth { - MyViewTraits *viewTraits = (MyViewTraits*)self.myDefaultSizeClass; - if (viewTraits.wrapContentWidth != wrapContentWidth) { - viewTraits.wrapContentWidth = wrapContentWidth; - if (self.superview != nil) { - [self.superview setNeedsLayout]; - } - } -} +_MYLAYOUT_PROPERTY_VIEW_OVERRIDE2(BOOL, WrapContentHeight, wrapContentHeight) -- (BOOL)wrapContentWidth { - //特殊处理,减少不必要的对象创建 - return self.myDefaultSizeClassInner.wrapContentWidth; -} +_MYLAYOUT_PROPERTY_VIEW_OVERRIDE2(BOOL, WrapContentWidth, wrapContentWidth) -- (BOOL)wrapContentSize { - return self.myDefaultSizeClassInner.wrapContentSize; -} +_MYLAYOUT_PROPERTY_VIEW_OVERRIDE2(BOOL, WrapContentSize, wrapContentSize) -- (void)setWrapContentSize:(BOOL)wrapContentSize { - MyViewTraits *viewTraits = (MyViewTraits*)self.myDefaultSizeClass; - viewTraits.wrapContentSize = wrapContentSize; - if (self.superview != nil) { - [self.superview setNeedsLayout]; - } -} +_MYLAYOUT_PROPERTY_VIEW_OVERRIDE2(CGFloat, Weight, weight) -- (CGFloat)weight { - return self.myDefaultSizeClassInner.weight; -} +_MYLAYOUT_PROPERTY_VIEW_OVERRIDE2(BOOL, UseFrame, useFrame) -- (void)setWeight:(CGFloat)weight { - MyViewTraits *viewTraits = (MyViewTraits*)self.myDefaultSizeClass; - if (viewTraits.weight != weight) { - viewTraits.weight = weight; - if (self.superview != nil) { - [self.superview setNeedsLayout]; - } - } -} - -- (BOOL)useFrame { - return self.myDefaultSizeClassInner.useFrame; -} - -- (void)setUseFrame:(BOOL)useFrame { - MyViewTraits *viewTraits = (MyViewTraits*)self.myDefaultSizeClass; - if (viewTraits.useFrame != useFrame) { - viewTraits.useFrame = useFrame; - if (self.superview != nil) { - [self.superview setNeedsLayout]; - } - } -} +_MYLAYOUT_PROPERTY_VIEW_OVERRIDE2(BOOL, NoLayout, noLayout) -- (BOOL)noLayout { - return self.myDefaultSizeClassInner.noLayout; -} - -- (void)setNoLayout:(BOOL)noLayout { - MyViewTraits *viewTraits = (MyViewTraits*)self.myDefaultSizeClass; - if (viewTraits.noLayout != noLayout) { - viewTraits.noLayout = noLayout; - if (self.superview != nil) { - [self.superview setNeedsLayout]; - } - } -} - -- (MyVisibility)visibility { - return self.myDefaultSizeClassInner.visibility; -} +_MYLAYOUT_PROPERTY_VIEW_GET2(MyVisibility, visibility) - (void)setVisibility:(MyVisibility)visibility { - MyViewTraits *viewTraits = (MyViewTraits*)self.myDefaultSizeClass; + MyViewTraitsImpl *viewTraits = (MyViewTraitsImpl*)self.myDefaultTraits; if (viewTraits.visibility != visibility) { viewTraits.visibility = visibility; self.hidden = (visibility != MyVisibility_Visible); @@ -345,26 +105,33 @@ - (void)setVisibility:(MyVisibility)visibility { } } -- (MyGravity)alignment { - return self.myDefaultSizeClassInner.alignment; -} +_MYLAYOUT_PROPERTY_VIEW_OVERRIDE2(MyGravity, Alignment, alignment) -- (void)setAlignment:(MyGravity)alignment { - MyViewTraits *viewTraits = (MyViewTraits*)self.myDefaultSizeClass; - if (viewTraits.alignment != alignment) { - viewTraits.alignment = alignment; + +- (void)setReverseFloat:(BOOL)reverseFloat { + MyViewTraitsImpl *viewTraits = (MyViewTraitsImpl*)self.myDefaultTraits; + if (viewTraits.isReverseFloat != reverseFloat) { + viewTraits.reverseFloat = reverseFloat; if (self.superview != nil) { [self.superview setNeedsLayout]; } } } +- (BOOL)isReverseFloat { + return self.myDefaultTraitsInner.isReverseFloat; +} + + +_MYLAYOUT_PROPERTY_VIEW_OVERRIDE2(BOOL, ClearFloat, clearFloat) + + - (void (^)(MyBaseLayout *, UIView *))viewLayoutCompleteBlock { - return self.myDefaultSizeClassInner.viewLayoutCompleteBlock; + return self.myEngineInner.viewLayoutCompleteBlock; } - (void)setViewLayoutCompleteBlock:(void (^)(MyBaseLayout *, UIView *))viewLayoutCompleteBlock { - self.myDefaultSizeClass.viewLayoutCompleteBlock = viewLayoutCompleteBlock; + self.myEngine.viewLayoutCompleteBlock = viewLayoutCompleteBlock; } - (CGRect)estimatedRect { @@ -375,59 +142,82 @@ - (CGRect)estimatedRect { return rect; } +- (void)clearLayoutTraits { + [self clearLayoutTraitsInSizeClass:MySizeClass_wAny | MySizeClass_hAny]; +} + +- (void)clearLayoutTraitsInSizeClass:(MySizeClass)sizeClass { + [self.myEngine.traitsDict removeObjectForKey:@(sizeClass)]; +} + +- (id)fetchLayoutTraitsInSizeClass:(MySizeClass)sizeClass { + return [self fetchLayoutTraitsInSizeClass:sizeClass copyFrom:MySizeClass_All]; +} + +- (id)fetchLayoutTraitsInSizeClass:(MySizeClass)sizeClass copyFrom:(MySizeClass)srcSizeClass { + return [self.myEngine fetchView:self traitsInSizeClass:sizeClass copyFrom:srcSizeClass]; +} + +- (void)setLayoutTraits:(id)value inSizeClass:(MySizeClass)sizeClass { + [self.myEngine setView:self newTraits:(MyViewTraitsImpl*)value at:sizeClass]; +} + - (void)resetMyLayoutSetting { - [self resetMyLayoutSettingInSizeClass:MySizeClass_wAny | MySizeClass_hAny]; + [self clearLayoutTraits]; } - (void)resetMyLayoutSettingInSizeClass:(MySizeClass)sizeClass { - [self.myEngine.sizeClasses removeObjectForKey:@(sizeClass)]; + [self clearLayoutTraitsInSizeClass:sizeClass]; } - (instancetype)fetchLayoutSizeClass:(MySizeClass)sizeClass { - return [self fetchLayoutSizeClass:sizeClass copyFrom:0xFF]; + return [self fetchLayoutSizeClass:sizeClass copyFrom:MySizeClass_All]; } - (instancetype)fetchLayoutSizeClass:(MySizeClass)sizeClass copyFrom:(MySizeClass)srcSizeClass { - return (UIView *)[self.myEngine fetchView:self layoutSizeClass:sizeClass copyFrom:srcSizeClass]; + return (UIView *)[self fetchLayoutTraitsInSizeClass:sizeClass copyFrom:srcSizeClass]; } - (void)setLayoutSizeClass:(MySizeClass)sizeClass withValue:(id)value { - [self.myEngine setView:self layoutSizeClass:sizeClass withTraits:value]; + [self setLayoutTraits:value inSizeClass:sizeClass]; } @end @implementation UIView (MyLayoutExtInner) -- (instancetype)myDefaultSizeClass { +- (__kindof MyViewTraitsImpl *)myDefaultTraits { MyLayoutEngine *viewEngine = self.myEngine; - if (viewEngine.defaultSizeClass == nil) { - viewEngine.defaultSizeClass = [viewEngine fetchView:self layoutSizeClass:MySizeClass_wAny | MySizeClass_hAny copyFrom:0xFF]; + if (viewEngine.defaultTraits == nil) { + viewEngine.defaultTraits = [viewEngine fetchView:self traitsInSizeClass:MySizeClass_wAny | MySizeClass_hAny copyFrom:MySizeClass_All]; + } + if (viewEngine.currentTraits == nil) { + viewEngine.currentTraits = viewEngine.defaultTraits; } - return (UIView *)viewEngine.defaultSizeClass; + return viewEngine.defaultTraits; } -- (instancetype)myDefaultSizeClassInner { - return (UIView *)self.myEngineInner.defaultSizeClass; +- (__kindof MyViewTraitsImpl *)myDefaultTraitsInner { + return self.myEngineInner.defaultTraits; } -- (instancetype)myCurrentSizeClass { +- (__kindof MyViewTraitsImpl *)myCurrentTraits { MyLayoutEngine *viewEngine = self.myEngine; - if (viewEngine.currentSizeClass == nil) { - viewEngine.currentSizeClass = (MyViewTraits *)[self myDefaultSizeClass]; + if (viewEngine.currentTraits == nil) { + viewEngine.currentTraits = [self myDefaultTraits]; } - return (UIView *)viewEngine.currentSizeClass; + return viewEngine.currentTraits; } -- (instancetype)myCurrentSizeClassInner { +- (__kindof MyViewTraitsImpl *)myCurrentTraitsInner { //如果没有则不会建立,为了优化减少不必要的建立。 - return (UIView *)self.myEngineInner.currentSizeClass; + return self.myEngineInner.currentTraits; } - (MyLayoutEngine *)myEngine { MyLayoutEngine *obj = objc_getAssociatedObject(self, ASSOCIATEDOBJECT_KEY_MYLAYOUT_ENGINE); - if (obj == nil) { - obj = [MyLayoutEngine new]; + if (obj == nil){ + obj = [[MyLayoutEngine alloc] initWithTraitsClass:[MyViewTraitsImpl class]]; objc_setAssociatedObject(self, ASSOCIATEDOBJECT_KEY_MYLAYOUT_ENGINE, obj, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } return obj; @@ -437,53 +227,6 @@ - (MyLayoutEngine *)myEngineInner { return (MyLayoutEngine*)objc_getAssociatedObject(self, ASSOCIATEDOBJECT_KEY_MYLAYOUT_ENGINE); } -- (id)createSizeClassInstance { - return [MyViewTraits new]; -} - -- (MyLayoutPos *)topPosInner { - return self.myDefaultSizeClassInner.topPosInner; -} - -- (MyLayoutPos *)leadingPosInner { - return self.myDefaultSizeClassInner.leadingPosInner; -} - -- (MyLayoutPos *)bottomPosInner { - return self.myDefaultSizeClassInner.bottomPosInner; -} - -- (MyLayoutPos *)trailingPosInner { - return self.myDefaultSizeClassInner.trailingPosInner; -} - -- (MyLayoutPos *)centerXPosInner { - return self.myDefaultSizeClassInner.centerXPosInner; -} - -- (MyLayoutPos *)centerYPosInner { - return self.myDefaultSizeClassInner.centerYPosInner; -} - -- (MyLayoutPos *)leftPosInner { - return self.myDefaultSizeClassInner.leftPosInner; -} - -- (MyLayoutPos *)rightPosInner { - return self.myDefaultSizeClassInner.rightPosInner; -} - -- (MyLayoutPos *)baselinePosInner { - return self.myDefaultSizeClassInner.baselinePosInner; -} - -- (MyLayoutSize *)widthSizeInner { - return self.myDefaultSizeClassInner.widthSizeInner; -} - -- (MyLayoutSize *)heightSizeInner { - return self.myDefaultSizeClassInner.heightSizeInner; -} - (CGFloat)myEstimatedWidth { //如果视图的父视图不是布局视图则直接返回宽度值。 @@ -510,6 +253,26 @@ - (CGFloat)myEstimatedHeight { } } +- (CGFloat)myLayoutPaddingTop { + return 0.0; +} +- (CGFloat)myLayoutPaddingBottom { + return 0.0; +} +- (CGFloat)myLayoutPaddingLeft { + return 0.0; +} +- (CGFloat)myLayoutPaddingRight { + return 0.0; +} +- (CGFloat)myLayoutPaddingLeading { + return 0.0; +} +- (CGFloat)myLayoutPaddingTrailing { + return 0.0; +} + + @end @implementation MyBaseLayout { @@ -529,209 +292,149 @@ - (void)dealloc { #pragma mark-- Public Methods + (BOOL)isRTL { - return [MyViewTraits isRTL]; + return [MyViewTraitsImpl isRTL]; } + (void)setIsRTL:(BOOL)isRTL { - [MyViewTraits setIsRTL:isRTL]; + [MyViewTraitsImpl setIsRTL:isRTL]; } + (void)updateRTL:(BOOL)isRTL inWindow:(UIWindow *)window { [window myUpdateRTL:isRTL]; } -//+ (void)myUpArabicUI:(BOOL)isArabicUI inWindow:(UIWindow *)window { -// [self updateRTL:isArabicUI inWindow:window]; -//} -- (CGFloat)paddingTop { - return self.myDefaultSizeClassInner.paddingTop; -} +_MYLAYOUT_PROPERTY_GET1(MyLayoutPos *,topPos) +_MYLAYOUT_PROPERTY_GET1(MyLayoutPos *,leadingPos) +_MYLAYOUT_PROPERTY_GET1(MyLayoutPos *,bottomPos) +_MYLAYOUT_PROPERTY_GET1(MyLayoutPos *,trailingPos) +_MYLAYOUT_PROPERTY_GET1(MyLayoutPos *,centerXPos) +_MYLAYOUT_PROPERTY_GET1(MyLayoutPos *,centerYPos) +_MYLAYOUT_PROPERTY_GET1(MyLayoutPos *,leftPos) +_MYLAYOUT_PROPERTY_GET1(MyLayoutPos *,rightPos) +_MYLAYOUT_PROPERTY_GET1(MyLayoutPos *,baselinePos) -- (void)setPaddingTop:(CGFloat)paddingTop { - MyLayoutTraits *layoutTraits = (MyLayoutTraits*)self.myDefaultSizeClass; - if (layoutTraits.paddingTop != paddingTop) { - layoutTraits.paddingTop = paddingTop; - [self setNeedsLayout]; - } -} +_MYLAYOUT_PROPERTY_OVERRIDE1(CGFloat,MyTop,myTop) +_MYLAYOUT_PROPERTY_OVERRIDE1(CGFloat,MyLeading,myLeading) +_MYLAYOUT_PROPERTY_OVERRIDE1(CGFloat,MyBottom,myBottom) +_MYLAYOUT_PROPERTY_OVERRIDE1(CGFloat,MyTrailing,myTrailing) +_MYLAYOUT_PROPERTY_OVERRIDE1(CGFloat,MyCenterX,myCenterX) +_MYLAYOUT_PROPERTY_OVERRIDE1(CGFloat,MyCenterY,myCenterY) +_MYLAYOUT_PROPERTY_OVERRIDE1(CGPoint,MyCenter,myCenter) +_MYLAYOUT_PROPERTY_OVERRIDE1(CGFloat,MyLeft,myLeft) +_MYLAYOUT_PROPERTY_OVERRIDE1(CGFloat,MyRight,myRight) +_MYLAYOUT_PROPERTY_OVERRIDE1(CGFloat,MyMargin,myMargin) +_MYLAYOUT_PROPERTY_OVERRIDE1(CGFloat,MyHorzMargin,myHorzMargin) +_MYLAYOUT_PROPERTY_OVERRIDE1(CGFloat,MyVertMargin,myVertMargin) -- (CGFloat)paddingLeading { - return self.myDefaultSizeClassInner.paddingLeading; -} +_MYLAYOUT_PROPERTY_GET1(MyLayoutSize *,widthSize) +_MYLAYOUT_PROPERTY_GET1(MyLayoutSize *,heightSize) -- (void)setPaddingLeading:(CGFloat)paddingLeading { - MyLayoutTraits *layoutTraits = (MyLayoutTraits*)self.myDefaultSizeClass; - if (layoutTraits.paddingLeading != paddingLeading) { - layoutTraits.paddingLeading = paddingLeading; - [self setNeedsLayout]; - } -} -- (CGFloat)paddingBottom { - return self.myDefaultSizeClassInner.paddingBottom; -} +_MYLAYOUT_PROPERTY_OVERRIDE1(CGFloat,MyWidth,myWidth) +_MYLAYOUT_PROPERTY_OVERRIDE1(CGFloat,MyHeight,myHeight) +_MYLAYOUT_PROPERTY_OVERRIDE1(CGSize,MySize,mySize) -- (void)setPaddingBottom:(CGFloat)paddingBottom { - MyLayoutTraits *layoutTraits = (MyLayoutTraits*)self.myDefaultSizeClass; - if (layoutTraits.paddingBottom != paddingBottom) { - layoutTraits.paddingBottom = paddingBottom; - [self setNeedsLayout]; - } -} +_MYLAYOUT_PROPERTY_GET1(BOOL,wrapContentWidth) +_MYLAYOUT_PROPERTY_GET1(BOOL,wrapContentHeight) +_MYLAYOUT_PROPERTY_GET1(BOOL,wrapContentSize) -- (CGFloat)paddingTrailing { - return self.myDefaultSizeClassInner.paddingTrailing; -} -- (void)setPaddingTrailing:(CGFloat)paddingTrailing { - MyLayoutTraits *layoutTraits = (MyLayoutTraits*)self.myDefaultSizeClass; - if (layoutTraits.paddingTrailing != paddingTrailing) { - layoutTraits.paddingTrailing = paddingTrailing; - [self setNeedsLayout]; - } -} -- (UIEdgeInsets)padding { - return self.myDefaultSizeClassInner.padding; -} +_MYLAYOUT_PROPERTY_OVERRIDE1(CGFloat,Weight,weight) +_MYLAYOUT_PROPERTY_OVERRIDE1(BOOL,UseFrame,useFrame) +_MYLAYOUT_PROPERTY_OVERRIDE1(BOOL,NoLayout,noLayout) +_MYLAYOUT_PROPERTY_OVERRIDE1(MyVisibility,Visibility,visibility) +_MYLAYOUT_PROPERTY_OVERRIDE1(MyGravity,Alignment,alignment) +_MYLAYOUT_PROPERTY_OVERRIDE1(BOOL,ReverseFloat,isReverseFloat) +_MYLAYOUT_PROPERTY_OVERRIDE1(BOOL,ClearFloat,clearFloat) + + + + +_MYLAYOUT_PROPERTY_LAYOUTVIEW_OVERRIDE1(MyLayoutTraitsImpl, CGFloat, PaddingTop, paddingTop) +_MYLAYOUT_PROPERTY_LAYOUTVIEW_OVERRIDE1(MyLayoutTraitsImpl, CGFloat, PaddingLeading, paddingLeading) +_MYLAYOUT_PROPERTY_LAYOUTVIEW_OVERRIDE1(MyLayoutTraitsImpl, CGFloat, PaddingBottom, paddingBottom) +_MYLAYOUT_PROPERTY_LAYOUTVIEW_OVERRIDE1(MyLayoutTraitsImpl, CGFloat, PaddingTrailing, paddingTrailing) + + +_MYLAYOUT_PROPERTY_LAYOUTVIEW_GET1(MyLayoutTraitsImpl, UIEdgeInsets, padding) + - (void)setPadding:(UIEdgeInsets)padding { - MyLayoutTraits *layoutTraits = (MyLayoutTraits*)self.myDefaultSizeClass; + MyLayoutTraitsImpl *layoutTraits = (MyLayoutTraitsImpl*)self.myDefaultTraits; if (!UIEdgeInsetsEqualToEdgeInsets(layoutTraits.padding, padding)) { layoutTraits.padding = padding; [self setNeedsLayout]; } } -- (CGFloat)paddingLeft { - return self.myDefaultSizeClassInner.paddingLeft; -} -- (void)setPaddingLeft:(CGFloat)paddingLeft { - MyLayoutTraits *layoutTraits = (MyLayoutTraits*)self.myDefaultSizeClass; - if (layoutTraits.paddingLeft != paddingLeft) { - layoutTraits.paddingLeft = paddingLeft; - [self setNeedsLayout]; - } -} +_MYLAYOUT_PROPERTY_LAYOUTVIEW_OVERRIDE1(MyLayoutTraitsImpl, CGFloat, PaddingLeft, paddingLeft) +_MYLAYOUT_PROPERTY_LAYOUTVIEW_OVERRIDE1(MyLayoutTraitsImpl, CGFloat, PaddingRight, paddingRight) -- (CGFloat)paddingRight { - return self.myDefaultSizeClassInner.paddingRight; -} - -- (void)setPaddingRight:(CGFloat)paddingRight { - MyLayoutTraits *layoutTraits = (MyLayoutTraits*)self.myDefaultSizeClass; - if (layoutTraits.paddingRight != paddingRight) { - layoutTraits.paddingRight = paddingRight; - [self setNeedsLayout]; - } -} - (BOOL)zeroPadding { //这里不用myDefaultSizeClassInner的原因是这个属性默认是YES! - return self.myDefaultSizeClass.zeroPadding; + return ((MyLayoutTraitsImpl*)self.myDefaultTraits).zeroPadding; } -- (void)setZeroPadding:(BOOL)zeroPadding { - MyLayoutTraits *layoutTraits = (MyLayoutTraits*)self.myDefaultSizeClass; - if (layoutTraits.zeroPadding != zeroPadding) { - layoutTraits.zeroPadding = zeroPadding; - [self setNeedsLayout]; - } -} +_MYLAYOUT_PROPERTY_LAYOUTVIEW_SET1(MyLayoutTraitsImpl, BOOL,ZeroPadding,zeroPadding) + - (UIRectEdge)insetsPaddingFromSafeArea { //这里不用myDefaultSizeClassInner的原因是这个属性默认是UIRectEdgeLeft | UIRectEdgeRight ! - return self.myDefaultSizeClass.insetsPaddingFromSafeArea; + return ((MyLayoutTraitsImpl*)self.myDefaultTraits).insetsPaddingFromSafeArea; } -- (void)setInsetsPaddingFromSafeArea:(UIRectEdge)insetsPaddingFromSafeArea { - MyLayoutTraits *layoutTraits = (MyLayoutTraits*)self.myDefaultSizeClass; - if (layoutTraits.insetsPaddingFromSafeArea != insetsPaddingFromSafeArea) { - layoutTraits.insetsPaddingFromSafeArea = insetsPaddingFromSafeArea; - [self setNeedsLayout]; - } -} +_MYLAYOUT_PROPERTY_LAYOUTVIEW_SET1(MyLayoutTraitsImpl, UIRectEdge,InsetsPaddingFromSafeArea,insetsPaddingFromSafeArea) -- (BOOL)insetLandscapeFringePadding { - return self.myDefaultSizeClassInner.insetLandscapeFringePadding; -} -- (void)setInsetLandscapeFringePadding:(BOOL)insetLandscapeFringePadding { - MyLayoutTraits *layoutTraits = (MyLayoutTraits*)self.myDefaultSizeClass; - if (layoutTraits.insetLandscapeFringePadding != insetLandscapeFringePadding) { - layoutTraits.insetLandscapeFringePadding = insetLandscapeFringePadding; - [self setNeedsLayout]; - } -} +_MYLAYOUT_PROPERTY_LAYOUTVIEW_OVERRIDE1(MyLayoutTraitsImpl, BOOL, InsetLandscapeFringePadding, insetLandscapeFringePadding) + + +_MYLAYOUT_PROPERTY_LAYOUTVIEW_OVERRIDE1(MyLayoutTraitsImpl, CGFloat, SubviewHSpacing, subviewHSpacing) +_MYLAYOUT_PROPERTY_LAYOUTVIEW_OVERRIDE1(MyLayoutTraitsImpl, CGFloat, SubviewVSpacing, subviewVSpacing) +_MYLAYOUT_PROPERTY_LAYOUTVIEW_OVERRIDE1(MyLayoutTraitsImpl, CGFloat, SubviewSpacing, subviewSpacing) + - (void)setSubviewHSpace:(CGFloat)subviewHSpace { - MyLayoutTraits *layoutTraits = (MyLayoutTraits*)self.myDefaultSizeClass; - if (layoutTraits.subviewHSpace != subviewHSpace) { - layoutTraits.subviewHSpace = subviewHSpace; - [self setNeedsLayout]; - } + self.subviewHSpacing = subviewHSpace; } - (CGFloat)subviewHSpace { - return self.myDefaultSizeClassInner.subviewHSpace; + return self.subviewHSpacing; } - (void)setSubviewVSpace:(CGFloat)subviewVSpace { - MyLayoutTraits *layoutTraits = (MyLayoutTraits*)self.myDefaultSizeClass; - if (layoutTraits.subviewVSpace != subviewVSpace) { - layoutTraits.subviewVSpace = subviewVSpace; - [self setNeedsLayout]; - } + self.subviewVSpacing = subviewVSpace; } - (CGFloat)subviewVSpace { - return self.myDefaultSizeClassInner.subviewVSpace; + return self.subviewVSpacing; } - (void)setSubviewSpace:(CGFloat)subviewSpace { - MyLayoutTraits *layoutTraits = (MyLayoutTraits*)self.myDefaultSizeClass; - if (layoutTraits.subviewSpace != subviewSpace) { - layoutTraits.subviewSpace = subviewSpace; - [self setNeedsLayout]; - } + self.subviewSpacing = subviewSpace; } - (CGFloat)subviewSpace { - return self.myDefaultSizeClassInner.subviewSpace; -} - -- (void)setGravity:(MyGravity)gravity { - MyLayoutTraits *layoutTraits = (MyLayoutTraits*)self.myDefaultSizeClass; - if (layoutTraits.gravity != gravity) { - layoutTraits.gravity = gravity; - [self setNeedsLayout]; - } + return self.subviewSpacing; } -- (MyGravity)gravity { - return self.myDefaultSizeClassInner.gravity; -} - -- (void)setReverseLayout:(BOOL)reverseLayout { - MyLayoutTraits *layoutTraits = (MyLayoutTraits*)self.myDefaultSizeClass; - if (layoutTraits.reverseLayout != reverseLayout) { - layoutTraits.reverseLayout = reverseLayout; - [self setNeedsLayout]; - } -} +_MYLAYOUT_PROPERTY_LAYOUTVIEW_OVERRIDE1(MyLayoutTraitsImpl, MyGravity, Gravity, gravity) +_MYLAYOUT_PROPERTY_LAYOUTVIEW_OVERRIDE1(MyLayoutTraitsImpl, BOOL, ReverseLayout, reverseLayout) -- (BOOL)reverseLayout { - return self.myDefaultSizeClassInner.reverseLayout; -} - (CGAffineTransform)layoutTransform { - //这里不用myDefaultSizeClassInner的原因是这个属性默认是CGAffineTransformIdentity - return self.myDefaultSizeClass.layoutTransform; + if (self.myDefaultTraitsInner == nil) { + return CGAffineTransformIdentity; + } else { + return ((MyLayoutTraitsImpl*)self.myDefaultTraits).layoutTransform; + } } - (void)setLayoutTransform:(CGAffineTransform)layoutTransform { - MyLayoutTraits *layoutTraits = (MyLayoutTraits*)self.myDefaultSizeClass; + MyLayoutTraitsImpl *layoutTraits = self.myDefaultTraits; if (!CGAffineTransformEqualToTransform(layoutTraits.layoutTransform, layoutTransform)) { layoutTraits.layoutTransform = layoutTransform; [self setNeedsLayout]; @@ -901,8 +604,8 @@ - (MyLayoutDragger *)createLayoutDragger { MyLayoutDragger *dragger = [MyLayoutDragger new]; dragger.currentIndex = -1; dragger.oldIndex = -1; - dragger.hasDragging = NO; - dragger.layout = self; + dragger.layoutView = self; + dragger.overlapRatio = 0.25; return dragger; } @@ -951,6 +654,19 @@ - (void)layoutAnimationWithDuration:(NSTimeInterval)duration options:(UIViewAnim _optionalData.aniCompletion = completion; } +- (id)fetchLayoutTraitsInSizeClass:(MySizeClass)sizeClass { + return [super fetchLayoutTraitsInSizeClass:sizeClass]; +} + +- (id)fetchLayoutTraitsInSizeClass:(MySizeClass)sizeClass copyFrom:(MySizeClass)srcSizeClass { + return [super fetchLayoutTraitsInSizeClass:sizeClass copyFrom:srcSizeClass]; +} + +- (void)setLayoutTraits:(id)value inSizeClass:(MySizeClass)sizeClass { + return [super setLayoutTraits:value inSizeClass:sizeClass]; + +} + #pragma mark-- Touches Event - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { @@ -979,9 +695,9 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(UIView *)object cha //监控非布局父视图的frame的变化,而改变自身的位置和尺寸 if (context == _myObserverContextC) { //只监控父视图的尺寸变换 - CGRect rcOld = [change[NSKeyValueChangeOldKey] CGRectValue]; - CGRect rcNew = [change[NSKeyValueChangeNewKey] CGRectValue]; - if (!_myCGSizeEqual(rcOld.size, rcNew.size)) { + CGRect oldRect = [change[NSKeyValueChangeOldKey] CGRectValue]; + CGRect newRect = [change[NSKeyValueChangeNewKey] CGRectValue]; + if (!_myCGSizeEqual(oldRect.size, newRect.size)) { [self myUpdateLayoutRectInNoLayoutSuperview:object]; } return; @@ -990,7 +706,7 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(UIView *)object cha //监控子视图的frame的变化以便重新进行布局 if (!self.isMyLayouting) { if (context == _myObserverContextA) { - if (!object.myCurrentSizeClassInner.useFrame) { + if (!object.myCurrentTraitsInner.useFrame) { [self setNeedsLayout]; //这里添加的原因是有可能子视图取消隐藏后不会绘制自身,所以这里要求子视图重新绘制自身 if ([keyPath isEqualToString:@"hidden"] && ![change[NSKeyValueChangeNewKey] boolValue]) { @@ -998,7 +714,7 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(UIView *)object cha } } } else if (context == _myObserverContextB) { //针对UILabel特殊处理。。 - MyViewTraits *subviewTraits = (MyViewTraits*)object.myDefaultSizeClass; + MyViewTraitsImpl *subviewTraits = (MyViewTraitsImpl*)object.myCurrentTraits; if (subviewTraits.widthSizeInner.wrapVal || subviewTraits.heightSizeInner.wrapVal) { [self setNeedsLayout]; } @@ -1008,24 +724,11 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(UIView *)object cha #pragma mark-- Override Methods -- (void)setWrapContentHeight:(BOOL)wrapContentHeight { - MyLayoutTraits *layoutTraits = (MyLayoutTraits*)self.myDefaultSizeClass; - if (layoutTraits.wrapContentHeight != wrapContentHeight) { - layoutTraits.wrapContentHeight = wrapContentHeight; - [self setNeedsLayout]; - } -} - -- (void)setWrapContentWidth:(BOOL)wrapContentWidth { - MyLayoutTraits *layoutTraits = (MyLayoutTraits*)self.myDefaultSizeClass; - if (layoutTraits.wrapContentWidth != wrapContentWidth) { - layoutTraits.wrapContentWidth = wrapContentWidth; - [self setNeedsLayout]; - } -} +_MYLAYOUT_PROPERTY_LAYOUTVIEW_SET1(MyLayoutTraitsImpl, BOOL, WrapContentHeight, wrapContentHeight) +_MYLAYOUT_PROPERTY_LAYOUTVIEW_SET1(MyLayoutTraitsImpl, BOOL, WrapContentWidth, wrapContentWidth) - (void)setWrapContentSize:(BOOL)wrapContentSize { - MyLayoutTraits *layoutTraits = (MyLayoutTraits*)self.myDefaultSizeClass; + MyLayoutTraitsImpl *layoutTraits = self.myDefaultTraits; layoutTraits.wrapContentSize = wrapContentSize; [self setNeedsLayout]; } @@ -1042,14 +745,23 @@ - (CGSize)calcLayoutSize:(CGSize)size subviewEngines:(NSMutableArray *)subviewEn context->selfSize.height = size.height; } } + + MyLayoutTraitsImpl *layoutTraits = context->layoutViewEngine.currentTraits; + context->paddingTop = layoutTraits.myLayoutPaddingTop; + context->paddingBottom = layoutTraits.myLayoutPaddingBottom; + context->paddingLeading = layoutTraits.myLayoutPaddingLeading; + context->paddingTrailing = layoutTraits.myLayoutPaddingTrailing; + context->vertGravity = MYVERTGRAVITY(layoutTraits.gravity); + context->horzGravity = [MyViewTraitsImpl convertLeadingTrailingGravityFromLeftRightGravity:MYHORZGRAVITY(layoutTraits.gravity)]; + context->vertSpacing = layoutTraits.subviewVSpacing; + context->horzSpacing = layoutTraits.subviewHSpacing; + if (context->subviewEngines == nil) { + context->subviewEngines = [layoutTraits filterEngines:subviewEngines]; + } return context->selfSize; } -- (id)createSizeClassInstance { - return [MyLayoutTraits new]; -} - - (CGSize)sizeThatFits:(CGSize)size { return [self sizeThatFits:size inSizeClass:MySizeClass_wAny | MySizeClass_hAny]; } @@ -1125,7 +837,7 @@ - (void)willMoveToWindow:(UIWindow *)newWindow { - (void)willMoveToSuperview:(UIView *)newSuperview { [super willMoveToSuperview:newSuperview]; - MyBaseLayout *layoutTraits = self.myDefaultSizeClass; + MyLayoutTraitsImpl *layoutTraits = self.myCurrentTraits; //特殊处理如果视图是控制器根视图则取消高度或者宽度包裹,安全区padding的缩进, 以及adjustScrollViewContentSizeMode的设置。 //但是有一种特殊情况是控制器是子视图控制器。因此还需要添加判断父视图是否是非布局父视图,只有是非布局父视图下才将自适应设置清除 @@ -1285,13 +997,15 @@ - (void)safeAreaInsetsDidChange { [super safeAreaInsetsDidChange]; #endif + + MyViewTraitsImpl *viewTraits = self.myCurrentTraitsInner; if (self.superview != nil && ![self.superview isKindOfClass:[MyBaseLayout class]] && - (self.leadingPosInner.isSafeAreaPos || - self.trailingPosInner.isSafeAreaPos || - self.topPosInner.isSafeAreaPos || - self.bottomPosInner.isSafeAreaPos)) { + (viewTraits.leadingPosInner.isSafeAreaPos || + viewTraits.trailingPosInner.isSafeAreaPos || + viewTraits.topPosInner.isSafeAreaPos || + viewTraits.bottomPosInner.isSafeAreaPos)) { if (!self.isMyLayouting) { self.isMyLayouting = YES; [self myUpdateLayoutRectInNoLayoutSuperview:self.superview]; @@ -1307,10 +1021,11 @@ - (void)setNeedsLayout { - (CGSize)intrinsicContentSize { CGSize size = [super intrinsicContentSize]; - if (self.translatesAutoresizingMaskIntoConstraints == NO && (self.widthSizeInner.wrapVal || self.heightSizeInner.wrapVal)) { - if (self.widthSizeInner.wrapVal && self.heightSizeInner.wrapVal) { + MyViewTraitsImpl *viewTraits = self.myCurrentTraitsInner; + if (self.translatesAutoresizingMaskIntoConstraints == NO && (viewTraits.widthSizeInner.wrapVal || viewTraits.heightSizeInner.wrapVal)) { + if (viewTraits.widthSizeInner.wrapVal && viewTraits.heightSizeInner.wrapVal) { size = [self sizeThatFits:CGSizeZero]; - } else if (self.widthSizeInner.wrapVal) { + } else if (viewTraits.widthSizeInner.wrapVal) { //动态宽度 NSLayoutConstraint *heightConstraint = nil; for (NSLayoutConstraint *constraint in self.constraints) { @@ -1423,14 +1138,14 @@ - (void)doLayoutSubviews { } //得到当前布局视图和子视图的最佳的sizeClass MyLayoutEngine *layoutViewEngine = self.myEngine; - layoutViewEngine.currentSizeClass = [layoutViewEngine fetchView:self bestLayoutSizeClass:sizeClass]; + layoutViewEngine.currentTraits = [layoutViewEngine fetchView:self bestTraitsAt:sizeClass]; NSMutableArray *subviewEngines = [NSMutableArray arrayWithCapacity:self.subviews.count]; for (UIView *subview in self.subviews) { MyLayoutEngine *subviewEngine = subview.myEngine; - subviewEngine.currentSizeClass = [subviewEngine fetchView:subview bestLayoutSizeClass:sizeClass]; + subviewEngine.currentTraits = [subviewEngine fetchView:subview bestTraitsAt:sizeClass]; - if (!subviewEngine.hasObserver && subviewEngine.currentSizeClass != nil && !subviewEngine.currentSizeClass.useFrame) { + if (!subviewEngine.hasObserver && subviewEngine.currentTraits != nil && !subviewEngine.currentTraits.useFrame) { subviewEngine.hasObserver = YES; [self myAddSubviewObserver:subview]; } @@ -1438,7 +1153,7 @@ - (void)doLayoutSubviews { [subviewEngines addObject:subviewEngine]; } - MyLayoutTraits *layoutTraits = (MyLayoutTraits*)layoutViewEngine.currentSizeClass; + MyLayoutTraitsImpl *layoutTraits = layoutViewEngine.currentTraits; //计算布局 CGSize oldSelfSize = self.bounds.size; @@ -1469,7 +1184,7 @@ - (void)doLayoutSubviews { CGPoint sbvOldCenter = subview.center; MyLayoutEngine *subviewEngine = subview.myEngine; - MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; if (subviewEngine.leading != CGFLOAT_MAX && subviewEngine.top != CGFLOAT_MAX && !subviewTraits.noLayout && !subviewTraits.useFrame) { if (subviewEngine.width < 0) { @@ -1521,9 +1236,9 @@ - (void)doLayoutSubviews { if (subviewTraits.visibility == MyVisibility_Gone && !subview.isHidden) { subview.bounds = CGRectMake(sbvOldBounds.origin.x, sbvOldBounds.origin.y, 0, 0); } - if (subviewEngine.currentSizeClass.viewLayoutCompleteBlock != nil) { - subviewEngine.currentSizeClass.viewLayoutCompleteBlock(self, subview); - subviewEngine.currentSizeClass.viewLayoutCompleteBlock = nil; + if (subviewEngine.viewLayoutCompleteBlock != nil) { + subviewEngine.viewLayoutCompleteBlock(self, subview); + subviewEngine.viewLayoutCompleteBlock = nil; } [subviewEngine reset]; @@ -1538,7 +1253,7 @@ - (void)doLayoutSubviews { //如果父视图也是布局视图,并且自己隐藏则不调整自身的尺寸和位置。 BOOL isAdjustSelf = YES; if (self.superview != nil && [self.superview isKindOfClass:[MyBaseLayout class]]) { - if ([(MyViewTraits*)self.myCurrentSizeClass invalid]) { + if ([self.myCurrentTraits invalid]) { isAdjustSelf = NO; } } @@ -1650,18 +1365,19 @@ - (void)doLayoutSubviews { //这里处理当布局视图的父视图是非布局父视图,且父视图具有wrap属性时需要调整父视图的尺寸。 if (superview != nil && ![superview isKindOfClass:[MyBaseLayout class]]) { - if (superview.heightSizeInner.wrapVal || superview.widthSizeInner.wrapVal) { + MyViewTraitsImpl *superviewTraits = superview.myCurrentTraitsInner; + if (superviewTraits.heightSizeInner.wrapVal || superviewTraits.widthSizeInner.wrapVal) { //调整父视图的高度和宽度。frame值。 CGRect superBounds = superview.bounds; CGPoint superCenter = superview.center; - if (superview.heightSizeInner.wrapVal) { - superBounds.size.height = [self myValidMeasure:superview.heightSizeInner subview:superview calcSize:layoutTraits.myTop + newSelfSize.height + layoutTraits.myBottom subviewSize:superBounds.size selfLayoutSize:newSelfSize]; + if (superviewTraits.heightSizeInner.wrapVal) { + superBounds.size.height = [self myValidMeasure:superviewTraits.heightSizeInner subview:superview calcSize:layoutTraits.myTop + newSelfSize.height + layoutTraits.myBottom subviewSize:superBounds.size selfLayoutSize:newSelfSize]; superCenter.y += (superBounds.size.height - superview.bounds.size.height) * superview.layer.anchorPoint.y; } - if (superview.widthSizeInner.wrapVal) { - superBounds.size.width = [self myValidMeasure:superview.widthSizeInner subview:superview calcSize:layoutTraits.myLeading + newSelfSize.width + layoutTraits.myTrailing subviewSize:superBounds.size selfLayoutSize:newSelfSize]; + if (superviewTraits.widthSizeInner.wrapVal) { + superBounds.size.width = [self myValidMeasure:superviewTraits.widthSizeInner subview:superview calcSize:layoutTraits.myLeading + newSelfSize.width + layoutTraits.myTrailing subviewSize:superBounds.size selfLayoutSize:newSelfSize]; superCenter.x += (superBounds.size.width - superview.bounds.size.width) * superview.layer.anchorPoint.x; } @@ -1748,9 +1464,13 @@ - (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection { #pragma mark-- Private Methods +- (Class) myTratisClass { + return [MyLayoutTraitsImpl class]; +} + - (MyLayoutEngine *)myEngine { if (_myEngine == nil) { - _myEngine = [MyLayoutEngine new]; + _myEngine = [[MyLayoutEngine alloc] initWithTraitsClass:[self myTratisClass]]; } return _myEngine; } @@ -1772,12 +1492,12 @@ - (CGSize)myEstimateLayoutSize:(CGSize)size inSizeClass:(MySizeClass)sizeClass s context.sizeClass = sizeClass; context.layoutViewEngine = layoutViewEngine; - if (layoutViewEngine.currentSizeClass.widthSizeInner.numberVal != nil) { - size.width = MAX(layoutViewEngine.currentSizeClass.widthSizeInner.measure, size.width); + if (layoutViewEngine.currentTraits.widthSizeInner.numberVal != nil) { + size.width = MAX(layoutViewEngine.currentTraits.widthSizeInner.measure, size.width); } - if (layoutViewEngine.currentSizeClass.heightSizeInner.numberVal != nil) { - size.height = MAX(layoutViewEngine.currentSizeClass.heightSizeInner.measure, size.height); + if (layoutViewEngine.currentTraits.heightSizeInner.numberVal != nil) { + size.height = MAX(layoutViewEngine.currentTraits.heightSizeInner.measure, size.height); } CGSize selfSize = [self calcLayoutSize:size subviewEngines:subviewEngines context:&context]; @@ -1796,7 +1516,7 @@ - (CGFloat)myCalcSubview:(MyLayoutEngine *)subviewEngine baselinePos:(CGFloat)baselinePos withContext:(MyLayoutContext *)context { - MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; UIView *subview = subviewTraits.view; CGFloat layoutViewContentHeight = context->selfSize.height - context->paddingTop - context->paddingBottom; @@ -1863,7 +1583,7 @@ - (CGFloat)myCalcSubview:(MyLayoutEngine *)subviewEngine horzGravity:(MyGravity)horzGravity withContext:(MyLayoutContext *)context { - MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; UIView *subview = subviewTraits.view; CGFloat layoutViewContentWidth = context->selfSize.width - context->paddingLeading - context->paddingTrailing; CGFloat marginLeading = [self myValidMargin:subviewTraits.leadingPosInner subview:subview calcPos:[subviewTraits.leadingPosInner measureWith:layoutViewContentWidth] selfLayoutSize:context->selfSize]; @@ -1921,8 +1641,8 @@ - (CGSize)myCalcSizeInNoLayoutSuperview:(UIView *)newSuperview currentSize:(CGSi return size; } CGRect rectSuper = newSuperview.bounds; - MyViewTraits *superviewTraits = (MyViewTraits*)newSuperview.myDefaultSizeClassInner; //非布局父视图只有默认布局样式 - MyLayoutTraits *layoutTraits = (MyLayoutTraits*)self.myCurrentSizeClass; + MyViewTraitsImpl *superviewTraits = newSuperview.myCurrentTraitsInner; + MyLayoutTraitsImpl *layoutTraits = self.myCurrentTraits; if (!superviewTraits.widthSizeInner.wrapVal) { if (layoutTraits.widthSizeInner.anchorVal.view == newSuperview) { @@ -1980,7 +1700,7 @@ - (BOOL)myUpdateLayoutRectInNoLayoutSuperview:(UIView *)newSuperview { BOOL isAdjust = NO; //这个变量表明是否后续父视图尺寸变化后需要调整更新布局视图的尺寸。 CGRect rectSuper = newSuperview.bounds; - MyLayoutTraits *layoutTraits = (MyLayoutTraits *)self.myCurrentSizeClass; + MyLayoutTraitsImpl *layoutTraits = self.myCurrentTraits; CGFloat marginLeading = [layoutTraits.leadingPosInner measureWith:rectSuper.size.width]; CGFloat marginTrailing = [layoutTraits.trailingPosInner measureWith:rectSuper.size.width]; @@ -2041,40 +1761,28 @@ - (BOOL)myUpdateLayoutRectInNoLayoutSuperview:(UIView *)newSuperview { rectSelf.size.width = [self myValidMeasure:layoutTraits.widthSizeInner subview:self calcSize:rectSelf.size.width subviewSize:rectSelf.size selfLayoutSize:rectSuper.size]; } -#if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 110000) || (__TV_OS_VERSION_MAX_ALLOWED >= 110000) - - if (@available(iOS 11.0, *)) { - - //在ios11后如果是滚动视图的contentInsetAdjustmentBehavior设置为UIScrollViewContentInsetAdjustmentAlways - //那么系统不管contentSize如何总是会将安全区域叠加到contentInsets所以这里的边距不应该是偏移的边距而是0 - UIScrollView *scrollSuperView = nil; - if ([newSuperview isKindOfClass:[UIScrollView class]]) - scrollSuperView = (UIScrollView *)newSuperview; - if (scrollSuperView != nil && layoutTraits.leadingPosInner.isSafeAreaPos) { - marginLeading = layoutTraits.leadingPosInner.offsetVal + ([MyBaseLayout isRTL] ? scrollSuperView.safeAreaInsets.right : scrollSuperView.safeAreaInsets.left) - ([MyBaseLayout isRTL] ? scrollSuperView.adjustedContentInset.right : scrollSuperView.adjustedContentInset.left); - } + //在ios11后如果是滚动视图的contentInsetAdjustmentBehavior设置为UIScrollViewContentInsetAdjustmentAlways + //那么系统不管contentSize如何总是会将安全区域叠加到contentInsets所以这里的边距不应该是偏移的边距而是0 + UIScrollView *scrollSuperView = nil; + if ([newSuperview isKindOfClass:[UIScrollView class]]) + scrollSuperView = (UIScrollView *)newSuperview; + if (scrollSuperView != nil && layoutTraits.leadingPosInner.isSafeAreaPos) { + marginLeading = layoutTraits.leadingPosInner.offsetVal + ([MyBaseLayout isRTL] ? scrollSuperView.safeAreaInsets.right : scrollSuperView.safeAreaInsets.left) - ([MyBaseLayout isRTL] ? scrollSuperView.adjustedContentInset.right : scrollSuperView.adjustedContentInset.left); } -#endif - rectSelf.origin.x = marginLeading; } else if (layoutTraits.centerXPosInner.val != nil) { isAdjust = YES; rectSelf.origin.x = (rectSuper.size.width - rectSelf.size.width) / 2; rectSelf.origin.x += [layoutTraits.centerXPosInner measureWith:rectSuper.size.width]; } else if (layoutTraits.leadingPosInner.val != nil) { -#if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 110000) || (__TV_OS_VERSION_MAX_ALLOWED >= 110000) - - if (@available(iOS 11.0, *)) { - //iOS11中的滚动条的安全区会叠加到contentInset里面。因此这里要特殊处理,让x轴的开始位置不应该算偏移。 - UIScrollView *scrollSuperView = nil; - if ([newSuperview isKindOfClass:[UIScrollView class]]) { - scrollSuperView = (UIScrollView *)newSuperview; - } - if (scrollSuperView != nil && layoutTraits.leadingPosInner.isSafeAreaPos) { - marginLeading = layoutTraits.leadingPosInner.offsetVal + ([MyBaseLayout isRTL] ? scrollSuperView.safeAreaInsets.right : scrollSuperView.safeAreaInsets.left) - ([MyBaseLayout isRTL] ? scrollSuperView.adjustedContentInset.right : scrollSuperView.adjustedContentInset.left); - } + //iOS11中的滚动条的安全区会叠加到contentInset里面。因此这里要特殊处理,让x轴的开始位置不应该算偏移。 + UIScrollView *scrollSuperView = nil; + if ([newSuperview isKindOfClass:[UIScrollView class]]) { + scrollSuperView = (UIScrollView *)newSuperview; + } + if (scrollSuperView != nil && layoutTraits.leadingPosInner.isSafeAreaPos) { + marginLeading = layoutTraits.leadingPosInner.offsetVal + ([MyBaseLayout isRTL] ? scrollSuperView.safeAreaInsets.right : scrollSuperView.safeAreaInsets.left) - ([MyBaseLayout isRTL] ? scrollSuperView.adjustedContentInset.right : scrollSuperView.adjustedContentInset.left); } -#endif rectSelf.origin.x = marginLeading; } else if (layoutTraits.trailingPosInner.val != nil) { isAdjust = YES; @@ -2120,40 +1828,29 @@ - (BOOL)myUpdateLayoutRectInNoLayoutSuperview:(UIView *)newSuperview { rectSelf.size.height = [self myValidMeasure:layoutTraits.heightSizeInner subview:self calcSize:rectSelf.size.height subviewSize:rectSelf.size selfLayoutSize:rectSuper.size]; } -#if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 110000) || (__TV_OS_VERSION_MAX_ALLOWED >= 110000) - - if (@available(iOS 11.0, *)) { - - //在ios11后如果是滚动视图的contentInsetAdjustmentBehavior设置为UIScrollViewContentInsetAdjustmentAlways - //那么系统不管contentSize如何总是会将安全区域叠加到contentInsets所以这里的边距不应该是偏移的边距而是0 - UIScrollView *scrollSuperView = nil; - if ([newSuperview isKindOfClass:[UIScrollView class]]) - scrollSuperView = (UIScrollView *)newSuperview; - if (scrollSuperView != nil && layoutTraits.topPosInner.isSafeAreaPos) { - marginTop = layoutTraits.topPosInner.offsetVal + scrollSuperView.safeAreaInsets.top - scrollSuperView.adjustedContentInset.top; - } + //在ios11后如果是滚动视图的contentInsetAdjustmentBehavior设置为UIScrollViewContentInsetAdjustmentAlways + //那么系统不管contentSize如何总是会将安全区域叠加到contentInsets所以这里的边距不应该是偏移的边距而是0 + UIScrollView *scrollSuperView = nil; + if ([newSuperview isKindOfClass:[UIScrollView class]]) + scrollSuperView = (UIScrollView *)newSuperview; + if (scrollSuperView != nil && layoutTraits.topPosInner.isSafeAreaPos) { + marginTop = layoutTraits.topPosInner.offsetVal + scrollSuperView.safeAreaInsets.top - scrollSuperView.adjustedContentInset.top; } -#endif - + rectSelf.origin.y = marginTop; } else if (layoutTraits.centerYPosInner.val != nil) { isAdjust = YES; rectSelf.origin.y = (rectSuper.size.height - rectSelf.size.height) / 2 + [layoutTraits.centerYPosInner measureWith:rectSuper.size.height]; } else if (layoutTraits.topPosInner.val != nil) { -#if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 110000) || (__TV_OS_VERSION_MAX_ALLOWED >= 110000) - - if (@available(iOS 11.0, *)) { - //在ios11后如果是滚动视图的contentInsetAdjustmentBehavior设置为UIScrollViewContentInsetAdjustmentAlways - //那么系统不管contentSize如何总是会将安全区域叠加到contentInsets所以这里的边距不应该是偏移的边距而是0 - UIScrollView *scrollSuperView = nil; - if ([newSuperview isKindOfClass:[UIScrollView class]]) { - scrollSuperView = (UIScrollView *)newSuperview; - } - if (scrollSuperView != nil && layoutTraits.topPosInner.isSafeAreaPos) { - marginTop = layoutTraits.topPosInner.offsetVal + scrollSuperView.safeAreaInsets.top - scrollSuperView.adjustedContentInset.top; - } + //在ios11后如果是滚动视图的contentInsetAdjustmentBehavior设置为UIScrollViewContentInsetAdjustmentAlways + //那么系统不管contentSize如何总是会将安全区域叠加到contentInsets所以这里的边距不应该是偏移的边距而是0 + UIScrollView *scrollSuperView = nil; + if ([newSuperview isKindOfClass:[UIScrollView class]]) { + scrollSuperView = (UIScrollView *)newSuperview; + } + if (scrollSuperView != nil && layoutTraits.topPosInner.isSafeAreaPos) { + marginTop = layoutTraits.topPosInner.offsetVal + scrollSuperView.safeAreaInsets.top - scrollSuperView.adjustedContentInset.top; } -#endif rectSelf.origin.y = marginTop; } else if (layoutTraits.bottomPosInner.val != nil) { isAdjust = YES; @@ -2183,7 +1880,7 @@ - (BOOL)myUpdateLayoutRectInNoLayoutSuperview:(UIView *)newSuperview { return isAdjust; } -- (CGFloat)mySubview:(MyViewTraits *)subviewTraits wrapHeightSizeFits:(CGSize)size withContext:(MyLayoutContext *)context { +- (CGFloat)mySubview:(MyViewTraitsImpl *)subviewTraits wrapHeightSizeFits:(CGSize)size withContext:(MyLayoutContext *)context { UIView *subview = subviewTraits.view; CGFloat height = 0.0; @@ -2250,8 +1947,14 @@ - (CGFloat)myGetBoundLimitMeasure:(MyLayoutSize *)anchor subview:(UIView *)subvi } else { if (anchor.anchorVal.anchorType == MyLayoutAnchorType_Width) { value = anchor.anchorVal.view.myEstimatedWidth; + if (anchor.anchorVal.view == self.superview) { + value -= (self.superview.myLayoutPaddingLeading + self.superview.myLayoutPaddingTrailing); + } } else { value = anchor.anchorVal.view.myEstimatedHeight; + if (anchor.anchorVal.view == self.superview) { + value -= (self.superview.myLayoutPaddingTop + self.superview.myLayoutPaddingBottom); + } } } @@ -2355,7 +2058,7 @@ - (CGFloat)myValidMargin:(MyLayoutPos *)anchor subview:(UIView *)subview calcPos - (NSArray *)myUpdateCurrentSizeClass:(MySizeClass)sizeClass subviews:(NSArray *)subviews { MyLayoutEngine *layoutViewEngine = self.myEngine; - layoutViewEngine.currentSizeClass = (MyViewTraits*)[self fetchLayoutSizeClass:sizeClass]; + layoutViewEngine.currentTraits = (MyViewTraitsImpl*)[self fetchLayoutTraitsInSizeClass:sizeClass]; if (subviews == nil) { subviews = self.subviews; @@ -2363,7 +2066,10 @@ - (NSArray *)myUpdateCurrentSizeClass:(MySizeClass)sizeClass subviews:(NSArray *subviewEngines = [NSMutableArray arrayWithCapacity:subviews.count]; for (UIView *subview in subviews) { MyLayoutEngine *subviewEngine = subview.myEngine; - subviewEngine.currentSizeClass = (MyViewTraits*)[subview fetchLayoutSizeClass:sizeClass]; + subviewEngine.currentTraits = (MyViewTraitsImpl*)[subview fetchLayoutTraitsInSizeClass:sizeClass]; + if ([subviewEngine.currentTraits invalid]) { + [subviewEngine reset]; + } [subviewEngines addObject:subviewEngine]; } @@ -2372,7 +2078,7 @@ - (NSArray *)myUpdateCurrentSizeClass:(MySizeClass)sizeClass subviews:(NSArraylayoutViewEngine.currentSizeClass; + MyLayoutTraitsImpl *layoutTraits = context->layoutViewEngine.currentTraits; //调整布局视图自己的尺寸。 context->selfSize.height = [self myValidMeasure:layoutTraits.heightSizeInner subview:self calcSize:context->selfSize.height subviewSize:context->selfSize selfLayoutSize:self.superview.bounds.size]; @@ -2435,25 +2141,25 @@ - (UIFont *)myGetSubviewFont:(UIView *)subview { } - (CGFloat)myLayoutPaddingTop { - return self.myCurrentSizeClass.myLayoutPaddingTop; + return ((MyLayoutTraitsImpl*)self.myCurrentTraits).myLayoutPaddingTop; } - (CGFloat)myLayoutPaddingBottom { - return self.myCurrentSizeClass.myLayoutPaddingBottom; + return ((MyLayoutTraitsImpl*)self.myCurrentTraits).myLayoutPaddingBottom; } - (CGFloat)myLayoutPaddingLeft { - return self.myCurrentSizeClass.myLayoutPaddingLeft; + return ((MyLayoutTraitsImpl*)self.myCurrentTraits).myLayoutPaddingLeft; } - (CGFloat)myLayoutPaddingRight { - return self.myCurrentSizeClass.myLayoutPaddingRight; + return ((MyLayoutTraitsImpl*)self.myCurrentTraits).myLayoutPaddingRight; } - (CGFloat)myLayoutPaddingLeading { - return self.myCurrentSizeClass.myLayoutPaddingLeading; + return ((MyLayoutTraitsImpl*)self.myCurrentTraits).myLayoutPaddingLeading; } - (CGFloat)myLayoutPaddingTrailing { - return self.myCurrentSizeClass.myLayoutPaddingTrailing; + return ((MyLayoutTraitsImpl*)self.myCurrentTraits).myLayoutPaddingTrailing; } -- (void)myLayout:(MyLayoutTraits *)layoutTraits adjustScrollViewContentWithSize:(CGSize)newSize { +- (void)myLayout:(MyLayoutTraitsImpl *)layoutTraits adjustScrollViewContentWithSize:(CGSize)newSize { if (self.adjustScrollViewContentSizeMode == MyAdjustScrollViewContentSizeModeYes && self.superview != nil && [self.superview isKindOfClass:[UIScrollView class]]) { UIScrollView *scrollView = (UIScrollView *)self.superview; CGSize contsize = scrollView.contentSize; @@ -2464,26 +2170,19 @@ - (void)myLayout:(MyLayoutTraits *)layoutTraits adjustScrollViewContentWithSize: CGFloat marginTrailing = [layoutTraits.trailingPosInner measureWith:rectSuper.size.width]; CGFloat marginTop = [layoutTraits.topPosInner measureWith:rectSuper.size.height]; CGFloat marginBottom = [layoutTraits.bottomPosInner measureWith:rectSuper.size.height]; - -#if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 110000) || (__TV_OS_VERSION_MAX_ALLOWED >= 110000) - if (@available(iOS 11.0, *)) { - if (1) { - if (layoutTraits.leadingPosInner.isSafeAreaPos) { - marginLeading = layoutTraits.leadingPosInner.offsetVal; - } - if (layoutTraits.trailingPosInner.isSafeAreaPos) { - marginTrailing = layoutTraits.trailingPosInner.offsetVal; - } - if (layoutTraits.topPosInner.isSafeAreaPos) { - marginTop = layoutTraits.topPosInner.offsetVal; - } - if (layoutTraits.bottomPosInner.isSafeAreaPos) { - marginBottom = layoutTraits.bottomPosInner.offsetVal; - } - } + if (layoutTraits.leadingPosInner.isSafeAreaPos) { + marginLeading = layoutTraits.leadingPosInner.offsetVal; } -#endif - + if (layoutTraits.trailingPosInner.isSafeAreaPos) { + marginTrailing = layoutTraits.trailingPosInner.offsetVal; + } + if (layoutTraits.topPosInner.isSafeAreaPos) { + marginTop = layoutTraits.topPosInner.offsetVal; + } + if (layoutTraits.bottomPosInner.isSafeAreaPos) { + marginBottom = layoutTraits.bottomPosInner.offsetVal; + } + if (contsize.height != newSize.height + marginTop + marginBottom) { contsize.height = newSize.height + marginTop + marginBottom; } @@ -2557,7 +2256,7 @@ - (MySizeClass)myGetGlobalSizeClass { - (void)myRemoveSubviewObserver:(UIView *)subview { MyLayoutEngine *subviewEngine = subview.myEngineInner; if (subviewEngine != nil) { - subviewEngine.currentSizeClass.viewLayoutCompleteBlock = nil; + subviewEngine.viewLayoutCompleteBlock = nil; if (subviewEngine.hasObserver) { subviewEngine.hasObserver = NO; if (![subview isKindOfClass:[MyBaseLayout class]]) { @@ -2593,8 +2292,8 @@ - (void)myAddSubviewObserver:(UIView *)subview { - (void)myAdjustSizeSettingOfSubviewEngine:(MyLayoutEngine *)subviewEngine withContext:(MyLayoutContext *)context { - MyLayoutTraits *layoutTraits = (MyLayoutTraits*)context->layoutViewEngine.currentSizeClass; - MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + MyLayoutTraitsImpl *layoutTraits = context->layoutViewEngine.currentTraits; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; UIView *subview = subviewTraits.view; if (!context->isEstimate) { @@ -2628,8 +2327,8 @@ - (void)myAdjustSizeSettingOfSubviewEngine:(MyLayoutEngine *)subviewEngine withC - (CGFloat)myWidthSizeValueOfSubviewEngine:(MyLayoutEngine *)subviewEngine withContext:(MyLayoutContext *)context { - MyLayoutTraits *layoutTraits = (MyLayoutTraits*)context->layoutViewEngine.currentSizeClass; - MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + MyLayoutTraitsImpl *layoutTraits = context->layoutViewEngine.currentTraits; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; CGFloat retVal = subviewEngine.width; MyLayoutSize *sbvWidthSizeInner = subviewTraits.widthSizeInner; if (sbvWidthSizeInner.numberVal != nil) { //宽度等于固定的值。 @@ -2663,8 +2362,8 @@ - (CGFloat)myWidthSizeValueOfSubviewEngine:(MyLayoutEngine *)subviewEngine - (CGFloat)myHeightSizeValueOfSubviewEngine:(MyLayoutEngine *)subviewEngine withContext:(MyLayoutContext *)context { - MyLayoutTraits *layoutTraits = (MyLayoutTraits*)context->layoutViewEngine.currentSizeClass; - MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + MyLayoutTraitsImpl *layoutTraits = context->layoutViewEngine.currentTraits; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; MyLayoutSize *subviewHeightSizeInner = subviewTraits.heightSizeInner; CGFloat contentWidth = context->selfSize.width - context->paddingLeading - context->paddingTrailing; @@ -2697,8 +2396,8 @@ - (void)myCalcRectOfSubviewEngine:(MyLayoutEngine *)subviewEngine pMaxWrapSize:(CGSize *)pMaxWrapSize withContext:(MyLayoutContext *)context { - MyLayoutTraits *layoutTraits = (MyLayoutTraits*)context->layoutViewEngine.currentSizeClass; - MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + MyLayoutTraitsImpl *layoutTraits = context->layoutViewEngine.currentTraits; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; subviewEngine.width = [self myWidthSizeValueOfSubviewEngine:subviewEngine withContext:context]; subviewEngine.width = [self myValidMeasure:subviewTraits.widthSizeInner subview:subviewTraits.view calcSize:subviewEngine.width subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; @@ -2785,15 +2484,16 @@ - (void)myHookSublayout:(MyBaseLayout *)sublayout borderlineRect:(CGRect *)pRect - (void)myInvalidateIntrinsicContentSize { if (!self.translatesAutoresizingMaskIntoConstraints) { - if (self.widthSizeInner.wrapVal || self.heightSizeInner.wrapVal) { + MyViewTraitsImpl *viewTraits = (MyViewTraitsImpl*)self.myCurrentTraitsInner; + if (viewTraits.widthSizeInner.wrapVal || viewTraits.heightSizeInner.wrapVal) { [self invalidateIntrinsicContentSize]; } } } -- (void)myCalcSubviewsWrapContentSize:(MyLayoutContext *)context withCustomSetting:(void (^)(MyViewTraits *subviewTraits))customSetting { +- (void)myCalcSubviewsWrapContentSize:(MyLayoutContext *)context withCustomSetting:(void (^)(MyViewTraitsImpl *subviewTraits))customSetting { for (MyLayoutEngine *subviewEngine in context->subviewEngines) { - MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; UIView *subview = subviewTraits.view; if (customSetting != nil) { @@ -2806,55 +2506,7 @@ - (void)myCalcSubviewsWrapContentSize:(MyLayoutContext *)context withCustomSetti } #pragma mark -- Deprecated methods -// -// -//- (CGFloat)topPadding { -// return self.paddingTop; -//} -// -//- (void)setTopPadding:(CGFloat)topPadding { -// self.paddingTop = topPadding; -//} -// -//- (CGFloat)leadingPadding { -// return self.paddingLeading; -//} -// -//- (void)setLeadingPadding:(CGFloat)leadingPadding { -// self.paddingLeading = leadingPadding; -//} -// -//- (CGFloat)bottomPadding { -// return self.paddingBottom; -//} -// -//- (void)setBottomPadding:(CGFloat)bottomPadding { -// self.paddingBottom = bottomPadding; -//} -// -//- (CGFloat)trailingPadding { -// return self.paddingTrailing; -//} -// -//- (void)setTrailingPadding:(CGFloat)trailingPadding { -// self.paddingTrailing = trailingPadding; -//} -// -//- (CGFloat)leftPadding { -// return self.paddingLeft; -//} -// -//- (void)setLeftPadding:(CGFloat)leftPadding { -// self.paddingLeft = leftPadding; -//} -// -//- (CGFloat)rightPadding { -// return self.paddingRight; -//} -// -//- (void)setRightPadding:(CGFloat)rightPadding { -// self.paddingRight = rightPadding; -//} + @end @@ -2895,97 +2547,138 @@ @implementation MyLayoutDragger //开始拖动 - (void)dragView:(UIView *)view withEvent:(UIEvent *)event { - if (self.layout == nil) { + if (self.layoutView == nil) { + self.draggingView = nil; + self.hoveringView = nil; return; } - self.oldIndex = [self.layout.subviews indexOfObject:view]; + self.oldIndex = [self.layoutView.subviews indexOfObject:view]; self.currentIndex = self.oldIndex; - self.hasDragging = NO; - CGPoint point = [[event touchesForView:view].anyObject locationInView:self.layout]; + CGPoint point = [[event touchesForView:view].anyObject locationInView:self.layoutView]; CGPoint center = view.center; self.centerPointOffset = CGPointMake(center.x - point.x, center.y - point.y); + self.draggingView = nil; + self.hoveringView = nil; + if (self.delegate != nil && [self.delegate respondsToSelector:@selector(myLayoutDragger:startDragView:)]) { + [self.delegate myLayoutDragger:self startDragView:view]; + } } //拖动中,在拖动时要排除移动的子视图序列,动画的时长。返回是否拖动成功。 - (void)dragginView:(UIView *)view withEvent:(UIEvent *)event { - if (self.layout == nil) { + if (self.layoutView == nil) { + self.draggingView = nil; + self.hoveringView = nil; return; } - self.hasDragging = YES; + + self.draggingView = view; //取出拖动时当前的位置点。 - CGPoint point = [[event touchesForView:view].anyObject locationInView:self.layout]; + CGPoint point = [[event touchesForView:view].anyObject locationInView:self.layoutView]; - UIView *hoverView = nil; //保存拖动时手指所在的视图。 + BOOL hoverViewIsExclusiveView = NO; + UIView *hoveringView = nil; //保存拖动时手指所在的视图。 //判断当前手指在具体视图的位置。这里要排除exclusiveViews中指定的所有视图的位置,因为这些视图固定不会调整。 - for (UIView *subview in self.layout.subviews) { - if (subview != view && view.useFrame && ![self.exclusiveViews containsObject:subview]) { - if (CGRectContainsPoint(subview.frame, point)) { - hoverView = subview; + for (UIView *subview in self.layoutView.subviews) { + if (subview != view && view.useFrame) { + + CGRect subviewFrame = subview.frame; + CGRect validSubviewFrame = CGRectInset(subviewFrame, CGRectGetWidth(subviewFrame)*self.overlapRatio, CGRectGetHeight(subviewFrame)*self.overlapRatio); + if (CGRectContainsPoint(validSubviewFrame, point)) { + hoverViewIsExclusiveView = [self.exclusiveViews containsObject:subview]; + hoveringView = subview; break; } } } //如果拖动的view和手指下当前其他的兄弟视图有重合时则意味着需要将当前视图view插入到手指下其他视图所在的位置,并且调整其他视图的位置。 - if (hoverView != nil) { - if (self.animateDuration > 0) { - [self.layout layoutAnimationWithDuration:self.animateDuration]; - } - //得到要移动的视图的位置索引。 - self.currentIndex = [self.layout.subviews indexOfObjectIdenticalTo:hoverView]; - if (self.oldIndex != self.currentIndex) { - self.oldIndex = self.currentIndex; - } else { - if (!self.canHover) { - if (hoverView.center.x > view.center.x) { - self.currentIndex = self.oldIndex + 1; + if (hoveringView != nil) { + if (!hoverViewIsExclusiveView) { + if (self.animateDuration > 0) { + [self.layoutView layoutAnimationWithDuration:self.animateDuration]; + } + //得到要移动的视图的位置索引。 + self.currentIndex = [self.layoutView.subviews indexOfObjectIdenticalTo:hoveringView]; + if (self.oldIndex != self.currentIndex) { + self.oldIndex = self.currentIndex; + } else { + if (!self.canHover) { + if (hoveringView.center.x > view.center.x) { + self.currentIndex = self.oldIndex + 1; + } } } + + for (NSInteger i = self.layoutView.subviews.count - 1; i > self.currentIndex; i--) { + [self.layoutView exchangeSubviewAtIndex:i withSubviewAtIndex:i - 1]; + } + + //因为view在bringSubviewToFront后变为了最后一个子视图,因此要调整正确的位置。 + //经过上面的view的位置调整完成后,需要重新激发布局视图的布局,因此这里要设置autoresizesSubviews为YES。 + self.layoutView.autoresizesSubviews = YES; + view.useFrame = NO; + view.noLayout = YES; + //这里设置为YES表示布局时不会改变view的真实位置而只是在布局视图中占用一个位置和尺寸,正是因为只是占用位置,因此会调整其他视图的位置。 + [self.layoutView layoutIfNeeded]; } - - for (NSInteger i = self.layout.subviews.count - 1; i > self.currentIndex; i--) { - [self.layout exchangeSubviewAtIndex:i withSubviewAtIndex:i - 1]; - } - - //因为view在bringSubviewToFront后变为了最后一个子视图,因此要调整正确的位置。 - //经过上面的view的位置调整完成后,需要重新激发布局视图的布局,因此这里要设置autoresizesSubviews为YES。 - self.layout.autoresizesSubviews = YES; - view.useFrame = NO; - view.noLayout = YES; - //这里设置为YES表示布局时不会改变view的真实位置而只是在布局视图中占用一个位置和尺寸,正是因为只是占用位置,因此会调整其他视图的位置。 - [self.layout layoutIfNeeded]; } //在进行view的位置调整时,要把view移动到最顶端,也就子视图数组的的最后。同时布局视图不能激发子视图布局,因此要把autoresizesSubviews设置为NO,同时因为要自定义view的位置,因此要把useFrame设置为YES,并且恢复noLayout为NO。 - [self.layout bringSubviewToFront:view]; //把拖动的子视图放在最后,这样这个子视图在移动时就会在所有兄弟视图的上面。 - self.layout.autoresizesSubviews = NO; //在拖动时不要让布局视图激发布局 + [self.layoutView bringSubviewToFront:view]; //把拖动的子视图放在最后,这样这个子视图在移动时就会在所有兄弟视图的上面。 + self.layoutView.autoresizesSubviews = NO; //在拖动时不要让布局视图激发布局 view.useFrame = YES; //因为拖动时,拖动的控件需要自己确定位置,不能被布局约束,因此必须要将useFrame设置为YES下面的center设置才会有效。 view.center = CGPointMake(point.x + self.centerPointOffset.x, point.y + self.centerPointOffset.y); //因为useFrame设置为了YES所有这里可以直接调整center,从而实现了位置的自定义设置。 view.noLayout = NO; //恢复noLayout为NO。 + + if (self.hoveringView != nil && hoveringView == nil) { + if (self.delegate != nil && [self.delegate respondsToSelector:@selector(myLayoutDragger:draggingView:leaveFromHoveringView:)]) { + [self.delegate myLayoutDragger:self draggingView:self.draggingView leaveFromHoveringView:self.hoveringView]; + } + } + + UIView *oldHoveringView = self.hoveringView; + self.hoveringView = hoveringView; + + if (hoveringView != nil && oldHoveringView != hoveringView) { + if (self.delegate != nil && [self.delegate respondsToSelector:@selector(myLayoutDragger:draggingView:enterInHoveringView:)]) { + [self.delegate myLayoutDragger:self draggingView:self.draggingView enterInHoveringView:hoveringView]; + } + } } //结束拖动 - (void)dropView:(UIView *)view withEvent:(UIEvent *)event { - if (self.layout == nil) { + if (self.layoutView == nil) { + self.draggingView = nil; + self.hoveringView = nil; return; } - if (!self.hasDragging) { + if (self.draggingView != view) { return; } - self.hasDragging = NO; + self.draggingView = nil; //当抬起时,需要让拖动的子视图调整到正确的顺序,并重新参与布局,因此这里要把拖动的子视图的useFrame设置为NO,同时把布局视图的autoresizesSubviews还原为YES。 - for (NSInteger i = self.layout.subviews.count - 1; i > self.currentIndex; i--) { - [self.layout exchangeSubviewAtIndex:i withSubviewAtIndex:i - 1]; + for (NSInteger i = self.layoutView.subviews.count - 1; i > self.currentIndex; i--) { + [self.layoutView exchangeSubviewAtIndex:i withSubviewAtIndex:i - 1]; } view.useFrame = NO; //让拖动的子视图重新参与布局,将useFrame设置为NO - self.layout.autoresizesSubviews = YES; //让布局视图可以重新激发布局,这里还原为YES。 + self.layoutView.autoresizesSubviews = YES; //让布局视图可以重新激发布局,这里还原为YES。 + if (self.delegate != nil && [self.delegate respondsToSelector:@selector(myLayoutDragger:endDropView:)]) { + [self.delegate myLayoutDragger:self endDropView:view]; + } + + if (self.hoveringView != nil) { + if (self.delegate != nil && [self.delegate respondsToSelector:@selector(myLayoutDragger:draggingView:leaveFromHoveringView:)]) { + [self.delegate myLayoutDragger:self draggingView:view leaveFromHoveringView:self.hoveringView]; + } + } + + self.hoveringView = nil; } -- (BOOL)isHovering { - return self.hasDragging && self.canHover && self.oldIndex == self.currentIndex; -} @end diff --git a/MyLayout/Lib/MyFlexLayout.h b/MyLayout/Lib/MyFlexLayout.h index eb28ac1..2a24796 100644 --- a/MyLayout/Lib/MyFlexLayout.h +++ b/MyLayout/Lib/MyFlexLayout.h @@ -38,10 +38,12 @@ typedef enum : int { MyFlexGravity_Flex_End, /**居中位置对齐或停靠*/ MyFlexGravity_Center, - /**拉伸行或者列之间的间距*/ + /**拉伸行或者列之间的间距,头尾无间距*/ MyFlexGravity_Space_Between, /**拉伸行或者列之间的间距,头尾间距是其他间距的一半*/ MyFlexGravity_Space_Around, + /**拉伸行或者列之间的间距,头尾有间距*/ + MyFlexGravity_Space_Evenly, /**基线对齐*/ MyFlexGravity_Baseline, /**拉伸条目尺寸*/ @@ -78,8 +80,8 @@ extern const int MyFlex_Auto; @property (nonatomic, assign) MyFlexGravity align_content; @property (nonatomic, assign) NSInteger item_size; @property (nonatomic, assign) UIEdgeInsets padding; -@property (nonatomic, assign) CGFloat vert_space; -@property (nonatomic, assign) CGFloat horz_space; +@property (nonatomic, assign) CGFloat vert_spacing; +@property (nonatomic, assign) CGFloat horz_spacing; @end @@ -204,7 +206,7 @@ extern const int MyFlex_Auto; */ - (id (^)(int))flex_flow; /** - 设置或检索弹性盒子元素在主轴(横轴)方向上的对齐方式。可选值为:MyFlexGravity_Flex_Start | MyFlexGravity_Flex_End | MyFlexGravity_Center | MyFlexGravity_Space_Between | MyFlexGravity_Space_Around 中的一个,默认值为MyFlexGravity_Flex_Start + 设置或检索弹性盒子元素在主轴(横轴)方向上的对齐方式。可选值为:MyFlexGravity_Flex_Start | MyFlexGravity_Flex_End | MyFlexGravity_Center | MyFlexGravity_Space_Between | MyFlexGravity_Space_Around | MyFlexGravity_Space_Evenly 中的一个,默认值为MyFlexGravity_Flex_Start */ - (id (^)(MyFlexGravity))justify_content; /** @@ -212,7 +214,7 @@ extern const int MyFlex_Auto; */ - (id (^)(MyFlexGravity))align_items; /** - 设置或检索弹性盒堆叠伸缩行的对齐方式。可选值为:MyFlexGravity_Flex_Start | MyFlexGravity_Flex_End | MyFlexGravity_Center | MyFlexGravity_Between | MyFlexGravity_Around | MyFlexGravity_Stretch中的一个,默认值为MyFlexGravity_Stretch + 设置或检索弹性盒堆叠伸缩行的对齐方式。可选值为:MyFlexGravity_Flex_Start | MyFlexGravity_Flex_End | MyFlexGravity_Center | MyFlexGravity_Space_Between | MyFlexGravity_Space_Around | MyFlexGravity_Space_Evenly| MyFlexGravity_Stretch中的一个,默认值为MyFlexGravity_Stretch */ - (id (^)(MyFlexGravity))align_content; @@ -236,11 +238,11 @@ extern const int MyFlex_Auto; /** 设置弹性盒内所有条目视图之间的垂直间距 */ -- (id (^)(CGFloat))vert_space; +- (id (^)(CGFloat))vert_spacing; /** 设置弹性盒内所有条目视图之间的水平间距 */ -- (id (^)(CGFloat))horz_space; +- (id (^)(CGFloat))horz_spacing; @end diff --git a/MyLayout/Lib/MyFlexLayout.m b/MyLayout/Lib/MyFlexLayout.m index 32cb933..2403531 100644 --- a/MyLayout/Lib/MyFlexLayout.m +++ b/MyLayout/Lib/MyFlexLayout.m @@ -74,14 +74,15 @@ - (void)setAlign_self:(MyFlexGravity)align_self { } - (CGFloat)width { - if (self.view.widthSizeInner.isWrap) { + MyViewTraitsImpl *viewTraits = self.view.myDefaultTraitsInner; + if (viewTraits.widthSizeInner.isWrap) { return MyLayoutSize.wrap; - } else if (self.view.widthSizeInner.isFill) { - return self.view.widthSizeInner.multiVal == 1 ? MyLayoutSize.fill : self.view.widthSizeInner.multiVal; - } else if (self.view.widthSizeInner != nil && self.view.widthSizeInner.valType == MyLayoutValType_Nil) { + } else if (viewTraits.widthSizeInner.isFill) { + return viewTraits.widthSizeInner.multiVal == 1 ? MyLayoutSize.fill : viewTraits.widthSizeInner.multiVal; + } else if (viewTraits.widthSizeInner != nil && viewTraits.widthSizeInner.valType == MyLayoutValType_Nil) { return MyLayoutSize.empty; - } else if (self.view.widthSizeInner.numberVal != nil) { - return self.view.widthSizeInner.numberVal.doubleValue; + } else if (viewTraits.widthSizeInner.numberVal != nil) { + return viewTraits.widthSizeInner.numberVal.doubleValue; } else { return MyLayoutSize.wrap; } @@ -96,14 +97,15 @@ - (void)setWidth:(CGFloat)width { } - (CGFloat)height { - if (self.view.heightSizeInner.isWrap) { + MyViewTraitsImpl *viewTraits = self.view.myDefaultTraitsInner; + if (viewTraits.heightSizeInner.isWrap) { return MyLayoutSize.wrap; - } else if (self.view.heightSizeInner.isFill) { - return self.view.heightSizeInner.multiVal == 1 ? MyLayoutSize.fill : self.view.heightSizeInner.multiVal; - } else if (self.view.heightSizeInner != nil && self.view.heightSizeInner.valType == MyLayoutValType_Nil) { + } else if (viewTraits.heightSizeInner.isFill) { + return viewTraits.heightSizeInner.multiVal == 1 ? MyLayoutSize.fill : viewTraits.heightSizeInner.multiVal; + } else if (viewTraits.heightSizeInner != nil && viewTraits.heightSizeInner.valType == MyLayoutValType_Nil) { return MyLayoutSize.empty; - } else if (self.view.heightSizeInner.numberVal != nil) { - return self.view.heightSizeInner.numberVal.doubleValue; + } else if (viewTraits.heightSizeInner.numberVal != nil) { + return viewTraits.heightSizeInner.numberVal.doubleValue; } else { return MyLayoutSize.wrap; } @@ -129,8 +131,8 @@ @interface MyFlexBoxAttrs : MyFlexItemAttrs @property (nonatomic, assign) MyFlexGravity align_content; @property (nonatomic, assign) NSInteger item_size; @property (nonatomic, assign) UIEdgeInsets padding; -@property (nonatomic, assign) CGFloat vert_space; -@property (nonatomic, assign) CGFloat horz_space; +@property (nonatomic, assign) CGFloat vert_spacing; +@property (nonatomic, assign) CGFloat horz_spacing; @end @implementation MyFlexBoxAttrs @@ -198,20 +200,20 @@ - (void)setPadding:(UIEdgeInsets)padding { ((MyFlexLayout *)self.view).padding = padding; } -- (CGFloat)vert_space { - return ((MyFlexLayout *)self.view).subviewVSpace; +- (CGFloat)vert_spacing { + return ((MyFlexLayout *)self.view).subviewVSpacing; } -- (void)setVert_space:(CGFloat)vert_space { - ((MyFlexLayout *)self.view).subviewVSpace = vert_space; +- (void)setVert_spacing:(CGFloat)vert_spacing { + ((MyFlexLayout *)self.view).subviewVSpacing = vert_spacing; } -- (CGFloat)horz_space { - return ((MyFlexLayout *)self.view).subviewHSpace; +- (CGFloat)horz_spacing { + return ((MyFlexLayout *)self.view).subviewHSpacing; } -- (void)setHorz_space:(CGFloat)horz_space { - ((MyFlexLayout *)self.view).subviewHSpace = horz_space; +- (void)setHorz_spacing:(CGFloat)horz_spacing { + ((MyFlexLayout *)self.view).subviewHSpacing = horz_spacing; } @end @@ -488,16 +490,16 @@ @implementation MyFlexBox }; } -- (id (^)(CGFloat))vert_space { +- (id (^)(CGFloat))vert_spacing { return ^id(CGFloat val) { - ((MyFlexLayout *)self.view).subviewVSpace = val; + ((MyFlexLayout *)self.view).subviewVSpacing = val; return self; }; } -- (id (^)(CGFloat))horz_space { +- (id (^)(CGFloat))horz_spacing { return ^id(CGFloat val) { - ((MyFlexLayout *)self.view).subviewHSpace = val; + ((MyFlexLayout *)self.view).subviewHSpacing = val; return self; }; } @@ -530,10 +532,11 @@ @implementation MyFlexLayout - (instancetype)init { self = [super init]; if (self != nil) { - self.orientation = MyOrientation_Vert; //默认row - self.arrangedCount = NSIntegerMax; //默认单行 - self.isFlex = YES; //满足flexbox的需求。 - self.lastlineGravityPolicy = MyGravityPolicy_Always; + MyFlowLayoutTraitsImpl *layoutTraits = self.myDefaultTraits; + layoutTraits.orientation = MyOrientation_Vert; //默认row + layoutTraits.arrangedCount = NSIntegerMax; //默认单行 + layoutTraits.isFlex = YES; //满足flexbox的需求。 + layoutTraits.lastlineGravityPolicy = MyGravityPolicy_Always; MyFlexBoxAttrs *attrs = [MyFlexBoxAttrs new]; attrs.view = self; @@ -544,7 +547,7 @@ - (instancetype)init { - (CGSize)calcLayoutSize:(CGSize)size subviewEngines:(NSMutableArray *)subviewEngines context:(MyLayoutContext *)context { //将flexbox中的属性映射为MyFlowLayout中的属性。 - MyFlowLayoutTraits *layoutTraits = (MyFlowLayoutTraits*)context->layoutViewEngine.currentSizeClass; + MyFlowLayoutTraitsImpl *layoutTraits = context->layoutViewEngine.currentTraits; if (context->subviewEngines == nil) { context->subviewEngines = [layoutTraits filterEngines:subviewEngines]; } @@ -590,11 +593,11 @@ - (CGSize)calcLayoutSize:(CGSize)size subviewEngines:(NSMutableArraysubviewEngines sortWithOptions:NSSortStable usingComparator:^NSComparisonResult(MyLayoutEngine *_Nonnull obj1, MyLayoutEngine *_Nonnull obj2) { - return obj1.currentSizeClass.view.myFlex.attrs.order - obj2.currentSizeClass.view.myFlex.attrs.order; + return obj1.currentTraits.view.myFlex.attrs.order - obj2.currentTraits.view.myFlex.attrs.order; }]; for (MyLayoutEngine *subviewEngine in context->subviewEngines) { - MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; id flexItem = subviewTraits.view.myFlex; //flex_grow,如果子视图有设置grow则父视图的换行不起作用。 @@ -690,6 +693,13 @@ - (CGSize)calcLayoutSize:(CGSize)size subviewEngines:(NSMutableArray| - |<-----D---- -F-> --E-->| - @endcode - - 2. 如果父视图是水平浮动布局则默认正向浮动是向上浮动的,而反向浮动则是向下浮动。 - - @code - 下面是水平浮动布局中的正向浮动和反向浮动的效果图(正向浮动:A,B,D; 反向浮动:C,E,F): - - ----------- - ↑ ↑ - | | - A | - | D - | - ↑ | - B - | F - ↓ - | | - C E - ↓ ↓ - ------------ - @endcode - - @note 这个属性的定义是完全参考CSS样式表中float属性的定义 - */ -@property (nonatomic, assign, getter=isReverseFloat) BOOL reverseFloat; - -/** - 清除浮动,默认是NO。这个属性的意义也跟父浮动布局视图的方向相关。如果设置为了清除浮动属性则表示本子视图不会在浮动方向上紧跟在前一个浮动子视图的后面,而是会另外新起一行或者一列来重新排列。 - - @code - 垂直浮动布局下的浮动和清除浮动 - - |<--A-- <---B--- <-C--| - |<----D--- | - |<--E-- <---F-- | - |<-----G---- | - | ---I---> --H-->| - | -J-> | - - A(正向浮动);B(正向浮动);C(正向浮动);D(正向浮动);E(正向浮动);F(正向浮动);G(正向浮动,清除浮动);H(反向浮动);I(反向浮动);J(反向浮动,清除浮动) - @endcode - - @note 这个属性的定义是完全参考CSS样式表中clear属性的定义。 - - */ -@property (nonatomic, assign) BOOL clearFloat; - -@end - /** 浮动布局是一种里面的子视图按照约定的方向浮动停靠,当浮动布局的剩余空间不足容纳要加入的子视图的尺寸时会自动寻找最佳的位置进行浮动停靠的布局视图。浮动布局的理念源于HTML/CSS中的浮动定位技术,因此浮动布局可以专门用来实现那些不规则布局或者图文环绕的布局。 @note 根据浮动的方向不同,浮动布局可以分为左右浮动布局和上下浮动布局。我们称左右浮动的浮动布局为垂直浮动布局,因为左右浮动时最终整个方向是从上到下的;称上下浮动的浮动布局为水平浮动布局,因为上下浮动时最终整个方向是从左到右的。 */ -@interface MyFloatLayout : MyBaseLayout +@interface MyFloatLayout : MyBaseLayout> /** 初始化一个浮动布局并指定布局的方向。当方向设置为MyOrientation_Vert时表示为左右浮动布局视图,而设置为MyOrientation_Horz则表示为上下浮动布局视图。 @@ -100,29 +38,38 @@ /** 在一些应用场景中我们希望子视图的宽度是固定的但间距是浮动的,这样就尽可能在一排中容纳更多的子视图。比如设置每个子视图的宽度固定为80,那么在小屏幕下每排只能放3个,而大屏幕则每排能放4个或者5个子视图。 因此您可以通过如下方法来设置子视图的固定尺寸和最小最大浮动间距。这个方法会根据您当前布局的方向不同而具有不同的意义: - 1.如果您的布局方向是垂直的则设置的是每行内子视图的水平浮动间距,其中的subviewSize指定的是子视图的固定宽度;minSpace指定的是最小的水平间距;maxSpace指定的是最大的水平间距,如果指定的subviewSize计算出的间距大于最大间距maxSpace则会缩小subviewSize的宽度值。 + 1.如果您的布局方向是垂直的则设置的是每行内子视图的水平浮动间距,其中的subviewsSize指定的是子视图的固定宽度;minSpacing指定的是最小的水平间距;maxSpacing指定的是最大的水平间距,如果指定的subviewsSize计算出的间距大于最大间距maxSpacing则会缩小subviewsSize的宽度值。 - 2.如果您的布局方向是水平的则设置的是每列内子视图的垂直浮动间距,其中的subviewSize指定的是子视图的固定高度;minSpace指定的是最小的垂直间距;maxSpace指定的是最大的垂直间距,如果指定的subviewSize计算出的间距大于最大间距maxSpace则会调整subviewSize的高度值。 + 2.如果您的布局方向是水平的则设置的是每列内子视图的垂直浮动间距,其中的subviewsSize指定的是子视图的固定高度;minSpacing指定的是最小的垂直间距;maxSpacing指定的是最大的垂直间距,如果指定的subviewsSize计算出的间距大于最大间距maxSpacing则会调整subviewsSize的高度值。 - @note 如果您不想使用浮动间距则请将subviewSize设置为0就可以了。 + @note 如果您不想使用浮动间距则请将subviewsSize设置为0就可以了。 - @param subviewSize 指定子视图的尺寸。 - @param minSpace 指定子视图之间的最小间距 - @param maxSpace 指定子视图之间的最大间距 + @param subviewsSize 指定子视图的尺寸。 + @param minSpacing 指定子视图之间的最小间距 + @param maxSpacing 指定子视图之间的最大间距 */ -- (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace; -- (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace inSizeClass:(MySizeClass)sizeClass; +- (void)setSubviewsSize:(CGFloat)subviewsSize minSpacing:(CGFloat)minSpacing maxSpacing:(CGFloat)maxSpacing; +- (void)setSubviewsSize:(CGFloat)subviewsSize minSpacing:(CGFloat)minSpacing maxSpacing:(CGFloat)maxSpacing inSizeClass:(MySizeClass)sizeClass; /** 上面函数的加强版本。 - @param subviewSize 指定子视图的尺寸 - @param minSpace 指定子视图之间的最小间距 - @param maxSpace 指定子视图之间的最大间距 + @param subviewsSize 指定子视图的尺寸 + @param minSpacing 指定子视图之间的最小间距 + @param maxSpacing 指定子视图之间的最大间距 @param centered 指定是否所有子视图居中 */ -- (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace centered:(BOOL)centered; -- (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace centered:(BOOL)centered inSizeClass:(MySizeClass)sizeClass; +- (void)setSubviewsSize:(CGFloat)subviewsSize minSpacing:(CGFloat)minSpacing maxSpacing:(CGFloat)maxSpacing centered:(BOOL)centered; +- (void)setSubviewsSize:(CGFloat)subviewsSize minSpacing:(CGFloat)minSpacing maxSpacing:(CGFloat)maxSpacing centered:(BOOL)centered inSizeClass:(MySizeClass)sizeClass; + +@end + +@interface MyFloatLayout (MyDeprecated) + +- (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace MYDEPRECATED("use setSubviewsSize:minSpacing:maxSpacing: instead"); +- (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace inSizeClass:(MySizeClass)sizeClass MYDEPRECATED("use setSubviewsSize:minSpacing:maxSpacing:inSizeClass: instead"); +- (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace centered:(BOOL)centered MYDEPRECATED("use setSubviewsSize:minSpacing:maxSpacing:centered: instead"); +- (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace centered:(BOOL)centered inSizeClass:(MySizeClass)sizeClass MYDEPRECATED("use setSubviewsSize:minSpacing:maxSpacing:centered:inSizeClass: instead"); @end diff --git a/MyLayout/Lib/MyFloatLayout.m b/MyLayout/Lib/MyFloatLayout.m index 7a9ebda..ac8c1c7 100644 --- a/MyLayout/Lib/MyFloatLayout.m +++ b/MyLayout/Lib/MyFloatLayout.m @@ -9,38 +9,6 @@ #import "MyFloatLayout.h" #import "MyLayoutInner.h" -@implementation UIView (MyFloatLayoutExt) - -- (void)setReverseFloat:(BOOL)reverseFloat { - MyViewTraits *viewTraits = (MyViewTraits*)self.myDefaultSizeClass; - if (viewTraits.isReverseFloat != reverseFloat) { - viewTraits.reverseFloat = reverseFloat; - if (self.superview != nil) { - [self.superview setNeedsLayout]; - } - } -} - -- (BOOL)isReverseFloat { - return self.myDefaultSizeClassInner.isReverseFloat; -} - -- (void)setClearFloat:(BOOL)clearFloat { - MyViewTraits *viewTraits = (MyViewTraits*)self.myDefaultSizeClass; - if (viewTraits.clearFloat != clearFloat) { - viewTraits.clearFloat = clearFloat; - if (self.superview != nil) { - [self.superview setNeedsLayout]; - } - } -} - -- (BOOL)clearFloat { - return self.myDefaultSizeClassInner.clearFloat; -} - -@end - @implementation MyFloatLayout #pragma mark-- Public Methods @@ -48,7 +16,7 @@ @implementation MyFloatLayout - (instancetype)initWithFrame:(CGRect)frame orientation:(MyOrientation)orientation { self = [super initWithFrame:frame]; if (self != nil) { - self.myDefaultSizeClass.orientation = orientation; + ((MyFloatLayoutTraitsImpl*)self.myDefaultTraits).orientation = orientation; } return self; } @@ -58,73 +26,67 @@ - (instancetype)initWithOrientation:(MyOrientation)orientation { } + (instancetype)floatLayoutWithOrientation:(MyOrientation)orientation { - MyFloatLayout *layout = [[[self class] alloc] initWithOrientation:orientation]; - return layout; + return [[[self class] alloc] initWithOrientation:orientation]; } -- (void)setOrientation:(MyOrientation)orientation { - MyFloatLayoutTraits *layoutTraits = (MyFloatLayoutTraits*)self.myDefaultSizeClass; - if (layoutTraits.orientation != orientation) { - layoutTraits.orientation = orientation; - [self setNeedsLayout]; - } +_MYLAYOUT_PROPERTY_LAYOUTVIEW_OVERRIDE1(MyFloatLayoutTraitsImpl, MyOrientation, Orientation, orientation) + +- (void)setSubviewsSize:(CGFloat)subviewsSize minSpacing:(CGFloat)minSpacing maxSpacing:(CGFloat)maxSpacing { + [self setSubviewsSize:subviewsSize minSpacing:minSpacing maxSpacing:maxSpacing inSizeClass:MySizeClass_hAny | MySizeClass_wAny]; } -- (MyOrientation)orientation { - return self.myDefaultSizeClassInner.orientation; +- (void)setSubviewsSize:(CGFloat)subviewsSize minSpacing:(CGFloat)minSpacing maxSpacing:(CGFloat)maxSpacing inSizeClass:(MySizeClass)sizeClass { + [self setSubviewsSize:subviewsSize minSpacing:minSpacing maxSpacing:maxSpacing centered:NO inSizeClass:sizeClass]; } +- (void)setSubviewsSize:(CGFloat)subviewsSize minSpacing:(CGFloat)minSpacing maxSpacing:(CGFloat)maxSpacing centered:(BOOL)centered { + [self setSubviewsSize:subviewsSize minSpacing:minSpacing maxSpacing:maxSpacing centered:centered inSizeClass:MySizeClass_hAny | MySizeClass_wAny]; +} + +- (void)setSubviewsSize:(CGFloat)subviewsSize minSpacing:(CGFloat)minSpacing maxSpacing:(CGFloat)maxSpacing centered:(BOOL)centered inSizeClass:(MySizeClass)sizeClass { + MyFloatLayoutTraitsImpl *layoutTraits = (MyFloatLayoutTraitsImpl *)[self fetchLayoutTraitsInSizeClass:sizeClass]; + if (subviewsSize == 0) { + layoutTraits.flexSpacing = nil; + } else { + if (layoutTraits.flexSpacing == nil) { + layoutTraits.flexSpacing = [MySequentLayoutFlexSpacing new]; + } + layoutTraits.flexSpacing.subviewsSize = subviewsSize; + layoutTraits.flexSpacing.minSpacing = minSpacing; + layoutTraits.flexSpacing.maxSpacing = maxSpacing; + layoutTraits.flexSpacing.centered = centered; + } + [self setNeedsLayout]; +} + +//// - (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace { - [self setSubviewsSize:subviewSize minSpace:minSpace maxSpace:maxSpace inSizeClass:MySizeClass_hAny | MySizeClass_wAny]; + [self setSubviewsSize:subviewSize minSpacing:minSpace maxSpacing:maxSpace inSizeClass:MySizeClass_hAny | MySizeClass_wAny]; } - (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace inSizeClass:(MySizeClass)sizeClass { - [self setSubviewsSize:subviewSize minSpace:minSpace maxSpace:maxSpace centered:NO inSizeClass:sizeClass]; + [self setSubviewsSize:subviewSize minSpacing:minSpace maxSpacing:maxSpace centered:NO inSizeClass:sizeClass]; } - (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace centered:(BOOL)centered { - [self setSubviewsSize:subviewSize minSpace:minSpace maxSpace:maxSpace centered:centered inSizeClass:MySizeClass_hAny | MySizeClass_wAny]; + [self setSubviewsSize:subviewSize minSpacing:minSpace maxSpacing:maxSpace centered:centered inSizeClass:MySizeClass_hAny | MySizeClass_wAny]; } - (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace centered:(BOOL)centered inSizeClass:(MySizeClass)sizeClass { - MySequentLayoutTraits *layoutTraits = (MySequentLayoutTraits *)[self fetchLayoutSizeClass:sizeClass]; - if (subviewSize == 0) { - layoutTraits.flexSpace = nil; - } else { - if (layoutTraits.flexSpace == nil) { - layoutTraits.flexSpace = [MySequentLayoutFlexSpacing new]; - } - layoutTraits.flexSpace.subviewSize = subviewSize; - layoutTraits.flexSpace.minSpace = minSpace; - layoutTraits.flexSpace.maxSpace = maxSpace; - layoutTraits.flexSpace.centered = centered; - } - [self setNeedsLayout]; + [self setSubviewsSize:subviewSize minSpacing:minSpace maxSpacing:maxSpace centered:centered inSizeClass:sizeClass]; } + #pragma mark-- Override Methods - (CGSize)calcLayoutSize:(CGSize)size subviewEngines:(NSMutableArray *)subviewEngines context:(MyLayoutContext *)context { [super calcLayoutSize:size subviewEngines:subviewEngines context:context]; - MyFloatLayoutTraits *layoutTraits = (MyFloatLayoutTraits *)context->layoutViewEngine.currentSizeClass; - context->subviewEngines = [layoutTraits filterEngines:subviewEngines]; - context->paddingTop = layoutTraits.myLayoutPaddingTop; - context->paddingBottom = layoutTraits.myLayoutPaddingBottom; - context->paddingLeading = layoutTraits.myLayoutPaddingLeading; - context->paddingTrailing = layoutTraits.myLayoutPaddingTrailing; - context->vertGravity = MYVERTGRAVITY(layoutTraits.gravity); - context->horzGravity = [MyViewTraits convertLeadingTrailingGravityFromLeftRightGravity:MYHORZGRAVITY(layoutTraits.gravity)]; - context->vertSpace = layoutTraits.subviewVSpace; - context->horzSpace = layoutTraits.subviewHSpace; - if (context->subviewEngines == nil) { - context->subviewEngines = [layoutTraits filterEngines:subviewEngines]; - } - + MyFloatLayoutTraitsImpl *layoutTraits = context->layoutViewEngine.currentTraits; MyOrientation orientation = layoutTraits.orientation; [self myCalcSubviewsWrapContentSize:context - withCustomSetting:^(MyViewTraits *subviewTraits) { + withCustomSetting:^(MyViewTraitsImpl *subviewTraits) { if (subviewTraits.widthSizeInner.wrapVal && orientation == MyOrientation_Vert && subviewTraits.weight != 0 && @@ -146,8 +108,8 @@ - (CGSize)calcLayoutSize:(CGSize)size subviewEngines:(NSMutableArraysubviewEngines) { - MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; if (specialMeasure != 0.0) { if (isWidth) { subviewEngine.width = specialMeasure; @@ -314,7 +276,7 @@ - (void)myCalcSubviewsSize:(CGFloat)specialMeasure isWidth:(BOOL)isWidth withCon } - (void)myDoVertOrientationLayoutWithContext:(MyLayoutContext *)context { - MyFloatLayoutTraits *layoutTraits = (MyFloatLayoutTraits*)context->layoutViewEngine.currentSizeClass; + MyFloatLayoutTraitsImpl *layoutTraits = context->layoutViewEngine.currentTraits; BOOL isBeyondFlag = NO; //子视图是否超出剩余空间需要换行。 @@ -324,7 +286,7 @@ - (void)myDoVertOrientationLayoutWithContext:(MyLayoutContext *)context { } //支持浮动水平间距。 - CGFloat subviewWidth = [layoutTraits.flexSpace calcMaxMinSubviewSizeForContent:context->selfSize.width paddingStart:&context->paddingLeading paddingEnd:&context->paddingTrailing space:&context->horzSpace]; + CGFloat subviewWidth = [layoutTraits.flexSpacing calcMaxMinSubviewsSizeForContent:context->selfSize.width paddingStart:&context->paddingLeading paddingEnd:&context->paddingTrailing spacing:&context->horzSpacing]; //计算所有子视图的宽度和高度。 [self myCalcSubviewsSize:subviewWidth isWidth:YES withContext:context]; @@ -355,7 +317,7 @@ - (void)myDoVertOrientationLayoutWithContext:(MyLayoutContext *)context { for (NSInteger idx = 0; idx < context->subviewEngines.count; idx++) { MyLayoutEngine *subviewEngine = context->subviewEngines[idx]; - MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; CGFloat topSpacing = subviewTraits.topPosInner.measure; CGFloat leadingSpacing = subviewTraits.leadingPosInner.measure; @@ -444,7 +406,7 @@ - (void)myDoVertOrientationLayoutWithContext:(MyLayoutContext *)context { } //这里有可能子视图本身的宽度会超过布局视图本身,但是我们的候选区域则不存储超过的宽度部分。 - CGRect cRect = CGRectMake(_myCGFloatMax((subviewEngine.leading - leadingSpacing - context->horzSpace), context->paddingLeading), subviewEngine.top - topSpacing, _myCGFloatMin((subviewEngine.width + leadingSpacing + trailingSpacing), (context->selfSize.width - context->paddingLeading - context->paddingTrailing)), subviewEngine.height + topSpacing + bottomSpacing + context->vertSpace); + CGRect cRect = CGRectMake(_myCGFloatMax((subviewEngine.leading - leadingSpacing - context->horzSpacing), context->paddingLeading), subviewEngine.top - topSpacing, _myCGFloatMin((subviewEngine.width + leadingSpacing + trailingSpacing), (context->selfSize.width - context->paddingLeading - context->paddingTrailing)), subviewEngine.height + topSpacing + bottomSpacing + context->vertSpacing); //把新的候选区域添加到数组中去。并删除高度小于新候选区域的其他区域 for (NSInteger i = trailingCandidateRects.count - 1; i >= 0; i--) { @@ -478,8 +440,8 @@ - (void)myDoVertOrientationLayoutWithContext:(MyLayoutContext *)context { [trailingCandidateRects addObject:[NSValue valueWithCGRect:cRect]]; trailingLastYOffset = subviewEngine.top - topSpacing; - if (_myCGFloatGreat(subviewEngine.top + subviewEngine.height + bottomSpacing + context->vertSpace, trailingMaxHeight)) { - trailingMaxHeight = subviewEngine.top + subviewEngine.height + bottomSpacing + context->vertSpace; + if (_myCGFloatGreat(subviewEngine.top + subviewEngine.height + bottomSpacing + context->vertSpacing, trailingMaxHeight)) { + trailingMaxHeight = subviewEngine.top + subviewEngine.height + bottomSpacing + context->vertSpacing; } } else { CGPoint nextPoint = {context->paddingLeading, trailingLastYOffset}; @@ -558,7 +520,7 @@ - (void)myDoVertOrientationLayoutWithContext:(MyLayoutContext *)context { } } - CGRect cRect = CGRectMake(subviewEngine.leading - leadingSpacing, subviewEngine.top - topSpacing, _myCGFloatMin((subviewEngine.width + leadingSpacing + trailingSpacing + context->horzSpace), (context->selfSize.width - context->paddingLeading - context->paddingTrailing)), subviewEngine.height + topSpacing + bottomSpacing + context->vertSpace); + CGRect cRect = CGRectMake(subviewEngine.leading - leadingSpacing, subviewEngine.top - topSpacing, _myCGFloatMin((subviewEngine.width + leadingSpacing + trailingSpacing + context->horzSpacing), (context->selfSize.width - context->paddingLeading - context->paddingTrailing)), subviewEngine.height + topSpacing + bottomSpacing + context->vertSpacing); //把新添加到候选中去。并删除高度小于的候选键。和高度 for (NSInteger i = leadingCandidateRects.count - 1; i >= 0; i--) { @@ -591,22 +553,22 @@ - (void)myDoVertOrientationLayoutWithContext:(MyLayoutContext *)context { [leadingCandidateRects addObject:[NSValue valueWithCGRect:cRect]]; leadingLastYOffset = subviewEngine.top - topSpacing; - if (_myCGFloatGreat(subviewEngine.top + subviewEngine.height + bottomSpacing + context->vertSpace, leadingMaxHeight)) { - leadingMaxHeight = subviewEngine.top + subviewEngine.height + bottomSpacing + context->vertSpace; + if (_myCGFloatGreat(subviewEngine.top + subviewEngine.height + bottomSpacing + context->vertSpacing, leadingMaxHeight)) { + leadingMaxHeight = subviewEngine.top + subviewEngine.height + bottomSpacing + context->vertSpacing; } } - if (_myCGFloatGreat(subviewEngine.top + subviewEngine.height + bottomSpacing + context->vertSpace, maxLayoutHeight)) { - maxLayoutHeight = subviewEngine.top + subviewEngine.height + bottomSpacing + context->vertSpace; + if (_myCGFloatGreat(subviewEngine.top + subviewEngine.height + bottomSpacing + context->vertSpacing, maxLayoutHeight)) { + maxLayoutHeight = subviewEngine.top + subviewEngine.height + bottomSpacing + context->vertSpacing; } - if (_myCGFloatGreat(subviewEngine.leading + subviewEngine.width + trailingSpacing + context->horzSpace, maxLayoutWidth)) { - maxLayoutWidth = subviewEngine.leading + subviewEngine.width + trailingSpacing + context->horzSpace; + if (_myCGFloatGreat(subviewEngine.leading + subviewEngine.width + trailingSpacing + context->horzSpacing, maxLayoutWidth)) { + maxLayoutWidth = subviewEngine.leading + subviewEngine.width + trailingSpacing + context->horzSpacing; } } if (context->subviewEngines.count > 0) { - maxLayoutHeight -= context->vertSpace; - maxLayoutWidth -= context->horzSpace; + maxLayoutHeight -= context->vertSpacing; + maxLayoutWidth -= context->horzSpacing; } maxLayoutWidth += context->paddingTrailing; @@ -655,7 +617,7 @@ - (void)myDoVertOrientationLayoutWithContext:(MyLayoutContext *)context { CGFloat lineMaxHeight = 0; for (NSInteger i = lineFirstIndex; i <= idx; i++) { MyLayoutEngine *subviewEngine = context->subviewEngines[i]; - MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; if (subviewEngine.height > lineMaxHeight) { lineMaxHeight = subviewEngine.height; } @@ -667,7 +629,7 @@ - (void)myDoVertOrientationLayoutWithContext:(MyLayoutContext *)context { CGFloat baselinePos = CGFLOAT_MAX; for (NSInteger i = lineFirstIndex; i <= idx; i++) { MyLayoutEngine *subviewEngine = context->subviewEngines[i]; - MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; MyGravity subviewVertAlignment = MYVERTGRAVITY(subviewTraits.alignment); UIFont *subviewFont = nil; if (subviewVertAlignment == MyGravity_Vert_Baseline) { @@ -711,7 +673,7 @@ - (void)myDoVertOrientationLayoutWithContext:(MyLayoutContext *)context { } - (void)myDoHorzOrientationLayoutWithContext:(MyLayoutContext *)context { - MyFloatLayoutTraits *layoutTraits = (MyFloatLayoutTraits*)context->layoutViewEngine.currentSizeClass; + MyFloatLayoutTraitsImpl *layoutTraits = context->layoutViewEngine.currentTraits; NSArray *subviewEngines = context->subviewEngines; //如果没有边界限制我们将高度设置为最大。。 BOOL isBeyondFlag = NO; //子视图是否超出剩余空间需要换行。 @@ -721,7 +683,7 @@ - (void)myDoHorzOrientationLayoutWithContext:(MyLayoutContext *)context { } //支持浮动垂直间距。 - CGFloat subviewHeight = [layoutTraits.flexSpace calcMaxMinSubviewSizeForContent:context->selfSize.height paddingStart:&context->paddingTop paddingEnd:&context->paddingBottom space:&context->vertSpace]; + CGFloat subviewHeight = [layoutTraits.flexSpacing calcMaxMinSubviewsSizeForContent:context->selfSize.height paddingStart:&context->paddingTop paddingEnd:&context->paddingBottom spacing:&context->vertSpacing]; //设置子视图的宽度和高度。 [self myCalcSubviewsSize:subviewHeight isWidth:NO withContext:context]; @@ -752,7 +714,7 @@ - (void)myDoHorzOrientationLayoutWithContext:(MyLayoutContext *)context { for (NSInteger idx = 0; idx < subviewEngines.count; idx++) { MyLayoutEngine *subviewEngine = subviewEngines[idx]; - MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; CGFloat topSpacing = subviewTraits.topPosInner.measure; CGFloat leadingSpacing = subviewTraits.leadingPosInner.measure; @@ -835,7 +797,7 @@ - (void)myDoHorzOrientationLayoutWithContext:(MyLayoutContext *)context { } //这里有可能子视图本身的高度会超过布局视图本身,但是我们的候选区域则不存储超过的高度部分。 - CGRect cRect = CGRectMake(subviewEngine.leading - leadingSpacing, _myCGFloatMax((subviewEngine.top - topSpacing - context->vertSpace), context->paddingTop), subviewEngine.width + leadingSpacing + trailingSpacing + context->horzSpace, _myCGFloatMin((subviewEngine.height + topSpacing + bottomSpacing), (context->selfSize.height - context->paddingTop - context->paddingBottom))); + CGRect cRect = CGRectMake(subviewEngine.leading - leadingSpacing, _myCGFloatMax((subviewEngine.top - topSpacing - context->vertSpacing), context->paddingTop), subviewEngine.width + leadingSpacing + trailingSpacing + context->horzSpacing, _myCGFloatMin((subviewEngine.height + topSpacing + bottomSpacing), (context->selfSize.height - context->paddingTop - context->paddingBottom))); //把新的候选区域添加到数组中去。并删除高度小于新候选区域的其他区域 for (NSInteger i = bottomCandidateRects.count - 1; i >= 0; i--) { @@ -870,8 +832,8 @@ - (void)myDoHorzOrientationLayoutWithContext:(MyLayoutContext *)context { [bottomCandidateRects addObject:[NSValue valueWithCGRect:cRect]]; bottomLastXOffset = subviewEngine.leading - leadingSpacing; - if (_myCGFloatGreat(subviewEngine.leading + subviewEngine.width + trailingSpacing + context->horzSpace, bottomMaxWidth)) { - bottomMaxWidth = subviewEngine.leading + subviewEngine.width + trailingSpacing + context->horzSpace; + if (_myCGFloatGreat(subviewEngine.leading + subviewEngine.width + trailingSpacing + context->horzSpacing, bottomMaxWidth)) { + bottomMaxWidth = subviewEngine.leading + subviewEngine.width + trailingSpacing + context->horzSpacing; } } else { CGPoint nextPoint = {bottomLastXOffset, context->paddingTop}; @@ -943,7 +905,7 @@ - (void)myDoHorzOrientationLayoutWithContext:(MyLayoutContext *)context { } } - CGRect cRect = CGRectMake(subviewEngine.leading - leadingSpacing, subviewEngine.top - topSpacing, subviewEngine.width + leadingSpacing + trailingSpacing + context->horzSpace, _myCGFloatMin((subviewEngine.height + topSpacing + bottomSpacing + context->vertSpace), (context->selfSize.height - context->paddingTop - context->paddingBottom))); + CGRect cRect = CGRectMake(subviewEngine.leading - leadingSpacing, subviewEngine.top - topSpacing, subviewEngine.width + leadingSpacing + trailingSpacing + context->horzSpacing, _myCGFloatMin((subviewEngine.height + topSpacing + bottomSpacing + context->vertSpacing), (context->selfSize.height - context->paddingTop - context->paddingBottom))); //把新添加到候选中去。并删除宽度小于的最新候选区域的候选区域 for (NSInteger i = topCandidateRects.count - 1; i >= 0; i--) { @@ -977,23 +939,23 @@ - (void)myDoHorzOrientationLayoutWithContext:(MyLayoutContext *)context { [topCandidateRects addObject:[NSValue valueWithCGRect:cRect]]; topLastXOffset = subviewEngine.leading - leadingSpacing; - if (_myCGFloatGreat(subviewEngine.leading + subviewEngine.width + trailingSpacing + context->horzSpace, topMaxWidth)) { - topMaxWidth = subviewEngine.leading + subviewEngine.width + trailingSpacing + context->horzSpace; + if (_myCGFloatGreat(subviewEngine.leading + subviewEngine.width + trailingSpacing + context->horzSpacing, topMaxWidth)) { + topMaxWidth = subviewEngine.leading + subviewEngine.width + trailingSpacing + context->horzSpacing; } } - if (_myCGFloatGreat(subviewEngine.leading + subviewEngine.width + trailingSpacing + context->horzSpace, maxLayoutWidth)) { - maxLayoutWidth = subviewEngine.leading + subviewEngine.width + trailingSpacing + context->horzSpace; + if (_myCGFloatGreat(subviewEngine.leading + subviewEngine.width + trailingSpacing + context->horzSpacing, maxLayoutWidth)) { + maxLayoutWidth = subviewEngine.leading + subviewEngine.width + trailingSpacing + context->horzSpacing; } - if (_myCGFloatGreat(subviewEngine.top + subviewEngine.height + bottomSpacing + context->vertSpace, maxLayoutHeight)) { - maxLayoutHeight = subviewEngine.top + subviewEngine.height + bottomSpacing + context->vertSpace; + if (_myCGFloatGreat(subviewEngine.top + subviewEngine.height + bottomSpacing + context->vertSpacing, maxLayoutHeight)) { + maxLayoutHeight = subviewEngine.top + subviewEngine.height + bottomSpacing + context->vertSpacing; } } if (subviewEngines.count > 0) { - maxLayoutWidth -= context->horzSpace; - maxLayoutHeight -= context->vertSpace; + maxLayoutWidth -= context->horzSpacing; + maxLayoutHeight -= context->vertSpacing; } maxLayoutHeight += context->paddingBottom; @@ -1041,7 +1003,7 @@ - (void)myDoHorzOrientationLayoutWithContext:(MyLayoutContext *)context { CGFloat lineMaxWidth = 0; for (NSInteger i = lineFirstIndex; i <= idx; i++) { MyLayoutEngine *subviewEngine = subviewEngines[i]; - MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; if (subviewEngine.width > lineMaxWidth) { lineMaxWidth = subviewEngine.width; } @@ -1052,8 +1014,8 @@ - (void)myDoHorzOrientationLayoutWithContext:(MyLayoutContext *)context { if (lineHasAlignment) { for (NSInteger i = lineFirstIndex; i <= idx; i++) { MyLayoutEngine *subviewEngine = subviewEngines[i]; - MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; - switch ([MyViewTraits convertLeadingTrailingGravityFromLeftRightGravity:MYHORZGRAVITY(subviewTraits.alignment)]) { + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; + switch ([MyViewTraitsImpl convertLeadingTrailingGravityFromLeftRightGravity:MYHORZGRAVITY(subviewTraits.alignment)]) { case MyGravity_Horz_Center: subviewEngine.leading += (lineMaxWidth - subviewEngine.width) / 2.0; break; diff --git a/MyLayout/Lib/MyFlowLayout.h b/MyLayout/Lib/MyFlowLayout.h index d7398bf..b7b1c24 100644 --- a/MyLayout/Lib/MyFlowLayout.h +++ b/MyLayout/Lib/MyFlowLayout.h @@ -9,21 +9,21 @@ #import "MyBaseLayout.h" /** - 流式布局是一种条目按照添加的顺序依次排列,当满足某种规则后会另起一排再重新排列,最终呈现为多行多列展示的布局形态。 - 这里的换排规则主要有数量限制和尺寸限制两种,整体排列方向又分为垂直和水平两个方向,因此流式布局一共有垂直数量限制、垂直尺寸限制、水平数量限制、水平尺寸限制四种布局。流式布局主要应用于那些有规律排列的场景,在某种程度上可以作为UICollectionView的替代品,同时流式布局实现了CSS3的flexbox的几乎全部功能。 + 流式布局是一种条目视图(item)按照添加的顺序依次排列,当满足某种规则后会另起一排再重新排列,最终呈现为多行多列展示形态的一种布局。 + 这里排列的整体分为垂直和水平两个方向,换排规则主要有数量限制和尺寸限制两种。因此流式布局一共分为垂直数量限制、垂直尺寸限制、水平数量限制、水平尺寸限制四种布局。流式布局主要应用于那些有规律排列的场景,在某种程度上可以作为UICollectionView的替代品,同时流式布局实现了CSS3的flexbox的几乎全部功能。 @note - 需要注意的是这里的方向和排的概念,排的概念它是相对于流式布局的方向而具体定义的:如果是垂直流式布局那么一排内的子视图将是水平排列,而整体则是每排从上往下垂直排列;如果是水平流式布局那么一排内的子视图将是垂直排列,而整体则是每排从左往右水平排列。因此流式布局的方向所描述的是排整体的排列方向。 + 需要注意的是这里的方向和排的概念,排的概念是相对于流式布局的方向而具体定义的:如果是垂直流式布局那么一排内的条目视图将按从左往右水平排列,而每排则从上往下垂直排列;如果是水平流式布局那么一排内的条目视图将按从上往下垂直排列,而每排则从左往右水平排列。流式布局所指的方向是排之间排列的方向,而不是指每排内条目视图排列的方向。 @note - 所谓数量限制流式布局是指事先约定每排的条目视图的数量,当一排的条目视图数量超过约定的数量后新添加的条目视图就会换按约定的方向新起一排重新排列;而尺寸限制流式布局就是当一排内添加到布局中的条目视图的宽度(垂直流式布局)之和或者高度(水平流式布局)之和超过布局视图的宽度或者高度时新添加条目视图就会按约定的方向新起一排重新排列。 + 所谓数量限制流式布局是指约定每排内条目视图的数量,当一排的条目视图数量超过约定的数量后,如果再添加新的条目视图就会换按约定的方向新起一排重新排列;而尺寸限制流式布局则约定一排内添加到布局视图中的所有条目视图的宽度(垂直流式布局)之和或者高度(水平流式布局)之和超过布局视图的宽度或者高度时,如果再添加新的条目视图就会按约定的方向新起一排重新排列。 1.垂直数量限制流式布局 orientation为MyOrientation_Vert,arrangedCount不为0 @code 每排数量为3的垂直数量限制流式布局 - => + --> +------+---+-----+ | A | B | C | +---+--+-+-+-----+ @@ -38,7 +38,7 @@ orientation为MyOrientation_Vert,arrangedCount为0 @code 垂直尺寸限制流式布局 - => + --> +-----+-----------+ | A | B | +-----+-----+-----+ @@ -54,7 +54,7 @@ @code 每排数量为3的水平数量限制流式布局 - => + --> +-----+----+-----+ | A | D | | | |----| G | @@ -73,7 +73,7 @@ @code 水平尺寸限制流式布局 - => + --> +-----+----+-----+ | A | C | | | |----| | @@ -88,13 +88,13 @@ @endcode */ -@interface MyFlowLayout : MyBaseLayout +@interface MyFlowLayout : MyBaseLayout< id > /** 初始化一个流式布局并指定布局的方向和每排条目视图的数量,如果数量为0则表示尺寸限制流式布局。 - @param orientation 布局的方向,这个方向是指的排与排之间排列方向,而不是排内的子视图的排列方向。 - @param arrangedCount 每排内的子条目视图的数量,如果每排内的子条目视图的数量不固定则设置为0表示为尺寸限制流式布局。 + @param orientation 布局的方向,这个方向是指的排与排之间排列方向,而不是排内条目视图的排列方向。 + @param arrangedCount 每排内的条目视图的数量,如果每排内的条目视图的数量不固定则设置为0表示为尺寸限制流式布局。 @return 返回流式布局对象实例。 */ - (instancetype)initWithOrientation:(MyOrientation)orientation arrangedCount:(NSInteger)arrangedCount; @@ -102,16 +102,16 @@ 初始化一个流式布局并指定布局视图的frame值以及方向和每排条目视图的数量,如果数量为0则表示尺寸限制流式布局。 @param frame 布局视图的frame值,当布局视图的父视图是非布局视图时就可以用这个方法来初始化视图的frame。 - @param orientation 布局的方向,这个方向是指的排与排之间排列方向,而不是排内的子视图的排列方向。 - @param arrangedCount 每排内的子条目视图的数量,如果每排内的子条目视图的数量不固定则设置为0表示为尺寸限制流式布局。 + @param orientation 布局的方向,这个方向是指的排与排之间排列方向,而不是排内条目视图的排列方向。 + @param arrangedCount 每排内的条目视图的数量,如果每排内的条目视图的数量不固定则设置为0表示为尺寸限制流式布局。 @return 返回流式布局对象实例 */ - (instancetype)initWithFrame:(CGRect)frame orientation:(MyOrientation)orientation arrangedCount:(NSInteger)arrangedCount; /** 初始化一个流式布局并指定布局的方向和排内条目视图的数量,如果数量为0则表示尺寸限制流式布局。 - @param orientation 布局的方向,这个方向是指的排与排之间排列方向,而不是排内的子视图的排列方向。 - @param arrangedCount 每排内的子条目视图的数量,如果每排内的子视图的数量不固定则设置为0表示为尺寸限制流式布局。 + @param orientation 布局的方向,这个方向是指的排与排之间排列方向,而不是排内条目视图的排列方向。 + @param arrangedCount 每排内的条目视图的数量,如果每排内的条目视图的数量不固定则设置为0表示为尺寸限制流式布局。 @return 返回流式布局对象实例。 */ + (instancetype)flowLayoutWithOrientation:(MyOrientation)orientation arrangedCount:(NSInteger)arrangedCount; @@ -119,19 +119,19 @@ /** 流式布局的布局方向: - 1. MyOrientation_Vert 表示排内子视图从左到右(RTL从右到左)水平排列,排与排之间依次从上到下垂直排列。这个方向是默认方向。 + 1. MyOrientation_Vert 表示一排内条目视图从左往右(RTL从右往左)水平排列,排与排之间依次从上往下垂直排列。这个方向是默认方向。 - 2. MyOrientation_Horz 表示排内子视图从上到下垂直排列,排与排之间依次从左到右(RTL从右到左)水平排列。 + 2. MyOrientation_Horz 表示一排内条目视图从上往下垂直排列,排与排之间依次从左往右(RTL从右到左)水平排列。 */ @property (nonatomic, assign) MyOrientation orientation; /** - 每排内的子条目视图数量。默认是0表示每排内的子条目视图的数量不固定,而是根据子视图的尺寸自动换行或者换列处理。如果非0则每排内的子视图数量等于这个值后会自动换排。 + 每排内的条目视图数量。默认是0表示每排内的条目视图的数量不固定,而是根据一排内条目视图的尺寸之和是否超过布局视图尺寸自动换排处理。如果非0则每排内的条目视图数量大于这个值后会自动换排处理。 */ @property (nonatomic, assign) NSInteger arrangedCount; /** - 为流式布局提供分页展示的能力,默认是0表不支持分页展示。当设置为非0时则要求必须是arrangedCount的整数倍数,表示每页的子视图的数量。而arrangedCount则表示每排内的子视图的数量。当启用pagedCount时如果流式布局的父视图是UIScrollView或者其派生类就会有分页滚动的效果。只有数量限制流式布局才支持分页展示的功能,通过pagedCount和设置尺寸的高度或者宽度自适应配合使用能实现不同的分页展示能力: + 为流式布局提供分页展示的能力,默认是0表不支持分页展示。当设置为非0时则要求必须是arrangedCount的整数倍数,表示每页的子视图的数量。而arrangedCount则表示每排内的子视图的数量。当启用pagedCount时如果流式布局的父视图是UIScrollView或者其派生类就会有分页滚动的效果(要想按页滚动,需要将UIScrollView的 pagingEnabled属性设置为YES)。只有数量限制流式布局才支持分页展示的功能,通过pagedCount和将布局视图的高度或者宽度设置为自适应尺寸进行配合就能实现不同的分页展示能力: @note 1. 垂直数量限制流式布局的高度设置为自适应时则以UIScrollView的尺寸作为一页展示的大小,因为指定了一页的子视图数量,以及指定了一排的子视图数量,因此默认也会自动计算出子视图的宽度和高度,而不需要单独指出高度和宽度(子视图的宽度你也可以自定义),整体的分页滚动是从上到下滚动。(每页布局时从左到右再从上到下排列,新页往下滚动继续排列): @code @@ -172,9 +172,10 @@ 当流式布局是尺寸限制流式布局时,则当设置为YES时则根据子视图的尺寸自动填充,而不是根据加入的顺序来填充,以便保证不会出现多余空隙的情况。 当流式布局是数量限制流式布局时,则当设置为YES时每个子视图会按顺序找一个最佳的存放地点进行填充。 + 因此当此属性设置为YES后,所有条目视图的排列顺序将可能不会按照添加顺序进行排列了。 @note - 如果在尺寸限制流式布局中使用此属性时,请在将所有子视图添加完毕并且初始布局完成后再设置这个属性,否则如果预先设置这个属性则在后续添加子视图时可能会非常耗性能。 + 如果在尺寸限制流式布局中使用此属性时,请在将所有子视图添加完毕并且初始布局完成后再设置这个属性,否则如果预先设置这个属性则后续每添加一个条目视图都会触发重新布局,从而引起性能问题。 */ @property (nonatomic, assign) BOOL autoArrange; @@ -240,33 +241,44 @@ */ @property (nonatomic, copy) MyGravity (^lineGravity)(MyFlowLayout *layout, NSInteger lineIndex, NSInteger itemCount, BOOL isLastLine); +/** + 设置在布局宽高尺寸自适应时显示最大的行数。默认值是NSIntegerMax,表示不受限制。这个属性需要配合布局视图的clipsToBounds属性同时使用才能生效。 + 如果你想要控制布局视图行数显示的限制则可以使用此属性。 + */ +@property (nonatomic, assign) NSInteger maxLines; + +/** + 布局完成后的总行数,这个属性只有在布局完成后或者调用sizeThatFits方法评估后 读取到的值才是正确的。你也可以通过kvo的方式来读取布局后的行数,或者在endLayoutBlock中读取此属性值。 + */ +@property (nonatomic, assign, readonly) NSInteger arrangedLines; + + /** 在流式布局的一些应用场景中我们希望子视图的宽度或者高度是固定的但间距是浮动的,这样就尽可能在一排中容纳更多的子视图。比如设置每个子视图的宽度固定为80,那么在小屏幕下每排只能放3个,而大屏幕则每排能放4个或者5个子视图。 因此您可以通过如下方法来设置子视图的固定尺寸和最小最大浮动间距。这个方法会根据您当前布局的方向不同而具有不同的意义: - 1.如果您的布局方向是垂直的则设置的是每排内子视图的水平浮动间距,其中的subviewSize指定的是子视图的固定宽度;minSpace指定的是最小的水平间距;maxSpace指定的是最大的水平间距,如果指定的subviewSize计算出的间距大于最大间距maxSpace则会缩小subviewSize的宽度值。 + 1.如果您的布局方向是垂直的则设置的是每排内子视图的水平浮动间距,其中的subviewsSize指定的是子视图的固定宽度;minSpacing指定的是最小的水平间距;maxSpacing指定的是最大的水平间距,如果指定的subviewsSize计算出的间距大于最大间距maxSpacing则会缩小subviewsSize的宽度值。 - 2.如果您的布局方向是水平的则设置的是每排内子视图的垂直浮动间距,其中的subviewSize指定的是子视图的固定高度;minSpace指定的是最小的垂直间距;maxSpace指定的是最大的垂直间距,如果指定的subviewSize计算出的间距大于最大间距maxSpace则会调整subviewSize的高度值。 + 2.如果您的布局方向是水平的则设置的是每排内子视图的垂直浮动间距,其中的subviewsSize指定的是子视图的固定高度;minSpacing指定的是最小的垂直间距;maxSpacing指定的是最大的垂直间距,如果指定的subviewsSize计算出的间距大于最大间距maxSpacing则会调整subviewsSize的高度值。 - @note 如果您不想使用浮动间距则请将subviewSize设置为0就可以了。 + @note 如果您不想使用浮动间距则请将subviewsSize设置为0就可以了。 @note 对于数量限制流式布局来说,因为每排和每列的数量的固定的,因此不存在根据屏幕的大小自动换排的能力以及进行最佳数量的排列,但是可以使用这个方法来实现所有子视图尺寸固定但是间距是浮动的功能需求。 - @param subviewSize 指定子视图的尺寸。 - @param minSpace 指定子视图之间的最小间距 - @param maxSpace 指定子视图之间的最大间距 + @param subviewsSize 指定子视图的尺寸。 + @param minSpacing 指定子视图之间的最小间距 + @param maxSpacing 指定子视图之间的最大间距 + @param centered 指定是否所有子视图居中 */ -- (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace; -- (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace inSizeClass:(MySizeClass)sizeClass; +- (void)setSubviewsSize:(CGFloat)subviewsSize minSpacing:(CGFloat)minSpacing maxSpacing:(CGFloat)maxSpacing centered:(BOOL)centered; +- (void)setSubviewsSize:(CGFloat)subviewsSize minSpacing:(CGFloat)minSpacing maxSpacing:(CGFloat)maxSpacing centered:(BOOL)centered inSizeClass:(MySizeClass)sizeClass; -/** - 上面函数的加强版本。 +@end - @param subviewSize 指定子视图的尺寸 - @param minSpace 指定子视图之间的最小间距 - @param maxSpace 指定子视图之间的最大间距 - @param centered 指定是否所有子视图居中 - */ -- (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace centered:(BOOL)centered; -- (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace centered:(BOOL)centered inSizeClass:(MySizeClass)sizeClass; +@interface MyFlowLayout (MyDeprecated) + +- (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace MYDEPRECATED("use setSubviewsSize:minSpacing:maxSpacing:centered:NO instead"); +- (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace inSizeClass:(MySizeClass)sizeClass MYDEPRECATED("use setSubviewsSize:minSpacing:maxSpacing:centered:NO inSizeClass: instead"); +- (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace centered:(BOOL)centered MYDEPRECATED("use setSubviewsSize:minSpacing:maxSpacing:centered: instead"); +- (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace centered:(BOOL)centered inSizeClass:(MySizeClass)sizeClass MYDEPRECATED("use setSubviewsSize:minSpacing:maxSpacing:centered:inSizeClass: instead"); @end diff --git a/MyLayout/Lib/MyFlowLayout.m b/MyLayout/Lib/MyFlowLayout.m index 6ce46e2..49cbb49 100644 --- a/MyLayout/Lib/MyFlowLayout.m +++ b/MyLayout/Lib/MyFlowLayout.m @@ -9,6 +9,10 @@ #import "MyFlowLayout.h" #import "MyLayoutInner.h" +@interface MyFlowLayout() +@property (nonatomic, assign, readwrite) NSInteger arrangedLines; +@end + @implementation MyFlowLayout #pragma mark-- Public Methods @@ -16,7 +20,7 @@ @implementation MyFlowLayout - (instancetype)initWithFrame:(CGRect)frame orientation:(MyOrientation)orientation arrangedCount:(NSInteger)arrangedCount { self = [self initWithFrame:frame]; if (self != nil) { - MyFlowLayoutTraits *layoutTraits = (MyFlowLayoutTraits*)self.myDefaultSizeClass; + MyFlowLayoutTraitsImpl *layoutTraits = self.myDefaultTraits; layoutTraits.orientation = orientation; layoutTraits.arrangedCount = arrangedCount; } @@ -28,149 +32,97 @@ - (instancetype)initWithOrientation:(MyOrientation)orientation arrangedCount:(NS } + (instancetype)flowLayoutWithOrientation:(MyOrientation)orientation arrangedCount:(NSInteger)arrangedCount { - MyFlowLayout *layout = [[[self class] alloc] initWithOrientation:orientation arrangedCount:arrangedCount]; - return layout; + return [[[self class] alloc] initWithOrientation:orientation arrangedCount:arrangedCount]; } -- (void)setOrientation:(MyOrientation)orientation { - MyFlowLayoutTraits *layoutTraits = (MyFlowLayoutTraits*)self.myDefaultSizeClass; - if (layoutTraits.orientation != orientation) { - layoutTraits.orientation = orientation; - [self setNeedsLayout]; - } -} +_MYLAYOUT_PROPERTY_LAYOUTVIEW_OVERRIDE1(MyFlowLayoutTraitsImpl, MyOrientation, Orientation, orientation) +_MYLAYOUT_PROPERTY_LAYOUTVIEW_OVERRIDE1(MyFlowLayoutTraitsImpl, NSInteger, ArrangedCount, arrangedCount) +_MYLAYOUT_PROPERTY_LAYOUTVIEW_OVERRIDE1(MyFlowLayoutTraitsImpl, NSInteger, PagedCount, pagedCount) +_MYLAYOUT_PROPERTY_LAYOUTVIEW_OVERRIDE1(MyFlowLayoutTraitsImpl, BOOL, AutoArrange, autoArrange) +_MYLAYOUT_PROPERTY_LAYOUTVIEW_OVERRIDE1(MyFlowLayoutTraitsImpl, MyGravity, ArrangedGravity, arrangedGravity) +_MYLAYOUT_PROPERTY_LAYOUTVIEW_OVERRIDE1(MyFlowLayoutTraitsImpl, MyGravityPolicy, LastlineGravityPolicy, lastlineGravityPolicy) -- (MyOrientation)orientation { - return self.myDefaultSizeClassInner.orientation; -} - -- (void)setArrangedCount:(NSInteger)arrangedCount { - MyFlowLayoutTraits *layoutTraits = (MyFlowLayoutTraits*)self.myDefaultSizeClass; - if (layoutTraits.arrangedCount != arrangedCount) { - layoutTraits.arrangedCount = arrangedCount; +- (void)setIsFlex:(BOOL)isFlex { + MyFlowLayoutTraitsImpl *layoutTraits = self.myDefaultTraits; + if (layoutTraits.isFlex != isFlex) { + layoutTraits.isFlex = isFlex; + if (isFlex) { + layoutTraits.lastlineGravityPolicy = MyGravityPolicy_Always; + } else { + layoutTraits.lastlineGravityPolicy = MyGravityPolicy_No; + } [self setNeedsLayout]; } } -- (NSInteger)arrangedCount { - return self.myDefaultSizeClassInner.arrangedCount; -} +_MYLAYOUT_PROPERTY_LAYOUTVIEW_GET1(MyFlowLayoutTraitsImpl, BOOL, isFlex) -- (void)setPagedCount:(NSInteger)pagedCount { - MyFlowLayoutTraits *layoutTraits = (MyFlowLayoutTraits*)self.myDefaultSizeClass; - if (layoutTraits.pagedCount != pagedCount) { - layoutTraits.pagedCount = pagedCount; - [self setNeedsLayout]; - } -} +_MYLAYOUT_PROPERTY_LAYOUTVIEW_SET1(MyFlowLayoutTraitsImpl, NSInteger, MaxLines, maxLines) -- (NSInteger)pagedCount { - return self.myDefaultSizeClassInner.pagedCount; -} - -- (void)setAutoArrange:(BOOL)autoArrange { - MyFlowLayoutTraits *layoutTraits = (MyFlowLayoutTraits*)self.myDefaultSizeClass; - if (layoutTraits.autoArrange != autoArrange) { - layoutTraits.autoArrange = autoArrange; - [self setNeedsLayout]; +- (NSInteger)maxLines { + if (self.myDefaultTraitsInner == nil) { + return NSIntegerMax; + } else { + return ((MyFlowLayoutTraitsImpl*)self.myDefaultTraits).maxLines; } } -- (BOOL)autoArrange { - return self.myDefaultSizeClassInner.autoArrange; +- (void)setSubviewsSize:(CGFloat)subviewsSize minSpacing:(CGFloat)minSpacing maxSpacing:(CGFloat)maxSpacing { + [self setSubviewsSize:subviewsSize minSpacing:minSpacing maxSpacing:maxSpacing inSizeClass:MySizeClass_hAny | MySizeClass_wAny]; } -- (void)setArrangedGravity:(MyGravity)arrangedGravity { - MyFlowLayoutTraits *layoutTraits = (MyFlowLayoutTraits*)self.myDefaultSizeClass; - if (layoutTraits.arrangedGravity != arrangedGravity) { - layoutTraits.arrangedGravity = arrangedGravity; - [self setNeedsLayout]; - } +- (void)setSubviewsSize:(CGFloat)subviewsSize minSpacing:(CGFloat)minSpacing maxSpacing:(CGFloat)maxSpacing inSizeClass:(MySizeClass)sizeClass { + [self setSubviewsSize:subviewsSize minSpacing:minSpacing maxSpacing:maxSpacing centered:NO inSizeClass:sizeClass]; } -- (MyGravity)arrangedGravity { - return self.myDefaultSizeClassInner.arrangedGravity; +- (void)setSubviewsSize:(CGFloat)subviewsSize minSpacing:(CGFloat)minSpacing maxSpacing:(CGFloat)maxSpacing centered:(BOOL)centered { + [self setSubviewsSize:subviewsSize minSpacing:minSpacing maxSpacing:maxSpacing centered:centered inSizeClass:MySizeClass_hAny | MySizeClass_wAny]; } -- (void)setIsFlex:(BOOL)isFlex { - MyFlowLayoutTraits *layoutTraits = (MyFlowLayoutTraits*)self.myDefaultSizeClass; - if (layoutTraits.isFlex != isFlex) { - layoutTraits.isFlex = isFlex; - if (isFlex) { - layoutTraits.lastlineGravityPolicy = MyGravityPolicy_Always; - } else { - layoutTraits.lastlineGravityPolicy = MyGravityPolicy_No; +- (void)setSubviewsSize:(CGFloat)subviewsSize minSpacing:(CGFloat)minSpacing maxSpacing:(CGFloat)maxSpacing centered:(BOOL)centered inSizeClass:(MySizeClass)sizeClass { + MyFlowLayoutTraitsImpl *layoutTraits = (MyFlowLayoutTraitsImpl*)[self fetchLayoutTraitsInSizeClass:sizeClass]; + if (subviewsSize == 0) { + layoutTraits.flexSpacing = nil; + } else { + if (layoutTraits.flexSpacing == nil) { + layoutTraits.flexSpacing = [MySequentLayoutFlexSpacing new]; } - [self setNeedsLayout]; - } -} - -- (BOOL)isFlex { - return self.myDefaultSizeClassInner.isFlex; -} - -- (void)setLastlineGravityPolicy:(MyGravityPolicy)lastlineGravityPolicy { - MyFlowLayoutTraits *layoutTraits = (MyFlowLayoutTraits*)self.myDefaultSizeClass; - if (layoutTraits.lastlineGravityPolicy != lastlineGravityPolicy) { - layoutTraits.lastlineGravityPolicy = lastlineGravityPolicy; - [self setNeedsLayout]; + layoutTraits.flexSpacing.subviewsSize = subviewsSize; + layoutTraits.flexSpacing.minSpacing = minSpacing; + layoutTraits.flexSpacing.maxSpacing = maxSpacing; + layoutTraits.flexSpacing.centered = centered; } + [self setNeedsLayout]; } -- (MyGravityPolicy)lastlineGravityPolicy { - return self.myDefaultSizeClassInner.lastlineGravityPolicy; -} - +//// - (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace { - [self setSubviewsSize:subviewSize minSpace:minSpace maxSpace:maxSpace inSizeClass:MySizeClass_hAny | MySizeClass_wAny]; + [self setSubviewsSize:subviewSize minSpacing:minSpace maxSpacing:maxSpace inSizeClass:MySizeClass_hAny | MySizeClass_wAny]; } - (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace inSizeClass:(MySizeClass)sizeClass { - [self setSubviewsSize:subviewSize minSpace:minSpace maxSpace:maxSpace centered:NO inSizeClass:sizeClass]; + [self setSubviewsSize:subviewSize minSpacing:minSpace maxSpacing:maxSpace centered:NO inSizeClass:sizeClass]; } - (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace centered:(BOOL)centered { - [self setSubviewsSize:subviewSize minSpace:minSpace maxSpace:maxSpace centered:centered inSizeClass:MySizeClass_hAny | MySizeClass_wAny]; + [self setSubviewsSize:subviewSize minSpacing:minSpace maxSpacing:maxSpace centered:centered inSizeClass:MySizeClass_hAny | MySizeClass_wAny]; } - (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace centered:(BOOL)centered inSizeClass:(MySizeClass)sizeClass { - MySequentLayoutTraits *layoutTraits = (MySequentLayoutTraits *)[self fetchLayoutSizeClass:sizeClass]; - if (subviewSize == 0) { - layoutTraits.flexSpace = nil; - } else { - if (layoutTraits.flexSpace == nil) { - layoutTraits.flexSpace = [MySequentLayoutFlexSpacing new]; - } - layoutTraits.flexSpace.subviewSize = subviewSize; - layoutTraits.flexSpace.minSpace = minSpace; - layoutTraits.flexSpace.maxSpace = maxSpace; - layoutTraits.flexSpace.centered = centered; - } - [self setNeedsLayout]; + [self setSubviewsSize:subviewSize minSpacing:minSpace maxSpacing:maxSpace centered:centered inSizeClass:sizeClass]; } #pragma mark-- Override Methods - (CGSize)calcLayoutSize:(CGSize)size subviewEngines:(NSMutableArray *)subviewEngines context:(MyLayoutContext *)context { [super calcLayoutSize:size subviewEngines:subviewEngines context:context]; - - MyFlowLayoutTraits *layoutTraits = (MyFlowLayoutTraits *)context->layoutViewEngine.currentSizeClass; - context->paddingTop = layoutTraits.myLayoutPaddingTop; - context->paddingBottom = layoutTraits.myLayoutPaddingBottom; - context->paddingLeading = layoutTraits.myLayoutPaddingLeading; - context->paddingTrailing = layoutTraits.myLayoutPaddingTrailing; - context->vertGravity = MYVERTGRAVITY(layoutTraits.gravity); - context->horzGravity = [MyViewTraits convertLeadingTrailingGravityFromLeftRightGravity:MYHORZGRAVITY(layoutTraits.gravity)]; - context->vertSpace = layoutTraits.subviewVSpace; - context->horzSpace = layoutTraits.subviewHSpace; - if (context->subviewEngines == nil) { - context->subviewEngines = [layoutTraits filterEngines:subviewEngines]; - } - + + MyFlowLayoutTraitsImpl *layoutTraits = context->layoutViewEngine.currentTraits; + MyOrientation orientation = layoutTraits.orientation; MyGravity arrangedGravity = layoutTraits.arrangedGravity; [self myCalcSubviewsWrapContentSize:context - withCustomSetting:^(MyViewTraits *subviewTraits) { + withCustomSetting:^(MyViewTraitsImpl *subviewTraits) { if (subviewTraits.widthSizeInner.wrapVal) { if (layoutTraits.pagedCount > 0 || (orientation == MyOrientation_Vert && subviewTraits.weight != 0.0) || @@ -213,8 +165,8 @@ - (CGSize)calcLayoutSize:(CGSize)size subviewEngines:(NSMutableArraylayoutViewEngine.currentSizeClass; + MyFlowLayoutTraitsImpl *layoutTraits = context->layoutViewEngine.currentTraits; NSArray *subviewEngines = context->subviewEngines; //按照flex规约,如果总的比重小于1则只会将剩余宽度的总比重部分来进行按比例拉伸。 @@ -235,7 +187,7 @@ - (void)myVertLayoutCalculateSinglelineWeight:(CGFloat)lineTotalWeight lineSpare for (NSInteger itemIndex = startItemIndex; itemIndex < startItemIndex + itemCount; itemIndex++) { MyLayoutEngine *subviewEngine = subviewEngines[itemIndex]; - MyViewTraits *subviewTraits = (MyViewTraits *)subviewEngine.currentSizeClass; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; if (subviewTraits.weight != 0) { CGFloat weightWidth = _myCGFloatRound((lineSpareWidth * subviewTraits.weight / lineTotalWeight)); if (subviewTraits.widthSizeInner != nil && subviewTraits.widthSizeInner.val == nil) { @@ -252,7 +204,7 @@ - (void)myHorzLayoutCalculateSinglelineWeight:(CGFloat)lineTotalWeight lineSpare return; } - MyFlowLayoutTraits *layoutTraits = (MyFlowLayoutTraits *)context->layoutViewEngine.currentSizeClass; + MyFlowLayoutTraitsImpl *layoutTraits = context->layoutViewEngine.currentTraits; NSArray *subviewEngines = context->subviewEngines; if (layoutTraits.isFlex && lineTotalWeight < 1.0) { @@ -261,7 +213,7 @@ - (void)myHorzLayoutCalculateSinglelineWeight:(CGFloat)lineTotalWeight lineSpare for (NSInteger itemIndex = startItemIndex; itemIndex < startItemIndex + itemCount; itemIndex++) { MyLayoutEngine *subviewEngine = subviewEngines[itemIndex]; - MyViewTraits *subviewTraits = (MyViewTraits *)subviewEngine.currentSizeClass; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; if (subviewTraits.weight != 0) { CGFloat weightHeight = _myCGFloatRound((lineSpareHeight * subviewTraits.weight / lineTotalWeight)); if (subviewTraits.heightSizeInner != nil && subviewTraits.heightSizeInner.val == nil) { @@ -281,7 +233,7 @@ - (void)myVertLayoutAdjustSingleline:(NSInteger)lineIndex lineSpareWidth:(CGFloa return; } - MyFlowLayoutTraits *layoutTraits = (MyFlowLayoutTraits*)context->layoutViewEngine.currentSizeClass; + MyFlowLayoutTraitsImpl *layoutTraits = context->layoutViewEngine.currentTraits; NSArray *subviewEngines = context->subviewEngines; MyGravity lineHorzGravity = context->horzGravity; @@ -310,7 +262,7 @@ - (void)myVertLayoutAdjustSingleline:(NSInteger)lineIndex lineSpareWidth:(CGFloa for (NSInteger itemIndex = startItemIndex; itemIndex < startItemIndex + itemCount; itemIndex++) { MyLayoutEngine *subviewEngine = subviewEngines[itemIndex]; - MyViewTraits *subviewTraits = (MyViewTraits *)subviewEngine.currentSizeClass; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; if (subviewTraits.weight != 0.0 && lineTotalWeight != 0.0) { CGFloat weightWidth = _myCGFloatRound((lineSpareWidth * subviewTraits.weight / lineTotalWeight)); if (subviewTraits.widthSizeInner != nil && subviewTraits.widthSizeInner.val == nil) { @@ -331,7 +283,7 @@ - (void)myHorzLayoutAdjustSingleline:(NSInteger)lineIndex lineSpareHeight:(CGFlo return; } - MyFlowLayoutTraits *layoutTraits = (MyFlowLayoutTraits*)context->layoutViewEngine.currentSizeClass; + MyFlowLayoutTraitsImpl *layoutTraits = context->layoutViewEngine.currentTraits; NSArray *subviewEngines = context->subviewEngines; MyGravity lineVertGravity = context->vertGravity; @@ -360,7 +312,7 @@ - (void)myHorzLayoutAdjustSingleline:(NSInteger)lineIndex lineSpareHeight:(CGFlo for (NSInteger itemIndex = startItemIndex; itemIndex < startItemIndex + itemCount; itemIndex++) { MyLayoutEngine *subviewEngine = subviewEngines[itemIndex]; - MyViewTraits *subviewTraits = (MyViewTraits *)subviewEngine.currentSizeClass; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; if (subviewTraits.weight != 0.0 && lineTotalWeight != 0.0) { CGFloat weightHeight = _myCGFloatRound((lineSpareHeight * subviewTraits.weight / lineTotalWeight)); if (subviewTraits.heightSizeInner != nil && subviewTraits.heightSizeInner.val == nil) { @@ -383,7 +335,7 @@ - (void)myVertLayoutCalculateSinglelineShrink:(CGFloat)lineTotalShrink lineSpare return; } - MyFlowLayoutTraits *layoutTraits = (MyFlowLayoutTraits*)context->layoutViewEngine.currentSizeClass; + MyFlowLayoutTraitsImpl *layoutTraits = context->layoutViewEngine.currentTraits; NSArray *subviewEngines = context->subviewEngines; //根据flex规约:如果总的压缩比重小于1则超出部分会乘以这个压缩比再进行压缩。 @@ -393,7 +345,7 @@ - (void)myVertLayoutCalculateSinglelineShrink:(CGFloat)lineTotalShrink lineSpare //如果有压缩则调整子视图的宽度。 for (NSInteger itemIndex = startItemIndex; itemIndex < startItemIndex + itemCount; itemIndex++) { MyLayoutEngine *subviewEngine = subviewEngines[itemIndex]; - MyViewTraits *subviewTraits = (MyViewTraits *)subviewEngine.currentSizeClass; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; if (subviewTraits.widthSizeInner.shrink != 0.0) { subviewEngine.width += (subviewTraits.widthSizeInner.shrink / lineTotalShrink) * lineSpareWidth; if (subviewEngine.width < 0.0) { @@ -411,7 +363,7 @@ - (void)myHorzLayoutCalculateSinglelineShrink:(CGFloat)lineTotalShrink lineSpare return; } - MyFlowLayoutTraits *layoutTraits = (MyFlowLayoutTraits*)context->layoutViewEngine.currentSizeClass; + MyFlowLayoutTraitsImpl *layoutTraits = context->layoutViewEngine.currentTraits; NSArray *subviewEngines = context->subviewEngines; //根据flex规约:如果总的压缩比重小于1则超出部分会乘以这个压缩比再进行压缩。 @@ -421,7 +373,7 @@ - (void)myHorzLayoutCalculateSinglelineShrink:(CGFloat)lineTotalShrink lineSpare //如果有压缩则调整子视图的高度。 for (NSInteger itemIndex = startItemIndex; itemIndex < startItemIndex + itemCount; itemIndex++) { MyLayoutEngine *subviewEngine = subviewEngines[itemIndex]; - MyViewTraits *subviewTraits = (MyViewTraits *)subviewEngine.currentSizeClass; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; if (subviewTraits.heightSizeInner.shrink != 0.0) { subviewEngine.height += (subviewTraits.heightSizeInner.shrink / lineTotalShrink) * lineSpareHeight; if (subviewEngine.height < 0.0) { @@ -436,7 +388,7 @@ - (void)myVertLayoutCalculateSingleline:(NSInteger)lineIndex vertAlignment:(MyGr return; } - MyFlowLayoutTraits *layoutTraits = (MyFlowLayoutTraits *)context->layoutViewEngine.currentSizeClass; + MyFlowLayoutTraitsImpl *layoutTraits = context->layoutViewEngine.currentTraits; NSArray *subviewEngines = context->subviewEngines; CGFloat layoutContentWidth = context->selfSize.width - context->paddingLeading - context->paddingTrailing; CGFloat lineSpareWidth = layoutContentWidth - lineMaxWidth; @@ -458,7 +410,7 @@ - (void)myVertLayoutCalculateSingleline:(NSInteger)lineIndex vertAlignment:(MyGr if (lineHorzGravity == MyGravity_None) { lineHorzGravity = context->horzGravity; } else { - lineHorzGravity = [MyViewTraits convertLeadingTrailingGravityFromLeftRightGravity:lineHorzGravity]; + lineHorzGravity = [MyViewTraitsImpl convertLeadingTrailingGravityFromLeftRightGravity:lineHorzGravity]; } lineVertAlignment = MYVERTGRAVITY(lineGravity); if (lineVertAlignment == MyGravity_None) { @@ -514,7 +466,7 @@ - (void)myVertLayoutCalculateSingleline:(NSInteger)lineIndex vertAlignment:(MyGr //将整行的位置进行调整。 for (NSInteger itemIndex = startItemIndex; itemIndex < startItemIndex + itemCount; itemIndex++) { MyLayoutEngine *subviewEngine = subviewEngines[itemIndex]; - MyViewTraits *subviewTraits = (MyViewTraits *)subviewEngine.currentSizeClass; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; if (!context->isEstimate && self.intelligentBorderline != nil) { if ([subviewTraits.view isKindOfClass:[MyBaseLayout class]]) { MyBaseLayout *sublayout = (MyBaseLayout *)subviewTraits.view; @@ -537,34 +489,34 @@ - (void)myVertLayoutCalculateSingleline:(NSInteger)lineIndex vertAlignment:(MyGr sublayout.trailingBorderline = self.intelligentBorderline; } //如果有垂直间距则不是第一行就画上 - if (context->vertSpace != 0 && startItemIndex != 0) { + if (context->vertSpacing != 0 && startItemIndex != 0) { sublayout.topBorderline = self.intelligentBorderline; } //如果有水平间距则不是第一列就画左 - if (context->horzSpace != 0 && itemIndex != startItemIndex) { + if (context->horzSpacing != 0 && itemIndex != startItemIndex) { sublayout.leadingBorderline = self.intelligentBorderline; } } } } - MyGravity sbvVertAlignment = MYVERTGRAVITY(subviewTraits.alignment); - if (sbvVertAlignment == MyGravity_None) { - sbvVertAlignment = lineVertAlignment; + MyGravity subviewVertAlignment = MYVERTGRAVITY(subviewTraits.alignment); + if (subviewVertAlignment == MyGravity_None) { + subviewVertAlignment = lineVertAlignment; } //因为单行内的垂直间距拉伸被赋予紧凑排列,所以这里的定制化将不起作用。 if (vertAlignment == MyGravity_Vert_Between) { - sbvVertAlignment = MyGravity_None; + subviewVertAlignment = MyGravity_None; } UIFont *subviewFont = nil; - if (sbvVertAlignment == MyGravity_Vert_Baseline) { + if (subviewVertAlignment == MyGravity_Vert_Baseline) { subviewFont = [self myGetSubviewFont:subviewTraits.view]; if (subviewFont == nil) { - sbvVertAlignment = MyGravity_Vert_Top; + subviewVertAlignment = MyGravity_Vert_Top; } } - if ((sbvVertAlignment != MyGravity_None && sbvVertAlignment != MyGravity_Vert_Top) || _myCGFloatNotEqual(addXPos, 0.0) || _myCGFloatNotEqual(addXPosInc, 0.0) || applyLastlineGravityPolicy || lineTotalShrink != 0.0) { + if ((subviewVertAlignment != MyGravity_None && subviewVertAlignment != MyGravity_Vert_Top) || _myCGFloatNotEqual(addXPos, 0.0) || _myCGFloatNotEqual(addXPosInc, 0.0) || applyLastlineGravityPolicy || lineTotalShrink != 0.0) { subviewEngine.leading += addXPos; //处理对间距的压缩 @@ -585,7 +537,7 @@ - (void)myVertLayoutCalculateSingleline:(NSInteger)lineIndex vertAlignment:(MyGr subviewEngine.leading = subviewEngines[itemIndex - layoutTraits.arrangedCount].leading; } - switch (sbvVertAlignment) { + switch (subviewVertAlignment) { case MyGravity_Vert_Center: { subviewEngine.top += (lineMaxHeight - subviewTraits.topPosInner.measure - subviewTraits.bottomPosInner.measure - subviewEngine.height) / 2; } break; @@ -619,7 +571,7 @@ - (void)myHorzLayoutCalculateSingleline:(NSInteger)lineIndex horzAlignment:(MyGr return; } - MyFlowLayoutTraits *layoutTraits = (MyFlowLayoutTraits *)context->layoutViewEngine.currentSizeClass; + MyFlowLayoutTraitsImpl *layoutTraits = context->layoutViewEngine.currentTraits; NSArray *subviewEngines = context->subviewEngines; CGFloat layoutContentHeight = context->selfSize.height - context->paddingTop - context->paddingBottom; CGFloat lineSpareHeight = layoutContentHeight - lineMaxHeight; @@ -641,7 +593,7 @@ - (void)myHorzLayoutCalculateSingleline:(NSInteger)lineIndex horzAlignment:(MyGr if (lineHorzAlignment == MyGravity_None) { lineHorzAlignment = horzAlignment; } else { - lineHorzAlignment = [MyViewTraits convertLeadingTrailingGravityFromLeftRightGravity:lineHorzAlignment]; + lineHorzAlignment = [MyViewTraitsImpl convertLeadingTrailingGravityFromLeftRightGravity:lineHorzAlignment]; } lineVertGravity = MYVERTGRAVITY(lineGravity); if (lineVertGravity == MyGravity_None) { @@ -695,7 +647,7 @@ - (void)myHorzLayoutCalculateSingleline:(NSInteger)lineIndex horzAlignment:(MyGr //将整行的位置进行调整。 for (NSInteger itemIndex = startItemIndex; itemIndex < startItemIndex + itemCount; itemIndex++) { MyLayoutEngine *subviewEngine = subviewEngines[itemIndex]; - MyViewTraits *subviewTraits = (MyViewTraits *)subviewEngine.currentSizeClass; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; if (!context->isEstimate && self.intelligentBorderline != nil) { if ([subviewTraits.view isKindOfClass:[MyBaseLayout class]]) { @@ -719,18 +671,18 @@ - (void)myHorzLayoutCalculateSingleline:(NSInteger)lineIndex horzAlignment:(MyGr sublayout.bottomBorderline = self.intelligentBorderline; } //如果有垂直间距则不是第一行就画上 - if (context->vertSpace != 0 && itemIndex != startItemIndex) { + if (context->vertSpacing != 0 && itemIndex != startItemIndex) { sublayout.topBorderline = self.intelligentBorderline; } //如果有水平间距则不是第一列就画左 - if (context->horzSpace != 0 && startItemIndex != 0) { + if (context->horzSpacing != 0 && startItemIndex != 0) { sublayout.leadingBorderline = self.intelligentBorderline; } } } } - MyGravity subviewHorzAlignment = [MyViewTraits convertLeadingTrailingGravityFromLeftRightGravity:MYHORZGRAVITY(subviewTraits.alignment)]; + MyGravity subviewHorzAlignment = [MyViewTraitsImpl convertLeadingTrailingGravityFromLeftRightGravity:MYHORZGRAVITY(subviewTraits.alignment)]; if (subviewHorzAlignment == MyGravity_None) { subviewHorzAlignment = lineHorzAlignment; } @@ -781,18 +733,18 @@ - (void)myHorzLayoutCalculateSingleline:(NSInteger)lineIndex horzAlignment:(MyGr } } -- (CGFloat)myCalcSinglelineSize:(NSArray *)subviewEngines space:(CGFloat)space { +- (CGFloat)myCalcSinglelineSize:(NSArray *)subviewEngines spacing:(CGFloat)spacing { CGFloat size = 0; for (MyLayoutEngine *subviewEngine in subviewEngines) { size += subviewEngine.trailing; if (subviewEngine != subviewEngines.lastObject) { - size += space; + size += spacing; } } return size; } -- (NSArray *)myGetAutoArrangeSubviews:(NSMutableArray *)subviewEngines selfSize:(CGFloat)selfSize space:(CGFloat)space { +- (NSArray *)myGetAutoArrangeSubviews:(NSMutableArray *)subviewEngines selfSize:(CGFloat)selfSize spacing:(CGFloat)spacing { NSMutableArray *retArray = [NSMutableArray arrayWithCapacity:subviewEngines.count]; //保存每行最佳 @@ -804,7 +756,7 @@ - (NSArray *)myGetAutoArrangeSubviews:(NSMutableArray *)subvie index:0 calcArray:@[] selfSize:selfSize - space:space + spacing:spacing bestSinglelineArray:bestSinglelineArray]; [retArray addObjectsFromArray:bestSinglelineArray]; @@ -824,12 +776,12 @@ - (void)myCalcAutoArrangeSinglelineSubviews:(NSMutableArray *) index:(NSInteger)index calcArray:(NSArray *)calcArray selfSize:(CGFloat)selfSize - space:(CGFloat)space + spacing:(CGFloat)spacing bestSinglelineArray:(NSMutableArray *)bestSinglelineArray { if (index >= subviewEngines.count) { - CGFloat s1 = [self myCalcSinglelineSize:calcArray space:space]; - CGFloat s2 = [self myCalcSinglelineSize:bestSinglelineArray space:space]; - if (_myCGFloatLess(fabs(selfSize - s1), fabs(selfSize - s2)) && _myCGFloatLessOrEqual(s1, selfSize)) { + CGFloat size1 = [self myCalcSinglelineSize:calcArray spacing:spacing]; + CGFloat size2 = [self myCalcSinglelineSize:bestSinglelineArray spacing:spacing]; + if (_myCGFloatLess(fabs(selfSize - size1), fabs(selfSize - size2)) && _myCGFloatLessOrEqual(size1, selfSize)) { [bestSinglelineArray setArray:calcArray]; } return; @@ -840,20 +792,20 @@ - (void)myCalcAutoArrangeSinglelineSubviews:(NSMutableArray *) NSMutableArray *calcArray2 = [NSMutableArray arrayWithArray:calcArray]; [calcArray2 addObject:subviewEngines[i]]; - CGFloat s1 = [self myCalcSinglelineSize:calcArray2 space:space]; - if (_myCGFloatLessOrEqual(s1, selfSize)) { - CGFloat s2 = [self myCalcSinglelineSize:bestSinglelineArray space:space]; - if (_myCGFloatLess(fabs(selfSize - s1), fabs(selfSize - s2))) { + CGFloat size1 = [self myCalcSinglelineSize:calcArray2 spacing:spacing]; + if (_myCGFloatLessOrEqual(size1, selfSize)) { + CGFloat size2 = [self myCalcSinglelineSize:bestSinglelineArray spacing:spacing]; + if (_myCGFloatLess(fabs(selfSize - size1), fabs(selfSize - size2))) { [bestSinglelineArray setArray:calcArray2]; } - if (_myCGFloatEqual(s1, selfSize)) { + if (_myCGFloatEqual(size1, selfSize)) { break; } [self myCalcAutoArrangeSinglelineSubviews:subviewEngines index:i + 1 calcArray:calcArray2 selfSize:selfSize - space:space + spacing:spacing bestSinglelineArray:bestSinglelineArray]; } else { @@ -864,21 +816,22 @@ - (void)myCalcAutoArrangeSinglelineSubviews:(NSMutableArray *) - (void)myDoVertOrientationContentLayoutWithContext:(MyLayoutContext *)context { - MyFlowLayoutTraits *layoutTraits = (MyFlowLayoutTraits*)context->layoutViewEngine.currentSizeClass; + MyFlowLayoutTraitsImpl *layoutTraits = context->layoutViewEngine.currentTraits; NSMutableArray *subviewEngines = context->subviewEngines; MyGravity vertAlignment = MYVERTGRAVITY(layoutTraits.arrangedGravity); //支持浮动水平间距。 - CGFloat subviewWidth = [layoutTraits.flexSpace calcMaxMinSubviewSizeForContent:context->selfSize.width paddingStart:&context->paddingLeading paddingEnd:&context->paddingTrailing space:&context->horzSpace]; + CGFloat subviewWidth = [layoutTraits.flexSpacing calcMaxMinSubviewsSizeForContent:context->selfSize.width paddingStart:&context->paddingLeading paddingEnd:&context->paddingTrailing spacing :&context->horzSpacing]; CGFloat paddingHorz = context->paddingLeading + context->paddingTrailing; CGFloat paddingVert = context->paddingTop + context->paddingBottom; - CGFloat xPos = context->paddingLeading; - CGFloat yPos = context->paddingTop; + CGFloat nextSubviewLeadingPos = context->paddingLeading; + CGFloat nextSubviewTopPos = context->paddingTop; CGFloat lineMaxHeight = 0.0; //某一行的最高值。 CGFloat lineMaxWidth = 0.0; //某一行的最宽值。 CGFloat maxLayoutWidth = 0.0; //所有行中最宽的值。 + CGFloat maxLayoutHeight = 0.0; //limitedSelfWidth是用来限制子视图换行的宽度,默认是selfSize.width //但是一种特殊情况就是布局视图宽度自适应,但是设置了最宽宽度的情况。 @@ -893,7 +846,7 @@ - (void)myDoVertOrientationContentLayoutWithContext:(MyLayoutContext *)context { if (layoutTraits.autoArrange) { //计算出每个子视图的宽度。 for (MyLayoutEngine *subviewEngine in subviewEngines) { - MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; #ifdef DEBUG //约束异常:垂直流式布局设置autoArrange为YES时,子视图不能将weight设置为非0. @@ -912,7 +865,7 @@ - (void)myDoVertOrientationContentLayoutWithContext:(MyLayoutContext *)context { } } - [subviewEngines setArray:[self myGetAutoArrangeSubviews:subviewEngines selfSize:context->selfSize.width - paddingHorz space:context->horzSpace]]; + [subviewEngines setArray:[self myGetAutoArrangeSubviews:subviewEngines selfSize:context->selfSize.width - paddingHorz spacing:context->horzSpacing]]; } //每行行首子视图的索引位置。 @@ -920,10 +873,14 @@ - (void)myDoVertOrientationContentLayoutWithContext:(MyLayoutContext *)context { NSInteger lineIndex = 0; //行的索引。 NSInteger itemIndex = 0; //行内子视图的索引 CGFloat lineTotalWeight = 0.0; + NSInteger maxLines = layoutTraits.maxLines; + if (!layoutTraits.heightSizeInner.wrapVal) { + maxLines = NSIntegerMax; + } NSInteger i = 0; for (; i < subviewEngines.count; i++) { MyLayoutEngine *subviewEngine = subviewEngines[i]; - MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; CGFloat leadingSpacing = subviewTraits.leadingPosInner.measure; CGFloat trailingSpacing = subviewTraits.trailingPosInner.measure; @@ -947,9 +904,9 @@ - (void)myDoVertOrientationContentLayoutWithContext:(MyLayoutContext *)context { if (layoutTraits.isFlex) { subviewEngine.width = 0.0; } else { - CGFloat lineSpareWidth = context->selfSize.width - context->paddingTrailing - xPos - leadingSpacing - trailingSpacing; + CGFloat lineSpareWidth = context->selfSize.width - context->paddingTrailing - nextSubviewLeadingPos - leadingSpacing - trailingSpacing; if (itemIndex != 0) { - lineSpareWidth -= context->horzSpace; + lineSpareWidth -= context->horzSpacing; } if (_myCGFloatLessOrEqual(lineSpareWidth, 0.0)) { //如果当前行的剩余空间不够,则需要换行来计算相对的宽度占比,这时候剩余空间就是按整行来算,并且将行索引设置为0 @@ -969,9 +926,9 @@ - (void)myDoVertOrientationContentLayoutWithContext:(MyLayoutContext *)context { } //计算xPos的值加上leadingSpacing + subviewEngine.width + trailingSpacing 的值要小于整体的宽度。 - CGFloat place = xPos + leadingSpacing + subviewEngine.width + trailingSpacing; + CGFloat place = nextSubviewLeadingPos + leadingSpacing + subviewEngine.width + trailingSpacing; if (itemIndex != 0) { - place += context->horzSpace; + place += context->horzSpacing; } place += context->paddingTrailing; @@ -983,9 +940,9 @@ - (void)myDoVertOrientationContentLayoutWithContext:(MyLayoutContext *)context { [lineFirstSubviewIndexSet addIndex:i - itemIndex]; //拉伸以及调整行内子视图的宽度。 - [self myVertLayoutAdjustSingleline:lineIndex lineSpareWidth:context->selfSize.width - context->paddingTrailing - xPos lineTotalWeight:lineTotalWeight startItemIndex:i - itemIndex itemCount:itemIndex withContext:context]; + [self myVertLayoutAdjustSingleline:lineIndex lineSpareWidth:context->selfSize.width - context->paddingTrailing - nextSubviewLeadingPos lineTotalWeight:lineTotalWeight startItemIndex:i - itemIndex itemCount:itemIndex withContext:context]; - xPos = context->paddingLeading; + nextSubviewLeadingPos = context->paddingLeading; //如果这个sbv的宽度大于整体布局视图的宽度。则将子视图的宽度缩小变为和布局视图一样宽 if (_myCGFloatGreat(leadingSpacing + trailingSpacing + subviewEngine.width, context->selfSize.width - paddingHorz)) { @@ -998,10 +955,10 @@ - (void)myDoVertOrientationContentLayoutWithContext:(MyLayoutContext *)context { } if (itemIndex != 0) { - xPos += context->horzSpace; + nextSubviewLeadingPos += context->horzSpacing; } - subviewEngine.leading = xPos + leadingSpacing; - xPos += leadingSpacing + subviewEngine.width + trailingSpacing; + subviewEngine.leading = nextSubviewLeadingPos + leadingSpacing; + nextSubviewLeadingPos += leadingSpacing + subviewEngine.width + trailingSpacing; if (layoutTraits.isFlex && subviewTraits.weight != 0.0) { lineTotalWeight += subviewTraits.weight; @@ -1011,33 +968,35 @@ - (void)myDoVertOrientationContentLayoutWithContext:(MyLayoutContext *)context { } //最后一行的行首索引 - [lineFirstSubviewIndexSet addIndex:i - itemIndex]; + if (subviewEngines.count > 0) { + [lineFirstSubviewIndexSet addIndex:i - itemIndex]; + } //在宽度为自适应时,如果没有设置最大宽度限制,那么就一定是单行,因此宽度就是子视图的总和。 //如果设置了最大宽度限制时,那就要区分最后一行是单行还是多行,所以我们取限宽和当前计算出的宽度的最小值,并且再取selfSize.width和前面比较结果的最大值。 if (layoutTraits.widthSizeInner.wrapVal) { if (limitedSelfWidth == CGFLOAT_MAX) { - context->selfSize.width = _myCGFloatMax(xPos + context->paddingTrailing, [self myGetBoundLimitMeasure:layoutTraits.widthSizeInner.uBoundValInner subview:self anchorType:layoutTraits.widthSizeInner.anchorType subviewSize:context->selfSize selfLayoutSize:self.superview.bounds.size isUBound:NO]); + context->selfSize.width = _myCGFloatMax(nextSubviewLeadingPos + context->paddingTrailing, [self myGetBoundLimitMeasure:layoutTraits.widthSizeInner.uBoundValInner subview:self anchorType:layoutTraits.widthSizeInner.anchorType subviewSize:context->selfSize selfLayoutSize:self.superview.bounds.size isUBound:NO]); } else { - context->selfSize.width = _myCGFloatMax(_myCGFloatMin(xPos + context->paddingTrailing, limitedSelfWidth), context->selfSize.width); + context->selfSize.width = _myCGFloatMax(_myCGFloatMin(nextSubviewLeadingPos + context->paddingTrailing, limitedSelfWidth), context->selfSize.width); } } - [self myVertLayoutAdjustSingleline:lineIndex lineSpareWidth:context->selfSize.width - context->paddingTrailing - xPos lineTotalWeight:lineTotalWeight startItemIndex:i - itemIndex itemCount:itemIndex withContext:context]; + [self myVertLayoutAdjustSingleline:lineIndex lineSpareWidth:context->selfSize.width - context->paddingTrailing - nextSubviewLeadingPos lineTotalWeight:lineTotalWeight startItemIndex:i - itemIndex itemCount:itemIndex withContext:context]; - xPos = context->paddingLeading; + nextSubviewLeadingPos = context->paddingLeading; lineIndex = 0; //行的索引。 itemIndex = 0; //行内的子视图索引 NSInteger oldLineFirstIndex = 0; i = 0; for (; i < subviewEngines.count; i++) { MyLayoutEngine *subviewEngine = subviewEngines[i]; - MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; - CGFloat topSpace = subviewTraits.topPosInner.measure; - CGFloat leadingSpace = subviewTraits.leadingPosInner.measure; - CGFloat bottomSpace = subviewTraits.bottomPosInner.measure; - CGFloat trailingSpace = subviewTraits.trailingPosInner.measure; + CGFloat topSpacing = subviewTraits.topPosInner.measure; + CGFloat leadingSpacing = subviewTraits.leadingPosInner.measure; + CGFloat bottomSpacing = subviewTraits.bottomPosInner.measure; + CGFloat trailingSpacing = subviewTraits.trailingPosInner.measure; subviewEngine.width = [self myValidMeasure:subviewTraits.widthSizeInner subview:subviewTraits.view calcSize:subviewEngine.width subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; @@ -1056,19 +1015,23 @@ - (void)myDoVertOrientationContentLayoutWithContext:(MyLayoutContext *)context { } //计算xPos的值加上leadingSpacing + subviewEngine.width + trailingSpacing 的值要小于整体的宽度。 - maxLayoutWidth = xPos + leadingSpace + subviewEngine.width + trailingSpace; + maxLayoutWidth = nextSubviewLeadingPos + leadingSpacing + subviewEngine.width + trailingSpacing; if (itemIndex != 0) { - maxLayoutWidth += context->horzSpace; + maxLayoutWidth += context->horzSpacing; } maxLayoutWidth += context->paddingTrailing; NSUInteger lineFirstIndex = [lineFirstSubviewIndexSet indexLessThanOrEqualToIndex:i]; - if (oldLineFirstIndex != lineFirstIndex) { + if (oldLineFirstIndex != lineFirstIndex) { //新的一行开始 oldLineFirstIndex = lineFirstIndex; - xPos = context->paddingLeading; - yPos += context->vertSpace; - yPos += lineMaxHeight; + nextSubviewLeadingPos = context->paddingLeading; + nextSubviewTopPos += lineMaxHeight; + if (lineIndex < maxLines) { + maxLayoutHeight = nextSubviewTopPos; + } + nextSubviewTopPos += context->vertSpacing; + [self myVertLayoutCalculateSingleline:lineIndex vertAlignment:vertAlignment lineMaxHeight:lineMaxHeight lineMaxWidth:lineMaxWidth lineTotalShrink:0 startItemIndex:i - itemIndex itemCount:itemIndex withContext:context]; @@ -1079,29 +1042,34 @@ - (void)myDoVertOrientationContentLayoutWithContext:(MyLayoutContext *)context { } if (itemIndex != 0) { - xPos += context->horzSpace; + nextSubviewLeadingPos += context->horzSpacing; } - subviewEngine.leading = xPos + leadingSpace; - subviewEngine.top = yPos + topSpace; - xPos += leadingSpace + subviewEngine.width + trailingSpace; + subviewEngine.leading = nextSubviewLeadingPos + leadingSpacing; + subviewEngine.top = nextSubviewTopPos + topSpacing; + nextSubviewLeadingPos += leadingSpacing + subviewEngine.width + trailingSpacing; - if (_myCGFloatLess(lineMaxHeight, topSpace + bottomSpace + subviewEngine.height)) { - lineMaxHeight = topSpace + bottomSpace + subviewEngine.height; + if (_myCGFloatLess(lineMaxHeight, topSpacing + bottomSpacing + subviewEngine.height)) { + lineMaxHeight = topSpacing + bottomSpacing + subviewEngine.height; } - if (_myCGFloatLess(lineMaxWidth, (xPos - context->paddingLeading))) { - lineMaxWidth = (xPos - context->paddingLeading); + if (_myCGFloatLess(lineMaxWidth, (nextSubviewLeadingPos - context->paddingLeading))) { + lineMaxWidth = (nextSubviewLeadingPos - context->paddingLeading); } itemIndex++; } - yPos += lineMaxHeight + context->paddingBottom; + nextSubviewTopPos += lineMaxHeight + context->paddingBottom; + if (lineIndex < maxLines) { + maxLayoutHeight = nextSubviewTopPos; + } else { + maxLayoutHeight += context->paddingBottom; + } //内容填充约束布局的宽度包裹计算。 if (layoutTraits.widthSizeInner.wrapVal) { context->selfSize.width = [self myValidMeasure:layoutTraits.widthSizeInner subview:self calcSize:maxLayoutWidth subviewSize:context->selfSize selfLayoutSize:self.superview.bounds.size]; } if (layoutTraits.heightSizeInner.wrapVal) { - context->selfSize.height = [self myValidMeasure:layoutTraits.heightSizeInner subview:self calcSize:yPos subviewSize:context->selfSize selfLayoutSize:self.superview.bounds.size]; + context->selfSize.height = [self myValidMeasure:layoutTraits.heightSizeInner subview:self calcSize:maxLayoutHeight subviewSize:context->selfSize selfLayoutSize:self.superview.bounds.size]; } NSInteger arranges = lineFirstSubviewIndexSet.count; //根据flex规则:如果只有一行则整个高度都作为子视图的拉伸和停靠区域。 @@ -1112,23 +1080,24 @@ - (void)myDoVertOrientationContentLayoutWithContext:(MyLayoutContext *)context { [self myVertLayoutCalculateSingleline:lineIndex vertAlignment:vertAlignment lineMaxHeight:lineMaxHeight lineMaxWidth:lineMaxWidth lineTotalShrink:0 startItemIndex:i - itemIndex itemCount:itemIndex withContext:context]; //整体的停靠 - if (context->vertGravity != MyGravity_None && context->selfSize.height != yPos) { + if (context->vertGravity != MyGravity_None && context->selfSize.height != maxLayoutHeight) { //根据flex标准:只有在多行下vertGravity才有意义。非flex标准则不受这个条件约束。 if (arranges > 1 || !layoutTraits.isFlex) { CGFloat addYPos = 0.0; CGFloat between = 0.0; CGFloat fill = 0.0; + CGFloat remainingHeight = context->selfSize.height - maxLayoutHeight; if (arranges <= 1 && context->vertGravity == MyGravity_Vert_Around) { context->vertGravity = MyGravity_Vert_Center; } if (context->vertGravity == MyGravity_Vert_Center) { - addYPos = (context->selfSize.height - yPos) / 2; + addYPos = remainingHeight / 2; } else if (context->vertGravity == MyGravity_Vert_Bottom) { - addYPos = context->selfSize.height - yPos; + addYPos = remainingHeight; } else if (context->vertGravity == MyGravity_Vert_Fill || context->vertGravity == MyGravity_Vert_Stretch) { if (arranges > 0) { - fill = (context->selfSize.height - yPos) / arranges; + fill = remainingHeight / arranges; } //满足flex规则:如果剩余的空间是负数,该值等效于'flex-start' if (fill < 0.0 && context->vertGravity == MyGravity_Vert_Stretch) { @@ -1136,12 +1105,12 @@ - (void)myDoVertOrientationContentLayoutWithContext:(MyLayoutContext *)context { } } else if (context->vertGravity == MyGravity_Vert_Between) { if (arranges > 1) { - between = (context->selfSize.height - yPos) / (arranges - 1); + between = remainingHeight / (arranges - 1); } } else if (context->vertGravity == MyGravity_Vert_Around) { - between = (context->selfSize.height - yPos) / arranges; + between = remainingHeight / arranges; } else if (context->vertGravity == MyGravity_Vert_Among) { - between = (context->selfSize.height - yPos) / (arranges + 1); + between = remainingHeight / (arranges + 1); } if (addYPos != 0.0 || between != 0.0 || fill != 0.0) { @@ -1161,7 +1130,7 @@ - (void)myDoVertOrientationContentLayoutWithContext:(MyLayoutContext *)context { } if (context->vertGravity == MyGravity_Vert_Stretch) { - MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; //只有在没有约束,或者非布局视图下的高度自适应约束才会被拉伸。 if (subviewTraits.heightSizeInner.val == nil || (subviewTraits.heightSizeInner.wrapVal && ![subviewTraits.view isKindOfClass:[MyBaseLayout class]])) { subviewEngine.height += fill; @@ -1194,11 +1163,13 @@ - (void)myDoVertOrientationContentLayoutWithContext:(MyLayoutContext *)context { } } } + self.arrangedLines = arranges; + } - (void)myDoVertOrientationCountLayoutWithContext:(MyLayoutContext *)context { - MyFlowLayoutTraits *layoutTraits = (MyFlowLayoutTraits*)context->layoutViewEngine.currentSizeClass; + MyFlowLayoutTraitsImpl *layoutTraits = context->layoutViewEngine.currentTraits; NSMutableArray *subviewEngines = context->subviewEngines; @@ -1207,18 +1178,19 @@ - (void)myDoVertOrientationCountLayoutWithContext:(MyLayoutContext *)context { MyGravity vertAlignment = MYVERTGRAVITY(layoutTraits.arrangedGravity); - CGFloat subviewWidth = [layoutTraits.flexSpace calcMaxMinSubviewSize:context->selfSize.width arrangedCount:arrangedCount paddingStart:&context->paddingLeading paddingEnd:&context->paddingTrailing space:&context->horzSpace]; + CGFloat subviewWidth = [layoutTraits.flexSpacing calcMaxMinSubviewsSize:context->selfSize.width arrangedCount:arrangedCount paddingStart:&context->paddingLeading paddingEnd:&context->paddingTrailing spacing:&context->horzSpacing]; CGFloat paddingHorz = context->paddingLeading + context->paddingTrailing; CGFloat paddingVert = context->paddingTop + context->paddingBottom; - CGFloat xPos = context->paddingLeading; - CGFloat yPos = context->paddingTop; + CGFloat nextSubviewLeadingPos = context->paddingLeading; + CGFloat nextSubviewTopPos = context->paddingTop; CGFloat lineMaxHeight = 0.0; //某一行的最高值。 CGFloat lineMaxWidth = 0.0; //某一行的最宽值 CGFloat maxLayoutWidth = 0.0; //全部行的最大宽度 CGFloat maxLayoutHeight = context->paddingTop; //最大的高度 + #if TARGET_OS_IOS //判断父滚动视图是否分页滚动 BOOL isPagingScroll = (self.superview != nil && @@ -1238,25 +1210,25 @@ - (void)myDoVertOrientationCountLayoutWithContext:(MyLayoutContext *)context { if (layoutTraits.widthSizeInner.wrapVal) { isHorzPaging = YES; if (isPagingScroll) { - pagingItemWidth = (CGRectGetWidth(self.superview.bounds) - paddingHorz - (arrangedCount - 1) * context->horzSpace) / arrangedCount; + pagingItemWidth = (CGRectGetWidth(self.superview.bounds) - paddingHorz - (arrangedCount - 1) * context->horzSpacing) / arrangedCount; } else { - pagingItemWidth = (CGRectGetWidth(self.superview.bounds) - context->paddingLeading - arrangedCount * context->horzSpace) / arrangedCount; + pagingItemWidth = (CGRectGetWidth(self.superview.bounds) - context->paddingLeading - arrangedCount * context->horzSpacing) / arrangedCount; } //如果是水平滚动则如果布局不是高度自适应才让条目的高度生效。 if (!layoutTraits.heightSizeInner.wrapVal) { - pagingItemHeight = (context->selfSize.height - paddingVert - (rows - 1) * context->vertSpace) / rows; + pagingItemHeight = (context->selfSize.height - paddingVert - (rows - 1) * context->vertSpacing) / rows; } } else { isVertPaging = YES; - pagingItemWidth = (context->selfSize.width - paddingHorz - (arrangedCount - 1) * context->horzSpace) / arrangedCount; + pagingItemWidth = (context->selfSize.width - paddingHorz - (arrangedCount - 1) * context->horzSpacing) / arrangedCount; //分页滚动时和非分页滚动时的高度计算是不一样的。 if (isPagingScroll) { - pagingItemHeight = (CGRectGetHeight(self.superview.bounds) - paddingVert - (rows - 1) * context->vertSpace) / rows; + pagingItemHeight = (CGRectGetHeight(self.superview.bounds) - paddingVert - (rows - 1) * context->vertSpacing) / rows; } else { if ([self.superview isKindOfClass:[UIScrollView class]]) { - pagingItemHeight = (CGRectGetHeight(self.superview.bounds) - context->paddingTop - rows * context->vertSpace) / rows; + pagingItemHeight = (CGRectGetHeight(self.superview.bounds) - context->paddingTop - rows * context->vertSpacing) / rows; } else { - pagingItemHeight = (context->selfSize.height - paddingVert - (rows - 1) * context->vertSpace) / rows; + pagingItemHeight = (context->selfSize.height - paddingVert - (rows - 1) * context->vertSpacing) / rows; } } } @@ -1269,7 +1241,7 @@ - (void)myDoVertOrientationCountLayoutWithContext:(MyLayoutContext *)context { //平均宽度,当布局的gravity设置为Horz_Fill时指定这个平均宽度值。 CGFloat averageWidth = 0.0; if (context->horzGravity == MyGravity_Horz_Fill) { - averageWidth = (context->selfSize.width - paddingHorz - (arrangedCount - 1) * context->horzSpace) / arrangedCount; + averageWidth = (context->selfSize.width - paddingHorz - (arrangedCount - 1) * context->horzSpacing) / arrangedCount; if (averageWidth < 0.0) { averageWidth = 0.0; } @@ -1290,9 +1262,14 @@ - (void)myDoVertOrientationCountLayoutWithContext:(MyLayoutContext *)context { NSInteger i = 0; NSInteger itemIndex = 0; NSInteger lineIndex = 0; //行索引 + NSInteger maxLines = layoutTraits.maxLines; + if (!layoutTraits.heightSizeInner.wrapVal) { + maxLines = NSIntegerMax; + } + for (; i < subviewEngines.count; i++) { MyLayoutEngine *subviewEngine = subviewEngines[i]; - MyViewTraits *subviewTraits = (MyViewTraits *)subviewEngine.currentSizeClass; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; CGFloat leadingSpacing = subviewTraits.leadingPosInner.measure; CGFloat trailingSpacing = subviewTraits.trailingPosInner.measure; @@ -1352,7 +1329,7 @@ - (void)myDoVertOrientationCountLayoutWithContext:(MyLayoutContext *)context { lineTotalFixedWidth += subviewEngine.width; lineTotalFixedWidth += leadingSpacing + trailingSpacing; if (itemIndex < arrangedCount) { - lineTotalFixedWidth += context->horzSpace; + lineTotalFixedWidth += context->horzSpacing; } //计算总拉伸比。 if (subviewTraits.weight != 0.0) { @@ -1367,7 +1344,7 @@ - (void)myDoVertOrientationCountLayoutWithContext:(MyLayoutContext *)context { //最后一行。 if (arranges > 0) { if (itemIndex < arrangedCount) { - lineTotalFixedWidth -= context->horzSpace; + lineTotalFixedWidth -= context->horzSpacing; } lineTotalWeights[lineIndex] = lineTotalWeight; lineTotalFixedWidths[lineIndex] = lineTotalFixedWidth; @@ -1427,13 +1404,13 @@ - (void)myDoVertOrientationCountLayoutWithContext:(MyLayoutContext *)context { i = 0; for (; i < subviewEngines.count; i++) { MyLayoutEngine *subviewEngine = subviewEngines[i]; - MyViewTraits *subviewTraits = (MyViewTraits *)subviewEngine.currentSizeClass; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; //新的一行 if (itemIndex >= arrangedCount) { itemIndex = 0; - yPos += context->vertSpace; - yPos += lineMaxHeight; + nextSubviewTopPos += context->vertSpacing; + nextSubviewTopPos += lineMaxHeight; [self myVertLayoutCalculateSingleline:lineIndex vertAlignment:vertAlignment lineMaxHeight:lineMaxHeight lineMaxWidth:lineMaxWidth lineTotalShrink:lineTotalShrink startItemIndex:i - arrangedCount itemCount:arrangedCount withContext:context]; @@ -1445,7 +1422,7 @@ - (void)myDoVertOrientationCountLayoutWithContext:(MyLayoutContext *)context { if (!isPagingScroll) { pageWidth -= context->paddingLeading; } - yPos = context->paddingTop; + nextSubviewTopPos = context->paddingTop; } } @@ -1453,13 +1430,13 @@ - (void)myDoVertOrientationCountLayoutWithContext:(MyLayoutContext *)context { //如果是分页滚动则要多添加垂直间距。 if (i % layoutTraits.pagedCount == 0) { if (isPagingScroll) { - yPos -= context->vertSpace; - yPos += paddingVert; + nextSubviewTopPos -= context->vertSpacing; + nextSubviewTopPos += paddingVert; } } } - xPos = context->paddingLeading + pageWidth; + nextSubviewLeadingPos = context->paddingLeading + pageWidth; lineMaxHeight = 0.0; lineMaxWidth = 0.0; @@ -1520,55 +1497,55 @@ - (void)myDoVertOrientationCountLayoutWithContext:(MyLayoutContext *)context { } //找到的minNextPointIndex中的 - xPos = minPoint.x; - yPos = minPoint.y; + nextSubviewLeadingPos = minPoint.x; + nextSubviewTopPos = minPoint.y; - minPoint.y = minPoint.y + topSpacing + subviewEngine.height + bottomSpacing + context->vertSpace; + minPoint.y = minPoint.y + topSpacing + subviewEngine.height + bottomSpacing + context->vertSpacing; nextPointOfRows[minNextPointIndex] = [NSValue valueWithCGPoint:minPoint]; if (minNextPointIndex + 1 <= arrangedCount - 1) { minPoint = nextPointOfRows[minNextPointIndex + 1].CGPointValue; - minPoint.x = xPos + leadingSpacing + subviewEngine.width + trailingSpacing + context->horzSpace; + minPoint.x = nextSubviewLeadingPos + leadingSpacing + subviewEngine.width + trailingSpacing + context->horzSpacing; nextPointOfRows[minNextPointIndex + 1] = [NSValue valueWithCGPoint:minPoint]; } - if (_myCGFloatLess(maxLayoutHeight, yPos + topSpacing + subviewEngine.height + bottomSpacing)) { - maxLayoutHeight = yPos + topSpacing + subviewEngine.height + bottomSpacing; + if (lineIndex < maxLines && _myCGFloatLess(maxLayoutHeight, nextSubviewTopPos + topSpacing + subviewEngine.height + bottomSpacing)) { + maxLayoutHeight = nextSubviewTopPos + topSpacing + subviewEngine.height + bottomSpacing; } } else if (vertAlignment == MyGravity_Vert_Between) { //当列是紧凑排列时需要特殊处理当前的垂直位置。 //第0行特殊处理。 if (i - arrangedCount < 0) { - yPos = context->paddingTop; + nextSubviewTopPos = context->paddingTop; } else { //取前一行的对应的列的子视图。 MyLayoutEngine *prevColSubviewEngine = subviewEngines[i - arrangedCount]; - MyViewTraits *prevColSubviewTraits = prevColSubviewEngine.currentSizeClass; + MyViewTraitsImpl *prevColSubviewTraits = prevColSubviewEngine.currentTraits; //当前子视图的位置等于前一行对应列的最大y的值 + 前面对应列的底部间距 + 子视图之间的行间距。 - yPos = CGRectGetMaxY(prevColSubviewEngine.frame) + prevColSubviewTraits.bottomPosInner.measure + context->vertSpace; + nextSubviewTopPos = CGRectGetMaxY(prevColSubviewEngine.frame) + prevColSubviewTraits.bottomPosInner.measure + context->vertSpacing; } - if (_myCGFloatLess(maxLayoutHeight, yPos + topSpacing + subviewEngine.height + bottomSpacing)) { - maxLayoutHeight = yPos + topSpacing + subviewEngine.height + bottomSpacing; + if (lineIndex < maxLines && _myCGFloatLess(maxLayoutHeight, nextSubviewTopPos + topSpacing + subviewEngine.height + bottomSpacing)) { + maxLayoutHeight = nextSubviewTopPos + topSpacing + subviewEngine.height + bottomSpacing; } } else { //正常排列。 //这里的最大其实就是最后一个视图的位置加上最高的子视图的尺寸。 - if (_myCGFloatLess(maxLayoutHeight, yPos + lineMaxHeight)) { - maxLayoutHeight = yPos + lineMaxHeight; + if (lineIndex < maxLines && _myCGFloatLess(maxLayoutHeight, nextSubviewTopPos + lineMaxHeight)) { + maxLayoutHeight = nextSubviewTopPos + lineMaxHeight; } } - subviewEngine.leading = xPos + leadingSpacing; - subviewEngine.top = yPos + topSpacing; - xPos += leadingSpacing + subviewEngine.width + trailingSpacing; + subviewEngine.leading = nextSubviewLeadingPos + leadingSpacing; + subviewEngine.top = nextSubviewTopPos + topSpacing; + nextSubviewLeadingPos += leadingSpacing + subviewEngine.width + trailingSpacing; - if (_myCGFloatLess(lineMaxWidth, (xPos - context->paddingLeading))) { - lineMaxWidth = (xPos - context->paddingLeading); + if (_myCGFloatLess(lineMaxWidth, (nextSubviewLeadingPos - context->paddingLeading))) { + lineMaxWidth = (nextSubviewLeadingPos - context->paddingLeading); } - if (_myCGFloatLess(maxLayoutWidth, xPos)) { - maxLayoutWidth = xPos; + if (_myCGFloatLess(maxLayoutWidth, nextSubviewLeadingPos)) { + maxLayoutWidth = nextSubviewLeadingPos; } if (itemIndex != (arrangedCount - 1) && !autoArrange) { - xPos += context->horzSpace; + nextSubviewLeadingPos += context->horzSpacing; } itemIndex++; @@ -1608,17 +1585,18 @@ - (void)myDoVertOrientationCountLayoutWithContext:(MyLayoutContext *)context { CGFloat addYPos = 0.0; CGFloat between = 0.0; CGFloat fill = 0.0; + CGFloat remainingHeight = context->selfSize.height - maxLayoutHeight; if (arranges <= 1 && context->vertGravity == MyGravity_Vert_Around) { context->vertGravity = MyGravity_Vert_Center; } if (context->vertGravity == MyGravity_Vert_Center) { - addYPos = (context->selfSize.height - maxLayoutHeight) / 2; + addYPos = remainingHeight / 2; } else if (context->vertGravity == MyGravity_Vert_Bottom) { - addYPos = context->selfSize.height - maxLayoutHeight; + addYPos = remainingHeight; } else if (context->vertGravity == MyGravity_Vert_Fill || context->vertGravity == MyGravity_Vert_Stretch) { if (arranges > 0) { - fill = (context->selfSize.height - maxLayoutHeight) / arranges; + fill = remainingHeight / arranges; } //满足flex规则:如果剩余的空间是负数,该值等效于'flex-start' if (fill < 0.0 && context->vertGravity == MyGravity_Vert_Stretch) { @@ -1626,12 +1604,12 @@ - (void)myDoVertOrientationCountLayoutWithContext:(MyLayoutContext *)context { } } else if (context->vertGravity == MyGravity_Vert_Between) { if (arranges > 1) { - between = (context->selfSize.height - maxLayoutHeight) / (arranges - 1); + between = remainingHeight / (arranges - 1); } } else if (context->vertGravity == MyGravity_Vert_Around) { - between = (context->selfSize.height - maxLayoutHeight) / arranges; + between = remainingHeight / arranges; } else if (context->vertGravity == MyGravity_Vert_Among) { - between = (context->selfSize.height - maxLayoutHeight) / (arranges + 1); + between = remainingHeight / (arranges + 1); } if (addYPos != 0.0 || between != 0.0 || fill != 0.0) { @@ -1641,7 +1619,7 @@ - (void)myDoVertOrientationCountLayoutWithContext:(MyLayoutContext *)context { int lineidx = i / arrangedCount; if (context->vertGravity == MyGravity_Vert_Stretch) { - MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; if (subviewTraits.heightSizeInner.val == nil || (subviewTraits.heightSizeInner.wrapVal && ![subviewTraits.view isKindOfClass:[MyBaseLayout class]])) { subviewEngine.height += fill; } else { @@ -1689,27 +1667,30 @@ - (void)myDoVertOrientationCountLayoutWithContext:(MyLayoutContext *)context { } } } + + self.arrangedLines = arranges; } - (void)myDoHorzOrientationContentLayoutWithContext:(MyLayoutContext *)context { - MyFlowLayoutTraits *layoutTraits = (MyFlowLayoutTraits*)context->layoutViewEngine.currentSizeClass; + MyFlowLayoutTraitsImpl *layoutTraits = context->layoutViewEngine.currentTraits; NSMutableArray *subviewEngines = context->subviewEngines; - MyGravity horzAlignment = [MyViewTraits convertLeadingTrailingGravityFromLeftRightGravity:MYHORZGRAVITY(layoutTraits.arrangedGravity)]; + MyGravity horzAlignment = [MyViewTraitsImpl convertLeadingTrailingGravityFromLeftRightGravity:MYHORZGRAVITY(layoutTraits.arrangedGravity)]; //支持浮动垂直间距。 - CGFloat subviewHeight = [layoutTraits.flexSpace calcMaxMinSubviewSizeForContent:context->selfSize.height paddingStart:&context->paddingTop paddingEnd:&context->paddingBottom space:&context->vertSpace]; + CGFloat subviewHeight = [layoutTraits.flexSpacing calcMaxMinSubviewsSizeForContent:context->selfSize.height paddingStart:&context->paddingTop paddingEnd:&context->paddingBottom spacing:&context->vertSpacing]; CGFloat paddingVert = context->paddingTop + context->paddingBottom; CGFloat paddingHorz = context->paddingLeading + context->paddingTrailing; - CGFloat xPos = context->paddingLeading; - CGFloat yPos = context->paddingTop; + CGFloat nextSubviewLeadingPos = context->paddingLeading; + CGFloat nextSubviewTopPos = context->paddingTop; CGFloat lineMaxWidth = 0.0; //某一列的最宽值。 CGFloat lineMaxHeight = 0.0; //某一列的最高值 CGFloat maxLayoutHeight = 0.0; //所有列的最宽行 + CGFloat maxLayoutWidth = 0.0; //limitedSelfHeight是用来限制子视图换行的高度,默认是selfSize.height //但是一种特殊情况就是布局视图高度自适应,但是设置了最高高度的情况。 @@ -1724,7 +1705,7 @@ - (void)myDoHorzOrientationContentLayoutWithContext:(MyLayoutContext *)context { if (layoutTraits.autoArrange) { //计算出每个子视图的宽度。 for (MyLayoutEngine *subviewEngine in subviewEngines) { - MyViewTraits *subviewTraits = (MyViewTraits *)subviewEngine.currentSizeClass; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; #ifdef DEBUG //约束异常:水平流式布局设置autoArrange为YES时,子视图不能将weight设置为非0. @@ -1747,17 +1728,21 @@ - (void)myDoHorzOrientationContentLayoutWithContext:(MyLayoutContext *)context { } } - [subviewEngines setArray:[self myGetAutoArrangeSubviews:subviewEngines selfSize:context->selfSize.height - paddingVert space:context->vertSpace]]; + [subviewEngines setArray:[self myGetAutoArrangeSubviews:subviewEngines selfSize:context->selfSize.height - paddingVert spacing:context->vertSpacing]]; } NSMutableIndexSet *lineFirstSubviewIndexSet = [NSMutableIndexSet new]; NSInteger lineIndex = 0; NSInteger itemIndex = 0; CGFloat lineTotalWeight = 0.0; + NSInteger maxLines = layoutTraits.maxLines; + if (!layoutTraits.widthSizeInner.wrapVal) { + maxLines = NSIntegerMax; + } NSInteger i = 0; for (; i < subviewEngines.count; i++) { MyLayoutEngine *subviewEngine = subviewEngines[i]; - MyViewTraits *subviewTraits = (MyViewTraits *)subviewEngine.currentSizeClass; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; CGFloat topSpacing = subviewTraits.topPosInner.measure; CGFloat leadingSpacing = subviewTraits.leadingPosInner.measure; @@ -1797,9 +1782,9 @@ - (void)myDoHorzOrientationContentLayoutWithContext:(MyLayoutContext *)context { subviewEngine.height = 0.0; } else { //如果超过了布局尺寸,则表示当前的剩余空间为0了,所以就按新的一行来算。。 - CGFloat lineSpareHeight = context->selfSize.height - context->paddingBottom - yPos - topSpacing - bottomSpacing; + CGFloat lineSpareHeight = context->selfSize.height - context->paddingBottom - nextSubviewTopPos - topSpacing - bottomSpacing; if (itemIndex != 0) { - lineSpareHeight -= context->vertSpace; + lineSpareHeight -= context->vertSpacing; } if (_myCGFloatLessOrEqual(lineSpareHeight, 0.0)) { lineSpareHeight = context->selfSize.height - paddingVert; @@ -1818,9 +1803,9 @@ - (void)myDoHorzOrientationContentLayoutWithContext:(MyLayoutContext *)context { } //计算yPos的值加上topSpacing + subviewEngine.height + bottomSpacing的值要小于整体的高度。 - CGFloat place = yPos + topSpacing + subviewEngine.height + bottomSpacing; + CGFloat place = nextSubviewTopPos + topSpacing + subviewEngine.height + bottomSpacing; if (itemIndex != 0) { - place += context->vertSpace; + place += context->vertSpacing; } place += context->paddingBottom; @@ -1831,9 +1816,9 @@ - (void)myDoHorzOrientationContentLayoutWithContext:(MyLayoutContext *)context { [lineFirstSubviewIndexSet addIndex:i - itemIndex]; //拉伸以及调整行内子视图的高度。 - [self myHorzLayoutAdjustSingleline:lineIndex lineSpareHeight:context->selfSize.height - context->paddingBottom - yPos lineTotalWeight:lineTotalWeight startItemIndex:i - itemIndex itemCount:itemIndex withContext:context]; + [self myHorzLayoutAdjustSingleline:lineIndex lineSpareHeight:context->selfSize.height - context->paddingBottom - nextSubviewTopPos lineTotalWeight:lineTotalWeight startItemIndex:i - itemIndex itemCount:itemIndex withContext:context]; - yPos = context->paddingTop; + nextSubviewTopPos = context->paddingTop; //计算单独的sbv的高度是否大于整体的高度。如果大于则缩小高度。 if (_myCGFloatGreat(topSpacing + bottomSpacing + subviewEngine.height, context->selfSize.height - paddingVert)) { @@ -1846,11 +1831,11 @@ - (void)myDoHorzOrientationContentLayoutWithContext:(MyLayoutContext *)context { } if (itemIndex != 0) { - yPos += context->vertSpace; + nextSubviewTopPos += context->vertSpacing; } - subviewEngine.top = yPos + topSpacing; - yPos += topSpacing + subviewEngine.height + bottomSpacing; + subviewEngine.top = nextSubviewTopPos + topSpacing; + nextSubviewTopPos += topSpacing + subviewEngine.height + bottomSpacing; if (layoutTraits.isFlex && subviewTraits.weight != 0) { lineTotalWeight += subviewTraits.weight; @@ -1860,28 +1845,30 @@ - (void)myDoHorzOrientationContentLayoutWithContext:(MyLayoutContext *)context { } //最后一行的行首索引 - [lineFirstSubviewIndexSet addIndex:i - itemIndex]; + if (subviewEngines.count > 0) { + [lineFirstSubviewIndexSet addIndex:i - itemIndex]; + } //在高度为自适应时,如果没有设置最大高度限制,那么就一定是单行,因此高度就是子视图的总和。 //如果设置了最大高度限制时,那就要区分最后一行是单行还是多行,所以我们取限高和当前计算出的高度的最小值,并且再取selfSize.height和前面比较结果的最大值。 if (layoutTraits.heightSizeInner.wrapVal) { if (limitedSelfHeight == CGFLOAT_MAX) { - context->selfSize.height = _myCGFloatMax(yPos + context->paddingBottom, [self myGetBoundLimitMeasure:layoutTraits.heightSizeInner.uBoundValInner subview:self anchorType:layoutTraits.heightSizeInner.anchorType subviewSize:context->selfSize selfLayoutSize:self.superview.bounds.size isUBound:NO]); + context->selfSize.height = _myCGFloatMax(nextSubviewTopPos + context->paddingBottom, [self myGetBoundLimitMeasure:layoutTraits.heightSizeInner.uBoundValInner subview:self anchorType:layoutTraits.heightSizeInner.anchorType subviewSize:context->selfSize selfLayoutSize:self.superview.bounds.size isUBound:NO]); } else { - context->selfSize.height = _myCGFloatMax(_myCGFloatMin(yPos + context->paddingBottom, limitedSelfHeight), context->selfSize.height); + context->selfSize.height = _myCGFloatMax(_myCGFloatMin(nextSubviewTopPos + context->paddingBottom, limitedSelfHeight), context->selfSize.height); } } - [self myHorzLayoutAdjustSingleline:lineIndex lineSpareHeight:context->selfSize.height - context->paddingBottom - yPos lineTotalWeight:lineTotalWeight startItemIndex:i - itemIndex itemCount:itemIndex withContext:context]; + [self myHorzLayoutAdjustSingleline:lineIndex lineSpareHeight:context->selfSize.height - context->paddingBottom - nextSubviewTopPos lineTotalWeight:lineTotalWeight startItemIndex:i - itemIndex itemCount:itemIndex withContext:context]; - yPos = context->paddingTop; + nextSubviewTopPos = context->paddingTop; lineIndex = 0; //行的索引。 itemIndex = 0; //行内的子视图索引 NSInteger oldLineFirstIndex = 0; i = 0; for (; i < subviewEngines.count; i++) { MyLayoutEngine *subviewEngine = subviewEngines[i]; - MyViewTraits *subviewTraits = (MyViewTraits *)subviewEngine.currentSizeClass; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; CGFloat topSpacing = subviewTraits.topPosInner.measure; CGFloat leadingSpacing = subviewTraits.leadingPosInner.measure; @@ -1896,9 +1883,9 @@ - (void)myDoHorzOrientationContentLayoutWithContext:(MyLayoutContext *)context { } //计算yPos的值加上topSpacing + subviewEngine.height + bottomSpacing 的值要小于整体的高度。 - maxLayoutHeight = yPos + topSpacing + subviewEngine.height + bottomSpacing; + maxLayoutHeight = nextSubviewTopPos + topSpacing + subviewEngine.height + bottomSpacing; if (itemIndex != 0) { - maxLayoutHeight += context->vertSpace; + maxLayoutHeight += context->vertSpacing; } maxLayoutHeight += context->paddingBottom; @@ -1906,9 +1893,13 @@ - (void)myDoHorzOrientationContentLayoutWithContext:(MyLayoutContext *)context { if (oldLineFirstIndex != lineFirstIndex) { oldLineFirstIndex = lineFirstIndex; - yPos = context->paddingTop; - xPos += context->horzSpace; - xPos += lineMaxWidth; + nextSubviewTopPos = context->paddingTop; + nextSubviewLeadingPos += lineMaxWidth; + if (lineIndex < maxLines) { + maxLayoutWidth = nextSubviewLeadingPos; + } + nextSubviewLeadingPos += context->horzSpacing; + [self myHorzLayoutCalculateSingleline:lineIndex horzAlignment:horzAlignment lineMaxWidth:lineMaxWidth lineMaxHeight:lineMaxHeight lineTotalShrink:0 startItemIndex:i - itemIndex itemCount:itemIndex withContext:context]; @@ -1919,30 +1910,35 @@ - (void)myDoHorzOrientationContentLayoutWithContext:(MyLayoutContext *)context { } if (itemIndex != 0) { - yPos += context->vertSpace; + nextSubviewTopPos += context->vertSpacing; } - subviewEngine.leading = xPos + leadingSpacing; - subviewEngine.top = yPos + topSpacing; - yPos += topSpacing + subviewEngine.height + bottomSpacing; + subviewEngine.leading = nextSubviewLeadingPos + leadingSpacing; + subviewEngine.top = nextSubviewTopPos + topSpacing; + nextSubviewTopPos += topSpacing + subviewEngine.height + bottomSpacing; if (_myCGFloatLess(lineMaxWidth, leadingSpacing + trailingSpacing + subviewEngine.width)) { lineMaxWidth = leadingSpacing + trailingSpacing + subviewEngine.width; } - if (_myCGFloatLess(lineMaxHeight, (yPos - context->paddingTop))) { - lineMaxHeight = (yPos - context->paddingTop); + if (_myCGFloatLess(lineMaxHeight, (nextSubviewTopPos - context->paddingTop))) { + lineMaxHeight = (nextSubviewTopPos - context->paddingTop); } itemIndex++; } - xPos += lineMaxWidth + context->paddingTrailing; + nextSubviewLeadingPos += lineMaxWidth + context->paddingTrailing; + if (lineIndex < maxLines) { + maxLayoutWidth = nextSubviewLeadingPos; + } else { + maxLayoutWidth += context->paddingTrailing; + } if (layoutTraits.heightSizeInner.wrapVal) { context->selfSize.height = [self myValidMeasure:layoutTraits.heightSizeInner subview:self calcSize:maxLayoutHeight subviewSize:context->selfSize selfLayoutSize:self.superview.bounds.size]; } if (layoutTraits.widthSizeInner.wrapVal) { - context->selfSize.width = [self myValidMeasure:layoutTraits.widthSizeInner subview:self calcSize:xPos subviewSize:context->selfSize selfLayoutSize:self.superview.bounds.size]; + context->selfSize.width = [self myValidMeasure:layoutTraits.widthSizeInner subview:self calcSize:maxLayoutWidth subviewSize:context->selfSize selfLayoutSize:self.superview.bounds.size]; } NSInteger arranges = lineFirstSubviewIndexSet.count; //根据flex规则:如果只有一列则整个宽度都作为子视图的拉伸和停靠区域。 @@ -1953,23 +1949,25 @@ - (void)myDoHorzOrientationContentLayoutWithContext:(MyLayoutContext *)context { [self myHorzLayoutCalculateSingleline:lineIndex horzAlignment:horzAlignment lineMaxWidth:lineMaxWidth lineMaxHeight:lineMaxHeight lineTotalShrink:0 startItemIndex:i - itemIndex itemCount:itemIndex withContext:context]; //整体的停靠 - if (context->horzGravity != MyGravity_None && context->selfSize.width != xPos) { + if (context->horzGravity != MyGravity_None && context->selfSize.width != maxLayoutWidth) { //根据flex标准:只有在多行下horzGravity才有意义。非flex标准则不受这个条件约束。 if (arranges > 1 || !layoutTraits.isFlex) { CGFloat addXPos = 0.0; CGFloat fill = 0.0; CGFloat between = 0.0; + CGFloat remainingWidth = context->selfSize.width - maxLayoutWidth; + if (arranges <= 1 && context->horzGravity == MyGravity_Horz_Around) { context->horzGravity = MyGravity_Horz_Center; } if (context->horzGravity == MyGravity_Horz_Center) { - addXPos = (context->selfSize.width - xPos) / 2; + addXPos = remainingWidth / 2; } else if (context->horzGravity == MyGravity_Horz_Trailing) { - addXPos = context->selfSize.width - xPos; + addXPos = remainingWidth; } else if (context->horzGravity == MyGravity_Horz_Fill || context->horzGravity == MyGravity_Horz_Stretch) { if (arranges > 0) { - fill = (context->selfSize.width - xPos) / arranges; + fill = remainingWidth / arranges; } //满足flex规则:如果剩余的空间是负数,该值等效于'flex-start' if (fill < 0.0 && context->horzGravity == MyGravity_Horz_Stretch) { @@ -1977,12 +1975,12 @@ - (void)myDoHorzOrientationContentLayoutWithContext:(MyLayoutContext *)context { } } else if (context->horzGravity == MyGravity_Horz_Between) { if (arranges > 1) { - between = (context->selfSize.width - xPos) / (arranges - 1); + between = remainingWidth / (arranges - 1); } } else if (context->horzGravity == MyGravity_Horz_Around) { - between = (context->selfSize.width - xPos) / arranges; + between = remainingWidth / arranges; } else if (context->horzGravity == MyGravity_Horz_Among) { - between = (context->selfSize.width - xPos) / (arranges + 1); + between = remainingWidth / (arranges + 1); } if (addXPos != 0.0 || between != 0.0 || fill != 0.0) { @@ -2001,12 +1999,12 @@ - (void)myDoHorzOrientationContentLayoutWithContext:(MyLayoutContext *)context { } if (context->horzGravity == MyGravity_Horz_Stretch) { - MyViewTraits *subviewTraits = (MyViewTraits *)subviewEngine.currentSizeClass; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; if (subviewTraits.widthSizeInner.val == nil || (subviewTraits.widthSizeInner.wrapVal && ![subviewTraits.view isKindOfClass:[MyBaseLayout class]])) { subviewEngine.width += fill; } else { //因为每行都增加了fill。所以如果有行内对齐则需要这里调整。 - MyGravity subviewHorzAlignment = [MyViewTraits convertLeadingTrailingGravityFromLeftRightGravity:MYHORZGRAVITY(subviewTraits.alignment)]; + MyGravity subviewHorzAlignment = [MyViewTraitsImpl convertLeadingTrailingGravityFromLeftRightGravity:MYHORZGRAVITY(subviewTraits.alignment)]; if (subviewHorzAlignment == MyGravity_None) { subviewHorzAlignment = horzAlignment; } @@ -2034,25 +2032,27 @@ - (void)myDoHorzOrientationContentLayoutWithContext:(MyLayoutContext *)context { } } } + + self.arrangedLines = arranges; } - (void)myDoHorzOrientationCountLayoutWithContext:(MyLayoutContext *)context { - MyFlowLayoutTraits *layoutTraits = (MyFlowLayoutTraits*)context->layoutViewEngine.currentSizeClass; + MyFlowLayoutTraitsImpl *layoutTraits = context->layoutViewEngine.currentTraits; NSMutableArray *subviewEngines = context->subviewEngines; BOOL autoArrange = layoutTraits.autoArrange; NSInteger arrangedCount = layoutTraits.arrangedCount; - MyGravity horzAlignment = [MyViewTraits convertLeadingTrailingGravityFromLeftRightGravity:MYHORZGRAVITY(layoutTraits.arrangedGravity)]; + MyGravity horzAlignment = [MyViewTraitsImpl convertLeadingTrailingGravityFromLeftRightGravity:MYHORZGRAVITY(layoutTraits.arrangedGravity)]; - CGFloat subviewHeight = [layoutTraits.flexSpace calcMaxMinSubviewSize:context->selfSize.height arrangedCount:arrangedCount paddingStart:&context->paddingTop paddingEnd:&context->paddingBottom space:&context->vertSpace]; + CGFloat subviewHeight = [layoutTraits.flexSpacing calcMaxMinSubviewsSize:context->selfSize.height arrangedCount:arrangedCount paddingStart:&context->paddingTop paddingEnd:&context->paddingBottom spacing:&context->vertSpacing]; CGFloat paddingHorz = context->paddingLeading + context->paddingTrailing; CGFloat paddingVert = context->paddingTop + context->paddingBottom; - CGFloat xPos = context->paddingLeading; - CGFloat yPos = context->paddingTop; + CGFloat nextSubviewLeadingPos = context->paddingLeading; + CGFloat nextSubviewTopPos = context->paddingTop; CGFloat lineMaxWidth = 0.0; //每一列的最大宽度 CGFloat lineMaxHeight = 0.0; //每一列的最大高度 CGFloat maxLayoutHeight = 0.0; //全列的最大高度 @@ -2079,25 +2079,25 @@ - (void)myDoHorzOrientationCountLayoutWithContext:(MyLayoutContext *)context { if (layoutTraits.heightSizeInner.wrapVal) { isVertPaging = YES; if (isPagingScroll) { - pagingItemHeight = (CGRectGetHeight(self.superview.bounds) - paddingVert - (arrangedCount - 1) * context->vertSpace) / arrangedCount; + pagingItemHeight = (CGRectGetHeight(self.superview.bounds) - paddingVert - (arrangedCount - 1) * context->vertSpacing) / arrangedCount; } else { - pagingItemHeight = (CGRectGetHeight(self.superview.bounds) - context->paddingTop - arrangedCount * context->vertSpace) / arrangedCount; + pagingItemHeight = (CGRectGetHeight(self.superview.bounds) - context->paddingTop - arrangedCount * context->vertSpacing) / arrangedCount; } //如果是水平滚动则如果布局不是高度自适应才让条目的高度生效。 if (!layoutTraits.widthSizeInner.wrapVal) { - pagingItemWidth = (context->selfSize.width - paddingHorz - (cols - 1) * context->horzSpace) / cols; + pagingItemWidth = (context->selfSize.width - paddingHorz - (cols - 1) * context->horzSpacing) / cols; } } else { isHorzPaging = YES; - pagingItemHeight = (context->selfSize.height - paddingVert - (arrangedCount - 1) * context->vertSpace) / arrangedCount; + pagingItemHeight = (context->selfSize.height - paddingVert - (arrangedCount - 1) * context->vertSpacing) / arrangedCount; //分页滚动时和非分页滚动时的宽度计算是不一样的。 if (isPagingScroll) { - pagingItemWidth = (CGRectGetWidth(self.superview.bounds) - paddingHorz - (cols - 1) * context->horzSpace) / cols; + pagingItemWidth = (CGRectGetWidth(self.superview.bounds) - paddingHorz - (cols - 1) * context->horzSpacing) / cols; } else { if ([self.superview isKindOfClass:[UIScrollView class]]) { - pagingItemWidth = (CGRectGetWidth(self.superview.bounds) - context->paddingLeading - cols * context->horzSpace) / cols; + pagingItemWidth = (CGRectGetWidth(self.superview.bounds) - context->paddingLeading - cols * context->horzSpacing) / cols; } else { - pagingItemWidth = (context->selfSize.width - paddingHorz - (cols - 1) * context->horzSpace) / cols; + pagingItemWidth = (context->selfSize.width - paddingHorz - (cols - 1) * context->horzSpacing) / cols; } } } @@ -2110,7 +2110,7 @@ - (void)myDoHorzOrientationCountLayoutWithContext:(MyLayoutContext *)context { //平均高度,当布局的gravity设置为Vert_Fill时指定这个平均高度值。 CGFloat averageHeight = 0.0; if (context->vertGravity == MyGravity_Vert_Fill) { - averageHeight = (context->selfSize.height - paddingVert - (arrangedCount - 1) * context->vertSpace) / arrangedCount; + averageHeight = (context->selfSize.height - paddingVert - (arrangedCount - 1) * context->vertSpacing) / arrangedCount; } //得到行数 NSInteger arranges = floor((subviewEngines.count + arrangedCount - 1.0) / arrangedCount); @@ -2127,9 +2127,13 @@ - (void)myDoHorzOrientationCountLayoutWithContext:(MyLayoutContext *)context { NSInteger i = 0; NSInteger itemIndex = 0; NSInteger lineIndex = 0; //行索引 + NSInteger maxLines = layoutTraits.maxLines; + if (!layoutTraits.widthSizeInner.wrapVal) { + maxLines = NSIntegerMax; + } for (; i < subviewEngines.count; i++) { MyLayoutEngine *subviewEngine = subviewEngines[i]; - MyViewTraits *subviewTraits = (MyViewTraits *)subviewEngine.currentSizeClass; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; CGFloat topSpacing = subviewTraits.topPosInner.measure; CGFloat bottomSpacing = subviewTraits.bottomPosInner.measure; @@ -2205,7 +2209,7 @@ - (void)myDoHorzOrientationCountLayoutWithContext:(MyLayoutContext *)context { lineTotalFixedHeight += subviewEngine.height; lineTotalFixedHeight += topSpacing + bottomSpacing; if (itemIndex < arrangedCount) { - lineTotalFixedHeight += context->vertSpace; + lineTotalFixedHeight += context->vertSpacing; } if (subviewTraits.weight != 0) { lineTotalWeight += subviewTraits.weight; @@ -2218,7 +2222,7 @@ - (void)myDoHorzOrientationCountLayoutWithContext:(MyLayoutContext *)context { //最后一行。 if (arranges > 0) { if (itemIndex < arrangedCount) { - lineTotalFixedHeight -= context->vertSpace; + lineTotalFixedHeight -= context->vertSpacing; } lineTotalFixedHeights[lineIndex] = lineTotalFixedHeight; lineTotalWeights[lineIndex] = lineTotalWeight; @@ -2277,12 +2281,12 @@ - (void)myDoHorzOrientationCountLayoutWithContext:(MyLayoutContext *)context { i = 0; for (; i < subviewEngines.count; i++) { MyLayoutEngine *subviewEngine = subviewEngines[i]; - MyViewTraits *subviewTraits = (MyViewTraits *)subviewEngine.currentSizeClass; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; if (itemIndex >= arrangedCount) { itemIndex = 0; - xPos += context->horzSpace; - xPos += lineMaxWidth; + nextSubviewLeadingPos += context->horzSpacing; + nextSubviewLeadingPos += lineMaxWidth; [self myHorzLayoutCalculateSingleline:lineIndex horzAlignment:horzAlignment lineMaxWidth:lineMaxWidth lineMaxHeight:lineMaxHeight lineTotalShrink:lineTotalShrink startItemIndex:i - arrangedCount itemCount:arrangedCount withContext:context]; @@ -2294,7 +2298,7 @@ - (void)myDoHorzOrientationCountLayoutWithContext:(MyLayoutContext *)context { if (!isPagingScroll) { pageHeight -= context->paddingTop; } - xPos = context->paddingLeading; + nextSubviewLeadingPos = context->paddingLeading; } } @@ -2302,13 +2306,13 @@ - (void)myDoHorzOrientationCountLayoutWithContext:(MyLayoutContext *)context { //如果是分页滚动则要多添加垂直间距。 if (i % layoutTraits.pagedCount == 0) { if (isPagingScroll) { - xPos -= context->horzSpace; - xPos += paddingHorz; + nextSubviewLeadingPos -= context->horzSpacing; + nextSubviewLeadingPos += paddingHorz; } } } - yPos = context->paddingTop + pageHeight; + nextSubviewTopPos = context->paddingTop + pageHeight; lineMaxWidth = 0.0; lineMaxHeight = 0.0; @@ -2339,56 +2343,56 @@ - (void)myDoHorzOrientationCountLayoutWithContext:(MyLayoutContext *)context { } //找到的minNextPointIndex中的 - xPos = minPoint.x; - yPos = minPoint.y; + nextSubviewLeadingPos = minPoint.x; + nextSubviewTopPos = minPoint.y; - minPoint.x = minPoint.x + leadingSpacing + subviewEngine.width + trailingSpacing + context->horzSpace; + minPoint.x = minPoint.x + leadingSpacing + subviewEngine.width + trailingSpacing + context->horzSpacing; nextPointOfCols[minNextPointIndex] = [NSValue valueWithCGPoint:minPoint]; if (minNextPointIndex + 1 <= arrangedCount - 1) { minPoint = nextPointOfCols[minNextPointIndex + 1].CGPointValue; - minPoint.y = yPos + topSpacing + subviewEngine.height + bottomSpacing + context->vertSpace; + minPoint.y = nextSubviewTopPos + topSpacing + subviewEngine.height + bottomSpacing + context->vertSpacing; nextPointOfCols[minNextPointIndex + 1] = [NSValue valueWithCGPoint:minPoint]; } - if (_myCGFloatLess(maxLayoutWidth, xPos + leadingSpacing + subviewEngine.width + trailingSpacing)) { - maxLayoutWidth = xPos + leadingSpacing + subviewEngine.width + trailingSpacing; + if (lineIndex < maxLines && _myCGFloatLess(maxLayoutWidth, nextSubviewLeadingPos + leadingSpacing + subviewEngine.width + trailingSpacing)) { + maxLayoutWidth = nextSubviewLeadingPos + leadingSpacing + subviewEngine.width + trailingSpacing; } } else if (horzAlignment == MyGravity_Horz_Between) { //当列是紧凑排列时需要特殊处理当前的水平位置。 //第0列特殊处理。 if (i - arrangedCount < 0) { - xPos = context->paddingLeading; + nextSubviewLeadingPos = context->paddingLeading; } else { //取前一列的对应的行的子视图。 MyLayoutEngine *prevColSubviewEngine = subviewEngines[i - arrangedCount]; - MyViewTraits *prevColSubviewTraits = prevColSubviewEngine.currentSizeClass; + MyViewTraitsImpl *prevColSubviewTraits = prevColSubviewEngine.currentTraits; //当前子视图的位置等于前一列对应行的最大x的值 + 前面对应行的尾部间距 + 子视图之间的列间距。 - xPos = CGRectGetMaxX(prevColSubviewEngine.frame) + prevColSubviewTraits.trailingPosInner.measure + context->horzSpace; + nextSubviewLeadingPos = CGRectGetMaxX(prevColSubviewEngine.frame) + prevColSubviewTraits.trailingPosInner.measure + context->horzSpacing; } - if (_myCGFloatLess(maxLayoutWidth, xPos + leadingSpacing + subviewEngine.width + trailingSpacing)) { - maxLayoutWidth = xPos + leadingSpacing + subviewEngine.width + trailingSpacing; + if (lineIndex < maxLines && _myCGFloatLess(maxLayoutWidth, nextSubviewLeadingPos + leadingSpacing + subviewEngine.width + trailingSpacing)) { + maxLayoutWidth = nextSubviewLeadingPos + leadingSpacing + subviewEngine.width + trailingSpacing; } } else { //正常排列。 //这里的最大其实就是最后一个视图的位置加上最宽的子视图的尺寸。 - if (_myCGFloatLess(maxLayoutWidth, xPos + lineMaxWidth)) { - maxLayoutWidth = xPos + lineMaxWidth; + if (lineIndex < maxLines && _myCGFloatLess(maxLayoutWidth, nextSubviewLeadingPos + lineMaxWidth)) { + maxLayoutWidth = nextSubviewLeadingPos + lineMaxWidth; } } - subviewEngine.leading = xPos + leadingSpacing; - subviewEngine.top = yPos + topSpacing; - yPos += topSpacing + subviewEngine.height + bottomSpacing; + subviewEngine.leading = nextSubviewLeadingPos + leadingSpacing; + subviewEngine.top = nextSubviewTopPos + topSpacing; + nextSubviewTopPos += topSpacing + subviewEngine.height + bottomSpacing; - if (_myCGFloatLess(lineMaxHeight, (yPos - context->paddingTop))) { - lineMaxHeight = yPos - context->paddingTop; + if (_myCGFloatLess(lineMaxHeight, (nextSubviewTopPos - context->paddingTop))) { + lineMaxHeight = nextSubviewTopPos - context->paddingTop; } - if (_myCGFloatLess(maxLayoutHeight, yPos)) { - maxLayoutHeight = yPos; + if (_myCGFloatLess(maxLayoutHeight, nextSubviewTopPos)) { + maxLayoutHeight = nextSubviewTopPos; } //不是最后一行以及非自动排列时才添加布局视图设置的行间距。自动排列的情况下上面已经有添加行间距了。 if (itemIndex != (arrangedCount - 1) && !autoArrange) { - yPos += context->vertSpace; + nextSubviewTopPos += context->vertSpacing; } itemIndex++; @@ -2428,18 +2432,19 @@ - (void)myDoHorzOrientationCountLayoutWithContext:(MyLayoutContext *)context { CGFloat addXPos = 0.0; CGFloat between = 0.0; CGFloat fill = 0.0; + CGFloat remainingWidth = context->selfSize.width - maxLayoutWidth; if (arranges <= 1 && context->horzGravity == MyGravity_Horz_Around) { context->horzGravity = MyGravity_Horz_Center; } if (context->horzGravity == MyGravity_Horz_Center) { - addXPos = (context->selfSize.width - maxLayoutWidth) / 2; + addXPos = remainingWidth / 2; } else if (context->horzGravity == MyGravity_Horz_Trailing) { - addXPos = context->selfSize.width - maxLayoutWidth; + addXPos = remainingWidth; } else if (context->horzGravity == MyGravity_Horz_Fill || context->horzGravity == MyGravity_Horz_Stretch) { if (arranges > 0) { - fill = (context->selfSize.width - maxLayoutWidth) / arranges; + fill = remainingWidth / arranges; } //满足flex规则:如果剩余的空间是负数,该值等效于'flex-start' if (fill < 0.0 && context->horzGravity == MyGravity_Horz_Stretch) { @@ -2447,12 +2452,12 @@ - (void)myDoHorzOrientationCountLayoutWithContext:(MyLayoutContext *)context { } } else if (context->horzGravity == MyGravity_Horz_Between) { if (arranges > 1) { - between = (context->selfSize.width - maxLayoutWidth) / (arranges - 1); + between = remainingWidth / (arranges - 1); } } else if (context->horzGravity == MyGravity_Horz_Around) { - between = (context->selfSize.width - maxLayoutWidth) / arranges; + between = remainingWidth / arranges; } else if (context->horzGravity == MyGravity_Horz_Among) { - between = (context->selfSize.width - maxLayoutWidth) / (arranges + 1); + between = remainingWidth / (arranges + 1); } if (addXPos != 0.0 || between != 0.0 || fill != 0.0) { @@ -2462,12 +2467,12 @@ - (void)myDoHorzOrientationCountLayoutWithContext:(MyLayoutContext *)context { int lineidx = i / arrangedCount; if (context->horzGravity == MyGravity_Horz_Stretch) { - MyViewTraits *subviewTraits = (MyViewTraits *)subviewEngine.currentSizeClass; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; if (subviewTraits.widthSizeInner.val == nil || (subviewTraits.widthSizeInner.wrapVal && ![subviewTraits.view isKindOfClass:[MyBaseLayout class]])) { subviewEngine.width += fill; } else { //因为每行都增加了fill。所以如果有行内对齐则需要这里调整。 - MyGravity subviewHorzAlignment = [MyViewTraits convertLeadingTrailingGravityFromLeftRightGravity:MYHORZGRAVITY(subviewTraits.alignment)]; + MyGravity subviewHorzAlignment = [MyViewTraitsImpl convertLeadingTrailingGravityFromLeftRightGravity:MYHORZGRAVITY(subviewTraits.alignment)]; if (subviewHorzAlignment == MyGravity_None) { subviewHorzAlignment = horzAlignment; } @@ -2509,6 +2514,8 @@ - (void)myDoHorzOrientationCountLayoutWithContext:(MyLayoutContext *)context { } } } + + self.arrangedLines = arranges; } @end diff --git a/MyLayout/Lib/MyFrameLayout.h b/MyLayout/Lib/MyFrameLayout.h index 9990227..7c6679a 100644 --- a/MyLayout/Lib/MyFrameLayout.h +++ b/MyLayout/Lib/MyFrameLayout.h @@ -11,6 +11,6 @@ /** 框架布局是一种里面的子视图停靠在父视图特定方位并且可以重叠的布局视图。框架布局里面的子视图的布局位置和添加的顺序无关,框架布局中的所有子视图的约束依赖都只是针对于和父布局视图的。框架布局是一种特殊的相对布局,因此如果某些布局里面的子视图只依赖于父视图的边界时则可以用框架布局来代替,从而加快布局的速度。 */ -@interface MyFrameLayout : MyBaseLayout +@interface MyFrameLayout : MyBaseLayout> @end diff --git a/MyLayout/Lib/MyFrameLayout.m b/MyLayout/Lib/MyFrameLayout.m index 2a4cfde..db1b03c 100644 --- a/MyLayout/Lib/MyFrameLayout.m +++ b/MyLayout/Lib/MyFrameLayout.m @@ -16,17 +16,8 @@ @implementation MyFrameLayout - (CGSize)calcLayoutSize:(CGSize)size subviewEngines:(NSMutableArray *)subviewEngines context:(MyLayoutContext *)context { [super calcLayoutSize:size subviewEngines:subviewEngines context:context]; - MyFrameLayoutTraits *layoutTraits = (MyFrameLayoutTraits *)context->layoutViewEngine.currentSizeClass; - context->paddingTop = layoutTraits.myLayoutPaddingTop; - context->paddingBottom = layoutTraits.myLayoutPaddingBottom; - context->paddingLeading = layoutTraits.myLayoutPaddingLeading; - context->paddingTrailing = layoutTraits.myLayoutPaddingTrailing; - context->vertGravity = MYVERTGRAVITY(layoutTraits.gravity); - context->horzGravity = [MyViewTraits convertLeadingTrailingGravityFromLeftRightGravity:MYHORZGRAVITY(layoutTraits.gravity)]; - if (context->subviewEngines == nil) { - context->subviewEngines = [layoutTraits filterEngines:subviewEngines]; - } - + MyFrameLayoutTraitsImpl *layoutTraits = context->layoutViewEngine.currentTraits; + CGSize maxWrapSize = CGSizeMake(context->paddingLeading + context->paddingTrailing, context->paddingTop + context->paddingBottom); CGSize *pMaxWrapSize = &maxWrapSize; if (!layoutTraits.heightSizeInner.wrapVal && !layoutTraits.widthSizeInner.wrapVal) { @@ -47,7 +38,7 @@ - (CGSize)calcLayoutSize:(CGSize)size subviewEngines:(NSMutableArrayhorzGravity != MyGravity_Horz_Fill) || (layoutTraits.heightSizeInner.wrapVal && context->vertGravity != MyGravity_Vert_Fill)) { for (MyLayoutEngine *subviewEngine in context->subviewEngines) { - MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; //只有子视图的尺寸或者位置依赖父视图的情况下才需要重新计算位置和尺寸。 if ((subviewTraits.trailingPosInner.val != nil) || @@ -65,8 +56,8 @@ - (CGSize)calcLayoutSize:(CGSize)size subviewEngines:(NSMutableArray类型, extern NSString *const kMyGridCols; //NSArray类型,表示里面的值是列子栅格数组.数组的元素是一个个子栅格字典对象 extern NSString *const kMyGridSize; //NSString类型或者NSNumber类型。设置栅格的尺寸,可以为特定的值vMyGridSizeWrap或者vMyGridSizeFill,也可以为某个具体的数字比如20.0, 还可以为百分比数字比如:@"20%" 或者@"-20%"。 extern NSString *const kMyGridPadding; //NSString类型,设置栅格的内边距,对应MyGrid的padding属性,具体的值的格式为:@"{上,左,下,右}" -extern NSString *const kMyGridSpace; //NSNumber类型,栅格的内子栅格的间距,对应MyGrid的subviewSpace属性。 +extern NSString *const kMyGridSpacing; //NSNumber类型,栅格的内子栅格的间距,对应MyGrid的subviewSpacing属性。 extern NSString *const kMyGridGravity; //NSString类型,栅格的停靠属性,对应MyGrid的gravity属性,具体的值请参考下面的定义,比如:@"top|left" extern NSString *const kMyGridPlaceholder; //NSNumber类型,栅格的占位属性,对应MyGrid的placeholder 属性,具体的值设置为YES or NO extern NSString *const kMyGridAnchor; //NSNumber类型,栅格的锚点属性,对应MyGrid的anchor属性,具体的值设置为YES or NO @@ -100,7 +100,7 @@ extern NSString *const vMyGridGravityHeightFill; //对应MyGravity_Vert_Fill /** 栅格内子栅格之间的间距。 */ -@property (nonatomic, assign) CGFloat subviewSpace; +@property (nonatomic, assign) CGFloat subviewSpacing; /** 栅格内子栅格或者叶子栅格内视图的四周内边距。 diff --git a/MyLayout/Lib/MyGridLayout.h b/MyLayout/Lib/MyGridLayout.h index 5d77612..7724839 100644 --- a/MyLayout/Lib/MyGridLayout.h +++ b/MyLayout/Lib/MyGridLayout.h @@ -16,7 +16,7 @@ 所谓栅格其实就是一个矩形区域,我们知道一个视图其实就是一个矩形区域,而子视图的frame属性其实就是父视图中的某个特定的子区域部分。既然子视图最终占据的是父视图的某个子矩形区域。那么我们也可以先将一个矩形区域按照某种规则分解为多个子矩形区域,然后再将子视图填充到对应的子矩形区域中去,这就是栅格布局的实现思想。 */ -@interface MyGridLayout : MyBaseLayout +@interface MyGridLayout : MyBaseLayout > /** 建立一个栅格模板,注意栅格模板不能添加到栅格布局中去,而应该采用clone的方法来将克隆的栅格添加到栅格布局中去 @@ -158,7 +158,7 @@ {"rows | cols":[ {"size":100 | "100%" | "-20%" | "wrap" | size:"fill", "padding":"{10,10,10,10}", - "space":10.0, + "spacing":10.0, "gravity":@"top|bottom|left|right|centerX|centerY|width|height", "top-borderline":{"color":"#AAAAAA","thick":1.0, "head":1.0, "tail":1.0, "offset":1}, "bottom-borderline":{}, diff --git a/MyLayout/Lib/MyGridLayout.m b/MyLayout/Lib/MyGridLayout.m index eff6fed..44e9d07 100644 --- a/MyLayout/Lib/MyGridLayout.m +++ b/MyLayout/Lib/MyGridLayout.m @@ -17,7 +17,7 @@ NSString *const kMyGridCols = @"cols"; NSString *const kMyGridSize = @"size"; NSString *const kMyGridPadding = @"padding"; -NSString *const kMyGridSpace = @"space"; +NSString *const kMyGridSpacing = @"spacing"; NSString *const kMyGridGravity = @"gravity"; NSString *const kMyGridPlaceholder = @"placeholder"; NSString *const kMyGridAnchor = @"anchor"; @@ -78,7 +78,7 @@ + (instancetype)viewGroup:(NSArray *)viewGroup actionData:(id)actionData { @interface MyGridLayout () -@property (nonatomic, weak) MyGridLayoutTraits *lastSizeClass; +@property (nonatomic, weak) MyGridLayoutTraitsImpl *lastSizeClass; //保存某个标签下对应的 视图组和数据 数组。字典。一个标签下可以有N个视图组。 @property (nonatomic, strong) NSMutableDictionary *> *tagsDict; //这个锁并不是用于同步处理,而是用来区分是外部主动调用删除子视图还是类内部删除子视图。因为二者都会触发willRemoveSubview方法的调用,这个标志 @@ -110,7 +110,7 @@ - (void)removeGrids { } - (void)removeGridsIn:(MySizeClass)sizeClass { - id gridNode = (id)[self fetchLayoutSizeClass:sizeClass]; + id gridNode = (id)[self fetchLayoutTraitsInSizeClass:sizeClass]; [gridNode.subGrids removeAllObjects]; gridNode.subGridsType = MySubGridsType_Unknown; [self setNeedsLayout]; @@ -127,13 +127,13 @@ - (void)removeGridsIn:(MySizeClass)sizeClass { NSAssert([gridNode gridLayoutView] == self, @"oops! 非栅格布局中的栅格"); #endif - NSMutableArray *retSbs = [NSMutableArray new]; + NSMutableArray *retSubviews = [NSMutableArray new]; for (UIView *subview in self.subviews) { if (!subview.isHidden && CGRectContainsRect(gridNode.gridRect, subview.frame)) { - [retSbs addObject:subview]; + [retSubviews addObject:subview]; } } - return retSbs; + return retSubviews; } - (void)addViewGroup:(NSArray *)viewGroup withActionData:(id)actionData to:(NSInteger)gridTag { @@ -157,9 +157,9 @@ - (void)insertViewGroup:(NSArray *)viewGroup withActionData:(id)action } } - for (UIView *sbv in viewGroup) { - if (sbv != (UIView *)[NSNull null]) { - [self addSubview:sbv]; + for (UIView *subview in viewGroup) { + if (subview != (UIView *)[NSNull null]) { + [self addSubview:subview]; } } @@ -189,11 +189,11 @@ - (void)replaceViewGroup:(NSArray *)viewGroup withActionData:(id)actio va.actionData = actionData; if (va.viewGroup != viewGroup) { - for (UIView *sbv in viewGroup) { - NSUInteger oldIndex = [va.viewGroup indexOfObject:sbv]; + for (UIView *subview in viewGroup) { + NSUInteger oldIndex = [va.viewGroup indexOfObject:subview]; if (oldIndex == NSNotFound) { - if (sbv != (UIView *)[NSNull null]) { - [self addSubview:sbv]; + if (subview != (UIView *)[NSNull null]) { + [self addSubview:subview]; } } else { [va.viewGroup removeObjectAtIndex:oldIndex]; @@ -201,9 +201,9 @@ - (void)replaceViewGroup:(NSArray *)viewGroup withActionData:(id)actio } //原来多余的视图删除 - for (UIView *sbv in va.viewGroup) { - if (sbv != (UIView *)[NSNull null]) { - [sbv removeFromSuperview]; + for (UIView *subview in va.viewGroup) { + if (subview != (UIView *)[NSNull null]) { + [subview removeFromSuperview]; } } //将新的视图组给替换掉。 @@ -261,9 +261,9 @@ - (void)removeViewGroupAtIndex:(NSUInteger)index from:(NSInteger)gridTag { MyViewGroupAndActionData *va = viewGroupArray[index]; self.tagsDictLock = YES; - for (UIView *sbv in va.viewGroup) { - if (sbv != (UIView *)[NSNull null]) { - [sbv removeFromSuperview]; + for (UIView *subview in va.viewGroup) { + if (subview != (UIView *)[NSNull null]) { + [subview removeFromSuperview]; } } self.tagsDictLock = NO; @@ -288,9 +288,9 @@ - (void)removeViewGroupFrom:(NSInteger)gridTag { if (viewGroupArray != nil) { self.tagsDictLock = YES; for (MyViewGroupAndActionData *va in viewGroupArray) { - for (UIView *sbv in va.viewGroup) { - if (sbv != (UIView *)[NSNull null]) { - [sbv removeFromSuperview]; + for (UIView *subview in va.viewGroup) { + if (subview != (UIView *)[NSNull null]) { + [subview removeFromSuperview]; } } } @@ -368,162 +368,86 @@ - (NSUInteger)viewGroupCountOf:(NSInteger)gridTag { #pragma mark-- MyGrid -- (id)actionData { - return self.myDefaultSizeClass.actionData; -} +_MYLAYOUT_PROPERTY_GRIDLAYOUT_OVERRIDE(id, ActionData, actionData) -- (void)setActionData:(id)actionData { - MyGridLayout *lsc = self.myDefaultSizeClass; - lsc.actionData = actionData; -} //添加行。返回新的栅格。 - (id)addRow:(CGFloat)measure { - MyGridLayout *lsc = self.myDefaultSizeClass; - id node = (id)[lsc addRow:measure]; + MyGridLayoutTraitsImpl *layoutTraits = self.myDefaultTraits; + id node = (id)[layoutTraits addRow:measure]; node.superGrid = self; return node; } //添加列。返回新的栅格。 - (id)addCol:(CGFloat)measure { - MyGridLayout *lsc = self.myDefaultSizeClass; - id node = (id)[lsc addCol:measure]; + MyGridLayoutTraitsImpl *layoutTraits = self.myDefaultTraits; + id node = (id)[layoutTraits addCol:measure]; node.superGrid = self; return node; } - (id)addRowGrid:(id)grid { - MyGridLayout *lsc = self.myDefaultSizeClass; - id node = (id)[lsc addRowGrid:grid]; + MyGridLayoutTraitsImpl *layoutTraits = self.myDefaultTraits; + id node = (id)[layoutTraits addRowGrid:grid]; node.superGrid = self; return node; } - (id)addColGrid:(id)grid { - MyGridLayout *lsc = self.myDefaultSizeClass; - id node = (id)[lsc addColGrid:grid]; + MyGridLayoutTraitsImpl *layoutTraits = self.myDefaultTraits; + id node = (id)[layoutTraits addColGrid:grid]; node.superGrid = self; return node; } - (id)addRowGrid:(id)grid measure:(CGFloat)measure { - MyGridLayout *lsc = self.myDefaultSizeClass; - id node = (id)[lsc addRowGrid:grid measure:measure]; + MyGridLayoutTraitsImpl *layoutTraits = self.myDefaultTraits; + id node = (id)[layoutTraits addRowGrid:grid measure:measure]; node.superGrid = self; return node; } - (id)addColGrid:(id)grid measure:(CGFloat)measure { - MyGridLayout *lsc = self.myDefaultSizeClass; - id node = (id)[lsc addColGrid:grid measure:measure]; + MyGridLayoutTraitsImpl *layoutTraits = self.myDefaultTraits; + id node = (id)[layoutTraits addColGrid:grid measure:measure]; node.superGrid = self; return node; } - (id)cloneGrid { - return [self.myDefaultSizeClass cloneGrid]; + return [self.myDefaultTraits cloneGrid]; } - (void)removeFromSuperGrid { - return [self.myDefaultSizeClass removeFromSuperGrid]; -} - -- (id)superGrid { - return self.myDefaultSizeClass.superGrid; -} - -- (void)setSuperGrid:(id)superGrid { - MyGridLayout *lsc = self.myDefaultSizeClass; - lsc.superGrid = superGrid; + return [self.myDefaultTraits removeFromSuperGrid]; } -- (BOOL)placeholder { - return self.myDefaultSizeClass.placeholder; -} +_MYLAYOUT_PROPERTY_GRIDLAYOUT_OVERRIDE(id, SuperGrid, superGrid) +_MYLAYOUT_PROPERTY_GRIDLAYOUT_OVERRIDE(BOOL, Placeholder, placeholder) +_MYLAYOUT_PROPERTY_GRIDLAYOUT_OVERRIDE(BOOL, Anchor, anchor) +_MYLAYOUT_PROPERTY_GRIDLAYOUT_OVERRIDE(MyGravity, Overlap, overlap) +_MYLAYOUT_PROPERTY_GRIDLAYOUT_OVERRIDE(NSDictionary *, GridDictionary, gridDictionary) -- (void)setPlaceholder:(BOOL)placeholder { - MyGridLayout *lsc = self.myDefaultSizeClass; - lsc.placeholder = placeholder; -} - -- (BOOL)anchor { - return self.myDefaultSizeClass.anchor; -} - -- (void)setAnchor:(BOOL)anchor { - MyGridLayout *lsc = self.myDefaultSizeClass; - lsc.anchor = anchor; -} - -- (MyGravity)overlap { - return self.myDefaultSizeClass.overlap; -} - -- (void)setOverlap:(MyGravity)overlap { - MyGridLayout *lsc = self.myDefaultSizeClass; - lsc.overlap = overlap; -} - -- (NSDictionary *)gridDictionary { - return self.myDefaultSizeClass.gridDictionary; -} - -- (void)setGridDictionary:(NSDictionary *)gridDictionary { - MyGridLayout *lsc = self.myDefaultSizeClass; - lsc.gridDictionary = gridDictionary; -} #pragma mark-- MyGridNode -- (NSMutableArray> *)subGrids { - return (NSMutableArray> *)(self.myDefaultSizeClass.subGrids); -} - -- (void)setSubGrids:(NSMutableArray> *)subGrids { - MyGridLayout *lsc = self.myDefaultSizeClass; - lsc.subGrids = subGrids; -} - -- (MySubGridsType)subGridsType { - return self.myDefaultSizeClass.subGridsType; -} - -- (void)setSubGridsType:(MySubGridsType)subGridsType { - MyGridLayout *lsc = self.myDefaultSizeClass; - lsc.subGridsType = subGridsType; -} - -- (CGFloat)measure { - return self.myDefaultSizeClass.measure; -} - -- (void)setMeasure:(CGFloat)measure { - MyGridLayout *lsc = self.myDefaultSizeClass; - lsc.measure = measure; -} - -- (CGRect)gridRect { - return self.myDefaultSizeClass.gridRect; -} - -- (void)setGridRect:(CGRect)gridRect { - MyGridLayout *lsc = self.myDefaultSizeClass; - lsc.gridRect = gridRect; -} +_MYLAYOUT_PROPERTY_GRIDLAYOUT_OVERRIDE(NSMutableArray> *, SubGrids, subGrids) +_MYLAYOUT_PROPERTY_GRIDLAYOUT_OVERRIDE(MySubGridsType, SubGridsType, subGridsType) +_MYLAYOUT_PROPERTY_GRIDLAYOUT_OVERRIDE(CGFloat, Measure, measure) +_MYLAYOUT_PROPERTY_GRIDLAYOUT_OVERRIDE(CGRect, GridRect, gridRect) //更新格子尺寸。 - (CGFloat)updateGridSize:(CGSize)superSize superGrid:(id)superGrid withMeasure:(CGFloat)measure { - return [self.myDefaultSizeClass updateGridSize:superSize superGrid:superGrid withMeasure:measure]; + return [self.myCurrentTraits updateGridSize:superSize superGrid:superGrid withMeasure:measure]; } - (CGFloat)updateWrapGridSizeInSuperGrid:(id)superGrid withMeasure:(CGFloat)measure { - return [self.myDefaultSizeClass updateWrapGridSizeInSuperGrid:superGrid withMeasure:measure]; + return [self.myCurrentTraits updateWrapGridSizeInSuperGrid:superGrid withMeasure:measure]; } - - (CGFloat)updateGridOrigin:(CGPoint)superOrigin superGrid:(id)superGrid withOffset:(CGFloat)offset { - return [self.myDefaultSizeClass updateGridOrigin:superOrigin superGrid:superGrid withOffset:offset]; + return [self.myCurrentTraits updateGridOrigin:superOrigin superGrid:superGrid withOffset:offset]; } - (UIView *)gridLayoutView { @@ -535,24 +459,24 @@ - (SEL)gridAction { } - (void)setBorderlineNeedLayoutIn:(CGRect)rect withLayer:(CALayer *)layer { - MyGridLayout *lsc = self.myDefaultSizeClass; - [lsc setBorderlineNeedLayoutIn:rect withLayer:layer]; + MyGridLayoutTraitsImpl *layoutTraits = self.myCurrentTraits; + [layoutTraits setBorderlineNeedLayoutIn:rect withLayer:layer]; } - (void)showBorderline:(BOOL)show { - MyGridLayout *lsc = self.myDefaultSizeClass; - [lsc showBorderline:show]; + MyGridLayoutTraitsImpl *layoutTraits = self.myCurrentTraits; + [layoutTraits showBorderline:show]; } - (id)gridHitTest:(CGPoint)point { - return [self.myDefaultSizeClass gridHitTest:point]; + return [self.myCurrentTraits gridHitTest:point]; } #pragma mark-- Touches Event - (id)myBestHitGrid:(NSSet *)touches { MySizeClass sizeClass = [self myGetGlobalSizeClass]; - id bestSC = (id)[self.myEngine fetchView:self bestLayoutSizeClass:sizeClass]; + id bestSC = (id)[self.myEngine fetchView:self bestTraitsAt:sizeClass]; UITouch *touch = [touches anyObject]; CGPoint point = [touch locationInView:self]; @@ -626,27 +550,15 @@ - (void)willRemoveSubview:(UIView *)subview { - (CGSize)calcLayoutSize:(CGSize)size subviewEngines:(NSMutableArray *)subviewEngines context:(MyLayoutContext *)context { CGSize selfSize = [super calcLayoutSize:size subviewEngines:subviewEngines context:context]; - MyGridLayoutTraits *layoutTraits = (MyGridLayoutTraits *)context->layoutViewEngine.currentSizeClass; + MyGridLayoutTraitsImpl *layoutTraits = context->layoutViewEngine.currentTraits; - context->paddingTop = layoutTraits.myLayoutPaddingTop; - context->paddingBottom = layoutTraits.myLayoutPaddingBottom; - context->paddingLeading = layoutTraits.myLayoutPaddingLeading; - context->paddingTrailing = layoutTraits.myLayoutPaddingTrailing; - context->vertGravity = MYVERTGRAVITY(layoutTraits.gravity); - context->horzGravity = [MyViewTraits convertLeadingTrailingGravityFromLeftRightGravity:MYHORZGRAVITY(layoutTraits.gravity)]; - context->vertSpace = layoutTraits.subviewVSpace; - context->horzSpace = layoutTraits.subviewHSpace; - if (context->subviewEngines == nil) { - context->subviewEngines = [layoutTraits filterEngines:subviewEngines]; - } - //只有在非评估,并且当sizeclass的数量大于1个,并且当前的sizeclass和lastSizeClass不一致的时候 if (!context->isEstimate && context->layoutViewEngine.multiple) { //将子栅格中的layer隐藏。 - if (self.lastSizeClass != nil && ((MyGridLayoutTraits *)layoutTraits) != self.lastSizeClass) { + if (self.lastSizeClass != nil && layoutTraits != self.lastSizeClass) { [((id)self.lastSizeClass) showBorderline:NO]; } - self.lastSizeClass = (MyGridLayoutTraits *)layoutTraits; + self.lastSizeClass = layoutTraits; } //设置根格子的rect为布局视图的大小。 @@ -660,7 +572,7 @@ - (CGSize)calcLayoutSize:(CGSize)size subviewEngines:(NSMutableArray *)subviewEn //构造出子视图列表。 NSMutableArray *subviews = [NSMutableArray arrayWithCapacity:context->subviewEngines.count]; for (MyLayoutEngine *subviewEngine in context->subviewEngines) { - [subviews addObject:subviewEngine.currentSizeClass.view]; + [subviews addObject:subviewEngine.currentTraits.view]; } //遍历尺寸 @@ -686,9 +598,9 @@ - (CGSize)calcLayoutSize:(CGSize)size subviewEngines:(NSMutableArray *)subviewEn NSInteger viewGroupIndex = viewGroupIndexNumber.integerValue; for (NSInteger i = viewGroupIndex; i < viewGroupArray.count; i++) { MyViewGroupAndActionData *va = viewGroupArray[i]; - for (UIView *sbv in va.viewGroup) { - if (sbv != (UIView *)[NSNull null]) { - sbv.myEngine.frame = CGRectZero; + for (UIView *subview in va.viewGroup) { + if (subview != (UIView *)[NSNull null]) { + subview.myEngine.frame = CGRectZero; //这里面让所有视图的枚举器也走一遍,解决下面的重复设置的问题。 UIView *anyway = enumerator.nextObject; @@ -699,21 +611,21 @@ - (CGSize)calcLayoutSize:(CGSize)size subviewEngines:(NSMutableArray *)subviewEn }]; //处理那些剩余没有放入格子的子视图的frame设置为0 - for (UIView *sbv = enumerator.nextObject; sbv; sbv = enumerator.nextObject) { - sbv.myEngine.frame = CGRectZero; + for (UIView *subview = enumerator.nextObject; subview; subview = enumerator.nextObject) { + subview.myEngine.frame = CGRectZero; } context->selfSize = selfSize; return [self myAdjustLayoutViewSizeWithContext:context]; } -- (id)createSizeClassInstance { - return [MyGridLayoutTraits new]; +- (Class) myTratisClass { + return [MyGridLayoutTraitsImpl class]; } #pragma mark-- Private Methods //遍历位置 -- (void)myTraversalGrid:(id)grid gridOrigin:(CGPoint)gridOrigin layoutTraits:(MyGridLayoutTraits *)layoutTraits sbvEnumerator:(NSEnumerator *)sbvEnumerator tagViewGroupIndexDict:(NSMutableDictionary *)tagViewGroupIndexDict tagSbvEnumerator:(NSEnumerator *)tagSbvEnumerator withContext:(MyLayoutContext *)context { +- (void)myTraversalGrid:(id)grid gridOrigin:(CGPoint)gridOrigin layoutTraits:(MyGridLayoutTraitsImpl *)layoutTraits sbvEnumerator:(NSEnumerator *)sbvEnumerator tagViewGroupIndexDict:(NSMutableDictionary *)tagViewGroupIndexDict tagSbvEnumerator:(NSEnumerator *)tagSbvEnumerator withContext:(MyLayoutContext *)context { //这要优化减少不必要的空数组的建立。。 NSArray> *subGrids = nil; if (grid.subGridsType != MySubGridsType_Unknown) @@ -765,18 +677,18 @@ - (void)myTraversalGrid:(id)grid gridOrigin:(CGPoint)gridOrigin layo //处理叶子节点。 if ((grid.anchor || subGrids.count == 0) && !grid.placeholder) { //设置子视图的位置和尺寸。。 - UIView *sbv = nil; - UIView *tagSbv = tagSbvEnumerator.nextObject; + UIView *subview = nil; + UIView *tagSubview = tagSbvEnumerator.nextObject; - if (tagSbv != (UIView *)[NSNull null]) { - sbv = sbvEnumerator.nextObject; + if (tagSubview != (UIView *)[NSNull null]) { + subview = sbvEnumerator.nextObject; } - if (tagSbv != nil && tagSbv != (UIView *)[NSNull null] && tagSbvEnumerator != nil) { - sbv = tagSbv; + if (tagSubview != nil && tagSubview != (UIView *)[NSNull null] && tagSbvEnumerator != nil) { + subview = tagSubview; } - if (sbv != nil) { + if (subview != nil) { //调整位置和尺寸。。。 - MyLayoutEngine *subviewEngine = sbv.myEngine; + MyLayoutEngine *subviewEngine = subview.myEngine; //取垂直和水平对齐 MyGravity vertGravity = MYVERTGRAVITY(grid.gravity); if (vertGravity == MyGravity_None) { @@ -786,7 +698,7 @@ - (void)myTraversalGrid:(id)grid gridOrigin:(CGPoint)gridOrigin layo if (horzGravity == MyGravity_None) { horzGravity = MyGravity_Horz_Fill; } else { - horzGravity = [MyViewTraits convertLeadingTrailingGravityFromLeftRightGravity:horzGravity]; + horzGravity = [MyViewTraitsImpl convertLeadingTrailingGravityFromLeftRightGravity:horzGravity]; } //如果非叶子栅格设置为anchor则子视图的内容总是填充的 CGFloat tempPaddingTop = paddingTop; @@ -804,7 +716,7 @@ - (void)myTraversalGrid:(id)grid gridOrigin:(CGPoint)gridOrigin layo } //如果是尺寸为0,并且设置为了anchor的话那么就根据自身 //如果尺寸是0则因为前面有算出尺寸,所以这里就不进行调整了。 - if (grid.measure != 0 && [sbv isKindOfClass:[MyBaseLayout class]]) { + if (grid.measure != 0 && [subview isKindOfClass:[MyBaseLayout class]]) { context->horzGravity = horzGravity; context->vertGravity = vertGravity; context->selfSize = grid.gridRect.size; @@ -829,7 +741,7 @@ - (void)myTraversalGrid:(id)grid gridOrigin:(CGPoint)gridOrigin layo if (grid.subGridsType == MySubGridsType_Col) { offset = gridOrigin.x + paddingLeading; - MyGravity horzGravity = [MyViewTraits convertLeadingTrailingGravityFromLeftRightGravity:MYHORZGRAVITY(grid.gravity)]; + MyGravity horzGravity = [MyViewTraitsImpl convertLeadingTrailingGravityFromLeftRightGravity:MYHORZGRAVITY(grid.gravity)]; if (horzGravity == MyGravity_Horz_Center || horzGravity == MyGravity_Horz_Trailing) { //得出所有子栅格的宽度综合 CGFloat subGridsWidth = 0; @@ -838,7 +750,7 @@ - (void)myTraversalGrid:(id)grid gridOrigin:(CGPoint)gridOrigin layo } if (subGrids.count > 1) { - subGridsWidth += grid.subviewSpace * (subGrids.count - 1); + subGridsWidth += grid.subviewSpacing * (subGrids.count - 1); } if (horzGravity == MyGravity_Horz_Center) { offset += (grid.gridRect.size.width - paddingLeading - paddingTrailing - subGridsWidth) / 2; @@ -859,7 +771,7 @@ - (void)myTraversalGrid:(id)grid gridOrigin:(CGPoint)gridOrigin layo } if (subGrids.count > 1) { - subGridsHeight += grid.subviewSpace * (subGrids.count - 1); + subGridsHeight += grid.subviewSpacing * (subGrids.count - 1); } if (vertGravity == MyGravity_Vert_Center) { offset += (grid.gridRect.size.height - paddingTop - paddingBottom - subGridsHeight) / 2; @@ -872,16 +784,16 @@ - (void)myTraversalGrid:(id)grid gridOrigin:(CGPoint)gridOrigin layo CGPoint paddingGridOrigin = CGPointMake(gridOrigin.x + paddingLeading, gridOrigin.y + paddingTop); for (id sbvGrid in subGrids) { offset += [sbvGrid updateGridOrigin:paddingGridOrigin superGrid:grid withOffset:offset]; - offset += grid.subviewSpace; + offset += grid.subviewSpacing; [self myTraversalGrid:sbvGrid gridOrigin:sbvGrid.gridRect.origin layoutTraits:layoutTraits sbvEnumerator:sbvEnumerator tagViewGroupIndexDict:tagViewGroupIndexDict tagSbvEnumerator:((sbvGrid.tag != 0) ? nil : tagSbvEnumerator) withContext:context]; } //如果栅格中的tagSbvEnumerator还有剩余的视图没有地方可填,那么就将尺寸和位置设置为0 if (grid.tag != 0) { //枚举那些剩余的 - for (UIView *sbv = tagSbvEnumerator.nextObject; sbv; sbv = tagSbvEnumerator.nextObject) { - if (sbv != (UIView *)[NSNull null]) { - sbv.myEngine.frame = CGRectZero; + for (UIView *subview = tagSbvEnumerator.nextObject; subview; subview = tagSbvEnumerator.nextObject) { + if (subview != (UIView *)[NSNull null]) { + subview.myEngine.frame = CGRectZero; //所有子视图枚举器也要移动。 UIView *anyway = sbvEnumerator.nextObject; @@ -923,7 +835,7 @@ - (void)myBlankTraverse:(id)grid sbs:(NSArray *)sbs pIndex tagViewGroupIndexDict: 记录标签中的视图组索引字典。 函数返回格子的尺寸值。 */ -- (CGFloat)myTraversalGrid:(id)grid gridSize:(CGSize)gridSize lsc:(MyGridLayoutTraits *)lsc sbs:(NSArray *)sbs pIndex:(NSInteger *)pIndex tagViewGroupIndexDict:(NSMutableDictionary *)tagViewGroupIndexDict tagViewGroup:(NSArray *)tagViewGroup pTagIndex:(NSInteger *)pTagIndex withContext:(MyLayoutContext *)context { +- (CGFloat)myTraversalGrid:(id)grid gridSize:(CGSize)gridSize lsc:(MyGridLayoutTraitsImpl *)lsc sbs:(NSArray *)sbs pIndex:(NSInteger *)pIndex tagViewGroupIndexDict:(NSMutableDictionary *)tagViewGroupIndexDict tagViewGroup:(NSArray *)tagViewGroup pTagIndex:(NSInteger *)pTagIndex withContext:(MyLayoutContext *)context { NSArray> *subGrids = nil; if (grid.subGridsType != MySubGridsType_Unknown) { subGrids = grid.subGrids; @@ -949,7 +861,7 @@ - (CGFloat)myTraversalGrid:(id)grid gridSize:(CGSize)gridSize lsc:(M CGFloat fixedMeasure = 0; //固定部分的尺寸 CGFloat validMeasure = 0; //整体有效的尺寸 if (subGrids.count > 1) { - fixedMeasure += (subGrids.count - 1) * grid.subviewSpace; + fixedMeasure += (subGrids.count - 1) * grid.subviewSpacing; } if (grid.subGridsType == MySubGridsType_Col) { fixedMeasure += paddingLeading + paddingTrailing; @@ -1001,13 +913,13 @@ - (CGFloat)myTraversalGrid:(id)grid gridSize:(CGSize)gridSize lsc:(M if (*pTempIndex < tempSbs.count) { //加这个条件是根栅格如果是叶子栅格的话不处理这种情况。 if (grid.superGrid != nil) { - UIView *sbv = tempSbs[*pTempIndex]; - if (sbv != (UIView *)[NSNull null]) { + UIView *subview = tempSbs[*pTempIndex]; + if (subview != (UIView *)[NSNull null]) { //叶子节点 if (!grid.anchor || (grid.measure == 0 && grid.anchor)) { - MyLayoutEngine *subviewEngine = sbv.myEngine; - MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; - subviewEngine.frame = sbv.bounds; + MyLayoutEngine *subviewEngine = subview.myEngine; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; + subviewEngine.frame = subview.bounds; //如果子视图不设置任何约束要进行特殊处理。 if (subviewTraits.widthSizeInner.val == nil && subviewTraits.heightSizeInner.val == nil) { @@ -1017,7 +929,7 @@ - (CGFloat)myTraversalGrid:(id)grid gridSize:(CGSize)gridSize lsc:(M } else { size.height = gridSize.height - paddingTop - paddingBottom; } - size = [sbv sizeThatFits:size]; + size = [subview sizeThatFits:size]; subviewEngine.width = size.width; subviewEngine.height = size.height; diff --git a/MyLayout/Lib/MyGridNode.h b/MyLayout/Lib/MyGridNode.h index bc62e3d..6bc089b 100644 --- a/MyLayout/Lib/MyGridNode.h +++ b/MyLayout/Lib/MyGridNode.h @@ -77,7 +77,7 @@ typedef enum : unsigned char { @property (nonatomic, assign) MySubGridsType subGridsType; //格子内子栅格的间距 -@property (nonatomic, assign) CGFloat subviewSpace; +@property (nonatomic, assign) CGFloat subviewSpacing; //格子内视图的内边距。 @property (nonatomic, assign) UIEdgeInsets padding; diff --git a/MyLayout/Lib/MyGridNode.m b/MyLayout/Lib/MyGridNode.m index 37a2549..0574a05 100644 --- a/MyLayout/Lib/MyGridNode.m +++ b/MyLayout/Lib/MyGridNode.m @@ -83,7 +83,7 @@ - (void)dealloc { */ typedef struct _MyGridOptionalProperties2 { //格子内子栅格的间距 - CGFloat subviewSpace; + CGFloat subviewSpacing; //格子内视图的内边距。 UIEdgeInsets padding; //格子内子视图的对齐停靠方式。 @@ -293,7 +293,7 @@ - (void)setOverlap:(MyGravity)overlap { grid.tag = self.tag; grid.actionData = self.actionData; if (self->_optionalProperties2 != NULL) { - grid.subviewSpace = self.subviewSpace; + grid.subviewSpacing = self.subviewSpacing; grid.padding = self.padding; } @@ -344,16 +344,16 @@ - (void)removeFromSuperGrid { } //格子内子栅格的间距 -- (CGFloat)subviewSpace { +- (CGFloat)subviewSpacing { if (_optionalProperties2 == NULL) { return 0; } else { - return _optionalProperties2->subviewSpace; + return _optionalProperties2->subviewSpacing; } } -- (void)setSubviewSpace:(CGFloat)subviewSpace { - self.optionalProperties2->subviewSpace = subviewSpace; +- (void)setSubviewSpacing:(CGFloat)subviewSpacing { + self.optionalProperties2->subviewSpacing = subviewSpacing; } //格子内视图的内边距。 @@ -570,8 +570,8 @@ + (void)translateGridDicionary:(NSDictionary *)gridDictionary toGridNode:(id)gridNode { } } -//space:10.0 -+ (void)createSpace:(CGFloat)space gridNode:(id)gridNode { - if (space != 0.0) { - gridNode.subviewSpace = space; +//spacing:10.0 ++ (void)createSpacing:(CGFloat)spacing gridNode:(id)gridNode { + if (spacing != 0.0) { + gridNode.subviewSpacing = spacing; } } @@ -809,7 +809,7 @@ + (NSDictionary *)translateGridNode:(id)gridNode toGridDictionary:(N [self returnPaddingWithGridNode:gridNode result:gridDictionary]; - [self returnSpaceWithGridNode:gridNode result:gridDictionary]; + [self returnSpacingWithGridNode:gridNode result:gridDictionary]; [self returnPlaceholderWithGridNode:gridNode result:gridDictionary]; @@ -888,10 +888,10 @@ + (void)returnPaddingWithGridNode:(id)gridNode result:(NSMutableDict } } -//space:10.0 -+ (void)returnSpaceWithGridNode:(id)gridNode result:(NSMutableDictionary *)result { - if (gridNode.subviewSpace != 0.0) { - [result setObject:[NSNumber numberWithDouble:gridNode.subviewSpace] forKey:kMyGridSpace]; +//spacing:10.0 ++ (void)returnSpacingWithGridNode:(id)gridNode result:(NSMutableDictionary *)result { + if (gridNode.subviewSpacing != 0.0) { + [result setObject:[NSNumber numberWithDouble:gridNode.subviewSpacing] forKey:kMyGridSpacing]; } } @@ -1065,7 +1065,7 @@ + (void)returnArrayColsRowsGridNode:(id)gridNode result:(NSMutableDi @implementation UIColor (MyGrid) -static NSDictionary *myDefaultColors() { +static NSDictionary *myDefaultColors(void) { static NSDictionary *colors = nil; if (colors == nil) { colors = @{ diff --git a/MyLayout/Lib/MyLayout.h b/MyLayout/Lib/MyLayout.h index 7d48a7f..fafa828 100644 --- a/MyLayout/Lib/MyLayout.h +++ b/MyLayout/Lib/MyLayout.h @@ -36,25 +36,26 @@ SOFTWARE. */ -// Current version is 1.9.10, please open:https://github.com/youngsoft/MyLinearLayout/blob/master/CHANGELOG.md to show the changes. +// Current version is 2.0.0, please open:https://github.com/youngsoft/MyLinearLayout/blob/master/CHANGELOG.md to show the changes. -#ifndef MyLayout_MyLayout_h -#define MyLayout_MyLayout_h +#ifndef _MyLayout_h_ +#define _MyLayout_h_ -#import "MyPathLayout.h" -#import "MyRelativeLayout.h" +#import "MyLayoutDef.h" +#import "MyDimeScale.h" +#import "MyLayoutPos.h" +#import "MyLayoutSize.h" +#import "MyLayoutTraits.h" +#import "MyLinearLayout.h" #import "MyTableLayout.h" +#import "MyGridLayout.h" +#import "MyFlowLayout.h" #import "MyFlexLayout.h" #import "MyFloatLayout.h" -#import "MyFlowLayout.h" #import "MyFrameLayout.h" -#import "MyGridLayout.h" -#import "MyLinearLayout.h" -#import "MyLayoutPos.h" -#import "MyLayoutSize.h" -#import "MyLayoutDef.h" +#import "MyRelativeLayout.h" +#import "MyPathLayout.h" #import "MyMaker.h" -#import "MyDimeScale.h" #endif diff --git a/MyLayout/Lib/MyLayoutDef.h b/MyLayout/Lib/MyLayoutDef.h index 44354da..bfab830 100644 --- a/MyLayout/Lib/MyLayoutDef.h +++ b/MyLayout/Lib/MyLayoutDef.h @@ -172,14 +172,14 @@ typedef enum : unsigned char { //上面部分是压缩的策略,下面部分指定压缩的内容,因此一个shrinkType的指定时上面部分和下面部分的 //| 操作。比如让间距平均压缩:MySubviewsShrink_Average | - // MySubviewsShrink_Space + // MySubviewsShrink_Spacing /**只压缩尺寸,因为这里是0所以这部分可以不设置,为默认。*/ MySubviewsShrink_Size = 0 << 4, /**只压缩间距。*/ - MySubviewsShrink_Space = 1 << 4, + MySubviewsShrink_Spacing = 1 << 4, /**压缩尺寸和间距。暂时不支持!!!*/ - MySubviewsShrink_SizeAndSpace = 2 << 4 + MySubviewsShrink_SizeAndSpacing = 2 << 4 } MySubviewsShrinkType; @@ -220,8 +220,8 @@ typedef enum : unsigned char { 在默认情况下现有的布局以及子视图的约束设置都是基于w:Any h:Any的,如果我们要为某种SizeClass设置约束则可以调用视图的扩展方法: - -(instancetype)fetchLayoutSizeClass:(MySizeClass)sizeClass; - -(instancetype)fetchLayoutSizeClass:(MySizeClass)sizeClass + -(__kindof id)fetchLayoutTraitsInSizeClass:(MySizeClass)sizeClass; + -(__kindof id)fetchLayoutTraitsInSizeClass:(MySizeClass)sizeClass copyFrom:(MySizeClass)srcSizeClass; @@ -229,33 +229,30 @@ typedef enum : unsigned char { 运算符来组合。 比如: 1.想设置所有iPhone设备的横屏的约束 - UIView *lsc = [某视图 - fetchLayoutSizeClass:MySizeClass_wAny|MySizeClass_hCompact]; + id traits = [某视图 + fetchLayoutTraitsInSizeClass:MySizeClass_wAny|MySizeClass_hCompact]; 2.想设置所有iPad设备的横屏的约束 - UIView *lsc = [某视图 fetchLayoutSizeClass: MySizeClass_wRegular | + id traits = [某视图 fetchLayoutTraitsInSizeClass: MySizeClass_wRegular | MySizeClass_hRegular | MySizeClass_Landscape]; 3.想设置iphone6plus下的横屏的约束 - UIView *lsc = [某视图 - fetchLayoutSizeClass:MySizeClass_wRegular|MySizeClass_hCompact]; + id traits = [某视图 + fetchLayoutTraitsInSizeClass:MySizeClass_wRegular|MySizeClass_hCompact]; 4.想设置ipad下的约束 - UIView *lsc = [某视图 fetchLayoutSizeClass:MySizeClass_wRegular | + id traits = [某视图 fetchLayoutTraitsInSizeClass:MySizeClass_wRegular | MySizeClass_hRegular]; 5.想设置所有设备下的约束,也是默认的视图的约束 - UIView *lsc = [某视图 fetchLayoutSizeClass:MySizeClass_wAny | + id traits = [某视图 fetchLayoutTraitsInSizeClass:MySizeClass_wAny | MySizeClass_hAny]; 6.所有设备的竖屏约束: - UIView *lsc = [某视图 fetchLayoutSizeClass:MySizeClass_Portrait]; + id traits = [某视图 fetchLayoutTraitsInSizeClass:MySizeClass_Portrait]; 7.所有设备的横屏约束: - UIView *lsc = [某视图 fetchLayoutSizeClass:MySizeClass_Landscape]; - - fetchLayoutSizeClass虽然返回的是一个instancetype,但实际得到了一个MyLayoutSizeClass对象或者其派生类,而MyLayoutSizeClass类中又定义了跟UIView一样相同的布局方法,因此虽然是返回视图对象,并设置各种约束,但实际上是设置MyLayoutSizeClass对象的各种约束。 - + id traits = [某视图 fetchLayoutTraitsInSizeClass:MySizeClass_Landscape]; */ typedef enum : unsigned char { MySizeClass_wAny = 0, //宽度任意尺寸 @@ -270,4 +267,5 @@ typedef enum : unsigned char { MySizeClass_Portrait = 0x40, //竖屏 MySizeClass_Landscape = 0x80, //横屏,注意横屏和竖屏不支持 | 运算操作,只能指定一个。 + MySizeClass_All = 0xFF, //全部 } MySizeClass; diff --git a/MyLayout/Lib/MyLayoutInner.h b/MyLayout/Lib/MyLayoutInner.h index c8db240..8881f1c 100644 --- a/MyLayout/Lib/MyLayoutInner.h +++ b/MyLayout/Lib/MyLayoutInner.h @@ -9,7 +9,7 @@ #import "MyLayoutDef.h" #import "MyLayoutMath.h" #import "MyLayoutPosInner.h" -#import "MyLayoutSizeClass.h" +#import "MyLayoutTraitsImpl.h" #import "MyLayoutSizeInner.h" @@ -33,11 +33,37 @@ typedef struct _MyLayoutContext { CGFloat paddingBottom; - CGFloat horzSpace; - CGFloat vertSpace; + CGFloat horzSpacing; + CGFloat vertSpacing; } MyLayoutContext; +@interface UIView (MyLayoutExtInner) + +@property (nonatomic, strong, readonly) MyLayoutEngine *myEngine; +@property (nonatomic, strong, readonly) MyLayoutEngine *myEngineInner; + + +- (__kindof MyViewTraitsImpl *)myDefaultTraits; +- (__kindof MyViewTraitsImpl *)myDefaultTraitsInner; + +- (__kindof MyViewTraitsImpl *)myCurrentTraits; +- (__kindof MyViewTraitsImpl *)myCurrentTraitsInner; + +@property (nonatomic, readonly) CGFloat myEstimatedWidth; +@property (nonatomic, readonly) CGFloat myEstimatedHeight; + +//为支持iOS11的safeArea而进行的padding的转化 +- (CGFloat)myLayoutPaddingTop; +- (CGFloat)myLayoutPaddingBottom; +- (CGFloat)myLayoutPaddingLeft; +- (CGFloat)myLayoutPaddingRight; +- (CGFloat)myLayoutPaddingLeading; +- (CGFloat)myLayoutPaddingTrailing; + +@end + + @interface MyBaseLayout () @property (nonatomic, assign) BOOL isMyLayouting; @@ -45,7 +71,6 @@ typedef struct _MyLayoutContext { //派生类重载这个函数进行布局 - (CGSize)calcLayoutSize:(CGSize)size subviewEngines:(NSMutableArray *)subviewEngines context:(MyLayoutContext *)context; -- (id)createSizeClassInstance; - (CGFloat)myCalcSubview:(MyLayoutEngine *)subviewEngine vertGravity:(MyGravity)vertGravity @@ -56,7 +81,7 @@ typedef struct _MyLayoutContext { horzGravity:(MyGravity)horz withContext:(MyLayoutContext *)context; -- (CGFloat)mySubview:(MyViewTraits *)subviewTraits wrapHeightSizeFits:(CGSize)size withContext:(MyLayoutContext *)context; +- (CGFloat)mySubview:(id)subviewTraits wrapHeightSizeFits:(CGSize)size withContext:(MyLayoutContext *)context; - (CGFloat)myGetBoundLimitMeasure:(MyLayoutSize *)anchor subview:(UIView *)subview anchorType:(MyLayoutAnchorType)anchorType subviewSize:(CGSize)subviewSize selfLayoutSize:(CGSize)selfLayoutSize isUBound:(BOOL)isUBound; @@ -69,13 +94,6 @@ typedef struct _MyLayoutContext { - (CGSize)myAdjustLayoutViewSizeWithContext:(MyLayoutContext *)context; -//为支持iOS11的safeArea而进行的padding的转化 -- (CGFloat)myLayoutPaddingTop; -- (CGFloat)myLayoutPaddingBottom; -- (CGFloat)myLayoutPaddingLeft; -- (CGFloat)myLayoutPaddingRight; -- (CGFloat)myLayoutPaddingLeading; -- (CGFloat)myLayoutPaddingTrailing; - (void)myAdjustSizeSettingOfSubviewEngine:(MyLayoutEngine *)subviewEngine withContext:(MyLayoutContext *)context; @@ -98,7 +116,7 @@ typedef struct _MyLayoutContext { //给父布局视图机会来更改子布局视图的边界线的显示的rect - (void)myHookSublayout:(MyBaseLayout *)sublayout borderlineRect:(CGRect *)pRect; -- (void)myCalcSubviewsWrapContentSize:(MyLayoutContext *)context withCustomSetting:(void (^)(MyViewTraits *subviewTraits))customSetting; +- (void)myCalcSubviewsWrapContentSize:(MyLayoutContext *)context withCustomSetting:(void (^)(MyViewTraitsImpl *subviewTraits))customSetting; @end @@ -120,3 +138,94 @@ typedef struct _MyLayoutContext { +#define _MYLAYOUT_PROPERTY_GET1(type, name)\ +-(type)name {\ +return [super name];\ +}\ + +#define _MYLAYOUT_PROPERTY_SET1(type, setName, name)\ +-(void)set##setName:(type)name{\ +[super set##setName:name];\ +}\ + +#define _MYLAYOUT_PROPERTY_OVERRIDE1(type, setName, name)\ +_MYLAYOUT_PROPERTY_GET1(type, name)\ +_MYLAYOUT_PROPERTY_SET1(type, setName, name)\ + + +#define _MYLAYOUT_PROPERTY_VIEW_GET1(type, name)\ +-(type)name {\ +return self.myDefaultTraitsInner.name;\ +}\ + +#define _MYLAYOUT_PROPERTY_VIEW_SET1(type, setName, name)\ +- (void)set##setName:(type)name {\ +self.myDefaultTraits.name = name;\ +}\ + +#define _MYLAYOUT_PROPERTY_VIEW_OVERRIDE1(type, setName, name)\ +_MYLAYOUT_PROPERTY_VIEW_GET1(type, name)\ +_MYLAYOUT_PROPERTY_VIEW_SET1(type, setName, name)\ + + +#define _MYLAYOUT_PROPERTY_VIEW_GET2 _MYLAYOUT_PROPERTY_VIEW_GET1 + +#define _MYLAYOUT_PROPERTY_VIEW_SET2(type, setName, name)\ +- (void)set##setName:(type)name {\ + MyViewTraitsImpl *viewTraits = (MyViewTraitsImpl*)self.myDefaultTraits;\ + if (viewTraits.name != name) {\ + viewTraits.name = name;\ + if (self.superview != nil) {\ + [self.superview setNeedsLayout];\ + }\ + }\ +}\ + +#define _MYLAYOUT_PROPERTY_VIEW_OVERRIDE2(type, setName, name)\ +_MYLAYOUT_PROPERTY_VIEW_GET2(type,name)\ +_MYLAYOUT_PROPERTY_VIEW_SET2(type,setName,name)\ + + +#define _MYLAYOUT_PROPERTY_VIEW_GET3(type, name)\ +-(type)name {\ +return ((MyViewTraitsImpl *)self.myDefaultTraits).name;\ +}\ + + +#define _MYLAYOUT_PROPERTY_LAYOUTVIEW_GET1(traits,type, name)\ +-(type)name {\ +return ((traits *)self.myDefaultTraitsInner).name;\ +}\ + +#define _MYLAYOUT_PROPERTY_LAYOUTVIEW_SET1(traits, type, setName, name)\ +- (void)set##setName:(type)name {\ + traits *layoutTraits = (traits *)self.myDefaultTraits;\ + if (layoutTraits.name != name) {\ + layoutTraits.name = name;\ + [self setNeedsLayout];\ + }\ +}\ + + +#define _MYLAYOUT_PROPERTY_LAYOUTVIEW_OVERRIDE1(traits, type, setName, name)\ +_MYLAYOUT_PROPERTY_LAYOUTVIEW_GET1(traits,type,name)\ +_MYLAYOUT_PROPERTY_LAYOUTVIEW_SET1(traits,type,setName,name)\ + + + + +#define _MYLAYOUT_PROPERTY_GRIDLAYOUT_GET(type, name)\ +-(type)name {\ +return ((MyGridLayoutTraitsImpl*)self.myDefaultTraits).name;\ +}\ + +#define _MYLAYOUT_PROPERTY_GRIDLAYOUT_SET(type, setName, name)\ +- (void)set##setName:(type)name {\ +MyGridLayoutTraitsImpl *layoutTraits = self.myDefaultTraits;\ +layoutTraits.name = name;\ +}\ + +#define _MYLAYOUT_PROPERTY_GRIDLAYOUT_OVERRIDE(type, setName, name)\ +_MYLAYOUT_PROPERTY_GRIDLAYOUT_GET(type,name)\ +_MYLAYOUT_PROPERTY_GRIDLAYOUT_SET(type,setName,name)\ + diff --git a/MyLayout/Lib/MyLayoutPos.h b/MyLayout/Lib/MyLayoutPos.h index afec8de..b414fcb 100644 --- a/MyLayout/Lib/MyLayoutPos.h +++ b/MyLayout/Lib/MyLayoutPos.h @@ -19,7 +19,7 @@ 下面的表格描述了各种布局下的子视图的布局位置对象的equalTo方法可以设置的值。 为了表示方便我们把:线性布局简写为L、相对布局简写为R、表格布局简写为T、框架布局简写为FR、流式布局简写为FL、浮动布局简写为FO、全部简写为ALL,不支持为- +-----------+--------+---------------------+-------+------+--------+---------+----------+----------+---------------+------+ - |method\val |NSNumber|NSArray|leftPos|topPos|rightPos|bottomPos|centerXPos|centerYPos|UILayoutSupport|UIView| + |method\val |NSNumber|NSArray|leftPos|topPos|rightPos|bottomPos|centerXPos|centerYPos|UIView| +-----------+--------+---------------------+-------+------+--------+---------+----------+----------+---------------+------+ | topPos | ALL | - | - | R | - | R | - | R | L/R/FR/FO/FL |ALL | +-----------+--------+---------------------+-------+------+--------+---------+----------+----------+---------------+------+ @@ -46,7 +46,7 @@ 1.NSNumber: the layout position is equal to a number. e.g. leftPos.equalTo (@100) indicates that the value of the left boundary is 100. 2.MyLayoutPos: the layout position depends on aother layout position. e.g. A.leftPos.equalTo(B.rightPos) indicates that A is on the right side of B. 3.NSArray: all views in the array and View are centered. e.g. A.centerXPos.equalTo(@[B.centerXPos, C.centerXPos]) indicates that A,B,C are overall horizontal centered. - 4.id: you can only set topPos equalTo UIViewController‘s topLayoutGuide or bottomPos equalTo UIViewController‘s bottomLayoutGuide, then the view will always below the nav。 + 4.id: is not be supported in v1.9.20 please use @(MyLayoutPos.safeAreaMargin) instead !!! 5.UIView: the layout position is depends view's relevant position. 6.nil: the layout position value is clear. @@ -76,6 +76,9 @@ */ @interface MyLayoutPos : NSObject ++ (instancetype)new NS_UNAVAILABLE; +- (instancetype)init NS_UNAVAILABLE; + /** 特殊的位置。只用在布局视图和非布局父视图之间的位置约束和没有导航条时的布局视图内子视图的padding设置上。 iOS11以后提出了安全区域的概念,因此对于iOS11以下的版本就需要兼容处理,尤其是在那些没有导航条的情况下。通过将布局视图的边距设置为这个特殊值就可以实现在任何版本中都能完美的实现位置的偏移而且各版本保持统一。比如下面的例子: @@ -135,7 +138,7 @@ 3. NSArray表示位置和数组里面的其他位置整体居中。 - 4. id 对于iOS7以后视图控制器会根据导航条是否半透明而确定是占据整个屏幕。因此当topPos,bottomPos设置为视图控制器的topLayoutGuide或者bottomLayoutGuide时这样子视图就会偏移出导航条的高度,而如果没有导航条时则不会偏移出导航条的高度。注意的是这个值不能设置在非布局父视图的布局视图中。 + 4. id 本类型值不再被支持!! 请用 @(MyLayoutPos.safeAreaMargin) 代替。 5. UIView表示位置依赖指定视图的对应位置。 diff --git a/MyLayout/Lib/MyLayoutPos.m b/MyLayout/Lib/MyLayoutPos.m index fd41ec9..5c677b7 100644 --- a/MyLayout/Lib/MyLayoutPos.m +++ b/MyLayout/Lib/MyLayoutPos.m @@ -19,11 +19,12 @@ + (CGFloat)safeAreaMargin { return -20171003.0; } -- (id)init { - self = [super init]; +- (instancetype)initWithView:(UIView *)view anchorType:(MyLayoutAnchorType)anchorType { + self = [self init]; if (self != nil) { _active = YES; - _view = nil; + _view = view; + _anchorType = anchorType; _val = nil; _valType = MyLayoutValType_Nil; _offsetVal = 0; @@ -31,7 +32,6 @@ - (id)init { _uBoundVal = nil; _shrink = 0.0; } - return self; } @@ -146,21 +146,19 @@ - (CGFloat)maxVal { #pragma mark-- NSCopying - (id)copyWithZone:(NSZone *)zone { - MyLayoutPos *layoutPos = [[[self class] allocWithZone:zone] init]; - layoutPos.view = self.view; + MyLayoutPos *layoutPos = [[[self class] allocWithZone:zone] initWithView:_view anchorType:_anchorType]; layoutPos->_active = _active; layoutPos->_shrink = _shrink; - layoutPos->_anchorType = _anchorType; layoutPos->_valType = _valType; layoutPos->_val = _val; layoutPos->_offsetVal = _offsetVal; if (_lBoundVal != nil) { - layoutPos->_lBoundVal = [[[self class] allocWithZone:zone] init]; + layoutPos->_lBoundVal = [[[self class] allocWithZone:zone] initWithView:_view anchorType:_anchorType]; layoutPos->_lBoundVal->_active = _active; [[layoutPos->_lBoundVal _myEqualTo:_lBoundVal.val] _myOffset:_lBoundVal.offsetVal]; } if (_uBoundVal != nil) { - layoutPos->_uBoundVal = [[[self class] allocWithZone:zone] init]; + layoutPos->_uBoundVal = [[[self class] allocWithZone:zone] initWithView:_view anchorType:_anchorType]; layoutPos->_uBoundVal->_active = _active; [[layoutPos->_uBoundVal _myEqualTo:_uBoundVal.val] _myOffset:_uBoundVal.offsetVal]; } @@ -177,68 +175,28 @@ - (NSNumber *)numberVal { if (_valType == MyLayoutValType_Number) { return _val; } else if (_valType == MyLayoutValType_UILayoutSupport) { - //只有在11以后并且是设置了safearea缩进才忽略UILayoutSupport。 - UIView *superview = self.view.superview; - if (@available(iOS 11.0, *)) { - if (superview != nil && [superview isKindOfClass:[MyBaseLayout class]]) { - UIRectEdge edge = ((MyBaseLayout *)superview).insetsPaddingFromSafeArea; - if ((_anchorType == MyLayoutAnchorType_Top && (edge & UIRectEdgeTop) == UIRectEdgeTop) || - (_anchorType == MyLayoutAnchorType_Bottom && (edge & UIRectEdgeBottom) == UIRectEdgeBottom)) { - return @(0); - } - } - } - - return @([((id)_val) length]); - } else if (_valType == MyLayoutValType_SafeArea) { -#if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 110000) || (__TV_OS_VERSION_MAX_ALLOWED >= 110000) - - if (@available(iOS 11.0, *)) { - UIView *superView = self.view.superview; - switch (_anchorType) { - case MyLayoutAnchorType_Leading: - return [MyBaseLayout isRTL] ? @(superView.safeAreaInsets.right) : @(superView.safeAreaInsets.left); - break; - case MyLayoutAnchorType_Trailing: - return [MyBaseLayout isRTL] ? @(superView.safeAreaInsets.left) : @(superView.safeAreaInsets.right); - break; - case MyLayoutAnchorType_Top: - return @(superView.safeAreaInsets.top); - break; - case MyLayoutAnchorType_Bottom: - return @(superView.safeAreaInsets.bottom); - break; - default: - return @(0); - break; - } - } -#endif - if (_anchorType == MyLayoutAnchorType_Top) { - return @([self findContainerVC].topLayoutGuide.length); - } else if (_anchorType == MyLayoutAnchorType_Bottom) { - return @([self findContainerVC].bottomLayoutGuide.length); - } return @(0); - } - return nil; -} - -- (UIViewController *)findContainerVC { - UIViewController *vc = nil; - @try { - UIView *v = self.view; - while (v != nil) { - vc = [v valueForKey:@"viewDelegate"]; - if (vc != nil) { + } else if (_valType == MyLayoutValType_SafeArea) { + UIView *superView = self.view.superview; + switch (_anchorType) { + case MyLayoutAnchorType_Leading: + return [MyBaseLayout isRTL] ? @(superView.safeAreaInsets.right) : @(superView.safeAreaInsets.left); + break; + case MyLayoutAnchorType_Trailing: + return [MyBaseLayout isRTL] ? @(superView.safeAreaInsets.left) : @(superView.safeAreaInsets.right); + break; + case MyLayoutAnchorType_Top: + return @(superView.safeAreaInsets.top); + break; + case MyLayoutAnchorType_Bottom: + return @(superView.safeAreaInsets.bottom); + break; + default: + return @(0); break; - } - v = [v superview]; } - } @catch (NSException *exception) { } - - return vc; + return nil; } - (MyLayoutPos *)anchorVal { @@ -273,7 +231,7 @@ - (NSNumber *)mostVal { - (MyLayoutPos *)lBoundVal { if (_lBoundVal == nil) { - _lBoundVal = [[MyLayoutPos alloc] init]; + _lBoundVal = [[MyLayoutPos alloc] initWithView:_view anchorType:_anchorType]; _lBoundVal->_active = _active; [_lBoundVal _myEqualTo:@(-CGFLOAT_MAX)]; } @@ -282,7 +240,7 @@ - (MyLayoutPos *)lBoundVal { - (MyLayoutPos *)uBoundVal { if (_uBoundVal == nil) { - _uBoundVal = [[MyLayoutPos alloc] init]; + _uBoundVal = [[MyLayoutPos alloc] initWithView:_view anchorType:_anchorType]; _uBoundVal->_active = _active; [_uBoundVal _myEqualTo:@(CGFLOAT_MAX)]; } @@ -310,12 +268,6 @@ - (MyLayoutPos *)_myEqualTo:(id)val { _valType = MyLayoutValType_LayoutPos; } else if ([val isKindOfClass:[NSArray class]]) { _valType = MyLayoutValType_Array; - } else if ([val conformsToProtocol:@protocol(UILayoutSupport)]) { - //这里只有上边和下边支持,其他不支持。。 - if (_anchorType != MyLayoutAnchorType_Top && _anchorType != MyLayoutAnchorType_Bottom) { - NSAssert(0, @"oops! only topPos or bottomPos can set to id"); - } - _valType = MyLayoutValType_UILayoutSupport; } else if ([val isKindOfClass:[MyLayoutMostPos class]]) { _valType = MyLayoutValType_Most; } else if ([val isKindOfClass:[UIView class]]) { @@ -348,8 +300,10 @@ - (MyLayoutPos *)_myEqualTo:(id)val { NSAssert(0, @"oops!"); break; } - } else { + } else if (_val == nil) { _valType = MyLayoutValType_Nil; + } else { + NSAssert(0, @"oops!, not support type !!"); } _val = val; } @@ -432,7 +386,7 @@ - (BOOL)isRelativePos { } - (BOOL)isSafeAreaPos { - return self.isActive && (_valType == MyLayoutValType_SafeArea || _valType == MyLayoutValType_UILayoutSupport); + return self.isActive && (_valType == MyLayoutValType_SafeArea); } - (CGFloat)measureWith:(CGFloat)refVal { @@ -542,7 +496,7 @@ @implementation MyLayoutPos (Clone) - (MyLayoutPos * (^)(CGFloat offsetVal))clone { return ^id(CGFloat offsetVal) { - MyLayoutPos *clonedAnchor = [[[self class] allocWithZone:nil] init]; + MyLayoutPos *clonedAnchor = [[[self class] allocWithZone:nil] initWithView:nil anchorType:self.anchorType]; clonedAnchor->_offsetVal = offsetVal; clonedAnchor->_val = self; clonedAnchor->_valType = MyLayoutValType_LayoutAnchorClone; diff --git a/MyLayout/Lib/MyLayoutPosInner.h b/MyLayout/Lib/MyLayoutPosInner.h index c02f112..a813827 100644 --- a/MyLayout/Lib/MyLayoutPosInner.h +++ b/MyLayout/Lib/MyLayoutPosInner.h @@ -12,6 +12,8 @@ //布局位置内部定义 @interface MyLayoutPos () +- (instancetype)initWithView:(UIView *)view anchorType:(MyLayoutAnchorType)anchorType; + @property (nonatomic, weak) UIView *view; @property (nonatomic, assign) MyLayoutAnchorType anchorType; @property (nonatomic, assign) MyLayoutValType valType; diff --git a/MyLayout/Lib/MyLayoutSize.h b/MyLayout/Lib/MyLayoutSize.h index dc02af3..467ad73 100644 --- a/MyLayout/Lib/MyLayoutSize.h +++ b/MyLayout/Lib/MyLayoutSize.h @@ -25,6 +25,10 @@ */ @interface MyLayoutSize : NSObject ++ (instancetype)new NS_UNAVAILABLE; +- (instancetype)init NS_UNAVAILABLE; + + /**特殊的尺寸,表示尺寸由子视图决定或者由内容决定,也就是说尺寸自适应*/ @property (class, nonatomic, assign, readonly) NSInteger wrap; diff --git a/MyLayout/Lib/MyLayoutSize.m b/MyLayout/Lib/MyLayoutSize.m index eed4477..bbdefdd 100644 --- a/MyLayout/Lib/MyLayoutSize.m +++ b/MyLayout/Lib/MyLayoutSize.m @@ -29,11 +29,12 @@ + (NSInteger)average { return -99997; //这么定义纯粹是一个数字没有其他意义 } -- (id)init { - self = [super init]; +- (instancetype)initWithView:(UIView *)view anchorType:(MyLayoutAnchorType)anchorType { + self = [self init]; if (self != nil) { _active = YES; - _view = nil; + _view = view; + _anchorType = anchorType; _val = nil; _valType = MyLayoutValType_Nil; _addVal = 0; @@ -43,6 +44,7 @@ - (id)init { _shrink = 0.0; _priority = MyPriority_Normal; } + return self; } @@ -172,23 +174,21 @@ - (BOOL)isFill { #pragma mark-- NSCopying - (id)copyWithZone:(NSZone *)zone { - MyLayoutSize *layoutSize = [[[self class] allocWithZone:zone] init]; - layoutSize->_view = _view; + MyLayoutSize *layoutSize = [[[self class] allocWithZone:zone] initWithView:self.view anchorType:self.anchorType]; layoutSize->_active = _active; layoutSize->_shrink = _shrink; - layoutSize->_anchorType = _anchorType; layoutSize->_addVal = _addVal; layoutSize->_multiVal = _multiVal; layoutSize->_val = _val; layoutSize->_valType = _valType; layoutSize->_priority = _priority; if (_lBoundVal != nil) { - layoutSize->_lBoundVal = [[[self class] allocWithZone:zone] init]; + layoutSize->_lBoundVal = [[[self class] allocWithZone:zone] initWithView:self.view anchorType:self.anchorType]; layoutSize->_lBoundVal->_active = _active; [[[layoutSize->_lBoundVal _myEqualTo:_lBoundVal.val] _myAdd:_lBoundVal.addVal] _myMultiply:_lBoundVal.multiVal]; } if (_uBoundVal != nil) { - layoutSize->_uBoundVal = [[[self class] allocWithZone:zone] init]; + layoutSize->_uBoundVal = [[[self class] allocWithZone:zone] initWithView:self.view anchorType:self.anchorType]; layoutSize->_uBoundVal->_active = _active; [[[layoutSize->_uBoundVal _myEqualTo:_uBoundVal.val] _myAdd:_uBoundVal.addVal] _myMultiply:_uBoundVal.multiVal]; } @@ -240,7 +240,7 @@ - (BOOL)fillVal { - (MyLayoutSize *)lBoundVal { if (_lBoundVal == nil) { - _lBoundVal = [[MyLayoutSize alloc] init]; + _lBoundVal = [[MyLayoutSize alloc] initWithView:self.view anchorType:self.anchorType]; _lBoundVal->_active = _active; [_lBoundVal _myEqualTo:@(-CGFLOAT_MAX)]; } @@ -249,7 +249,7 @@ - (MyLayoutSize *)lBoundVal { - (MyLayoutSize *)uBoundVal { if (_uBoundVal == nil) { - _uBoundVal = [[MyLayoutSize alloc] init]; + _uBoundVal = [[MyLayoutSize alloc] initWithView:self.view anchorType:self.anchorType]; _uBoundVal->_active = _active; [_uBoundVal _myEqualTo:@(CGFLOAT_MAX)]; } @@ -492,7 +492,7 @@ @implementation MyLayoutSize (Clone) - (MyLayoutSize * (^)(CGFloat addVal, CGFloat multiVal))clone { return ^id(CGFloat addVal, CGFloat multiVal) { - MyLayoutSize *clonedAnchor = [[[self class] allocWithZone:nil] init]; + MyLayoutSize *clonedAnchor = [[[self class] allocWithZone:nil] initWithView:self.view anchorType:self.anchorType]; clonedAnchor->_addVal = addVal; clonedAnchor->_multiVal = multiVal; clonedAnchor->_val = self; @@ -506,8 +506,8 @@ @implementation MyLayoutSize (Clone) #pragma mark-- MyLayoutMostSize @implementation MyLayoutMostSize { - NSArray *_dimens; BOOL _isMax; + NSArray *_dimens; } - (instancetype)initWith:(NSArray *)dimens isMax:(BOOL)isMax { diff --git a/MyLayout/Lib/MyLayoutSizeClass.h b/MyLayout/Lib/MyLayoutSizeClass.h deleted file mode 100644 index 0327d26..0000000 --- a/MyLayout/Lib/MyLayoutSizeClass.h +++ /dev/null @@ -1,283 +0,0 @@ -// -// MyLayoutSizeClass.h -// MyLayout -// -// Created by oybq on 16/1/22. -// Copyright © 2016年 YoungSoft. All rights reserved. -// - -#import "MyGrid.h" -#import "MyLayoutDef.h" -#import "MyLayoutPos.h" -#import "MyLayoutSize.h" - -@class MyBaseLayout; - -@class MyViewTraits; - -//视图的布局引擎。 -@interface MyLayoutEngine : NSObject - -@property (nonatomic, assign) CGFloat top; -@property (nonatomic, assign) CGFloat leading; -@property (nonatomic, assign) CGFloat bottom; -@property (nonatomic, assign) CGFloat trailing; -@property (nonatomic, assign) CGFloat width; -@property (nonatomic, assign) CGFloat height; - -@property (nonatomic, assign) CGPoint origin; -@property (nonatomic, assign) CGSize size; -@property (nonatomic, assign) CGRect frame; - -//默认的布局分类,所有视图的布局相关的属性,都是设置到这个分类上。 -@property (nonatomic, weak) MyViewTraits *defaultSizeClass; - -//当前的布局分类,当前正在执行布局时所用的布局分类。 -@property (nonatomic, weak) MyViewTraits *currentSizeClass; - -@property (nonatomic, assign, readonly) BOOL multiple; //是否设置了多个sizeclass - -@property (nonatomic, strong) NSMutableDictionary *sizeClasses; - -@property (nonatomic, assign) BOOL hasObserver; - -- (void)reset; - -- (MyViewTraits *)fetchView:(UIView *)view layoutSizeClass:(MySizeClass)sizeClass copyFrom:(MySizeClass)srcSizeClass; - -- (MyViewTraits *)fetchView:(UIView *)view bestLayoutSizeClass:(MySizeClass)sizeClass; - -- (void)setView:(UIView *)view layoutSizeClass:(MySizeClass)sizeClass withTraits:(MyViewTraits *)traits; - -@end - - -@interface UIView (MyLayoutExtInner) - -@property (nonatomic, strong, readonly) MyLayoutEngine *myEngine; -@property (nonatomic, strong, readonly) MyLayoutEngine *myEngineInner; - - -- (id)createSizeClassInstance; - -- (instancetype)myDefaultSizeClass; -- (instancetype)myDefaultSizeClassInner; - -- (instancetype)myCurrentSizeClass; -- (instancetype)myCurrentSizeClassInner; - -@property (nonatomic, readonly) MyLayoutPos *topPosInner; -@property (nonatomic, readonly) MyLayoutPos *leadingPosInner; -@property (nonatomic, readonly) MyLayoutPos *bottomPosInner; -@property (nonatomic, readonly) MyLayoutPos *trailingPosInner; -@property (nonatomic, readonly) MyLayoutPos *centerXPosInner; -@property (nonatomic, readonly) MyLayoutPos *centerYPosInner; -@property (nonatomic, readonly) MyLayoutSize *widthSizeInner; -@property (nonatomic, readonly) MyLayoutSize *heightSizeInner; - -@property (nonatomic, readonly) MyLayoutPos *leftPosInner; -@property (nonatomic, readonly) MyLayoutPos *rightPosInner; - -@property (nonatomic, readonly) MyLayoutPos *baselinePosInner; - -@property (nonatomic, readonly) CGFloat myEstimatedWidth; -@property (nonatomic, readonly) CGFloat myEstimatedHeight; - -@end - - - -/* - 布局的属性特征集合类,这个类的功能用来存储涉及到布局的各种属性。MyViewTraits类中定义的各种属性跟视图和布局的各种扩展属性是一致的。 - */ -@interface MyViewTraits : NSObject - -@property (nonatomic, weak) UIView *view; - -//所有视图通用 -@property (nonatomic, strong) MyLayoutPos *topPos; -@property (nonatomic, strong) MyLayoutPos *leadingPos; -@property (nonatomic, strong) MyLayoutPos *bottomPos; -@property (nonatomic, strong) MyLayoutPos *trailingPos; -@property (nonatomic, strong) MyLayoutPos *centerXPos; -@property (nonatomic, strong) MyLayoutPos *centerYPos; - -@property (nonatomic, strong, readonly) MyLayoutPos *leftPos; -@property (nonatomic, strong, readonly) MyLayoutPos *rightPos; - -@property (nonatomic, strong) MyLayoutPos *baselinePos; - -@property (nonatomic, assign) CGFloat myTop; -@property (nonatomic, assign) CGFloat myLeading; -@property (nonatomic, assign) CGFloat myBottom; -@property (nonatomic, assign) CGFloat myTrailing; -@property (nonatomic, assign) CGFloat myCenterX; -@property (nonatomic, assign) CGFloat myCenterY; -@property (nonatomic, assign) CGPoint myCenter; - -@property (nonatomic, assign) CGFloat myLeft; -@property (nonatomic, assign) CGFloat myRight; - -@property (nonatomic, assign) CGFloat myMargin; -@property (nonatomic, assign) CGFloat myHorzMargin; -@property (nonatomic, assign) CGFloat myVertMargin; - -@property (nonatomic, strong) MyLayoutSize *widthSize; -@property (nonatomic, strong) MyLayoutSize *heightSize; - -@property (nonatomic, assign) CGFloat myWidth; -@property (nonatomic, assign) CGFloat myHeight; -@property (nonatomic, assign) CGSize mySize; - -@property (nonatomic, assign) BOOL wrapContentWidth; -@property (nonatomic, assign) BOOL wrapContentHeight; - -@property (nonatomic, assign) BOOL wrapContentSize; - -@property (nonatomic, assign) BOOL useFrame; -@property (nonatomic, assign) BOOL noLayout; - -@property (nonatomic, assign) MyVisibility visibility; -@property (nonatomic, assign) MyGravity alignment; - -@property (nonatomic, copy) void (^viewLayoutCompleteBlock)(MyBaseLayout *layout, UIView *v); - -//线性布局和浮动布局和流式布局子视图专用 -@property (nonatomic, assign) CGFloat weight; - -//浮动布局子视图专用 -@property (nonatomic, assign, getter=isReverseFloat) BOOL reverseFloat; -@property (nonatomic, assign) BOOL clearFloat; - -//内部属性 -@property (nonatomic, strong, readonly) MyLayoutPos *topPosInner; -@property (nonatomic, strong, readonly) MyLayoutPos *leadingPosInner; -@property (nonatomic, strong, readonly) MyLayoutPos *bottomPosInner; -@property (nonatomic, strong, readonly) MyLayoutPos *trailingPosInner; -@property (nonatomic, strong, readonly) MyLayoutPos *centerXPosInner; -@property (nonatomic, strong, readonly) MyLayoutPos *centerYPosInner; -@property (nonatomic, strong, readonly) MyLayoutSize *widthSizeInner; -@property (nonatomic, strong, readonly) MyLayoutSize *heightSizeInner; - -@property (nonatomic, strong, readonly) MyLayoutPos *leftPosInner; -@property (nonatomic, strong, readonly) MyLayoutPos *rightPosInner; - -@property (nonatomic, strong, readonly) MyLayoutPos *baselinePosInner; - -@property (class, nonatomic, assign) BOOL isRTL; - -//内部方法 -- (BOOL)invalid; - -+ (MyGravity)convertLeadingTrailingGravityFromLeftRightGravity:(MyGravity)horzGravity; -- (MyGravity)finalVertGravityFrom:(MyGravity)layoutVertGravity; -- (MyGravity)finalHorzGravityFrom:(MyGravity)layoutHorzGravity; - -@end - -@interface MyLayoutTraits : MyViewTraits - -@property (nonatomic, assign) BOOL zeroPadding; - -@property (nonatomic, assign) BOOL reverseLayout; -@property (nonatomic, assign) CGAffineTransform layoutTransform; //布局变换。 - -@property (nonatomic, assign) MyGravity gravity; - -@property (nonatomic, assign) BOOL insetLandscapeFringePadding; - -@property (nonatomic, assign) CGFloat paddingTop; -@property (nonatomic, assign) CGFloat paddingLeading; -@property (nonatomic, assign) CGFloat paddingBottom; -@property (nonatomic, assign) CGFloat paddingTrailing; -@property (nonatomic, assign) UIEdgeInsets padding; - -@property (nonatomic, assign) CGFloat paddingLeft; -@property (nonatomic, assign) CGFloat paddingRight; - -//兼容1.9.2以及以前的老版本,因为老版本的命名不符合规范,所以这里重新命名。 -//@property (nonatomic, assign, getter=paddingTop, setter=setPaddingTop:) CGFloat topPadding; -//@property (nonatomic, assign, getter=paddingLeading, setter=setPaddingLeading:) CGFloat leadingPadding; -//@property (nonatomic, assign, getter=paddingBottom, setter=setPaddingBottom:) CGFloat bottomPadding; -//@property (nonatomic, assign, getter=paddingTrailing, setter=setPaddingTrailing:) CGFloat trailingPadding; -//@property (nonatomic, assign, getter=paddingLeft, setter=setPaddingLeft:) CGFloat leftPadding; -//@property (nonatomic, assign, getter=paddingRight, setter=setPaddingRight:) CGFloat rightPadding; - -//为支持iOS11的safeArea而进行的padding的转化 -- (CGFloat)myLayoutPaddingTop; -- (CGFloat)myLayoutPaddingBottom; -- (CGFloat)myLayoutPaddingLeft; -- (CGFloat)myLayoutPaddingRight; -- (CGFloat)myLayoutPaddingLeading; -- (CGFloat)myLayoutPaddingTrailing; - -@property (nonatomic, assign) UIRectEdge insetsPaddingFromSafeArea; - -@property (nonatomic, assign) CGFloat subviewVSpace; -@property (nonatomic, assign) CGFloat subviewHSpace; -@property (nonatomic, assign) CGFloat subviewSpace; - -//从全部子视图引擎数组中过滤出需要进行布局的子视图布局引擎数组子集。 -- (NSMutableArray *)filterEngines:(NSMutableArray *)subviewEngines; - -@end - -@interface MySequentLayoutFlexSpacing : NSObject -@property (nonatomic, assign) CGFloat subviewSize; -@property (nonatomic, assign) CGFloat minSpace; -@property (nonatomic, assign) CGFloat maxSpace; -@property (nonatomic, assign) BOOL centered; - -- (CGFloat)calcMaxMinSubviewSizeForContent:(CGFloat)selfSize paddingStart:(CGFloat *)pStarPadding paddingEnd:(CGFloat *)pEndPadding space:(CGFloat *)pSpace; -- (CGFloat)calcMaxMinSubviewSize:(CGFloat)selfSize arrangedCount:(NSInteger)arrangedCount paddingStart:(CGFloat *)pStarPadding paddingEnd:(CGFloat *)pEndPadding space:(CGFloat *)pSpace; - -@end - -@interface MySequentLayoutTraits : MyLayoutTraits - -@property (nonatomic, assign) MyOrientation orientation; -@property (nonatomic, strong) MySequentLayoutFlexSpacing *flexSpace; - -@end - -@interface MyLinearLayoutTraits : MySequentLayoutTraits - -@property (nonatomic, assign) MySubviewsShrinkType shrinkType; - -@end - -@interface MyTableLayoutTraits : MyLinearLayoutTraits - -@end - -@interface MyFlowLayoutTraits : MySequentLayoutTraits - -@property (nonatomic, assign) MyGravity arrangedGravity; -@property (nonatomic, assign) BOOL autoArrange; -@property (nonatomic, assign) BOOL isFlex; -@property (nonatomic, assign) MyGravityPolicy lastlineGravityPolicy; - -@property (nonatomic, assign) NSInteger arrangedCount; -@property (nonatomic, assign) NSInteger pagedCount; - -@end - -@interface MyFloatLayoutTraits : MySequentLayoutTraits - -@end - -@interface MyRelativeLayoutTraits : MyLayoutTraits - -@end - -@interface MyFrameLayoutTraits : MyLayoutTraits - -@end - -@interface MyPathLayoutTraits : MyLayoutTraits - -@end - -@interface MyGridLayoutTraits : MyLayoutTraits - -@end diff --git a/MyLayout/Lib/MyLayoutSizeInner.h b/MyLayout/Lib/MyLayoutSizeInner.h index c21d157..cdab635 100644 --- a/MyLayout/Lib/MyLayoutSizeInner.h +++ b/MyLayout/Lib/MyLayoutSizeInner.h @@ -12,6 +12,8 @@ //尺寸对象内部定义 @interface MyLayoutSize () +- (instancetype)initWithView:(UIView *)view anchorType:(MyLayoutAnchorType)anchorType; + @property (nonatomic, weak) UIView *view; @property (nonatomic, assign) MyLayoutAnchorType anchorType; diff --git a/MyLayout/Lib/MyLayoutTraits.h b/MyLayout/Lib/MyLayoutTraits.h new file mode 100644 index 0000000..fd7d59c --- /dev/null +++ b/MyLayout/Lib/MyLayoutTraits.h @@ -0,0 +1,160 @@ +// +// MyLayoutTraits.h +// MyLayout +// +// Created by oubaiquan on 2024/11/5. +// Copyright © 2024 YoungSoft. All rights reserved. +// + +#ifndef MyLayoutTraits_h +#define MyLayoutTraits_h + +#import "MyLayoutDef.h" +#import "MyLayoutPos.h" +#import "MyLayoutSize.h" +#import "MyGrid.h" + +@class MyBaseLayout; + +@protocol MyViewTraits + +-(id)copy; + +//所有视图通用 +@property (nonatomic, strong, readonly) MyLayoutPos *topPos; +@property (nonatomic, strong, readonly) MyLayoutPos *leadingPos; +@property (nonatomic, strong, readonly) MyLayoutPos *bottomPos; +@property (nonatomic, strong, readonly) MyLayoutPos *trailingPos; +@property (nonatomic, strong, readonly) MyLayoutPos *centerXPos; +@property (nonatomic, strong, readonly) MyLayoutPos *centerYPos; + +@property (nonatomic, strong, readonly) MyLayoutPos *leftPos; +@property (nonatomic, strong, readonly) MyLayoutPos *rightPos; + +@property (nonatomic, strong, readonly) MyLayoutPos *baselinePos; + +// 上述位置对象快捷赋值属性 +@property (nonatomic, assign) CGFloat myTop; +@property (nonatomic, assign) CGFloat myLeading; +@property (nonatomic, assign) CGFloat myBottom; +@property (nonatomic, assign) CGFloat myTrailing; +@property (nonatomic, assign) CGFloat myCenterX; +@property (nonatomic, assign) CGFloat myCenterY; +@property (nonatomic, assign) CGPoint myCenter; + +@property (nonatomic, assign) CGFloat myLeft; +@property (nonatomic, assign) CGFloat myRight; + +@property (nonatomic, assign) CGFloat myMargin; +@property (nonatomic, assign) CGFloat myHorzMargin; +@property (nonatomic, assign) CGFloat myVertMargin; + +@property (nonatomic, strong, readonly) MyLayoutSize *widthSize; +@property (nonatomic, strong, readonly) MyLayoutSize *heightSize; + +// 上述尺寸属性快捷赋值属性。 +@property (nonatomic, assign) CGFloat myWidth; +@property (nonatomic, assign) CGFloat myHeight; +@property (nonatomic, assign) CGSize mySize; + +// 已经过期的属性,不建议再使用。 +@property (nonatomic, assign) BOOL wrapContentWidth; +@property (nonatomic, assign) BOOL wrapContentHeight; +@property (nonatomic, assign) BOOL wrapContentSize; + +//线性布局和浮动布局和流式布局子视图专用 +@property (nonatomic, assign) CGFloat weight; + +@property (nonatomic, assign) BOOL useFrame; +@property (nonatomic, assign) BOOL noLayout; + +@property (nonatomic, assign) MyVisibility visibility; +@property (nonatomic, assign) MyGravity alignment; + + +//浮动布局子视图专用 +@property (nonatomic, assign, getter=isReverseFloat) BOOL reverseFloat; +@property (nonatomic, assign) BOOL clearFloat; + +@end + +@protocol MyLayoutTraits + +@property (nonatomic, assign) BOOL zeroPadding; + +@property (nonatomic, assign) BOOL reverseLayout; +@property (nonatomic, assign) CGAffineTransform layoutTransform; //布局变换。 + +@property (nonatomic, assign) MyGravity gravity; + +@property (nonatomic, assign) BOOL insetLandscapeFringePadding; + +@property (nonatomic, assign) CGFloat paddingTop; +@property (nonatomic, assign) CGFloat paddingLeading; +@property (nonatomic, assign) CGFloat paddingBottom; +@property (nonatomic, assign) CGFloat paddingTrailing; +@property (nonatomic, assign) UIEdgeInsets padding; + +@property (nonatomic, assign) CGFloat paddingLeft; +@property (nonatomic, assign) CGFloat paddingRight; + +@property (nonatomic, assign) UIRectEdge insetsPaddingFromSafeArea; + +@property (nonatomic, assign) CGFloat subviewVSpacing; +@property (nonatomic, assign) CGFloat subviewHSpacing; +@property (nonatomic, assign) CGFloat subviewSpacing; + +@end + + +@protocol MySequentLayoutTraits + +@property (nonatomic, assign) MyOrientation orientation; + +@end + +@protocol MyLinearLayoutTraits + +@property (nonatomic, assign) MySubviewsShrinkType shrinkType; + +@end + +@protocol MyTableLayoutTraits + +@end + +@protocol MyFlowLayoutTraits + +@property (nonatomic, assign) MyGravity arrangedGravity; +@property (nonatomic, assign) BOOL autoArrange; +@property (nonatomic, assign) BOOL isFlex; +@property (nonatomic, assign) MyGravityPolicy lastlineGravityPolicy; +@property (nonatomic, assign) NSInteger maxLines; + +@property (nonatomic, assign) NSInteger arrangedCount; +@property (nonatomic, assign) NSInteger pagedCount; + +@end + +@protocol MyFloatLayoutTraits + +@end + +@protocol MyRelativeLayoutTraits + +@end + +@protocol MyFrameLayoutTraits + +@end + +@protocol MyPathLayoutTraits + +@end + +@protocol MyGridLayoutTraits + +@end + + +#endif /* MyLayoutTraits_h */ diff --git a/MyLayout/Lib/MyLayoutTraitsImpl.h b/MyLayout/Lib/MyLayoutTraitsImpl.h new file mode 100644 index 0000000..986af5c --- /dev/null +++ b/MyLayout/Lib/MyLayoutTraitsImpl.h @@ -0,0 +1,155 @@ +// +// MyLayoutTraitsImpl.h +// MyLayout +// +// Created by oybq on 16/1/22. +// Copyright © 2016年 YoungSoft. All rights reserved. +// + +#import "MyLayoutTraits.h" +#import "MyGridNode.h" + +@class MyViewTraitsImpl; + +//视图的布局引擎。 +@interface MyLayoutEngine : NSObject + +-(instancetype)initWithTraitsClass:(Class)traitsClass; + +@property (nonatomic, assign) CGFloat top; +@property (nonatomic, assign) CGFloat leading; +@property (nonatomic, assign) CGFloat bottom; +@property (nonatomic, assign) CGFloat trailing; +@property (nonatomic, assign) CGFloat width; +@property (nonatomic, assign) CGFloat height; + +@property (nonatomic, assign) CGPoint origin; +@property (nonatomic, assign) CGSize size; +@property (nonatomic, assign) CGRect frame; + + + +@property (nonatomic, copy) void (^viewLayoutCompleteBlock)(MyBaseLayout *, UIView *); + +//默认的布局特性,所有视图对象属性中所指定的布局特性。 +@property (nonatomic, weak) __kindof MyViewTraitsImpl *defaultTraits; + +//当前的布局特性,也就是视图所处当前sizeclass下的布局特性。 +@property (nonatomic, weak) __kindof MyViewTraitsImpl *currentTraits; + +@property (nonatomic, assign, readonly) BOOL multiple; //是否设置了多个布局特性集 + +@property (nonatomic, strong) NSMutableDictionary *traitsDict; + +@property (nonatomic, assign) BOOL hasObserver; + +- (void)reset; + +- (__kindof MyViewTraitsImpl* )fetchView:(UIView *)view traitsInSizeClass:(MySizeClass)sizeClass copyFrom:(MySizeClass)srcSizeClass; + +- (__kindof MyViewTraitsImpl* )fetchView:(UIView *)view bestTraitsAt:(MySizeClass)sizeClass; + +- (void)setView:(UIView *)view newTraits:(__kindof MyViewTraitsImpl* )traits at:(MySizeClass)sizeClass; + +@end + +/* + 布局的属性特征集合类,这个类的功能用来存储涉及到布局的各种属性。MyViewTraits类中定义的各种属性跟视图和布局的各种扩展属性是一致的。 + */ +@interface MyViewTraitsImpl : NSObject + +@property (nonatomic, weak) UIView *view; + + +@property (class, nonatomic, assign) BOOL isRTL; + + +//内部属性 +@property (nonatomic, strong, readonly) MyLayoutPos *topPosInner; +@property (nonatomic, strong, readonly) MyLayoutPos *leadingPosInner; +@property (nonatomic, strong, readonly) MyLayoutPos *bottomPosInner; +@property (nonatomic, strong, readonly) MyLayoutPos *trailingPosInner; +@property (nonatomic, strong, readonly) MyLayoutPos *centerXPosInner; +@property (nonatomic, strong, readonly) MyLayoutPos *centerYPosInner; +@property (nonatomic, strong, readonly) MyLayoutSize *widthSizeInner; +@property (nonatomic, strong, readonly) MyLayoutSize *heightSizeInner; + +@property (nonatomic, strong, readonly) MyLayoutPos *leftPosInner; +@property (nonatomic, strong, readonly) MyLayoutPos *rightPosInner; + +@property (nonatomic, strong, readonly) MyLayoutPos *baselinePosInner; + + +//内部方法 +- (BOOL)invalid; + ++ (MyGravity)convertLeadingTrailingGravityFromLeftRightGravity:(MyGravity)horzGravity; +- (MyGravity)finalVertGravityFrom:(MyGravity)layoutVertGravity; +- (MyGravity)finalHorzGravityFrom:(MyGravity)layoutHorzGravity; + +@end + +@interface MyLayoutTraitsImpl : MyViewTraitsImpl + +//为支持iOS11的safeArea而进行的padding的转化 +- (CGFloat)myLayoutPaddingTop; +- (CGFloat)myLayoutPaddingBottom; +- (CGFloat)myLayoutPaddingLeft; +- (CGFloat)myLayoutPaddingRight; +- (CGFloat)myLayoutPaddingLeading; +- (CGFloat)myLayoutPaddingTrailing; + + +//从全部子视图引擎数组中过滤出需要进行布局的子视图布局引擎数组子集。 +- (NSMutableArray *)filterEngines:(NSMutableArray *)subviewEngines; + +@end + +@interface MySequentLayoutFlexSpacing : NSObject +@property (nonatomic, assign) CGFloat subviewsSize; +@property (nonatomic, assign) CGFloat minSpacing; +@property (nonatomic, assign) CGFloat maxSpacing; +@property (nonatomic, assign) BOOL centered; + +- (CGFloat)calcMaxMinSubviewsSizeForContent:(CGFloat)selfSize paddingStart:(CGFloat *)pStarPadding paddingEnd:(CGFloat *)pEndPadding spacing:(CGFloat *)pSpacing; +- (CGFloat)calcMaxMinSubviewsSize:(CGFloat)selfSize arrangedCount:(NSInteger)arrangedCount paddingStart:(CGFloat *)pStarPadding paddingEnd:(CGFloat *)pEndPadding spacing:(CGFloat *)pSpacing; + +@end + +@interface MySequentLayoutTraitsImpl : MyLayoutTraitsImpl + +@property (nonatomic, strong) MySequentLayoutFlexSpacing *flexSpacing; + +@end + +@interface MyLinearLayoutTraitsImpl : MySequentLayoutTraitsImpl + +@end + +@interface MyTableLayoutTraitsImpl : MyLinearLayoutTraitsImpl + +@end + +@interface MyFlowLayoutTraitsImpl : MySequentLayoutTraitsImpl + +@end + +@interface MyFloatLayoutTraitsImpl : MySequentLayoutTraitsImpl + +@end + +@interface MyRelativeLayoutTraitsImpl : MyLayoutTraitsImpl + +@end + +@interface MyFrameLayoutTraitsImpl : MyLayoutTraitsImpl + +@end + +@interface MyPathLayoutTraitsImpl : MyLayoutTraitsImpl + +@end + +@interface MyGridLayoutTraitsImpl : MyLayoutTraitsImpl + +@end diff --git a/MyLayout/Lib/MyLayoutSizeClass.m b/MyLayout/Lib/MyLayoutTraitsImpl.m similarity index 71% rename from MyLayout/Lib/MyLayoutSizeClass.m rename to MyLayout/Lib/MyLayoutTraitsImpl.m index f36e8a9..87af98b 100644 --- a/MyLayout/Lib/MyLayoutSizeClass.m +++ b/MyLayout/Lib/MyLayoutTraitsImpl.m @@ -6,17 +6,24 @@ // Copyright © 2016年 YoungSoft. All rights reserved. // -#import "MyLayoutSizeClass.h" -#import "MyGridNode.h" +#import "MyLayoutTraitsImpl.h" #import "MyLayoutPosInner.h" #import "MyLayoutSizeInner.h" +@interface MyLayoutEngine() + +@property (nonatomic, readonly, strong) Class traitsClass; + +@end + @implementation MyLayoutEngine -- (id)init { - self = [super init]; + +-(instancetype)initWithTraitsClass:(Class)traitsClass { + self = [self init]; if (self != nil) { + _traitsClass = traitsClass; _leading = CGFLOAT_MAX; _trailing = CGFLOAT_MAX; _top = CGFLOAT_MAX; @@ -24,7 +31,6 @@ - (id)init { _width = CGFLOAT_MAX; _height = CGFLOAT_MAX; } - return self; } @@ -69,55 +75,57 @@ - (void)setOrigin:(CGPoint)origin { } - (BOOL)multiple { - return self.sizeClasses.count > 1; + return self.traitsDict.count > 1; } + + - (NSString *)description { return [NSString stringWithFormat:@"leading:%g, top:%g, width:%g, height:%g, trailing:%g, bottom:%g", _leading, _top, _width, _height, _trailing, _bottom]; } -- (MyViewTraits *)fetchView:(UIView *)view layoutSizeClass:(MySizeClass)sizeClass copyFrom:(MySizeClass)srcSizeClass { - MyViewTraits *viewTraits = nil; - if (self.sizeClasses == nil) { - self.sizeClasses = [NSMutableDictionary new]; +- (MyViewTraitsImpl *)fetchView:(UIView *)view traitsInSizeClass:(MySizeClass)sizeClass copyFrom:(MySizeClass)srcSizeClass { + MyViewTraitsImpl *viewTraits = nil; + if (self.traitsDict == nil) { + self.traitsDict = [NSMutableDictionary new]; } else { - viewTraits = (MyViewTraits *)[self.sizeClasses objectForKey:@(sizeClass)]; + viewTraits = (MyViewTraitsImpl *)[self.traitsDict objectForKey:@(sizeClass)]; } if (viewTraits == nil) { - MyViewTraits *srcTraits = (MyViewTraits *)[self.sizeClasses objectForKey:@(srcSizeClass)]; + MyViewTraitsImpl *srcTraits = (MyViewTraitsImpl *)[self.traitsDict objectForKey:@(srcSizeClass)]; if (srcTraits == nil) { - viewTraits = [view createSizeClassInstance]; + viewTraits = [self.traitsClass new]; } else { viewTraits = [srcTraits copy]; } viewTraits.view = view; - [self.sizeClasses setObject:viewTraits forKey:@(sizeClass)]; + [self.traitsDict setObject:viewTraits forKey:@(sizeClass)]; } return viewTraits; } -- (MyViewTraits *)fetchView:(UIView *)view bestLayoutSizeClass:(MySizeClass)sizeClass { +- (MyViewTraitsImpl *)fetchView:(UIView *)view bestTraitsAt:(MySizeClass)sizeClass { MySizeClass wscType = sizeClass & 0x03; MySizeClass hscType = sizeClass & 0x0C; MySizeClass oriType = sizeClass & 0xC0; - if (self.sizeClasses == nil) { - self.sizeClasses = [NSMutableDictionary new]; + if (self.traitsDict == nil) { + self.traitsDict = [NSMutableDictionary new]; } MySizeClass searchType; - MyViewTraits *viewTraits = nil; - if (self.sizeClasses.count > 1) { + MyViewTraitsImpl *viewTraits = nil; + if (self.traitsDict.count > 1) { //first search the most exact SizeClass searchType = wscType | hscType | oriType; - viewTraits = [self.sizeClasses objectForKey:@(searchType)]; + viewTraits = [self.traitsDict objectForKey:@(searchType)]; if (viewTraits != nil) { return viewTraits; } searchType = wscType | hscType; if (searchType != sizeClass) { - viewTraits = [self.sizeClasses objectForKey:@(searchType)]; + viewTraits = [self.traitsDict objectForKey:@(searchType)]; if (viewTraits != nil) { return viewTraits; } @@ -125,7 +133,7 @@ - (MyViewTraits *)fetchView:(UIView *)view bestLayoutSizeClass:(MySizeClass)size searchType = MySizeClass_wAny | hscType | oriType; if (oriType != 0 && searchType != sizeClass) { - viewTraits = [self.sizeClasses objectForKey:@(searchType)]; + viewTraits = [self.traitsDict objectForKey:@(searchType)]; if (viewTraits != nil) { return viewTraits; } @@ -133,7 +141,7 @@ - (MyViewTraits *)fetchView:(UIView *)view bestLayoutSizeClass:(MySizeClass)size searchType = MySizeClass_wAny | hscType; if (searchType != sizeClass) { - viewTraits = [self.sizeClasses objectForKey:@(searchType)]; + viewTraits = [self.traitsDict objectForKey:@(searchType)]; if (viewTraits != nil) { return viewTraits; } @@ -141,7 +149,7 @@ - (MyViewTraits *)fetchView:(UIView *)view bestLayoutSizeClass:(MySizeClass)size searchType = wscType | MySizeClass_hAny | oriType; if (oriType != 0 && searchType != sizeClass) { - viewTraits = [self.sizeClasses objectForKey:@(searchType)]; + viewTraits = [self.traitsDict objectForKey:@(searchType)]; if (viewTraits != nil) { return viewTraits; } @@ -149,7 +157,7 @@ - (MyViewTraits *)fetchView:(UIView *)view bestLayoutSizeClass:(MySizeClass)size searchType = wscType | MySizeClass_hAny; if (searchType != sizeClass) { - viewTraits = [self.sizeClasses objectForKey:@(searchType)]; + viewTraits = [self.traitsDict objectForKey:@(searchType)]; if (viewTraits != nil) { return viewTraits; } @@ -157,7 +165,7 @@ - (MyViewTraits *)fetchView:(UIView *)view bestLayoutSizeClass:(MySizeClass)size searchType = MySizeClass_wAny | MySizeClass_hAny | oriType; if (oriType != 0 && searchType != sizeClass) { - viewTraits = [self.sizeClasses objectForKey:@(searchType)]; + viewTraits = [self.traitsDict objectForKey:@(searchType)]; if (viewTraits != nil) { return viewTraits; } @@ -165,18 +173,18 @@ - (MyViewTraits *)fetchView:(UIView *)view bestLayoutSizeClass:(MySizeClass)size } searchType = MySizeClass_wAny | MySizeClass_hAny; - viewTraits = [self.sizeClasses objectForKey:@(searchType)]; + viewTraits = [self.traitsDict objectForKey:@(searchType)]; if (viewTraits == nil) { - viewTraits = [view createSizeClassInstance]; + viewTraits = [self.traitsClass new]; viewTraits.view = view; - [self.sizeClasses setObject:viewTraits forKey:@(searchType)]; + [self.traitsDict setObject:viewTraits forKey:@(searchType)]; } return viewTraits; } -- (void)setView:(UIView *)view layoutSizeClass:(MySizeClass)sizeClass withTraits:(MyViewTraits *)traits { - if (self.sizeClasses == nil) { - self.sizeClasses = [NSMutableDictionary new]; +- (void)setView:(UIView *)view newTraits:(__kindof MyViewTraitsImpl* )traits at:(MySizeClass)sizeClass { + if (self.traitsDict == nil) { + self.traitsDict = [NSMutableDictionary new]; } traits.view = view; traits.topPosInner.view = view; @@ -189,17 +197,35 @@ - (void)setView:(UIView *)view layoutSizeClass:(MySizeClass)sizeClass withTraits traits.widthSizeInner.view = view; traits.heightSizeInner.view = view; - [self.sizeClasses setObject:traits forKey:@(sizeClass)]; + [self.traitsDict setObject:traits forKey:@(sizeClass)]; } @end -@implementation MyViewTraits +@implementation MyViewTraitsImpl + +@synthesize topPos = _topPos; +@synthesize leadingPos = _leadingPos; +@synthesize bottomPos = _bottomPos; +@synthesize trailingPos = _trailingPos; +@synthesize centerXPos = _centerXPos; +@synthesize centerYPos = _centerYPos; +@synthesize baselinePos = _baselinePos; +@synthesize widthSize = _widthSize; +@synthesize heightSize = _heightSize; +@synthesize useFrame; +@synthesize noLayout; +@synthesize visibility; +@synthesize alignment; +@synthesize weight = _weight; +@synthesize reverseFloat; +@synthesize clearFloat; BOOL _myisRTL = NO; + + (BOOL)isRTL { return _myisRTL; } @@ -208,124 +234,62 @@ + (void)setIsRTL:(BOOL)isRTL { _myisRTL = isRTL; } -- (id)init { - return [super init]; -} -- (MyLayoutPos *)topPosInner { - return _topPos; -} - -- (MyLayoutPos *)leadingPosInner { - return _leadingPos; -} - -- (MyLayoutPos *)bottomPosInner { - return _bottomPos; -} - -- (MyLayoutPos *)trailingPosInner { - return _trailingPos; -} - -- (MyLayoutPos *)centerXPosInner { - return _centerXPos; -} - -- (MyLayoutPos *)centerYPosInner { - return _centerYPos; -} - -- (MyLayoutPos *)leftPosInner { - return [MyViewTraits isRTL] ? self.trailingPosInner : self.leadingPosInner; -} - -- (MyLayoutPos *)rightPosInner { - return [MyViewTraits isRTL] ? self.leadingPosInner : self.trailingPosInner; -} - -- (MyLayoutPos *)baselinePosInner { - return _baselinePos; -} - -- (MyLayoutSize *)widthSizeInner { - return _widthSize; -} - -- (MyLayoutSize *)heightSizeInner { - return _heightSize; -} - -//.. +//接口实现 - (MyLayoutPos *)topPos { if (_topPos == nil) { - _topPos = [MyLayoutPos new]; - _topPos.view = self.view; - _topPos.anchorType = MyLayoutAnchorType_Top; + _topPos = [[MyLayoutPos alloc] initWithView:self.view anchorType:MyLayoutAnchorType_Top]; } return _topPos; } - (MyLayoutPos *)leadingPos { if (_leadingPos == nil) { - _leadingPos = [MyLayoutPos new]; - _leadingPos.view = self.view; - _leadingPos.anchorType = MyLayoutAnchorType_Leading; + _leadingPos = [[MyLayoutPos alloc] initWithView:self.view anchorType:MyLayoutAnchorType_Leading]; } - return _leadingPos; } - (MyLayoutPos *)bottomPos { if (_bottomPos == nil) { - _bottomPos = [MyLayoutPos new]; - _bottomPos.view = self.view; - _bottomPos.anchorType = MyLayoutAnchorType_Bottom; + _bottomPos = [[MyLayoutPos alloc] initWithView:self.view anchorType:MyLayoutAnchorType_Bottom]; } return _bottomPos; } - (MyLayoutPos *)trailingPos { if (_trailingPos == nil) { - _trailingPos = [MyLayoutPos new]; - _trailingPos.view = self.view; - _trailingPos.anchorType = MyLayoutAnchorType_Trailing; + _trailingPos = [[MyLayoutPos alloc] initWithView:self.view anchorType:MyLayoutAnchorType_Trailing]; } return _trailingPos; } - (MyLayoutPos *)centerXPos { if (_centerXPos == nil) { - _centerXPos = [MyLayoutPos new]; - _centerXPos.view = self.view; - _centerXPos.anchorType = MyLayoutAnchorType_CenterX; + _centerXPos = [[MyLayoutPos alloc] initWithView:self.view anchorType:MyLayoutAnchorType_CenterX]; } return _centerXPos; } - (MyLayoutPos *)centerYPos { if (_centerYPos == nil) { - _centerYPos = [MyLayoutPos new]; - _centerYPos.view = self.view; - _centerYPos.anchorType = MyLayoutAnchorType_CenterY; + _centerYPos = [[MyLayoutPos alloc] initWithView:self.view anchorType:MyLayoutAnchorType_CenterY]; } return _centerYPos; } - (MyLayoutPos *)leftPos { - return [MyViewTraits isRTL] ? self.trailingPos : self.leadingPos; + return [MyViewTraitsImpl isRTL] ? self.trailingPos : self.leadingPos; } - (MyLayoutPos *)rightPos { - return [MyViewTraits isRTL] ? self.leadingPos : self.trailingPos; + return [MyViewTraitsImpl isRTL] ? self.leadingPos : self.trailingPos; } - (MyLayoutPos *)baselinePos { if (_baselinePos == nil) { - _baselinePos = [MyLayoutPos new]; - _baselinePos.view = self.view; - _baselinePos.anchorType = MyLayoutAnchorType_Baseline; + _baselinePos = [[MyLayoutPos alloc] initWithView:self.view anchorType:MyLayoutAnchorType_Baseline]; } return _baselinePos; } @@ -434,18 +398,14 @@ - (void)setMyVertMargin:(CGFloat)myVertMargin { - (MyLayoutSize *)widthSize { if (_widthSize == nil) { - _widthSize = [MyLayoutSize new]; - _widthSize.view = self.view; - _widthSize.anchorType = MyLayoutAnchorType_Width; + _widthSize = [[MyLayoutSize alloc] initWithView:self.view anchorType:MyLayoutAnchorType_Width]; } return _widthSize; } - (MyLayoutSize *)heightSize { if (_heightSize == nil) { - _heightSize = [MyLayoutSize new]; - _heightSize.view = self.view; - _heightSize.anchorType = MyLayoutAnchorType_Height; + _heightSize = [[MyLayoutSize alloc] initWithView:self.view anchorType:MyLayoutAnchorType_Height]; } return _heightSize; } @@ -535,6 +495,51 @@ - (void)setWrapContentSize:(BOOL)wrapContentSize { self.wrapContentWidth = self.wrapContentHeight = wrapContentSize; } +- (MyLayoutPos *)topPosInner { + return _topPos; +} + +- (MyLayoutPos *)leadingPosInner { + return _leadingPos; +} + +- (MyLayoutPos *)bottomPosInner { + return _bottomPos; +} + +- (MyLayoutPos *)trailingPosInner { + return _trailingPos; +} + +- (MyLayoutPos *)centerXPosInner { + return _centerXPos; +} + +- (MyLayoutPos *)centerYPosInner { + return _centerYPos; +} + +- (MyLayoutPos *)leftPosInner { + return [MyViewTraitsImpl isRTL] ? self.trailingPosInner : self.leadingPosInner; +} + +- (MyLayoutPos *)rightPosInner { + return [MyViewTraitsImpl isRTL] ? self.leadingPosInner : self.trailingPosInner; +} + +- (MyLayoutPos *)baselinePosInner { + return _baselinePos; +} + +- (MyLayoutSize *)widthSizeInner { + return _widthSize; +} + +- (MyLayoutSize *)heightSizeInner { + return _heightSize; +} + + - (NSString *)debugDescription { NSString *dbgDesc = [NSString stringWithFormat:@"\nView:\n%@\n%@\n%@\n%@\n%@\n%@\n%@\n%@\nweight=%f\nuseFrame=%@\nnoLayout=%@\nvisibility=%c\nalignment=%hu\nreverseFloat=%@\nclearFloat=%@", self.topPosInner, @@ -559,7 +564,7 @@ - (NSString *)debugDescription { #pragma mark-- NSCopying - (id)copyWithZone:(NSZone *)zone { - MyViewTraits *layoutTraits = [[[self class] allocWithZone:zone] init]; + MyViewTraitsImpl *layoutTraits = [[[self class] allocWithZone:zone] init]; //这里不会复制hidden属性 layoutTraits->_view = _view; @@ -646,7 +651,7 @@ - (MyGravity)finalVertGravityFrom:(MyGravity)layoutVertGravity { - (MyGravity)finalHorzGravityFrom:(MyGravity)layoutHorzGravity { MyGravity finalHorzGravity = MyGravity_Horz_Leading; - MyGravity horzAlignment = [MyViewTraits convertLeadingTrailingGravityFromLeftRightGravity:MYHORZGRAVITY(self.alignment)]; + MyGravity horzAlignment = [MyViewTraitsImpl convertLeadingTrailingGravityFromLeftRightGravity:MYHORZGRAVITY(self.alignment)]; if (layoutHorzGravity != MyGravity_None) { finalHorzGravity = layoutHorzGravity; if (horzAlignment != MyGravity_None) { @@ -675,14 +680,27 @@ - (MyGravity)finalHorzGravityFrom:(MyGravity)layoutHorzGravity { @end -@implementation MyLayoutTraits +@implementation MyLayoutTraitsImpl + +@synthesize zeroPadding = _zeroPadding; +@synthesize reverseLayout; +@synthesize layoutTransform = _layoutTransform; +@synthesize gravity; +@synthesize insetLandscapeFringePadding; +@synthesize paddingTop; +@synthesize paddingLeading; +@synthesize paddingBottom; +@synthesize paddingTrailing; +@synthesize insetsPaddingFromSafeArea = _insetsPaddingFromSafeArea; +@synthesize subviewVSpacing; +@synthesize subviewHSpacing; + - (id)init { self = [super init]; if (self != nil) { _zeroPadding = YES; _insetsPaddingFromSafeArea = UIRectEdgeLeft | UIRectEdgeRight; - _insetLandscapeFringePadding = NO; _layoutTransform = CGAffineTransformIdentity; } return self; @@ -700,11 +718,11 @@ - (void)setPadding:(UIEdgeInsets)padding { } - (CGFloat)paddingLeft { - return [MyViewTraits isRTL] ? self.paddingTrailing : self.paddingLeading; + return [MyViewTraitsImpl isRTL] ? self.paddingTrailing : self.paddingLeading; } - (void)setPaddingLeft:(CGFloat)paddingLeft { - if ([MyViewTraits isRTL]) { + if ([MyViewTraitsImpl isRTL]) { self.paddingTrailing = paddingLeft; } else { self.paddingLeading = paddingLeft; @@ -712,11 +730,11 @@ - (void)setPaddingLeft:(CGFloat)paddingLeft { } - (CGFloat)paddingRight { - return [MyViewTraits isRTL] ? self.paddingLeading : self.paddingTrailing; + return [MyViewTraitsImpl isRTL] ? self.paddingLeading : self.paddingTrailing; } - (void)setPaddingRight:(CGFloat)paddingRight { - if ([MyViewTraits isRTL]) { + if ([MyViewTraitsImpl isRTL]) { self.paddingLeading = paddingRight; } else { self.paddingTrailing = paddingRight; @@ -726,21 +744,11 @@ - (void)setPaddingRight:(CGFloat)paddingRight { - (CGFloat)myLayoutPaddingTop { //如果padding值是特殊的值。 if (self.paddingTop >= MyLayoutPos.safeAreaMargin - 2000 && self.paddingTop <= MyLayoutPos.safeAreaMargin + 2000) { - CGFloat paddingTopAdd = 20.0; //默认高度是状态栏的高度。 -#if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 110000) || (__TV_OS_VERSION_MAX_ALLOWED >= 110000) - if (@available(iOS 11.0, *)) { - paddingTopAdd = self.view.safeAreaInsets.top; - } -#endif - return self.paddingTop - MyLayoutPos.safeAreaMargin + paddingTopAdd; + return self.paddingTop - MyLayoutPos.safeAreaMargin + self.view.safeAreaInsets.top; } if ((self.insetsPaddingFromSafeArea & UIRectEdgeTop) == UIRectEdgeTop) { -#if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 110000) || (__TV_OS_VERSION_MAX_ALLOWED >= 110000) - if (@available(iOS 11.0, *)) { - return self.paddingTop + self.view.safeAreaInsets.top; - } -#endif + return self.paddingTop + self.view.safeAreaInsets.top; } return self.paddingTop; } @@ -748,111 +756,81 @@ - (CGFloat)myLayoutPaddingTop { - (CGFloat)myLayoutPaddingBottom { //如果padding值是特殊的值。 if (self.paddingBottom >= MyLayoutPos.safeAreaMargin - 2000 && self.paddingBottom <= MyLayoutPos.safeAreaMargin + 2000) { - CGFloat paddingBottomAdd = 0; -#if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 110000) || (__TV_OS_VERSION_MAX_ALLOWED >= 110000) - if (@available(iOS 11.0, *)) { - paddingBottomAdd = self.view.safeAreaInsets.bottom; - } -#endif - return self.paddingBottom - MyLayoutPos.safeAreaMargin + paddingBottomAdd; + return self.paddingBottom - MyLayoutPos.safeAreaMargin + self.view.safeAreaInsets.bottom; } if ((self.insetsPaddingFromSafeArea & UIRectEdgeBottom) == UIRectEdgeBottom) { -#if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 110000) || (__TV_OS_VERSION_MAX_ALLOWED >= 110000) - if (@available(iOS 11.0, *)) { - return self.paddingBottom + self.view.safeAreaInsets.bottom; - } -#endif + return self.paddingBottom + self.view.safeAreaInsets.bottom; } return self.paddingBottom; } - (CGFloat)myLayoutPaddingLeading { if (self.paddingLeading >= MyLayoutPos.safeAreaMargin - 2000 && self.paddingLeading <= MyLayoutPos.safeAreaMargin + 2000) { - CGFloat paddingLeadingAdd = 0; -#if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 110000) || (__TV_OS_VERSION_MAX_ALLOWED >= 110000) - if (@available(iOS 11.0, *)) { - paddingLeadingAdd = self.view.safeAreaInsets.left; //因为这里左右的缩进都是一样的,因此不需要考虑RTL的情况。 - } -#endif - return self.paddingLeading - MyLayoutPos.safeAreaMargin + paddingLeadingAdd; + return self.paddingLeading - MyLayoutPos.safeAreaMargin + self.view.safeAreaInsets.left; } CGFloat inset = 0; -#if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 110000) || (__TV_OS_VERSION_MAX_ALLOWED >= 110000) - if (@available(iOS 11.0, *)) { - UIRectEdge edge = [MyViewTraits isRTL] ? UIRectEdgeRight : UIRectEdgeLeft; + UIRectEdge edge = [MyViewTraitsImpl isRTL] ? UIRectEdgeRight : UIRectEdgeLeft; #if TARGET_OS_IOS - UIDeviceOrientation devori = [MyViewTraits isRTL] ? UIDeviceOrientationLandscapeLeft : UIDeviceOrientationLandscapeRight; + UIDeviceOrientation devori = [MyViewTraitsImpl isRTL] ? UIDeviceOrientationLandscapeLeft : UIDeviceOrientationLandscapeRight; #endif - if ((self.insetsPaddingFromSafeArea & edge) == edge) { + if ((self.insetsPaddingFromSafeArea & edge) == edge) { #if TARGET_OS_IOS - //如果只缩进刘海那一边。并且同时设置了左右缩进,并且当前刘海方向是尾部那么就不缩进了。 - if (self.insetLandscapeFringePadding && - (self.insetsPaddingFromSafeArea & (UIRectEdgeLeft | UIRectEdgeRight)) == (UIRectEdgeLeft | UIRectEdgeRight) && - [UIDevice currentDevice].orientation == devori) { - inset = 0; - } else + //如果只缩进刘海那一边。并且同时设置了左右缩进,并且当前刘海方向是尾部那么就不缩进了。 + if (self.insetLandscapeFringePadding && + (self.insetsPaddingFromSafeArea & (UIRectEdgeLeft | UIRectEdgeRight)) == (UIRectEdgeLeft | UIRectEdgeRight) && + [UIDevice currentDevice].orientation == devori) { + inset = 0; + } else #endif - inset = [MyViewTraits isRTL] ? self.view.safeAreaInsets.right : self.view.safeAreaInsets.left; - } + inset = [MyViewTraitsImpl isRTL] ? self.view.safeAreaInsets.right : self.view.safeAreaInsets.left; } -#endif return self.paddingLeading + inset; } - (CGFloat)myLayoutPaddingTrailing { if (self.paddingTrailing >= MyLayoutPos.safeAreaMargin - 2000 && self.paddingTrailing <= MyLayoutPos.safeAreaMargin + 2000) { - CGFloat paddingTrailingAdd = 0; -#if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 110000) || (__TV_OS_VERSION_MAX_ALLOWED >= 110000) - if (@available(iOS 11.0, *)) { - paddingTrailingAdd = self.view.safeAreaInsets.right; - } -#endif - return self.paddingTrailing - MyLayoutPos.safeAreaMargin + paddingTrailingAdd; + return self.paddingTrailing - MyLayoutPos.safeAreaMargin + self.view.safeAreaInsets.right; } CGFloat inset = 0; -#if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 110000) || (__TV_OS_VERSION_MAX_ALLOWED >= 110000) - if (@available(iOS 11.0, *)) { - UIRectEdge edge = [MyViewTraits isRTL] ? UIRectEdgeLeft : UIRectEdgeRight; + UIRectEdge edge = [MyViewTraitsImpl isRTL] ? UIRectEdgeLeft : UIRectEdgeRight; #if TARGET_OS_IOS - UIDeviceOrientation devori = [MyViewTraits isRTL] ? UIDeviceOrientationLandscapeRight : UIDeviceOrientationLandscapeLeft; + UIDeviceOrientation devori = [MyViewTraitsImpl isRTL] ? UIDeviceOrientationLandscapeRight : UIDeviceOrientationLandscapeLeft; #endif - if ((self.insetsPaddingFromSafeArea & edge) == edge) { + if ((self.insetsPaddingFromSafeArea & edge) == edge) { #if TARGET_OS_IOS - //如果只缩进刘海那一边。并且同时设置了左右缩进,并且当前刘海方向是头部那么就不缩进了。 - if (self.insetLandscapeFringePadding && - (self.insetsPaddingFromSafeArea & (UIRectEdgeLeft | UIRectEdgeRight)) == (UIRectEdgeLeft | UIRectEdgeRight) && - [UIDevice currentDevice].orientation == devori) { - inset = 0; - } else + //如果只缩进刘海那一边。并且同时设置了左右缩进,并且当前刘海方向是头部那么就不缩进了。 + if (self.insetLandscapeFringePadding && + (self.insetsPaddingFromSafeArea & (UIRectEdgeLeft | UIRectEdgeRight)) == (UIRectEdgeLeft | UIRectEdgeRight) && + [UIDevice currentDevice].orientation == devori) { + inset = 0; + } else #endif - inset = [MyViewTraits isRTL] ? self.view.safeAreaInsets.left : self.view.safeAreaInsets.right; - } + inset = [MyViewTraitsImpl isRTL] ? self.view.safeAreaInsets.left : self.view.safeAreaInsets.right; } -#endif return self.paddingTrailing + inset; } - (CGFloat)myLayoutPaddingLeft { - return [MyViewTraits isRTL] ? [self myLayoutPaddingTrailing] : [self myLayoutPaddingLeading]; + return [MyViewTraitsImpl isRTL] ? [self myLayoutPaddingTrailing] : [self myLayoutPaddingLeading]; } - (CGFloat)myLayoutPaddingRight { - return [MyViewTraits isRTL] ? [self myLayoutPaddingLeading] : [self myLayoutPaddingTrailing]; + return [MyViewTraitsImpl isRTL] ? [self myLayoutPaddingLeading] : [self myLayoutPaddingTrailing]; } -- (CGFloat)subviewSpace { - return self.subviewVSpace; +- (CGFloat)subviewSpacing { + return self.subviewVSpacing; } -- (void)setSubviewSpace:(CGFloat)subviewSpace { - self.subviewVSpace = subviewSpace; - self.subviewHSpace = subviewSpace; +- (void)setSubviewSpacing:(CGFloat)subviewSpacing { + self.subviewVSpacing = subviewSpacing; + self.subviewHSpacing = subviewSpacing; } - (id)copyWithZone:(NSZone *)zone { - MyLayoutTraits *layoutTraits = [super copyWithZone:zone]; + MyLayoutTraitsImpl *layoutTraits = [super copyWithZone:zone]; layoutTraits.paddingTop = self.paddingTop; layoutTraits.paddingLeading = self.paddingLeading; layoutTraits.paddingBottom = self.paddingBottom; @@ -863,22 +841,22 @@ - (id)copyWithZone:(NSZone *)zone { layoutTraits.gravity = self.gravity; layoutTraits.reverseLayout = self.reverseLayout; layoutTraits.layoutTransform = self.layoutTransform; - layoutTraits.subviewVSpace = self.subviewVSpace; - layoutTraits.subviewHSpace = self.subviewHSpace; + layoutTraits.subviewVSpacing = self.subviewVSpacing; + layoutTraits.subviewHSpacing = self.subviewHSpacing; return layoutTraits; } - (NSString *)debugDescription { NSString *dbgDesc = [super debugDescription]; - dbgDesc = [NSString stringWithFormat:@"%@\nLayout:\npadding=%@\nzeroPadding=%@\ngravity=%hu\nreverseLayout=%@\nsubviewVertSpace=%f\nsubviewHorzSpace=%f", + dbgDesc = [NSString stringWithFormat:@"%@\nLayout:\npadding=%@\nzeroPadding=%@\ngravity=%hu\nreverseLayout=%@\nsubviewVertSpacing=%f\nsubviewHorzSpacing=%f", dbgDesc, NSStringFromUIEdgeInsets(self.padding), self.zeroPadding ? @"YES" : @"NO", self.gravity, self.reverseLayout ? @"YES" : @"NO", - self.subviewVSpace, - self.subviewHSpace]; + self.subviewVSpacing, + self.subviewHSpacing]; return dbgDesc; } @@ -888,10 +866,11 @@ - (NSString *)debugDescription { - (NSMutableArray *)filterEngines:(NSMutableArray *)subviewEngines { if (subviewEngines == nil) { - subviewEngines = [NSMutableArray arrayWithCapacity:self.view.subviews.count]; - for (UIView *subview in self.view.subviews) { - [subviewEngines addObject:subview.myEngine]; - } + NSAssert(NO, @"oops!"); +// subviewEngines = [NSMutableArray arrayWithCapacity:self.view.subviews.count]; +// for (UIView *subview in self.view.subviews) { +// [subviewEngines addObject:subview.myEngine]; +// } } NSInteger count = subviewEngines.count; @@ -903,7 +882,7 @@ - (NSString *)debugDescription { } for (NSInteger i = count - 1; i >= 0; i--) { - MyViewTraits *subviewTraits = subviewEngines[i].currentSizeClass; + MyViewTraitsImpl *subviewTraits = subviewEngines[i].currentTraits; if ([subviewTraits invalid]) { [subviewEngines removeObjectAtIndex:i]; } @@ -917,72 +896,74 @@ - (NSString *)debugDescription { @implementation MySequentLayoutFlexSpacing -- (CGFloat)calcMaxMinSubviewSizeForContent:(CGFloat)selfSize paddingStart:(CGFloat *)pStarPadding paddingEnd:(CGFloat *)pEndPadding space:(CGFloat *)pSpace { - CGFloat subviewSize = self.subviewSize; +- (CGFloat)calcMaxMinSubviewsSizeForContent:(CGFloat)selfSize paddingStart:(CGFloat *)pStarPadding paddingEnd:(CGFloat *)pEndPadding spacing:(CGFloat *)pSpacing { + CGFloat subviewsSize = self.subviewsSize; - CGFloat extralSpace = self.minSpace; + CGFloat extralSpacing = self.minSpacing; if (self.centered) { - extralSpace *= -1; + extralSpacing *= -1; } - NSInteger rowCount = MAX(floor((selfSize - (*pStarPadding) - (*pEndPadding) + extralSpace) / (subviewSize + self.minSpace)), 1); - NSInteger spaceCount = rowCount - 1; + NSInteger rowCount = MAX(floor((selfSize - (*pStarPadding) - (*pEndPadding) + extralSpacing) / (subviewsSize + self.minSpacing)), 1); + NSInteger spacingCount = rowCount - 1; if (self.centered) { - spaceCount += 2; + spacingCount += 2; } - if (spaceCount > 0) { - *pSpace = (selfSize - (*pStarPadding) - (*pEndPadding) - subviewSize * rowCount) / spaceCount; - if (_myCGFloatGreat(*pSpace, self.maxSpace)) { - *pSpace = self.maxSpace; - subviewSize = (selfSize - (*pStarPadding) - (*pEndPadding) - (*pSpace) * spaceCount) / rowCount; + if (spacingCount > 0) { + *pSpacing = (selfSize - (*pStarPadding) - (*pEndPadding) - subviewsSize * rowCount) / spacingCount; + if (_myCGFloatGreat(*pSpacing, self.maxSpacing)) { + *pSpacing = self.maxSpacing; + subviewsSize = (selfSize - (*pStarPadding) - (*pEndPadding) - (*pSpacing) * spacingCount) / rowCount; } if (self.centered) { - *pStarPadding = (*pStarPadding + *pSpace); - *pEndPadding = (*pEndPadding + *pSpace); + *pStarPadding = (*pStarPadding + *pSpacing); + *pEndPadding = (*pEndPadding + *pSpacing); } } - return subviewSize; + return subviewsSize; } -- (CGFloat)calcMaxMinSubviewSize:(CGFloat)selfSize arrangedCount:(NSInteger)arrangedCount paddingStart:(CGFloat *)pStarPadding paddingEnd:(CGFloat *)pEndPadding space:(CGFloat *)pSpace { - CGFloat subviewSize = self.subviewSize; +- (CGFloat)calcMaxMinSubviewsSize:(CGFloat)selfSize arrangedCount:(NSInteger)arrangedCount paddingStart:(CGFloat *)pStarPadding paddingEnd:(CGFloat *)pEndPadding spacing:(CGFloat *)pSpacing { + CGFloat subviewsSize = self.subviewsSize; - NSInteger spaceCount = arrangedCount - 1; + NSInteger spacingCount = arrangedCount - 1; if (self.centered) { - spaceCount += 2; + spacingCount += 2; } - if (spaceCount > 0) { - *pSpace = (selfSize - *pStarPadding - *pEndPadding - subviewSize * arrangedCount) / spaceCount; + if (spacingCount > 0) { + *pSpacing = (selfSize - *pStarPadding - *pEndPadding - subviewsSize * arrangedCount) / spacingCount; - if (_myCGFloatGreat(*pSpace, self.maxSpace) || _myCGFloatLess(*pSpace, self.minSpace)) { - if (_myCGFloatGreat(*pSpace, self.maxSpace)) { - *pSpace = self.maxSpace; + if (_myCGFloatGreat(*pSpacing, self.maxSpacing) || _myCGFloatLess(*pSpacing, self.minSpacing)) { + if (_myCGFloatGreat(*pSpacing, self.maxSpacing)) { + *pSpacing = self.maxSpacing; } - if (_myCGFloatLess(*pSpace, self.minSpace)) { - *pSpace = self.minSpace; + if (_myCGFloatLess(*pSpacing, self.minSpacing)) { + *pSpacing = self.minSpacing; } - subviewSize = (selfSize - *pStarPadding - *pEndPadding - (*pSpace) * spaceCount) / arrangedCount; + subviewsSize = (selfSize - *pStarPadding - *pEndPadding - (*pSpacing) * spacingCount) / arrangedCount; } if (self.centered) { - *pStarPadding = (*pStarPadding + *pSpace); - *pEndPadding = (*pEndPadding + *pSpace); + *pStarPadding = (*pStarPadding + *pSpacing); + *pEndPadding = (*pEndPadding + *pSpacing); } } - return subviewSize; + return subviewsSize; } @end -@implementation MySequentLayoutTraits +@implementation MySequentLayoutTraitsImpl + +@synthesize orientation; - (id)copyWithZone:(NSZone *)zone { - MySequentLayoutTraits *layoutTraits = [super copyWithZone:zone]; + MySequentLayoutTraitsImpl *layoutTraits = [super copyWithZone:zone]; layoutTraits.orientation = self.orientation; - if (self.flexSpace != nil) { - layoutTraits.flexSpace = [MySequentLayoutFlexSpacing new]; - layoutTraits.flexSpace.subviewSize = self.flexSpace.subviewSize; - layoutTraits.flexSpace.minSpace = self.flexSpace.minSpace; - layoutTraits.flexSpace.maxSpace = self.flexSpace.maxSpace; - layoutTraits.flexSpace.centered = self.flexSpace.centered; + if (self.flexSpacing != nil) { + layoutTraits.flexSpacing = [MySequentLayoutFlexSpacing new]; + layoutTraits.flexSpacing.subviewsSize = self.flexSpacing.subviewsSize; + layoutTraits.flexSpacing.minSpacing = self.flexSpacing.minSpacing; + layoutTraits.flexSpacing.maxSpacing = self.flexSpacing.maxSpacing; + layoutTraits.flexSpacing.centered = self.flexSpacing.centered; } return layoutTraits; @@ -999,10 +980,13 @@ - (NSString *)debugDescription { @end -@implementation MyLinearLayoutTraits +@implementation MyLinearLayoutTraitsImpl + +@synthesize shrinkType; + - (id)copyWithZone:(NSZone *)zone { - MyLinearLayoutTraits *layoutTraits = [super copyWithZone:zone]; + MyLinearLayoutTraitsImpl *layoutTraits = [super copyWithZone:zone]; layoutTraits.shrinkType = self.shrinkType; return layoutTraits; } @@ -1017,27 +1001,45 @@ - (NSString *)debugDescription { @end -@implementation MyTableLayoutTraits +@implementation MyTableLayoutTraitsImpl @end -@implementation MyFloatLayoutTraits +@implementation MyFloatLayoutTraitsImpl - (id)copyWithZone:(NSZone *)zone { - MyFloatLayoutTraits *layoutTraits = [super copyWithZone:zone]; + MyFloatLayoutTraitsImpl *layoutTraits = [super copyWithZone:zone]; return layoutTraits; } @end -@implementation MyFlowLayoutTraits +@implementation MyFlowLayoutTraitsImpl + +@synthesize arrangedGravity; +@synthesize autoArrange; +@synthesize isFlex; +@synthesize lastlineGravityPolicy; +@synthesize maxLines = _maxLines; +@synthesize arrangedCount; +@synthesize pagedCount; + + +- (instancetype)init { + self = [super init]; + if (self != nil) { + _maxLines = NSIntegerMax; + } + return self; +} - (id)copyWithZone:(NSZone *)zone { - MyFlowLayoutTraits *layoutTraits = [super copyWithZone:zone]; + MyFlowLayoutTraitsImpl *layoutTraits = [super copyWithZone:zone]; layoutTraits.arrangedCount = self.arrangedCount; layoutTraits.autoArrange = self.autoArrange; layoutTraits.isFlex = self.isFlex; layoutTraits.lastlineGravityPolicy = self.lastlineGravityPolicy; + layoutTraits.maxLines = self.maxLines; layoutTraits.arrangedGravity = self.arrangedGravity; layoutTraits.pagedCount = self.pagedCount; return layoutTraits; @@ -1045,36 +1047,37 @@ - (id)copyWithZone:(NSZone *)zone { - (NSString *)debugDescription { NSString *dbgDesc = [super debugDescription]; - dbgDesc = [NSString stringWithFormat:@"%@\nFlowLayout: \narrangedCount=%ld\nautoArrange=%@\nisFlex=%@\narrangedGravity=%hu\npagedCount=%ld", + dbgDesc = [NSString stringWithFormat:@"%@\nFlowLayout: \narrangedCount=%ld\nautoArrange=%@\nisFlex=%@\narrangedGravity=%hu\npagedCount=%ld\nmaxLines=%ld", dbgDesc, (long)self.arrangedCount, self.autoArrange ? @"YES" : @"NO", self.isFlex ? @"YES" : @"NO", self.arrangedGravity, - (long)self.pagedCount]; + (long)self.pagedCount, + (long)self.maxLines]; return dbgDesc; } @end -@implementation MyRelativeLayoutTraits +@implementation MyRelativeLayoutTraitsImpl @end -@implementation MyFrameLayoutTraits +@implementation MyFrameLayoutTraitsImpl @end -@implementation MyPathLayoutTraits +@implementation MyPathLayoutTraitsImpl @end -@interface MyGridLayoutTraits () +@interface MyGridLayoutTraitsImpl () @property (nonatomic, strong) MyGridNode *rootGrid; @end -@implementation MyGridLayoutTraits +@implementation MyGridLayoutTraitsImpl - (MyGridNode *)rootGrid { if (_rootGrid == nil) { @@ -1325,7 +1328,7 @@ - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event } - (id)copyWithZone:(NSZone *)zone { - MyGridLayoutTraits *lsc = [super copyWithZone:zone]; + MyGridLayoutTraitsImpl *lsc = [super copyWithZone:zone]; lsc->_rootGrid = (MyGridNode *)[self.rootGrid cloneGrid]; return lsc; } diff --git a/MyLayout/Lib/MyLinearLayout.h b/MyLayout/Lib/MyLinearLayout.h index f6b74bb..3ab4776 100644 --- a/MyLayout/Lib/MyLinearLayout.h +++ b/MyLayout/Lib/MyLinearLayout.h @@ -33,7 +33,7 @@ ⥤ @endcode */ -@interface MyLinearLayout : MyBaseLayout +@interface MyLinearLayout : MyBaseLayout< id > /** 初始化一个线性布局,并指定子视图排列的方向。如果不明确指定方向则默认是建立一个垂直线性布局。 @@ -104,7 +104,7 @@ MyLinearLayout *horzLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Horz]; horzLayout.myHorzMargin = 0; horzLayout.myHeight = MyLayoutSize.wrap; - horzLayout.subviewSpace = 10; //二者的最小间距不能小于20 + horzLayout.subviewSpacing = 10; //二者的最小间距不能小于20 horzLayout.shrinkType = MySubviewsShrink_Auto; UILabel *A = [UILabel new]; @@ -123,14 +123,14 @@ [horzLayout addSubview:B]; @endcode - 5. MySubviewsShrink_Average | MySubviewsShrink_Space (平均压缩间距) + 5. MySubviewsShrink_Average | MySubviewsShrink_Spacing (平均压缩间距) 这种情况下,我们只会压缩那些具有固定间距的视图:A,D的间距,每个子视图的压缩的值是:剩余的尺寸40 / 固定间距的视图的数量2 = 20。 这样: @code A的最终topPos = 10 - 20 = -10 D的最终topPos = 20 - 20 = 0 @endcode - 6. MySubviewsShrink_Weight | MySubviewsShrink_Space: (按比重压缩间距) + 6. MySubviewsShrink_Weight | MySubviewsShrink_Spacing: (按比重压缩间距) 这种情况下,我们只会压缩那些具有固定间距的视图:A,D的间距,这些总的间距为10+20 = 30. 这样: @code A的最终topPos = 10 - 40 *(10/30) = -3 @@ -165,10 +165,10 @@ @note 这个方法只对已经加入布局的视图有效,方法调用后加入的子视图无效。 @param centered 指定是否所有子视图居中,当居中时对于垂直线性布局来说顶部和底部会保留出间距,而不居中时则顶部和底部不留出间距。水平线性布局则是指头部和尾部 - @param space 子视图之间的间距。 + @param spacing 子视图之间的间距。 */ -- (void)equalizeSubviews:(BOOL)centered withSpace:(CGFloat)space; -- (void)equalizeSubviews:(BOOL)centered withSpace:(CGFloat)space inSizeClass:(MySizeClass)sizeClass; +- (void)equalizeSubviews:(BOOL)centered withSpacing:(CGFloat)spacing; +- (void)equalizeSubviews:(BOOL)centered withSpacing:(CGFloat)spacing inSizeClass:(MySizeClass)sizeClass; /** 均分子视图之间的间距,上面方法会均分子视图的尺寸以及间距,而这个方法则子视图的尺寸保持不变而间距自动平均分配,也就是用布局视图的剩余空间来均分间距 @@ -177,24 +177,36 @@ @param centered 参数描述是否所有子视图居中,当居中时对于垂直线性布局来说顶部和底部会保留出间距,而不居中时则顶部和底部不保持间距。水平线性布局则是指头部和尾部 */ -- (void)equalizeSubviewsSpace:(BOOL)centered; -- (void)equalizeSubviewsSpace:(BOOL)centered inSizeClass:(MySizeClass)sizeClass; +- (void)equalizeSubviewsSpacing:(BOOL)centered; +- (void)equalizeSubviewsSpacing:(BOOL)centered inSizeClass:(MySizeClass)sizeClass; /** 在一些应用场景中我们希望子视图的宽度是固定的但间距是浮动的,这样就尽可能在一排中容纳更多的子视图。比如设置每个子视图的宽度固定为80,那么在小屏幕下每排只能放3个,而大屏幕则每排能放4个或者5个子视图。 因此您可以通过如下方法来设置子视图的固定尺寸和最小最大浮动间距。这个方法会根据您当前布局的方向不同而具有不同的意义: - 1.如果您的布局方向是垂直的则设置的是每行内子视图的水平浮动间距,其中的subviewSize指定的是子视图的固定宽度;minSpace指定的是最小的水平间距;maxSpace指定的是最大的水平间距,如果指定的subviewSize计算出的间距大于最大间距maxSpace则会缩小subviewSize的宽度值。 + 1.如果您的布局方向是垂直的则设置的是每行内子视图的水平浮动间距,其中的subviewSize指定的是子视图的固定宽度;minSpacing指定的是最小的水平间距;maxSpacing指定的是最大的水平间距,如果指定的subviewSize计算出的间距大于最大间距maxSpacing则会缩小subviewSize的宽度值。 - 2.如果您的布局方向是水平的则设置的是每列内子视图的垂直浮动间距,其中的subviewSize指定的是子视图的固定高度;minSpace指定的是最小的垂直间距;maxSpace指定的是最大的垂直间距,如果指定的subviewSize计算出的间距大于最大间距maxSpace则会调整subviewSize的高度值。 + 2.如果您的布局方向是水平的则设置的是每列内子视图的垂直浮动间距,其中的subviewSize指定的是子视图的固定高度;minSpacing指定的是最小的垂直间距;maxSpacing指定的是最大的垂直间距,如果指定的subviewSize计算出的间距大于最大间距maxSpacing则会调整subviewSize的高度值。 @note 如果您不想使用浮动间距则请将subviewSize设置为0就可以了。 - @param subviewSize 指定子视图的尺寸。 - @param minSpace 指定子视图之间的最小间距 - @param maxSpace 指定子视图之间的最大间距 + @param subviewsSize 指定子视图的尺寸。 + @param minSpacing 指定子视图之间的最小间距 + @param maxSpacing 指定子视图之间的最大间距 @param centered 指定是否所有子视图居中 */ -- (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace centered:(BOOL)centered; -- (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace centered:(BOOL)centered inSizeClass:(MySizeClass)sizeClass; +- (void)setSubviewsSize:(CGFloat)subviewsSize minSpacing:(CGFloat)minSpacing maxSpacing:(CGFloat)maxSpacing centered:(BOOL)centered; +- (void)setSubviewsSize:(CGFloat)subviewsSize minSpacing:(CGFloat)minSpacing maxSpacing:(CGFloat)maxSpacing centered:(BOOL)centered inSizeClass:(MySizeClass)sizeClass; + +@end + + +@interface MyLinearLayout (MyDeprecated) + +- (void)equalizeSubviews:(BOOL)centered withSpace:(CGFloat)space MYDEPRECATED("use equalizeSubviews:withSpacing: instead"); +- (void)equalizeSubviews:(BOOL)centered withSpace:(CGFloat)space inSizeClass:(MySizeClass)sizeClass MYDEPRECATED("use equalizeSubviews:withSpacing:inSizeClass: instead"); +- (void)equalizeSubviewsSpace:(BOOL)centered MYDEPRECATED("use equalizeSubviewsSpacing: instead"); +- (void)equalizeSubviewsSpace:(BOOL)centered inSizeClass:(MySizeClass)sizeClass MYDEPRECATED("use equalizeSubviewsSpacing:inSizeClass: instead"); +- (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace centered:(BOOL)centered MYDEPRECATED("use setSubviewsSize:minSpacing:maxSpacing:centered: instead"); +- (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace centered:(BOOL)centered inSizeClass:(MySizeClass)sizeClass MYDEPRECATED("use setSubviewsSize:minSpacing:maxSpacing:centered:inSizeClass: instead"); @end diff --git a/MyLayout/Lib/MyLinearLayout.m b/MyLayout/Lib/MyLinearLayout.m index 221466d..2625084 100644 --- a/MyLayout/Lib/MyLinearLayout.m +++ b/MyLayout/Lib/MyLinearLayout.m @@ -16,7 +16,7 @@ @implementation MyLinearLayout - (instancetype)initWithFrame:(CGRect)frame orientation:(MyOrientation)orientation { self = [super initWithFrame:frame]; if (self) { - MyLinearLayoutTraits *layoutTraits = (MyLinearLayoutTraits*)self.myDefaultSizeClass; + MyLinearLayoutTraitsImpl *layoutTraits = self.myDefaultTraits; if (orientation == MyOrientation_Vert) { [layoutTraits.heightSize _myEqualTo:@(MyLayoutSize.wrap) priority:MyPriority_Low]; //这是暂时先设置为低优先级,为了兼容老版本。 } else { @@ -35,88 +35,96 @@ + (instancetype)linearLayoutWithOrientation:(MyOrientation)orientation { return [[[self class] alloc] initWithOrientation:orientation]; } -- (void)setOrientation:(MyOrientation)orientation { - MyLinearLayoutTraits *layoutTraits = (MyLinearLayoutTraits*)self.myDefaultSizeClass; - if (layoutTraits.orientation != orientation) { - layoutTraits.orientation = orientation; - [self setNeedsLayout]; - } -} - -- (MyOrientation)orientation { - return self.myDefaultSizeClassInner.orientation; -} +_MYLAYOUT_PROPERTY_LAYOUTVIEW_OVERRIDE1(MyLinearLayoutTraitsImpl, MyOrientation, Orientation, orientation) -- (void)setShrinkType:(MySubviewsShrinkType)shrinkType { - MyLinearLayoutTraits *layoutTraits = (MyLinearLayoutTraits*)self.myDefaultSizeClass; - if (layoutTraits.shrinkType != shrinkType) { - layoutTraits.shrinkType = shrinkType; - [self setNeedsLayout]; - } -} - -- (MySubviewsShrinkType)shrinkType { - return self.myDefaultSizeClassInner.shrinkType; -} +_MYLAYOUT_PROPERTY_LAYOUTVIEW_OVERRIDE1(MyLinearLayoutTraitsImpl, MySubviewsShrinkType, ShrinkType, shrinkType) - (void)equalizeSubviews:(BOOL)centered { [self equalizeSubviews:centered inSizeClass:MySizeClass_hAny | MySizeClass_wAny]; } - (void)equalizeSubviews:(BOOL)centered inSizeClass:(MySizeClass)sizeClass { - [self equalizeSubviews:centered withSpace:CGFLOAT_MAX inSizeClass:sizeClass]; + [self equalizeSubviews:centered withSpacing:CGFLOAT_MAX inSizeClass:sizeClass]; } -- (void)equalizeSubviews:(BOOL)centered withSpace:(CGFloat)space { - [self equalizeSubviews:centered withSpace:space inSizeClass:MySizeClass_hAny | MySizeClass_wAny]; +- (void)equalizeSubviews:(BOOL)centered withSpacing:(CGFloat)spacing { + [self equalizeSubviews:centered withSpacing:spacing inSizeClass:MySizeClass_hAny | MySizeClass_wAny]; } -- (void)equalizeSubviews:(BOOL)centered withSpace:(CGFloat)space inSizeClass:(MySizeClass)sizeClass { +- (void)equalizeSubviews:(BOOL)centered withSpacing:(CGFloat)spacing inSizeClass:(MySizeClass)sizeClass { NSArray *tuple = [self myUpdateCurrentSizeClass:sizeClass subviews:nil]; MyLayoutEngine *layoutViewEngine = tuple.firstObject; NSMutableArray *subviewEngines = tuple.lastObject; - MyLinearLayoutTraits *layoutTraits = (MyLinearLayoutTraits*)layoutViewEngine.currentSizeClass; + MyLinearLayoutTraitsImpl *layoutTraits = layoutViewEngine.currentTraits; subviewEngines = [layoutTraits filterEngines:subviewEngines]; - [self myEqualizeSubviewEngines:subviewEngines orientation:layoutTraits.orientation centered:centered space:space]; + [self myEqualizeSubviewEngines:subviewEngines orientation:layoutTraits.orientation centered:centered spacing:spacing]; [self setNeedsLayout]; } -- (void)equalizeSubviewsSpace:(BOOL)centered { - [self equalizeSubviewsSpace:centered inSizeClass:MySizeClass_hAny | MySizeClass_wAny]; +- (void)equalizeSubviewsSpacing:(BOOL)centered { + [self equalizeSubviewsSpacing:centered inSizeClass:MySizeClass_hAny | MySizeClass_wAny]; } -- (void)equalizeSubviewsSpace:(BOOL)centered inSizeClass:(MySizeClass)sizeClass { +- (void)equalizeSubviewsSpacing:(BOOL)centered inSizeClass:(MySizeClass)sizeClass { NSArray *tuple = [self myUpdateCurrentSizeClass:sizeClass subviews:nil]; MyLayoutEngine *layoutViewEngine = tuple.firstObject; NSMutableArray *subviewEngines = tuple.lastObject; - MyLinearLayoutTraits *layoutTraits = (MyLinearLayoutTraits*)layoutViewEngine.currentSizeClass; + MyLinearLayoutTraitsImpl *layoutTraits = layoutViewEngine.currentTraits; subviewEngines = [layoutTraits filterEngines:subviewEngines]; [self myEqualizeSubviewEngines:subviewEngines orientation:layoutTraits.orientation centered:centered]; [self setNeedsLayout]; } -- (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace centered:(BOOL)centered { - [self setSubviewsSize:subviewSize minSpace:minSpace maxSpace:maxSpace centered:centered inSizeClass:MySizeClass_hAny | MySizeClass_wAny]; +- (void)setSubviewsSize:(CGFloat)subviewsSize minSpacing:(CGFloat)minSpacing maxSpacing:(CGFloat)maxSpacing centered:(BOOL)centered { + [self setSubviewsSize:subviewsSize minSpacing:minSpacing maxSpacing:maxSpacing centered:centered inSizeClass:MySizeClass_hAny | MySizeClass_wAny]; } -- (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace centered:(BOOL)centered inSizeClass:(MySizeClass)sizeClass { - MySequentLayoutTraits *layoutTraits = (MySequentLayoutTraits *)[self fetchLayoutSizeClass:sizeClass]; - if (subviewSize == 0.0) { - layoutTraits.flexSpace = nil; +- (void)setSubviewsSize:(CGFloat)subviewsSize minSpacing:(CGFloat)minSpacing maxSpacing:(CGFloat)maxSpacing centered:(BOOL)centered inSizeClass:(MySizeClass)sizeClass { + MySequentLayoutTraitsImpl *layoutTraits = (MySequentLayoutTraitsImpl *)[self fetchLayoutTraitsInSizeClass:sizeClass]; + if (subviewsSize == 0.0) { + layoutTraits.flexSpacing = nil; } else { - if (layoutTraits.flexSpace == nil) { - layoutTraits.flexSpace = [MySequentLayoutFlexSpacing new]; + if (layoutTraits.flexSpacing == nil) { + layoutTraits.flexSpacing = [MySequentLayoutFlexSpacing new]; } - layoutTraits.flexSpace.subviewSize = subviewSize; - layoutTraits.flexSpace.minSpace = minSpace; - layoutTraits.flexSpace.maxSpace = maxSpace; - layoutTraits.flexSpace.centered = centered; + layoutTraits.flexSpacing.subviewsSize = subviewsSize; + layoutTraits.flexSpacing.minSpacing = minSpacing; + layoutTraits.flexSpacing.maxSpacing = maxSpacing; + layoutTraits.flexSpacing.centered = centered; } [self setNeedsLayout]; } + +///.. + +- (void)equalizeSubviews:(BOOL)centered withSpace:(CGFloat)space { + [self equalizeSubviews:centered withSpacing:space]; +} + +- (void)equalizeSubviews:(BOOL)centered withSpace:(CGFloat)space inSizeClass:(MySizeClass)sizeClass { + [self equalizeSubviews:centered withSpacing:space inSizeClass:sizeClass]; +} + +- (void)equalizeSubviewsSpace:(BOOL)centered { + [self equalizeSubviewsSpacing:centered]; +} + +- (void)equalizeSubviewsSpace:(BOOL)centered inSizeClass:(MySizeClass)sizeClass { + [self equalizeSubviewsSpacing:centered inSizeClass:sizeClass]; +} + +- (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace centered:(BOOL)centered { + [self setSubviewsSize:subviewSize minSpacing:minSpace maxSpacing:maxSpace centered:centered]; +} + +- (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace centered:(BOOL)centered inSizeClass:(MySizeClass)sizeClass { + [self setSubviewsSize:subviewSize minSpacing:minSpace maxSpacing:maxSpace centered:centered inSizeClass:sizeClass]; +} + + #pragma mark-- Override Methods - (void)willRemoveSubview:(UIView *)subview { @@ -129,23 +137,11 @@ - (void)willRemoveSubview:(UIView *)subview { - (CGSize)calcLayoutSize:(CGSize)size subviewEngines:(NSMutableArray *)subviewEngines context:(MyLayoutContext *)context { [super calcLayoutSize:size subviewEngines:subviewEngines context:context]; - MyLinearLayoutTraits *layoutTraits = (MyLinearLayoutTraits *)context->layoutViewEngine.currentSizeClass; - context->paddingTop = layoutTraits.myLayoutPaddingTop; - context->paddingBottom = layoutTraits.myLayoutPaddingBottom; - context->paddingLeading = layoutTraits.myLayoutPaddingLeading; - context->paddingTrailing = layoutTraits.myLayoutPaddingTrailing; - context->vertGravity = MYVERTGRAVITY(layoutTraits.gravity); - context->horzGravity = [MyViewTraits convertLeadingTrailingGravityFromLeftRightGravity:MYHORZGRAVITY(layoutTraits.gravity)]; - context->vertSpace = layoutTraits.subviewVSpace; - context->horzSpace = layoutTraits.subviewHSpace; - if (context->subviewEngines == nil) { - context->subviewEngines = [layoutTraits filterEngines:subviewEngines]; - } - + MyLinearLayoutTraitsImpl *layoutTraits = context->layoutViewEngine.currentTraits; MyOrientation orientation = layoutTraits.orientation; [self myCalcSubviewsWrapContentSize:context - withCustomSetting:^(MyViewTraits *subviewTraits) { + withCustomSetting:^(MyViewTraitsImpl *subviewTraits) { [self myLayout:layoutTraits orientation:orientation gravity:(orientation == MyOrientation_Vert) ? context->horzGravity : context->vertGravity adjustSizeSettingOfSubview:subviewTraits]; }]; @@ -159,14 +155,14 @@ - (CGSize)calcLayoutSize:(CGSize)size subviewEngines:(NSMutableArraylayoutViewEngine.currentSizeClass; + MyLinearLayoutTraitsImpl *layoutTraits = context->layoutViewEngine.currentTraits; NSArray *subviewEngines = context->subviewEngines; if (self.intelligentBorderline == nil) { return; } BOOL isVert = (layoutTraits.orientation == MyOrientation_Vert); - CGFloat subviewSpace = (layoutTraits.orientation == MyOrientation_Vert) ? layoutTraits.subviewVSpace : layoutTraits.subviewHSpace; + CGFloat subviewSpacing = (layoutTraits.orientation == MyOrientation_Vert) ? layoutTraits.subviewVSpacing : layoutTraits.subviewHSpacing; for (int i = 0; i < subviewEngines.count; i++) { - UIView *subview = subviewEngines[i].currentSizeClass.view; + UIView *subview = subviewEngines[i].currentTraits.view; if (![subview isKindOfClass:[MyBaseLayout class]]) { continue; } - MyBaseLayout *sublayoutview = (MyBaseLayout *)subview; - if (sublayoutview.notUseIntelligentBorderline) { + MyBaseLayout *sublayout = (MyBaseLayout *)subview; + if (sublayout.notUseIntelligentBorderline) { continue; } if (isVert) { - sublayoutview.topBorderline = nil; - sublayoutview.bottomBorderline = nil; + sublayout.topBorderline = nil; + sublayout.bottomBorderline = nil; } else { - sublayoutview.leadingBorderline = nil; - sublayoutview.trailingBorderline = nil; + sublayout.leadingBorderline = nil; + sublayout.trailingBorderline = nil; } UIView *prevSiblingView = nil; UIView *nextSiblingView = nil; if (i != 0) { - prevSiblingView = subviewEngines[i - 1].currentSizeClass.view; + prevSiblingView = subviewEngines[i - 1].currentTraits.view; } if (i + 1 != subviewEngines.count) { - nextSiblingView = subviewEngines[i + 1].currentSizeClass.view; + nextSiblingView = subviewEngines[i + 1].currentTraits.view; } if (prevSiblingView != nil) { BOOL ok = YES; - if ([prevSiblingView isKindOfClass:[MyBaseLayout class]] && subviewSpace == 0) { + if ([prevSiblingView isKindOfClass:[MyBaseLayout class]] && subviewSpacing == 0) { MyBaseLayout *prevSiblingLayout = (MyBaseLayout *)prevSiblingView; if (prevSiblingLayout.notUseIntelligentBorderline) { ok = NO; @@ -246,32 +242,32 @@ - (void)mySetIntelligentBorderlineWithContext:(MyLayoutContext *)context { if (ok) { if (isVert) { - sublayoutview.topBorderline = self.intelligentBorderline; + sublayout.topBorderline = self.intelligentBorderline; } else { - sublayoutview.leadingBorderline = self.intelligentBorderline; + sublayout.leadingBorderline = self.intelligentBorderline; } } } - if (nextSiblingView != nil && (![nextSiblingView isKindOfClass:[MyBaseLayout class]] || subviewSpace != 0)) { + if (nextSiblingView != nil && (![nextSiblingView isKindOfClass:[MyBaseLayout class]] || subviewSpacing != 0)) { if (isVert) { - sublayoutview.bottomBorderline = self.intelligentBorderline; + sublayout.bottomBorderline = self.intelligentBorderline; } else { - sublayoutview.trailingBorderline = self.intelligentBorderline; + sublayout.trailingBorderline = self.intelligentBorderline; } } } } - (void)myDoVertOrientationLayoutWithContext:(MyLayoutContext *)context { - MyLinearLayoutTraits *layoutTraits = (MyLinearLayoutTraits *)context->layoutViewEngine.currentSizeClass; + MyLinearLayoutTraitsImpl *layoutTraits = context->layoutViewEngine.currentTraits; - CGFloat subviewHeight = [layoutTraits.flexSpace calcMaxMinSubviewSize:context->selfSize.height arrangedCount:context->subviewEngines.count paddingStart:&context->paddingTop paddingEnd:&context->paddingBottom space:&context->vertSpace]; + CGFloat subviewHeight = [layoutTraits.flexSpacing calcMaxMinSubviewsSize:context->selfSize.height arrangedCount:context->subviewEngines.count paddingStart:&context->paddingTop paddingEnd:&context->paddingBottom spacing:&context->vertSpacing]; CGFloat paddingVert = context->paddingTop + context->paddingBottom; CGFloat totalHeight = 0.0; //计算固定部分的总高度 CGFloat totalWeight = 0.0; //剩余部分的总比重 CGFloat totalShrink = 0.0; //总的压缩比重 - CGFloat addSpace = 0.0; //用于压缩时的间距压缩增量。 + CGFloat addSpacing = 0.0; //用于压缩时的间距压缩增量。 CGFloat maxLayoutWidth = 0.0; //高度可以被伸缩的子视图集合 @@ -283,7 +279,7 @@ - (void)myDoVertOrientationLayoutWithContext:(MyLayoutContext *)context { CGFloat totalFixedSpacing = 0.0; //固定间距的子视图的高度。 CGFloat pos = context->paddingTop; for (MyLayoutEngine *subviewEngine in context->subviewEngines) { - MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; if (context->vertGravity == MyGravity_Vert_Fill || context->vertGravity == MyGravity_Vert_Stretch) { BOOL isFlexEngine = YES; //比重高度的子视图不能被伸缩。 @@ -399,11 +395,11 @@ - (void)myDoVertOrientationLayoutWithContext:(MyLayoutContext *)context { pos += subviewTraits.bottomPosInner.measure; if (subviewEngine != context->subviewEngines.lastObject) { - totalHeight += context->vertSpace; - pos += context->vertSpace; - if (context->vertSpace != 0.0) { + totalHeight += context->vertSpacing; + pos += context->vertSpacing; + if (context->vertSpacing != 0.0) { fixedSpacingSubviewCount += 1; - totalFixedSpacing += context->vertSpace; + totalFixedSpacing += context->vertSpacing; } } } @@ -412,7 +408,7 @@ - (void)myDoVertOrientationLayoutWithContext:(MyLayoutContext *)context { if (totalWeight != 0.0) { //在包裹高度且总体比重不为0时则,则需要还原最小的高度,这样就不会使得高度在横竖屏或者多次计算后越来高。 CGFloat tempLayoutHeight = paddingVert; if (context->subviewEngines.count > 1) { - tempLayoutHeight += (context->subviewEngines.count - 1) * context->vertSpace; + tempLayoutHeight += (context->subviewEngines.count - 1) * context->vertSpacing; } context->selfSize.height = [self myValidMeasure:layoutTraits.heightSizeInner subview:self calcSize:tempLayoutHeight subviewSize:context->selfSize selfLayoutSize:self.superview.bounds.size]; } else { @@ -455,10 +451,10 @@ - (void)myDoVertOrientationLayoutWithContext:(MyLayoutContext *)context { } } } - } else if (sstContent == MySubviewsShrink_Space) { //压缩间距 + } else if (sstContent == MySubviewsShrink_Spacing) { //压缩间距 if (fixedSpacingSubviewCount > 0 && spareHeight < 0 && context->selfSize.height > 0 && totalFixedSpacing > 0) { if (sstMode == MySubviewsShrink_Average) { - addSpace = spareHeight / fixedSpacingSubviewCount; + addSpacing = spareHeight / fixedSpacingSubviewCount; } else if (sstMode == MySubviewsShrink_Weight) { isWeightShrinkSpacing = YES; weightShrinkSpacingTotalHeight = spareHeight; @@ -522,7 +518,7 @@ - (void)myDoVertOrientationLayoutWithContext:(MyLayoutContext *)context { for (MyLayoutEngine *subviewEngine in context->subviewEngines) { - MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; CGFloat topSpacing = subviewTraits.topPosInner.numberVal.doubleValue; CGFloat bottomSpacing = subviewTraits.bottomPosInner.numberVal.doubleValue; @@ -537,7 +533,7 @@ - (void)myDoVertOrientationLayoutWithContext:(MyLayoutContext *)context { } } else { if (topSpacing + subviewTraits.topPosInner.offsetVal != 0.0) { - pos += addSpace; + pos += addSpacing; if (isWeightShrinkSpacing) { pos += weightShrinkSpacingTotalHeight * (topSpacing + subviewTraits.topPosInner.offsetVal) / totalFixedSpacing; } @@ -592,14 +588,14 @@ - (void)myDoVertOrientationLayoutWithContext:(MyLayoutContext *)context { //分别处理相对底部间距和绝对底部间距 if (subviewTraits.bottomPosInner.isRelativePos) { - CGFloat bottomSpaceWeight = bottomSpacing; - bottomSpacing = _myCGFloatRound((bottomSpaceWeight / totalWeight) * spareHeight); + CGFloat bottomSpacingWeight = bottomSpacing; + bottomSpacing = _myCGFloatRound((bottomSpacingWeight / totalWeight) * spareHeight); if (_myCGFloatLessOrEqual(bottomSpacing, 0.0)) { bottomSpacing = 0.0; } } else { if (bottomSpacing + subviewTraits.bottomPosInner.offsetVal != 0.0) { - pos += addSpace; + pos += addSpacing; if (isWeightShrinkSpacing) { pos += weightShrinkSpacingTotalHeight * (bottomSpacing + subviewTraits.bottomPosInner.offsetVal) / totalFixedSpacing; } @@ -614,11 +610,11 @@ - (void)myDoVertOrientationLayoutWithContext:(MyLayoutContext *)context { //添加共有的子视图间距 if (subviewEngine != context->subviewEngines.lastObject) { - pos += context->vertSpace; - if (context->vertSpace != 0.0) { - pos += addSpace; + pos += context->vertSpacing; + if (context->vertSpacing != 0.0) { + pos += addSpacing; if (isWeightShrinkSpacing) { - pos += weightShrinkSpacingTotalHeight * context->vertSpace / totalFixedSpacing; + pos += weightShrinkSpacingTotalHeight * context->vertSpacing / totalFixedSpacing; } } pos += incSpacing; //只有mgvert为between才加这个间距拉伸。 @@ -635,12 +631,12 @@ - (void)myDoVertOrientationLayoutWithContext:(MyLayoutContext *)context { } - (void)myDoHorzOrientationLayoutWithContext:(MyLayoutContext *)context { - MyLinearLayoutTraits *layoutTraits = (MyLinearLayoutTraits*)context->layoutViewEngine.currentSizeClass; - CGFloat subviewWidth = [layoutTraits.flexSpace calcMaxMinSubviewSize:context->selfSize.width arrangedCount:context->subviewEngines.count paddingStart:&context->paddingLeading paddingEnd:&context->paddingTrailing space:&context->horzSpace]; + MyLinearLayoutTraitsImpl *layoutTraits = (MyLinearLayoutTraitsImpl*)context->layoutViewEngine.currentTraits; + CGFloat subviewWidth = [layoutTraits.flexSpacing calcMaxMinSubviewsSize:context->selfSize.width arrangedCount:context->subviewEngines.count paddingStart:&context->paddingLeading paddingEnd:&context->paddingTrailing spacing:&context->horzSpacing]; CGFloat totalWidth = 0.0; //计算固定部分的宽度 CGFloat totalWeight = 0.0; //剩余部分的总比重 CGFloat totalShrink = 0.0; //总的压缩比重 - CGFloat addSpace = 0.0; //用于压缩时的间距压缩增量。 + CGFloat addSpacing = 0.0; //用于压缩时的间距压缩增量。 CGFloat paddingHorz = context->paddingLeading + context->paddingTrailing; //宽度是可伸缩的子视图集合 @@ -657,7 +653,7 @@ - (void)myDoHorzOrientationLayoutWithContext:(MyLayoutContext *)context { CGFloat maxLayoutHeight = 0.0; for (MyLayoutEngine *subviewEngine in context->subviewEngines) { //计算出固定宽度部分以及weight部分。这里的宽度可能依赖高度。如果不是高度包裹则计算出所有高度。 - MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; if (context->horzGravity == MyGravity_Horz_Fill || context->horzGravity == MyGravity_Horz_Stretch) { BOOL isFlexEngine = YES; //设置了比重宽度的子视图不伸缩 @@ -779,11 +775,11 @@ - (void)myDoHorzOrientationLayoutWithContext:(MyLayoutContext *)context { totalShrink += subviewTraits.trailingPosInner.shrink; pos += subviewTraits.trailingPosInner.measure; if (subviewEngine != context->subviewEngines.lastObject) { - totalWidth += context->horzSpace; - pos += context->horzSpace; - if (context->horzSpace != 0.0) { + totalWidth += context->horzSpacing; + pos += context->horzSpacing; + if (context->horzSpacing != 0.0) { fixedSpacingSubviewCount += 1; - totalFixedSpacing += context->horzSpace; + totalFixedSpacing += context->horzSpacing; } } } @@ -793,7 +789,7 @@ - (void)myDoHorzOrientationLayoutWithContext:(MyLayoutContext *)context { if (totalWeight != 0.0) { CGFloat tempSelfWidth = paddingHorz; if (context->subviewEngines.count > 1) { - tempSelfWidth += (context->subviewEngines.count - 1) * context->horzSpace; + tempSelfWidth += (context->subviewEngines.count - 1) * context->horzSpacing; } context->selfSize.width = [self myValidMeasure:layoutTraits.widthSizeInner subview:self calcSize:tempSelfWidth subviewSize:context->selfSize selfLayoutSize:self.superview.bounds.size]; } else { @@ -867,10 +863,10 @@ - (void)myDoHorzOrientationLayoutWithContext:(MyLayoutContext *)context { } } } - } else if (sstContent == MySubviewsShrink_Space) { + } else if (sstContent == MySubviewsShrink_Spacing) { if (fixedSpacingSubviewCount > 0 && spareWidth < 0 && context->selfSize.width > 0 && totalFixedSpacing > 0) { if (sstMode == MySubviewsShrink_Average) { - addSpace = spareWidth / fixedSpacingSubviewCount; + addSpacing = spareWidth / fixedSpacingSubviewCount; } else if (sstMode == MySubviewsShrink_Weight) { isWeightShrinkSpacing = YES; weightShrinkSpacingTotalWidth = spareWidth; @@ -933,7 +929,7 @@ - (void)myDoHorzOrientationLayoutWithContext:(MyLayoutContext *)context { pos = context->paddingLeading; } for (MyLayoutEngine *subviewEngine in context->subviewEngines) { - MyViewTraits *subviewTraits = (MyViewTraits *)subviewEngine.currentSizeClass; + MyViewTraitsImpl *subviewTraits = (MyViewTraitsImpl *)subviewEngine.currentTraits; CGFloat leadingSpacing = subviewTraits.leadingPosInner.numberVal.doubleValue; CGFloat trailingSpacing = subviewTraits.trailingPosInner.numberVal.doubleValue; @@ -941,14 +937,14 @@ - (void)myDoHorzOrientationLayoutWithContext:(MyLayoutContext *)context { //分别处理相对顶部间距和绝对顶部间距 if (subviewTraits.leadingPosInner.isRelativePos) { - CGFloat topSpaceWeight = leadingSpacing; - leadingSpacing = _myCGFloatRound((topSpaceWeight / totalWeight) * spareWidth); + CGFloat topSpacingWeight = leadingSpacing; + leadingSpacing = _myCGFloatRound((topSpacingWeight / totalWeight) * spareWidth); if (_myCGFloatLessOrEqual(leadingSpacing, 0.0)) { leadingSpacing = 0.0; } } else { if (leadingSpacing + subviewTraits.leadingPosInner.offsetVal != 0.0) { - pos += addSpace; + pos += addSpacing; if (isWeightShrinkSpacing) { pos += weightShrinkSpacingTotalWidth * (leadingSpacing + subviewTraits.leadingPosInner.offsetVal) / totalFixedSpacing; @@ -1004,14 +1000,14 @@ - (void)myDoHorzOrientationLayoutWithContext:(MyLayoutContext *)context { //计算相对的右边边距和绝对的右边边距 if (subviewTraits.trailingPosInner.isRelativePos) { - CGFloat trailingSpaceWeight = trailingSpacing; - trailingSpacing = _myCGFloatRound((trailingSpaceWeight / totalWeight) * spareWidth); + CGFloat trailingSpacingWeight = trailingSpacing; + trailingSpacing = _myCGFloatRound((trailingSpacingWeight / totalWeight) * spareWidth); if (_myCGFloatLessOrEqual(trailingSpacing, 0.0)) { trailingSpacing = 0.0; } } else { if (trailingSpacing + subviewTraits.trailingPosInner.offsetVal != 0.0) { - pos += addSpace; + pos += addSpacing; if (isWeightShrinkSpacing) { pos += weightShrinkSpacingTotalWidth * (trailingSpacing + subviewTraits.trailingPosInner.offsetVal) / totalFixedSpacing; @@ -1025,11 +1021,11 @@ - (void)myDoHorzOrientationLayoutWithContext:(MyLayoutContext *)context { pos += [self myValidMargin:subviewTraits.trailingPosInner subview:subviewTraits.view calcPos:trailingSpacing selfLayoutSize:context->selfSize]; //添加共有的子视图间距 if (subviewEngine != context->subviewEngines.lastObject) { - pos += context->horzSpace; - if (context->horzSpace != 0.0) { - pos += addSpace; + pos += context->horzSpacing; + if (context->horzSpacing != 0.0) { + pos += addSpacing; if (isWeightShrinkSpacing) { - pos += weightShrinkSpacingTotalWidth * context->horzSpace / totalFixedSpacing; + pos += weightShrinkSpacingTotalWidth * context->horzSpacing / totalFixedSpacing; } } pos += incSpacing; //只有gravity为between或者around才加这个间距拉伸。 @@ -1046,7 +1042,7 @@ - (void)myDoHorzOrientationLayoutWithContext:(MyLayoutContext *)context { } - (CGFloat)mySubviewEngine:(MyLayoutEngine *)subviewEngine calcLeadingTrailingWithContext:(MyLayoutContext *)context { - MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; UIView *subview = subviewTraits.view; subviewEngine.width = [self myWidthSizeValueOfSubviewEngine:subviewEngine withContext:context]; if (subviewTraits.leadingPosInner.val != nil && subviewTraits.trailingPosInner.val != nil) { @@ -1060,7 +1056,7 @@ - (CGFloat)mySubviewEngine:(MyLayoutEngine *)subviewEngine calcLeadingTrailingWi } - (CGFloat)mySubviewEngine:(MyLayoutEngine *)subviewEngine baselinePos:(CGFloat)baselinePos calcTopBottomWithContext:(MyLayoutContext *)context { - MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; UIView *subview = subviewTraits.view; subviewEngine.height = [self myHeightSizeValueOfSubviewEngine:subviewEngine withContext:context]; if (subviewTraits.topPosInner.val != nil && subviewTraits.bottomPosInner.val != nil) { @@ -1072,24 +1068,24 @@ - (CGFloat)mySubviewEngine:(MyLayoutEngine *)subviewEngine baselinePos:(CGFloat) return [self myCalcSubview:subviewEngine vertGravity:[subviewTraits finalVertGravityFrom:context->vertGravity] baselinePos:baselinePos withContext:context]; } -- (void)myEqualizeSubviewEngines:(NSMutableArray *)subviewEngines orientation:(MyOrientation)orientation centered:(BOOL)centered space:(CGFloat)space { +- (void)myEqualizeSubviewEngines:(NSMutableArray *)subviewEngines orientation:(MyOrientation)orientation centered:(BOOL)centered spacing:(CGFloat)spacing { //如果居中和不居中则拆分出来的片段是不一样的。 CGFloat weight; - if (space == CGFLOAT_MAX) { + if (spacing == CGFLOAT_MAX) { CGFloat fragments = centered ? subviewEngines.count * 2 + 1 : subviewEngines.count * 2 - 1; weight = 1 / fragments; - space = weight; + spacing = weight; } else { weight = 1.0; } for (MyLayoutEngine *subviewEngine in subviewEngines) { - MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; if (orientation == MyOrientation_Vert) { - [subviewTraits.topPos _myEqualTo:@(space)]; + [subviewTraits.topPos _myEqualTo:@(spacing)]; [subviewTraits.bottomPos _myEqualTo:@0]; } else { - [subviewTraits.leadingPos _myEqualTo:@(space)]; + [subviewTraits.leadingPos _myEqualTo:@(spacing)]; [subviewTraits.trailingPos _myEqualTo:@0]; } @@ -1104,9 +1100,9 @@ - (void)myEqualizeSubviewEngines:(NSMutableArray *)subviewEngi } if (subviewEngine == subviewEngines.lastObject && centered) { if (orientation == MyOrientation_Vert) { - [subviewTraits.bottomPos _myEqualTo:@(space)]; + [subviewTraits.bottomPos _myEqualTo:@(spacing)]; } else { - [subviewTraits.trailingPos _myEqualTo:@(space)]; + [subviewTraits.trailingPos _myEqualTo:@(spacing)]; } } } @@ -1115,13 +1111,13 @@ - (void)myEqualizeSubviewEngines:(NSMutableArray *)subviewEngi - (void)myEqualizeSubviewEngines:(NSArray *)subviewEngines orientation:(MyOrientation)orientation centered:(BOOL)centered { //如果居中和不居中则拆分出来的片段是不一样的。 CGFloat fragments = centered ? subviewEngines.count + 1 : subviewEngines.count - 1; - CGFloat space = 1 / fragments; + CGFloat spacing = 1 / fragments; for (MyLayoutEngine *subviewEngine in subviewEngines) { - MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; if (orientation == MyOrientation_Vert) { - [subviewTraits.topPos _myEqualTo:@(space)]; + [subviewTraits.topPos _myEqualTo:@(spacing)]; } else { - [subviewTraits.leadingPos _myEqualTo:@(space)]; + [subviewTraits.leadingPos _myEqualTo:@(spacing)]; } if (subviewEngine == subviewEngines.firstObject && !centered) { if (orientation == MyOrientation_Vert) { @@ -1132,9 +1128,9 @@ - (void)myEqualizeSubviewEngines:(NSArray *)subviewEngines ori } if (subviewEngine == subviewEngines.lastObject) { if (orientation == MyOrientation_Vert) { - [subviewTraits.bottomPos _myEqualTo:centered ? @(space) : @0]; + [subviewTraits.bottomPos _myEqualTo:centered ? @(spacing) : @0]; } else { - [subviewTraits.trailingPos _myEqualTo:centered ? @(space) : @0]; + [subviewTraits.trailingPos _myEqualTo:centered ? @(spacing) : @0]; } } } diff --git a/MyLayout/Lib/MyMaker.h b/MyLayout/Lib/MyMaker.h index 4df7756..dc8270e 100644 --- a/MyLayout/Lib/MyMaker.h +++ b/MyLayout/Lib/MyMaker.h @@ -52,9 +52,9 @@ - (MyMaker *)padding; - (MyMaker *)zeroPadding; - (MyMaker *)reverseLayout; -- (MyMaker *)vertSpace; -- (MyMaker *)horzSpace; -- (MyMaker *)space; +- (MyMaker *)vertSpacing; +- (MyMaker *)horzSpacing; +- (MyMaker *)spacing; //线性布局和流式布局独有 - (MyMaker *)orientation; @@ -68,6 +68,9 @@ - (MyMaker *)autoArrange; - (MyMaker *)arrangedGravity; - (MyMaker *)pagedCount; +- (MyMaker *)maxLines; +- (MyMaker *)lastlineGravityPolicy; +- (MyMaker *)isFlex; //线性布局和浮动布局和流式布局子视图独有 - (MyMaker *)weight; @@ -97,12 +100,4 @@ @end -//@interface MyMaker(MyDeprecated) -//- (MyMaker *)topPadding MYDEPRECATED("use paddingTop instead"); -//- (MyMaker *)leftPadding MYDEPRECATED("use paddingTop instead"); -//- (MyMaker *)bottomPadding MYDEPRECATED("use paddingTop instead"); -//- (MyMaker *)rightPadding MYDEPRECATED("use paddingTop instead"); -//- (MyMaker *)leadingPadding MYDEPRECATED("use paddingTop instead"); -//- (MyMaker *)trailingPadding MYDEPRECATED("use paddingTop instead"); -//@end #endif diff --git a/MyLayout/Lib/MyMaker.m b/MyLayout/Lib/MyMaker.m index 0982111..7639907 100644 --- a/MyLayout/Lib/MyMaker.m +++ b/MyLayout/Lib/MyMaker.m @@ -187,8 +187,8 @@ - (MyMaker *)sizeToFit { return self; } -- (MyMaker *)space { - return [self addMethod:@"subviewSpace"]; +- (MyMaker *)spacing { + return [self addMethod:@"subviewSpacing"]; } - (MyMaker *)shrinkType { @@ -207,18 +207,31 @@ - (MyMaker *)arrangedGravity { return [self addMethod:@"arrangedGravity"]; } -- (MyMaker *)vertSpace { - return [self addMethod:@"subviewVSpace"]; +- (MyMaker *)vertSpacing { + return [self addMethod:@"subviewVSpacing"]; } -- (MyMaker *)horzSpace { - return [self addMethod:@"subviewHSpace"]; +- (MyMaker *)horzSpacing { + return [self addMethod:@"subviewHSpacing"]; } - (MyMaker *)pagedCount { return [self addMethod:@"pagedCount"]; } +- (MyMaker *)maxLines { + return [self addMethod:@"maxLines"]; +} + +- (MyMaker *)lastlineGravityPolicy { + return [self addMethod:@"lastlineGravityPolicy"]; +} + +- (MyMaker *)isFlex { + return [self addMethod:@"isFlex"]; +} + + - (MyMaker * (^)(id val))equalTo { _clear = YES; return ^id(id val) { @@ -334,25 +347,6 @@ - (MyMaker *)pagedCount { } #pragma mark -- Dreprecated methods -// -//- (MyMaker *)topPadding { -// return self.paddingTop; -//} -//- (MyMaker *)leftPadding { -// return self.paddingLeft; -//} -//- (MyMaker *)bottomPadding { -// return self.paddingBottom; -//} -//- (MyMaker *)rightPadding { -// return self.paddingRight; -//} -//- (MyMaker *)leadingPadding { -// return self.paddingLeading; -//} -//- (MyMaker *)trailingPadding { -// return self.paddingTrailing; -//} @end diff --git a/MyLayout/Lib/MyPathLayout.h b/MyLayout/Lib/MyPathLayout.h index ec25d02..5d7e8fa 100644 --- a/MyLayout/Lib/MyPathLayout.h +++ b/MyLayout/Lib/MyPathLayout.h @@ -12,27 +12,27 @@ *定义子视图在路径布局中的距离的类型。 */ typedef enum : NSUInteger { - MyPathSpace_Flexed, //浮动距离,子视图之间的距离根据路径布局的尺寸和子视图的数量而确定。 - MyPathSpace_Fixed, //固定距离,就是子视图之间的距离是固定的某个数值。 - MyPathSpace_Count, //固定数量距离,就是子视图之间的距离根据路径布局的尺寸和某个具体的数量而确定。 -} MyPathSpaceType; + MyPathSpacing_Flexed, //浮动距离,子视图之间的距离根据路径布局的尺寸和子视图的数量而确定。 + MyPathSpacing_Fixed, //固定距离,就是子视图之间的距离是固定的某个数值。 + MyPathSpacing_Count, //固定数量距离,就是子视图之间的距离根据路径布局的尺寸和某个具体的数量而确定。 +} MyPathSpacingType; /** *子视图之间的路径距离类,描述子视图在路径上的间隔距离的类型。 */ -@interface MyPathSpace : NSObject +@interface MyPathSpacing : NSObject /**浮动距离,根据布局视图的尺寸和子视图的数量动态决定*/ -+ (MyPathSpace *)flexed; ++ (MyPathSpacing *)flexed; /**固定距离,len为长度,每个子视图之间的距离都是len*/ -+ (MyPathSpace *)fixed:(CGFloat)len; ++ (MyPathSpacing *)fixed:(CGFloat)len; /**数量距离,根据布局视图的尺寸和指定的数量count动态决定。*/ -+ (MyPathSpace *)count:(NSInteger)count; ++ (MyPathSpacing *)count:(NSInteger)count; /**距离类型。*/ -@property (nonatomic, assign, readonly) MyPathSpaceType type; +@property (nonatomic, assign, readonly) MyPathSpacingType type; /**距离的值。*/ @property (nonatomic, assign, readonly) CGFloat value; @@ -83,7 +83,7 @@ typedef enum : NSUInteger { @note 也就是说MyPathLayout的派生类返回一个CAShapeLayer,那么系统将自动会在每次布局时将layer的path属性进行赋值操作。 */ -@interface MyPathLayout : MyBaseLayout +@interface MyPathLayout : MyBaseLayout> /** **下面部分是路径布局的三要素:坐标、函数、距离的定义 @@ -112,7 +112,7 @@ typedef enum : NSUInteger { /** *设置子视图在路径曲线上的距离的类型,一共有Flexed, Fixed, MaxCount,默认是Flexed, */ -@property (nonatomic, strong) MyPathSpace *spaceType; +@property (nonatomic, strong) MyPathSpacing *spacingType; /** *设置和获取布局视图中的原点视图,默认是nil。如果设置了原点视图则总会将原点视图作为布局视图中的最后一个子视图。原点视图将会显示在路径的坐标原点中心上,因此原点布局是不会参与在路径中的布局的。因为中心原点视图是布局视图中的最后一个子视图,而MyPathLayout重写了AddSubview方法,因此可以正常的使用这个方法来添加子视图。 @@ -155,7 +155,7 @@ typedef enum : NSUInteger { /** 创建布局的曲线的路径。用户需要负责销毁返回的值。调用者可以用这个方法来获得曲线的路径,进行一些绘制的工作。 - @param subviewCount 指定这个路径上子视图的数量的个数,如果设置为-1则是按照布局视图的子视图的数量来创建。需要注意的是如果布局视图的spaceType为Flexed,Count的话则这个参数设置无效。 + @param subviewCount 指定这个路径上子视图的数量的个数,如果设置为-1则是按照布局视图的子视图的数量来创建。需要注意的是如果布局视图的spacingType为Flexed,Count的话则这个参数设置无效。 @return 返回指定数量的子视图的曲线路径,用户需要负责销毁返回的对象。 */ - (CGPathRef)createPath:(NSInteger)subviewCount; diff --git a/MyLayout/Lib/MyPathLayout.m b/MyLayout/Lib/MyPathLayout.m index 425b578..d20e835 100644 --- a/MyLayout/Lib/MyPathLayout.m +++ b/MyLayout/Lib/MyPathLayout.m @@ -9,9 +9,9 @@ #import "MyPathLayout.h" #import "MyLayoutInner.h" -@implementation MyPathSpace +@implementation MyPathSpacing -- (MyPathSpace *)initWithSpaceType:(MyPathSpaceType)type value:(CGFloat)value { +- (MyPathSpacing *)initWithSpacingType:(MyPathSpacingType)type value:(CGFloat)value { self = [super init]; if (self != nil) { _type = type; @@ -21,18 +21,18 @@ - (MyPathSpace *)initWithSpaceType:(MyPathSpaceType)type value:(CGFloat)value { } //浮动距离,根据布局视图的尺寸和子视图的数量动态决定 -+ (MyPathSpace *)flexed { - return [[self alloc] initWithSpaceType:MyPathSpace_Flexed value:0]; ++ (MyPathSpacing *)flexed { + return [[self alloc] initWithSpacingType:MyPathSpacing_Flexed value:0]; } //固定距离,len为长度,每个子视图之间的距离都是len -+ (MyPathSpace *)fixed:(CGFloat)len { - return [[self alloc] initWithSpaceType:MyPathSpace_Fixed value:len]; ++ (MyPathSpacing *)fixed:(CGFloat)len { + return [[self alloc] initWithSpacingType:MyPathSpacing_Fixed value:len]; } //数量距离,根据布局视图的尺寸和指定的数量动态决定。 -+ (MyPathSpace *)count:(NSInteger)count { - return [[self alloc] initWithSpaceType:MyPathSpace_Count value:count]; ++ (MyPathSpacing *)count:(NSInteger)count { + return [[self alloc] initWithSpacingType:MyPathSpacing_Count value:count]; } @end @@ -124,7 +124,7 @@ @interface MyPathLayout () @end @implementation MyPathLayout -@synthesize spaceType = _spaceType; +@synthesize spacingType = _spacingType; #pragma mark-- Public Methods @@ -142,16 +142,16 @@ - (MyCoordinateSetting *)coordinateSetting { return _coordinateSetting; } -- (MyPathSpace *)spaceType { - if (_spaceType == nil) { - _spaceType = [MyPathSpace flexed]; +- (MyPathSpacing *)spacingType { + if (_spacingType == nil) { + _spacingType = [MyPathSpacing flexed]; } - return _spaceType; + return _spacingType; } -- (void)setSpaceType:(MyPathSpace *)spaceType { - if (_spaceType != spaceType) { - _spaceType = spaceType; +- (void)setSpacingType:(MyPathSpacing *)spacingType { + if (_spacingType != spacingType) { + _spacingType = spacingType; [self setNeedsLayout]; } } @@ -258,7 +258,7 @@ - (CGFloat)argumentFrom:(UIView *)subview { //开始和结束子视图之间的路径创建 - (void)beginSubviewPathPoint:(BOOL)full { //这里先把所有点都创建出来。 - MyPathLayoutTraits *layoutTraits = (MyPathLayoutTraits*)self.myDefaultSizeClass; + MyPathLayoutTraitsImpl *layoutTraits = self.myCurrentTraits; MyLayoutContext context; context.selfSize = self.bounds.size; @@ -344,7 +344,7 @@ - (NSArray *)getSubviewPathPoint:(NSInteger)fromIndex toIndex:(NSInteger)toIndex } - (CGPathRef)createPath:(NSInteger)subviewCount { - MyPathLayoutTraits *layoutTraits = (MyPathLayoutTraits*)self.myDefaultSizeClass; + MyPathLayoutTraitsImpl *layoutTraits = self.myCurrentTraits; MyLayoutContext context; context.selfSize = self.bounds.size; @@ -355,13 +355,13 @@ - (CGPathRef)createPath:(NSInteger)subviewCount { CGMutablePathRef retPath = CGPathCreateMutable(); - if (self.spaceType.type != MyPathSpace_Fixed) { - [self myCalcPathPoints:retPath pPathLen:NULL subviewCount:-1 pointIndexArray:nil viewSpace:0 withContext:&context]; + if (self.spacingType.type != MyPathSpacing_Fixed) { + [self myCalcPathPoints:retPath pPathLen:NULL subviewCount:-1 pointIndexArray:nil viewSpacing:0 withContext:&context]; } else { if (subviewCount == -1) { subviewCount = self.pathSubviews.count; } - [self myCalcPathPoints:retPath pPathLen:NULL subviewCount:subviewCount pointIndexArray:nil viewSpace:self.spaceType.value withContext:&context]; + [self myCalcPathPoints:retPath pPathLen:NULL subviewCount:subviewCount pointIndexArray:nil viewSpacing:self.spacingType.value withContext:&context]; } return retPath; } @@ -418,15 +418,15 @@ - (void)drawRect:(CGRect)rect { CGMutablePathRef path = CGPathCreateMutable(); - MyPathLayout *lsc = self.myCurrentSizeClass; + MyPathLayout *lsc = self.myCurrentTraits; NSArray *pts = nil; if (sbs.count > 0) { - if (self.spaceType.type != MyPathSpace_Fixed) + if (self.spacingType.type != MyPathSpacing_Fixed) { CGFloat totalLen = 0; - NSArray *tempArray = [self myCalcPathPoints:path pPathLen:&totalLen subviewCount:-1 pointIndexArray:nil viewSpace:0 lsc:lsc]; + NSArray *tempArray = [self myCalcPathPoints:path pPathLen:&totalLen subviewCount:-1 pointIndexArray:nil viewSpacing:0 lsc:lsc]; BOOL bClose = NO; if (tempArray.count > 1) { @@ -437,20 +437,20 @@ - (void)drawRect:(CGRect)rect { CGFloat viewSpacing = 0; NSInteger sbvcount = sbs.count; - if (self.spaceType.type == MyPathSpace_Count) - sbvcount = (NSInteger)self.spaceType.value; + if (self.spacingType.type == MyPathSpacing_Count) + sbvcount = (NSInteger)self.spacingType.value; if (sbvcount > 1) { viewSpacing = totalLen / (sbvcount - (bClose ? 0 : 1)); } - pts = [self myCalcPathPoints:nil pPathLen:NULL subviewCount:sbs.count pointIndexArray:nil viewSpace:viewSpacing lsc:lsc]; + pts = [self myCalcPathPoints:nil pPathLen:NULL subviewCount:sbs.count pointIndexArray:nil viewSpacing:viewSpacing lsc:lsc]; } else { - pts = [self myCalcPathPoints:path pPathLen:NULL subviewCount:sbs.count pointIndexArray:nil viewSpace:self.spaceType.value lsc:lsc]; + pts = [self myCalcPathPoints:path pPathLen:NULL subviewCount:sbs.count pointIndexArray:nil viewSpacing:self.spacingType.value lsc:lsc]; } } @@ -472,14 +472,7 @@ - (void)drawRect:(CGRect)rect { - (CGSize)calcLayoutSize:(CGSize)size subviewEngines:(NSMutableArray *)subviewEngines context:(MyLayoutContext *)context { [super calcLayoutSize:size subviewEngines:subviewEngines context:context]; - MyPathLayoutTraits *layoutTraits = (MyPathLayoutTraits *)context->layoutViewEngine.currentSizeClass; - context->paddingTop = layoutTraits.myLayoutPaddingTop; - context->paddingBottom = layoutTraits.myLayoutPaddingBottom; - context->paddingLeading = layoutTraits.myLayoutPaddingLeading; - context->paddingTrailing = layoutTraits.myLayoutPaddingTrailing; - if (context->subviewEngines == nil) { - context->subviewEngines = [layoutTraits filterEngines:subviewEngines]; - } + MyPathLayoutTraitsImpl *layoutTraits = context->layoutViewEngine.currentTraits; [self myCalcSubviewsWrapContentSize:context withCustomSetting:nil]; @@ -517,7 +510,7 @@ - (CGSize)calcLayoutSize:(CGSize)size subviewEngines:(NSMutableArraysubviewEngines.count; i++) { MyLayoutEngine *subviewEngine = context->subviewEngines[i]; - MyViewTraits *subviewTraits = (MyViewTraits *)subviewEngine.currentSizeClass; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; CGPoint point = CGPointZero; if (points.count > i) { @@ -561,7 +554,7 @@ - (CGSize)calcLayoutSize:(CGSize)size subviewEngines:(NSMutableArrayselfSize]; @@ -624,8 +617,8 @@ - (CGSize)calcLayoutSize:(CGSize)size subviewEngines:(NSMutableArray 1) { @@ -648,8 +641,8 @@ - (CGPoint)myGetNearestDistancePoint:(CGFloat)startArg lastXY:(CGPoint)lastXY di if (!isnan(realXY.x) && !isnan(realXY.y)) { CGFloat oldDistance = distance; distance += [self myCalcDistance:realXY with:lastXY]; - if (_myCGFloatGreatOrEqual(distance, viewSpace)) { - if (_myCGFloatLessOrEqual(distance - viewSpace, self.distanceError)) { + if (_myCGFloatGreatOrEqual(distance, viewSpacing)) { + if (_myCGFloatLessOrEqual(distance - viewSpacing, self.distanceError)) { *pLastValidArg = arg; return realXY; } else { @@ -682,7 +675,7 @@ - (void)myCalcPathPointsHelper:(NSMutableArray *)pathPointArray pPathLen:(CGFloat *)pPathLen subviewCount:(NSInteger)subviewCount pointIndexArray:(NSMutableArray *)pointIndexArray - viewSpace:(CGFloat)viewSpace + viewSpacing:(CGFloat)viewSpacing startArg:(CGFloat)startArg //定义域最小值 endArg:(CGFloat)endArg //定义域最大值 func:(CGPoint (^)(CGFloat))func { @@ -749,9 +742,9 @@ - (void)myCalcPathPointsHelper:(NSMutableArray *)pathPointArray //点的距离累加 CGFloat oldDistance = distance; distance += [self myCalcDistance:realXY with:lastXY]; - if (_myCGFloatGreatOrEqual(distance, viewSpace)) { //如果距离超过间距。则需要缩小自变量步长。以便达到最小的间距误差 - if (_myCGFloatGreatOrEqual(distance - viewSpace, self.distanceError)) { - realXY = [self myGetNearestDistancePoint:arg lastXY:lastXY distance:oldDistance viewSpace:viewSpace pLastValidArg:&lastValidArg func:func]; + if (_myCGFloatGreatOrEqual(distance, viewSpacing)) { //如果距离超过间距。则需要缩小自变量步长。以便达到最小的间距误差 + if (_myCGFloatGreatOrEqual(distance - viewSpacing, self.distanceError)) { + realXY = [self myGetNearestDistancePoint:arg lastXY:lastXY distance:oldDistance viewSpacing:viewSpacing pLastValidArg:&lastValidArg func:func]; } else { lastValidArg = arg; } @@ -763,7 +756,7 @@ - (void)myCalcPathPointsHelper:(NSMutableArray *)pathPointArray distance = 0; subviewCount -= 1; [self.argumentArray addObject:@(lastValidArg)]; - } else if (distance - viewSpace > -1 * self.distanceError) { //如果距离和间距小于误差则认为是一个视图可以存放的点。 + } else if (distance - viewSpacing > -1 * self.distanceError) { //如果距离和间距小于误差则认为是一个视图可以存放的点。 if (pointIndexArray == nil) { [pathPointArray addObject:[NSValue valueWithCGPoint:realXY]]; } else { @@ -807,7 +800,7 @@ - (void)myCalcPathPointsHelper:(NSMutableArray *)pathPointArray @param pPathLen 返回路径的总长度 @param subviewCount 子视图的数量,如果为-1则表示只计算路径的长度不会把每个子视图所在的路径点索引保存 @param pointIndexArray 返回所有子视图在路径点的索引 - @param viewSpace 子视图之间的间距 + @param viewSpacing 子视图之间的间距 @param context 布局上下文对象。 @return 返回所有路径点 */ @@ -815,7 +808,7 @@ - (NSArray *)myCalcPathPoints:(CGMutablePathRef)showPath pPathLen:(CGFloat *)pPathLen subviewCount:(NSInteger)subviewCount pointIndexArray:(NSMutableArray *)pointIndexArray - viewSpace:(CGFloat)viewSpace + viewSpacing:(CGFloat)viewSpacing withContext:(MyLayoutContext *)context { NSMutableArray *pathPointArray = [NSMutableArray new]; @@ -857,7 +850,7 @@ - (NSArray *)myCalcPathPoints:(CGMutablePathRef)showPath pPathLen:pPathLen subviewCount:subviewCount pointIndexArray:pointIndexArray - viewSpace:viewSpace + viewSpacing:viewSpacing startArg:startArg endArg:endArg func:^(CGFloat arg) { @@ -902,7 +895,7 @@ - (NSArray *)myCalcPathPoints:(CGMutablePathRef)showPath pPathLen:pPathLen subviewCount:subviewCount pointIndexArray:pointIndexArray - viewSpace:viewSpace + viewSpacing:viewSpacing startArg:startArg endArg:endArg func:^(CGFloat arg) { @@ -933,7 +926,7 @@ - (NSArray *)myCalcPathPoints:(CGMutablePathRef)showPath pPathLen:pPathLen subviewCount:subviewCount pointIndexArray:pointIndexArray - viewSpace:viewSpace + viewSpacing:viewSpacing startArg:startArg endArg:endArg func:^(CGFloat arg) { @@ -960,11 +953,11 @@ - (NSArray *)myCalcPathPoints:(CGMutablePathRef)showPath - (NSArray *)myCalcPointsOfSubviewCount:(NSInteger)count path:(CGMutablePathRef)path pointIndexArray:(NSMutableArray *)pointIndexArray withContext:(MyLayoutContext*)context { if (count > 0) { - if (self.spaceType.type != MyPathSpace_Fixed) { + if (self.spacingType.type != MyPathSpacing_Fixed) { //如果间距不是固定的,那么要先算出路径的长度pathLen,以及是否是封闭曲线标志来算出每个子视图在曲线上的间距viewSpacing CGFloat pathLen = 0; //曲线的总长度 - NSArray *tempArray = [self myCalcPathPoints:path pPathLen:&pathLen subviewCount:-1 pointIndexArray:nil viewSpace:0 withContext:context]; + NSArray *tempArray = [self myCalcPathPoints:path pPathLen:&pathLen subviewCount:-1 pointIndexArray:nil viewSpacing:0 withContext:context]; //判断是否是封闭路径,方法为第一个路径点和最后一个之间的距离差小于1 BOOL bClose = NO; @@ -974,19 +967,19 @@ - (NSArray *)myCalcPointsOfSubviewCount:(NSInteger)count path:(CGMutablePathRef) bClose = [self myCalcDistance:p1 with:p2] <= 1; } - CGFloat viewSpace = 0; //每个视图之间的间距。 + CGFloat viewSpacing = 0; //每个视图之间的间距。 NSInteger sbvcount = count; - if (self.spaceType.type == MyPathSpace_Count) { //如果是固定数量则按固定数量来分配间距 - sbvcount = (NSInteger)self.spaceType.value; + if (self.spacingType.type == MyPathSpacing_Count) { //如果是固定数量则按固定数量来分配间距 + sbvcount = (NSInteger)self.spacingType.value; } //总长度除视图数量得出子视图的间距。 if (sbvcount > 1) { - viewSpace = pathLen / (sbvcount - (bClose ? 0 : 1)); + viewSpacing = pathLen / (sbvcount - (bClose ? 0 : 1)); } //有间距后再重新计算一遍 - return [self myCalcPathPoints:nil pPathLen:NULL subviewCount:count pointIndexArray:pointIndexArray viewSpace:viewSpace withContext:context]; + return [self myCalcPathPoints:nil pPathLen:NULL subviewCount:count pointIndexArray:pointIndexArray viewSpacing:viewSpacing withContext:context]; } else { - return [self myCalcPathPoints:path pPathLen:NULL subviewCount:count pointIndexArray:pointIndexArray viewSpace:self.spaceType.value withContext:context]; + return [self myCalcPathPoints:path pPathLen:NULL subviewCount:count pointIndexArray:pointIndexArray viewSpacing:self.spacingType.value withContext:context]; } } return nil; diff --git a/MyLayout/Lib/MyRelativeLayout.h b/MyLayout/Lib/MyRelativeLayout.h index d12e120..fdf61ca 100644 --- a/MyLayout/Lib/MyRelativeLayout.h +++ b/MyLayout/Lib/MyRelativeLayout.h @@ -13,6 +13,6 @@ 相对布局里面的子视图的布局位置和添加的顺序无关,而是通过设置子视图的相对依赖关系来进行定位和布局的。 相对布局提供了和AutoLayout相似的能力。 */ -@interface MyRelativeLayout : MyBaseLayout +@interface MyRelativeLayout : MyBaseLayout> @end diff --git a/MyLayout/Lib/MyRelativeLayout.m b/MyLayout/Lib/MyRelativeLayout.m index a2d07de..942216c 100644 --- a/MyLayout/Lib/MyRelativeLayout.m +++ b/MyLayout/Lib/MyRelativeLayout.m @@ -16,11 +16,7 @@ @implementation MyRelativeLayout - (CGSize)calcLayoutSize:(CGSize)size subviewEngines:(NSMutableArray *)subviewEngines context:(MyLayoutContext *)context { [super calcLayoutSize:size subviewEngines:subviewEngines context:context]; - MyRelativeLayoutTraits *layoutTraits = (MyRelativeLayoutTraits *)context->layoutViewEngine.currentSizeClass; - context->paddingTop = layoutTraits.myLayoutPaddingTop; - context->paddingBottom = layoutTraits.myLayoutPaddingBottom; - context->paddingLeading = layoutTraits.myLayoutPaddingLeading; - context->paddingTrailing = layoutTraits.myLayoutPaddingTrailing; + MyRelativeLayoutTraitsImpl *layoutTraits = context->layoutViewEngine.currentTraits; context->subviewEngines = subviewEngines; if (layoutTraits.widthSizeInner.wrapVal) { @@ -30,7 +26,7 @@ - (CGSize)calcLayoutSize:(CGSize)size subviewEngines:(NSMutableArrayselfSize.height = [self myValidMeasure:layoutTraits.heightSizeInner subview:self calcSize:0.0 subviewSize:context->selfSize selfLayoutSize:self.superview.bounds.size]; } for (MyLayoutEngine *subviewEngine in context->subviewEngines) { - MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; if (subviewTraits.useFrame) { continue; @@ -78,15 +74,15 @@ - (CGSize)calcLayoutSize:(CGSize)size subviewEngines:(NSMutableArraylayoutViewEngine.currentSizeClass; - MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + MyRelativeLayoutTraitsImpl *layoutTraits = context->layoutViewEngine.currentTraits; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; //确定宽度,如果跟父一样宽则设置宽度和设置左右值,这时候三个参数设置完毕 //如果和其他视图的宽度一样,则先计算其他视图的宽度并返回其他视图的宽度 @@ -127,7 +123,7 @@ - (void)mySubviewEngine:(MyLayoutEngine *)subviewEngine calcLeadingTrailingWithC MyLayoutPos *nextAnchor = nil; for (NSInteger i = anchorArray.count - 1; i >= 0; i--) { MyLayoutPos *anchor = anchorArray[i]; - MyViewTraits *relativeViewTraits = anchor.view.myEngine.currentSizeClass; + MyViewTraitsImpl *relativeViewTraits = anchor.view.myEngine.currentTraits; if (![relativeViewTraits invalid]) { if (totalWidth != 0) { if (nextAnchor != nil) { @@ -159,7 +155,7 @@ - (void)mySubviewEngine:(MyLayoutEngine *)subviewEngine calcLeadingTrailingWithC [subviewTraits.leadingPos _myEqualTo:prevAxis]; prevAxis = subviewTraits.trailingPos; for (MyLayoutPos *anchor in anchorArray) { - MyViewTraits *relativeViewTraits = anchor.view.myEngine.currentSizeClass; + MyViewTraitsImpl *relativeViewTraits = anchor.view.myEngine.currentTraits; [[relativeViewTraits.leadingPos _myEqualTo:prevAxis] _myOffset:anchor.measure]; prevAxis = relativeViewTraits.trailingPos; } @@ -171,7 +167,7 @@ - (void)mySubviewEngine:(MyLayoutEngine *)subviewEngine calcLeadingTrailingWithC subviewEngine.leading = [self mySubviewEngine:relativeViewEngine getMeasureFromAnchorType:subviewTraits.centerXPosInner.anchorVal.anchorType withContext:context] - subviewEngine.width / 2 + subviewTraits.centerXPosInner.measure; - if (relativeView != nil && relativeView != self && [relativeViewEngine.currentSizeClass invalid]) { + if (relativeView != nil && relativeView != self && [relativeViewEngine.currentTraits invalid]) { subviewEngine.leading -= subviewTraits.centerXPosInner.measure; } if (subviewEngine.leading < 0.0 && relativeView == self && layoutTraits.widthSizeInner.wrapVal) { @@ -196,7 +192,7 @@ - (void)mySubviewEngine:(MyLayoutEngine *)subviewEngine calcLeadingTrailingWithC subviewEngine.leading = [self mySubviewEngine:relativeViewEngine getMeasureFromAnchorType:subviewTraits.leadingPosInner.anchorVal.anchorType withContext:context] + subviewTraits.leadingPosInner.measure; - if (relativeView != nil && relativeView != self && [relativeViewEngine.currentSizeClass invalid]) { + if (relativeView != nil && relativeView != self && [relativeViewEngine.currentTraits invalid]) { subviewEngine.leading -= subviewTraits.leadingPosInner.measure; } subviewEngine.trailing = subviewEngine.leading + subviewEngine.width + subviewTraits.trailingPosInner.measure; @@ -211,7 +207,7 @@ - (void)mySubviewEngine:(MyLayoutEngine *)subviewEngine calcLeadingTrailingWithC MyLayoutEngine *relativeViewEngine = relativeView.myEngine; subviewEngine.trailing = [self mySubviewEngine:relativeViewEngine getMeasureFromAnchorType:subviewTraits.trailingPosInner.anchorVal.anchorType withContext:context] - subviewTraits.trailingPosInner.measure + subviewTraits.leadingPosInner.measure; - if (relativeView != nil && relativeView != self && [relativeViewEngine.currentSizeClass invalid]) { + if (relativeView != nil && relativeView != self && [relativeViewEngine.currentTraits invalid]) { subviewEngine.trailing += subviewTraits.trailingPosInner.measure; } subviewEngine.leading = subviewEngine.trailing - subviewEngine.width; @@ -238,11 +234,11 @@ - (void)mySubviewEngine:(MyLayoutEngine *)subviewEngine calcLeadingTrailingWithC if (lBoundAnchor.anchorVal != nil && uBoundAnchor.anchorVal != nil) { //让宽度缩小并在最小和最大的中间排列。 CGFloat minLeading = [self mySubviewEngine:lBoundRelativeViewEngine getMeasureFromAnchorType:lBoundAnchor.anchorVal.anchorType withContext:context] + lBoundAnchor.offsetVal; - if (lBoundRelativeView != nil && lBoundRelativeView != self && [lBoundRelativeViewEngine.currentSizeClass invalid]) { + if (lBoundRelativeView != nil && lBoundRelativeView != self && [lBoundRelativeViewEngine.currentTraits invalid]) { minLeading -= lBoundAnchor.offsetVal; } CGFloat maxTrailing = [self mySubviewEngine:uBoundRelativeViewEngine getMeasureFromAnchorType:uBoundAnchor.anchorVal.anchorType withContext:context] - uBoundAnchor.offsetVal; - if (uBoundRelativeView != nil && uBoundRelativeView != self && [uBoundRelativeViewEngine.currentSizeClass invalid]) { + if (uBoundRelativeView != nil && uBoundRelativeView != self && [uBoundRelativeViewEngine.currentTraits invalid]) { maxTrailing += uBoundAnchor.offsetVal; } //用maxRight减去minLeft得到的宽度再减去视图的宽度,然后让其居中。。如果宽度超过则缩小视图的宽度。 @@ -257,7 +253,7 @@ - (void)mySubviewEngine:(MyLayoutEngine *)subviewEngine calcLeadingTrailingWithC } else if (lBoundAnchor.anchorVal != nil) { //得到左边的最小位置。如果当前的左边距小于这个位置则缩小视图的宽度。 CGFloat minLeading = [self mySubviewEngine:lBoundRelativeViewEngine getMeasureFromAnchorType:lBoundAnchor.anchorVal.anchorType withContext:context] + lBoundAnchor.offsetVal; - if (lBoundRelativeView != nil && lBoundRelativeView != self && [lBoundRelativeViewEngine.currentSizeClass invalid]) { + if (lBoundRelativeView != nil && lBoundRelativeView != self && [lBoundRelativeViewEngine.currentTraits invalid]) { minLeading -= lBoundAnchor.offsetVal; } if (_myCGFloatLess(subviewEngine.leading, minLeading)) { @@ -267,7 +263,7 @@ - (void)mySubviewEngine:(MyLayoutEngine *)subviewEngine calcLeadingTrailingWithC } else if (uBoundAnchor.anchorVal != nil) { //得到右边的最大位置。如果当前的右边距大于了这个位置则缩小视图的宽度。 CGFloat maxTrailing = [self mySubviewEngine:uBoundRelativeViewEngine getMeasureFromAnchorType:uBoundAnchor.anchorVal.anchorType withContext:context] - uBoundAnchor.offsetVal; - if (uBoundRelativeView != nil && uBoundRelativeView != self && [uBoundRelativeViewEngine.currentSizeClass invalid]) { + if (uBoundRelativeView != nil && uBoundRelativeView != self && [uBoundRelativeViewEngine.currentTraits invalid]) { maxTrailing += uBoundAnchor.offsetVal; } if (_myCGFloatGreat(subviewEngine.trailing, maxTrailing)) { @@ -279,8 +275,8 @@ - (void)mySubviewEngine:(MyLayoutEngine *)subviewEngine calcLeadingTrailingWithC - (void)mySubviewEngine:(MyLayoutEngine *)subviewEngine calcTopBottomWithContext:(MyLayoutContext *)context { - MyRelativeLayoutTraits *layoutTraits = (MyRelativeLayoutTraits *)context->layoutViewEngine.currentSizeClass; - MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + MyRelativeLayoutTraitsImpl *layoutTraits = context->layoutViewEngine.currentTraits; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; if (subviewEngine.top != CGFLOAT_MAX && subviewEngine.bottom != CGFLOAT_MAX && subviewEngine.height != CGFLOAT_MAX) { @@ -301,7 +297,7 @@ - (void)mySubviewEngine:(MyLayoutEngine *)subviewEngine calcTopBottomWithContext MyLayoutPos *nextAnchor = nil; for (NSInteger i = anchorArray.count - 1; i >= 0; i--) { MyLayoutPos *anchor = anchorArray[i]; - MyViewTraits *relativeViewTraits = anchor.view.myEngine.currentSizeClass; + MyViewTraitsImpl *relativeViewTraits = anchor.view.myEngine.currentTraits; if (![relativeViewTraits invalid]) { if (totalHeight != 0.0) { if (nextAnchor != nil) { @@ -332,7 +328,7 @@ - (void)mySubviewEngine:(MyLayoutEngine *)subviewEngine calcTopBottomWithContext [subviewTraits.topPos _myEqualTo:prevAxis]; prevAxis = subviewTraits.bottomPos; for (MyLayoutPos *anchor in anchorArray) { - MyViewTraits *relativeViewTraits = anchor.view.myEngine.currentSizeClass; + MyViewTraitsImpl *relativeViewTraits = anchor.view.myEngine.currentTraits; [[relativeViewTraits.topPos _myEqualTo:prevAxis] _myOffset:anchor.measure]; prevAxis = relativeViewTraits.bottomPos; } @@ -348,7 +344,7 @@ - (void)mySubviewEngine:(MyLayoutEngine *)subviewEngine calcTopBottomWithContext MyLayoutEngine *relativeViewEngine = relativeView.myEngine; subviewEngine.top = [self mySubviewEngine:relativeViewEngine getMeasureFromAnchorType:subviewTraits.baselinePosInner.anchorVal.anchorType withContext:context] - subviewFont.ascender - (subviewEngine.height - subviewFont.lineHeight) / 2 + subviewTraits.baselinePosInner.measure; - if (relativeView != nil && relativeView != self && [relativeViewEngine.currentSizeClass invalid]) { + if (relativeView != nil && relativeView != self && [relativeViewEngine.currentTraits invalid]) { subviewEngine.top -= subviewTraits.baselinePosInner.measure; } } else { @@ -381,7 +377,7 @@ - (void)mySubviewEngine:(MyLayoutEngine *)subviewEngine calcTopBottomWithContext subviewEngine.top = [self mySubviewEngine:relativeViewEngine getMeasureFromAnchorType :subviewTraits.centerYPosInner.anchorVal.anchorType withContext:context] - subviewEngine.height / 2 + subviewTraits.centerYPosInner.measure; - if (relativeView != nil && relativeView != self && [relativeViewEngine.currentSizeClass invalid]) { + if (relativeView != nil && relativeView != self && [relativeViewEngine.currentTraits invalid]) { subviewEngine.top -= subviewTraits.centerYPosInner.measure; } if (subviewEngine.top < 0.0 && relativeView == self && layoutTraits.heightSizeInner.wrapVal) { @@ -407,7 +403,7 @@ - (void)mySubviewEngine:(MyLayoutEngine *)subviewEngine calcTopBottomWithContext UIView *relativeView = subviewTraits.topPosInner.anchorVal.view; MyLayoutEngine *relativeViewEngine = relativeView.myEngine; subviewEngine.top = [self mySubviewEngine:relativeViewEngine getMeasureFromAnchorType:subviewTraits.topPosInner.anchorVal.anchorType withContext:context] + subviewTraits.topPosInner.measure; - if (relativeView != nil && relativeView != self && [relativeViewEngine.currentSizeClass invalid]) { + if (relativeView != nil && relativeView != self && [relativeViewEngine.currentTraits invalid]) { subviewEngine.top -= subviewTraits.topPosInner.measure; } subviewEngine.bottom = subviewEngine.top + subviewEngine.height + subviewTraits.bottomPosInner.measure; @@ -421,7 +417,7 @@ - (void)mySubviewEngine:(MyLayoutEngine *)subviewEngine calcTopBottomWithContext UIView *relativeView = subviewTraits.bottomPosInner.anchorVal.view; MyLayoutEngine *relativeViewEngine = relativeView.myEngine; subviewEngine.bottom = [self mySubviewEngine:relativeViewEngine getMeasureFromAnchorType:subviewTraits.bottomPosInner.anchorVal.anchorType withContext:context] - subviewTraits.bottomPosInner.measure + subviewTraits.topPosInner.measure; - if (relativeView != nil && relativeView != self && [relativeViewEngine.currentSizeClass invalid]) { + if (relativeView != nil && relativeView != self && [relativeViewEngine.currentTraits invalid]) { subviewEngine.bottom += subviewTraits.bottomPosInner.measure; } subviewEngine.top = subviewEngine.bottom - subviewEngine.height; @@ -449,11 +445,11 @@ - (void)mySubviewEngine:(MyLayoutEngine *)subviewEngine calcTopBottomWithContext if (lBoundAnchor.anchorVal != nil && uBoundAnchor.anchorVal != nil) { //让宽度缩小并在最小和最大的中间排列。 CGFloat minTop = [self mySubviewEngine:lBoundRelativeViewEngine getMeasureFromAnchorType:lBoundAnchor.anchorVal.anchorType withContext:context] + lBoundAnchor.offsetVal; - if (lBoundRelativeView != nil && lBoundRelativeView != self && [lBoundRelativeViewEngine.currentSizeClass invalid]) { + if (lBoundRelativeView != nil && lBoundRelativeView != self && [lBoundRelativeViewEngine.currentTraits invalid]) { minTop -= lBoundAnchor.offsetVal; } CGFloat maxBottom = [self mySubviewEngine:uBoundRelativeViewEngine getMeasureFromAnchorType:uBoundAnchor.anchorVal.anchorType withContext:context] - uBoundAnchor.offsetVal; - if (uBoundRelativeView != nil && uBoundRelativeView != self && [uBoundRelativeViewEngine.currentSizeClass invalid]) { + if (uBoundRelativeView != nil && uBoundRelativeView != self && [uBoundRelativeViewEngine.currentTraits invalid]) { maxBottom += uBoundAnchor.offsetVal; } //用maxRight减去minLeft得到的宽度再减去视图的宽度,然后让其居中。。如果宽度超过则缩小视图的宽度。 @@ -467,7 +463,7 @@ - (void)mySubviewEngine:(MyLayoutEngine *)subviewEngine calcTopBottomWithContext } else if (lBoundAnchor.anchorVal != nil) { //得到左边的最小位置。如果当前的左边距小于这个位置则缩小视图的宽度。 CGFloat minTop = [self mySubviewEngine:lBoundRelativeViewEngine getMeasureFromAnchorType:lBoundAnchor.anchorVal.anchorType withContext:context] + lBoundAnchor.offsetVal; - if (lBoundRelativeView != nil && lBoundRelativeView != self && [lBoundRelativeViewEngine.currentSizeClass invalid]) { + if (lBoundRelativeView != nil && lBoundRelativeView != self && [lBoundRelativeViewEngine.currentTraits invalid]) { minTop -= lBoundAnchor.offsetVal; } if (_myCGFloatLess(subviewEngine.top, minTop)) { @@ -478,7 +474,7 @@ - (void)mySubviewEngine:(MyLayoutEngine *)subviewEngine calcTopBottomWithContext } else if (uBoundAnchor.anchorVal != nil) { //得到右边的最大位置。如果当前的右边距大于了这个位置则缩小视图的宽度。 CGFloat maxBottom = [self mySubviewEngine:uBoundRelativeViewEngine getMeasureFromAnchorType:uBoundAnchor.anchorVal.anchorType withContext:context] - uBoundAnchor.offsetVal; - if (uBoundRelativeView != nil && uBoundRelativeView != self && [uBoundRelativeViewEngine.currentSizeClass invalid]) { + if (uBoundRelativeView != nil && uBoundRelativeView != self && [uBoundRelativeViewEngine.currentTraits invalid]) { maxBottom += uBoundAnchor.offsetVal; } if (_myCGFloatGreat(subviewEngine.bottom, maxBottom)) { @@ -490,7 +486,7 @@ - (void)mySubviewEngine:(MyLayoutEngine *)subviewEngine calcTopBottomWithContext - (CGFloat)mySubviewEngine:(MyLayoutEngine *)subviewEngine getMeasureFromAnchorType:(MyLayoutAnchorType)anchorType withContext:(MyLayoutContext *)context{ - UIView *relativeView = subviewEngine.currentSizeClass.view; + UIView *relativeView = subviewEngine.currentTraits.view; switch (anchorType) { case MyLayoutAnchorType_Leading: { @@ -610,7 +606,7 @@ - (CGFloat)mySubviewEngine:(MyLayoutEngine *)subviewEngine getMeasureFromAnchorT - (BOOL)mySubviewEngine:(MyLayoutEngine *)subviewEngine calcWidthWithContext:(MyLayoutContext *)context { - MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; BOOL hasMargin = NO; if (subviewEngine.width == CGFLOAT_MAX) { @@ -623,7 +619,7 @@ - (BOOL)mySubviewEngine:(MyLayoutEngine *)subviewEngine calcWidthWithContext:(My for (MyLayoutSize *anchor in anchorArray) { if (anchor.isActive) { MyLayoutEngine *relativeViewEngine = anchor.view.myEngine; - invalid = [relativeViewEngine.currentSizeClass invalid]; + invalid = [relativeViewEngine.currentTraits invalid]; if (!invalid) { if (anchor.val != nil) { [self mySubviewEngine:relativeViewEngine calcWidthWithContext:context]; @@ -654,7 +650,7 @@ - (BOOL)mySubviewEngine:(MyLayoutEngine *)subviewEngine calcWidthWithContext:(My for (MyLayoutSize *anchor in anchorArray) { MyLayoutEngine *relativeViewEngine = anchor.view.myEngine; - if (anchor.isActive && ![relativeViewEngine.currentSizeClass invalid]) { + if (anchor.isActive && ![relativeViewEngine.currentTraits invalid]) { if (anchor.val == nil) { tempWidth = _myCGFloatRound(spareWidth * (anchor.multiVal / totalMultiple)); spareWidth -= tempWidth; @@ -693,7 +689,7 @@ - (BOOL)mySubviewEngine:(MyLayoutEngine *)subviewEngine calcWidthWithContext:(My UIView *relaView = subviewTraits.leadingPosInner.anchorVal.view; MyLayoutEngine *relativeViewEngine = relaView.myEngine; subviewEngine.leading = [self mySubviewEngine:relativeViewEngine getMeasureFromAnchorType:subviewTraits.leadingPosInner.anchorVal.anchorType withContext:context] + subviewTraits.leadingPosInner.measure; - if (relaView != nil && relaView != self && [relativeViewEngine.currentSizeClass invalid]) { + if (relaView != nil && relaView != self && [relativeViewEngine.currentTraits invalid]) { subviewEngine.leading -= subviewTraits.leadingPosInner.measure; } } else if (subviewTraits.leadingPosInner.mostVal != nil) { @@ -706,7 +702,7 @@ - (BOOL)mySubviewEngine:(MyLayoutEngine *)subviewEngine calcWidthWithContext:(My UIView *relativeView = subviewTraits.trailingPosInner.anchorVal.view; MyLayoutEngine *relativeViewEngine = relativeView.myEngine; subviewEngine.trailing = [self mySubviewEngine:relativeViewEngine getMeasureFromAnchorType:subviewTraits.trailingPosInner.anchorVal.anchorType withContext:context] - subviewTraits.trailingPosInner.measure; - if (relativeView != nil && relativeView != self && [relativeViewEngine.currentSizeClass invalid]) { + if (relativeView != nil && relativeView != self && [relativeViewEngine.currentTraits invalid]) { subviewEngine.trailing += subviewTraits.trailingPosInner.measure; } } else if (subviewTraits.trailingPosInner.mostVal != nil) { @@ -746,7 +742,7 @@ - (BOOL)mySubviewEngine:(MyLayoutEngine *)subviewEngine calcWidthWithContext:(My - (BOOL)mySubviewEngine:(MyLayoutEngine *)subviewEngine calcHeightWithContext:(MyLayoutContext *)context{ - MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + MyViewTraitsImpl *subviewTraits = subviewEngine.currentTraits; BOOL hasMargin = NO; if (subviewEngine.height == CGFLOAT_MAX) { @@ -760,7 +756,7 @@ - (BOOL)mySubviewEngine:(MyLayoutEngine *)subviewEngine calcHeightWithContext:(M for (MyLayoutSize *anchor in anchorArray) { if (anchor.isActive) { MyLayoutEngine *relativeViewEngine = anchor.view.myEngine; - invalid = [relativeViewEngine.currentSizeClass invalid]; + invalid = [relativeViewEngine.currentTraits invalid]; if (!invalid) { if (anchor.val != nil) { [self mySubviewEngine:relativeViewEngine calcHeightWithContext:context]; @@ -790,7 +786,7 @@ - (BOOL)mySubviewEngine:(MyLayoutEngine *)subviewEngine calcHeightWithContext:(M for (MyLayoutSize *anchor in anchorArray) { MyLayoutEngine *relativeViewEngine = anchor.view.myEngine; - if (anchor.isActive && ![relativeViewEngine.currentSizeClass invalid]) { + if (anchor.isActive && ![relativeViewEngine.currentTraits invalid]) { if (anchor.val == nil) { tempHeight = _myCGFloatRound(spareHeight * (anchor.multiVal / totalMultiple)); spareHeight -= tempHeight; @@ -824,7 +820,7 @@ - (BOOL)mySubviewEngine:(MyLayoutEngine *)subviewEngine calcHeightWithContext:(M UIView *relativeView = subviewTraits.topPosInner.anchorVal.view; MyLayoutEngine *relativeViewEngine = relativeView.myEngine; subviewEngine.top = [self mySubviewEngine:relativeViewEngine getMeasureFromAnchorType:subviewTraits.topPosInner.anchorVal.anchorType withContext:context] + subviewTraits.topPosInner.measure; - if (relativeView != nil && relativeView != self && [relativeViewEngine.currentSizeClass invalid]) { + if (relativeView != nil && relativeView != self && [relativeViewEngine.currentTraits invalid]) { subviewEngine.top -= subviewTraits.topPosInner.measure; } } else if (subviewTraits.topPosInner.mostVal != nil) { @@ -837,7 +833,7 @@ - (BOOL)mySubviewEngine:(MyLayoutEngine *)subviewEngine calcHeightWithContext:(M UIView *relativeView = subviewTraits.bottomPosInner.anchorVal.view; MyLayoutEngine *relativeViewEngine = relativeView.myEngine; subviewEngine.bottom = [self mySubviewEngine:relativeViewEngine getMeasureFromAnchorType:subviewTraits.bottomPosInner.anchorVal.anchorType withContext:context] - subviewTraits.bottomPosInner.measure; - if (relativeView != nil && relativeView != self && [relativeViewEngine.currentSizeClass invalid]) { + if (relativeView != nil && relativeView != self && [relativeViewEngine.currentTraits invalid]) { subviewEngine.bottom += subviewTraits.bottomPosInner.measure; } } else if (subviewTraits.bottomPosInner.mostVal != nil) { @@ -872,7 +868,7 @@ - (BOOL)mySubviewEngine:(MyLayoutEngine *)subviewEngine calcHeightWithContext:(M } - (CGSize)myDoLayoutRecalcWidth:(BOOL *)pRecalcWidth recalcHeight:(BOOL *)pRecalcHeight withContext:(MyLayoutContext *)context { - MyRelativeLayoutTraits *layoutTraits = (MyRelativeLayoutTraits *)context->layoutViewEngine.currentSizeClass; + MyRelativeLayoutTraitsImpl *layoutTraits = context->layoutViewEngine.currentTraits; if (pRecalcWidth != NULL) { *pRecalcWidth = NO; @@ -884,7 +880,7 @@ - (CGSize)myDoLayoutRecalcWidth:(BOOL *)pRecalcWidth recalcHeight:(BOOL *)pRecal CGFloat maxLayoutWidth = context->paddingLeading + context->paddingTrailing; CGFloat maxLayoutHeight = context->paddingTop + context->paddingBottom; for (MyLayoutEngine *subviewEngine in context->subviewEngines) { - MyViewTraits *subviewTraits = (MyViewTraits *)subviewEngine.currentSizeClass; + MyViewTraitsImpl *subviewTraits = (MyViewTraitsImpl *)subviewEngine.currentTraits; [self mySubviewEngine:subviewEngine calcLeadingTrailingWithContext:context]; [self mySubviewEngine:subviewEngine calcTopBottomWithContext:context]; diff --git a/MyLayout/Lib/MyTableLayout.h b/MyLayout/Lib/MyTableLayout.h index 476375f..4c0a8d5 100644 --- a/MyLayout/Lib/MyTableLayout.h +++ b/MyLayout/Lib/MyTableLayout.h @@ -34,7 +34,7 @@ 2. 对于水平表格布局来说,行子视图是从左到右依次排列的,而列子视图则是在行子视图里面从上到下依次排列。 */ -@interface MyTableLayout : MyLinearLayout +@interface MyTableLayout : MyLinearLayout /** 构建一个表格布局视图 diff --git a/MyLayout/Lib/MyTableLayout.m b/MyLayout/Lib/MyTableLayout.m index cba4bd9..1a88801 100644 --- a/MyLayout/Lib/MyTableLayout.m +++ b/MyLayout/Lib/MyTableLayout.m @@ -28,7 +28,7 @@ - (instancetype)initWith:(CGFloat)rowSize colSize:(CGFloat)colSize orientation:( _rowSize = rowSize; _colSize = colSize; - UIView *layoutTraits = self.myDefaultSizeClass; + MyTableLayoutTraitsImpl *layoutTraits = self.myDefaultTraits; if (rowSize == MyLayoutSize.average) { layoutTraits.weight = 1; @@ -70,7 +70,7 @@ - (void)myHookSublayout:(MyBaseLayout *)sublayout borderlineRect:(CGRect *)pRect 因此我们重载这个方法来解决这个问题,这个方法可以将列子视图的边界线的区域进行扩充和调整,目的是为了让列子视图的边界线能够布满整个行布局上。 */ - MyLinearLayoutTraits *layoutTraits = (MyLinearLayoutTraits*)self.myEngine.currentSizeClass; + MyTableLayoutTraitsImpl *layoutTraits = self.myEngine.currentTraits; if (self.rowSize == MyLayoutSize.wrap) { if (layoutTraits.orientation == MyOrientation_Horz) { @@ -127,7 +127,7 @@ - (MyLinearLayout *)insertRow:(CGFloat)rowSize colCount:(NSUInteger)colCount atI } - (MyLinearLayout *)insertRow:(CGFloat)rowSize colSize:(CGFloat)colSize atIndex:(NSInteger)rowIndex { - MyTableLayout *layoutTraits = self.myDefaultSizeClass; + MyTableLayoutTraitsImpl *layoutTraits = self.myDefaultTraits; MyOrientation ori = MyOrientation_Vert; if (layoutTraits.orientation == MyOrientation_Vert) { @@ -138,9 +138,9 @@ - (MyLinearLayout *)insertRow:(CGFloat)rowSize colSize:(CGFloat)colSize atIndex: MyTableRowLayout *rowView = [MyTableRowLayout rowSize:rowSize colSize:colSize orientation:ori]; if (ori == MyOrientation_Horz) { - rowView.subviewHSpace = layoutTraits.subviewHSpace; + rowView.subviewHSpacing = layoutTraits.subviewHSpacing; } else { - rowView.subviewVSpace = layoutTraits.subviewVSpace; + rowView.subviewVSpacing = layoutTraits.subviewVSpacing; } rowView.intelligentBorderline = self.intelligentBorderline; [super insertSubview:rowView atIndex:rowIndex]; @@ -171,17 +171,17 @@ - (void)addCol:(UIView *)colView atRow:(NSInteger)rowIndex { - (void)insertCol:(UIView *)colView atIndexPath:(NSIndexPath *)indexPath { MyTableRowLayout *rowView = (MyTableRowLayout *)[self viewAtRowIndex:indexPath.row]; - MyLinearLayout *rowViewTraits = rowView.myDefaultSizeClass; - UIView *colViewTraits = colView.myDefaultSizeClass; + MyLinearLayoutTraitsImpl *rowViewTraits = rowView.myDefaultTraits; + MyViewTraitsImpl *colViewTraits = colView.myDefaultTraits; if (rowView.colSize == MyLayoutSize.average) { colViewTraits.weight = 1.0; } else if (rowView.colSize < sColCountTag) { NSUInteger colCount = sColCountTag - rowView.colSize; if (rowViewTraits.orientation == MyOrientation_Horz) { - [[[colViewTraits.widthSize _myEqualTo:rowView.widthSize] _myMultiply:(1.0 / colCount)] _myAdd:-1 * rowView.subviewHSpace * (colCount - 1.0) / colCount]; + [[[colViewTraits.widthSize _myEqualTo:rowView.widthSize] _myMultiply:(1.0 / colCount)] _myAdd:-1 * rowView.subviewHSpacing * (colCount - 1.0) / colCount]; } else { - [[[colViewTraits.heightSize _myEqualTo:rowView.heightSize] _myMultiply:(1.0 / colCount)] _myAdd:-1 * rowView.subviewVSpace * (colCount - 1.0) / colCount]; + [[[colViewTraits.heightSize _myEqualTo:rowView.heightSize] _myMultiply:(1.0 / colCount)] _myAdd:-1 * rowView.subviewVSpacing * (colCount - 1.0) / colCount]; } } else if (rowView.colSize > 0) { if (rowViewTraits.orientation == MyOrientation_Horz) { @@ -232,20 +232,20 @@ - (NSUInteger)countOfColInRow:(NSInteger)rowIndex { #pragma mark-- Override Methods -- (void)setSubviewVSpace:(CGFloat)subviewVSpace { - [super setSubviewVSpace:subviewVSpace]; +- (void)setSubviewVSpacing:(CGFloat)subviewVSpacing { + [super setSubviewVSpacing:subviewVSpacing]; if (self.orientation == MyOrientation_Horz) { for (NSInteger i = 0; i < self.countOfRow; i++) { - [self viewAtRowIndex:i].subviewVSpace = subviewVSpace; + [self viewAtRowIndex:i].subviewVSpacing = subviewVSpacing; } } } -- (void)setSubviewHSpace:(CGFloat)subviewHSpace { - [super setSubviewHSpace:subviewHSpace]; +- (void)setSubviewHSpacing:(CGFloat)subviewHSpacing { + [super setSubviewHSpacing:subviewHSpacing]; if (self.orientation == MyOrientation_Vert) { for (NSInteger i = 0; i < self.countOfRow; i++) { - [self viewAtRowIndex:i].subviewHSpace = subviewHSpace; + [self viewAtRowIndex:i].subviewHSpacing = subviewHSpacing; } } } @@ -270,8 +270,8 @@ - (void)insertSubview:(UIView *)view aboveSubview:(UIView *)siblingSubview { NSCAssert(0, @"Constraint exception!! Can't call insertSubview"); } -- (id)createSizeClassInstance { - return [MyTableLayoutTraits new]; +- (Class) myTratisClass { + return [MyTableLayoutTraitsImpl class]; } @end diff --git a/MyLayoutDemo/AllTest10Cell.m b/MyLayoutDemo/AllTest10Cell.m index 40f33ab..86e209e 100644 --- a/MyLayoutDemo/AllTest10Cell.m +++ b/MyLayoutDemo/AllTest10Cell.m @@ -35,13 +35,9 @@ - (CGSize)systemLayoutSizeFittingSize:(CGSize)targetSize withHorizontalFittingPr 因为cell的高度是自适应的,因此这里通过调用高度为wrap的布局视图的sizeThatFits来获取真实的高度。 */ - if (@available(iOS 11.0, *)) { - //如果你的界面要支持横屏的话,因为iPhoneX的横屏左右有44的安全区域,所以这里要减去左右的安全区域的值,来作为布局宽度尺寸的评估值。 - //如果您的界面不需要支持横屏,或者延伸到安全区域外则不需要做这个特殊处理,而直接使用else部分的代码即可。 - return [self.rootLayout sizeThatFits:CGSizeMake(targetSize.width - self.safeAreaInsets.left - self.safeAreaInsets.right, targetSize.height)]; - } else { - return [self.rootLayout sizeThatFits:targetSize]; //如果使用系统自带的分割线,请记得将返回的高度height+1 - } + //如果你的界面要支持横屏的话,因为iPhoneX的横屏左右有44的安全区域,所以这里要减去左右的安全区域的值,来作为布局宽度尺寸的评估值。 + //如果您的界面不需要支持横屏,或者延伸到安全区域外则不需要做这个特殊处理,而直接使用else部分的代码即可。 + return [self.rootLayout sizeThatFits:CGSizeMake(targetSize.width - self.safeAreaInsets.left - self.safeAreaInsets.right, targetSize.height)]; } #pragma mark -- Layout Construction diff --git a/MyLayoutDemo/AllTest10HeaderView.m b/MyLayoutDemo/AllTest10HeaderView.m index 1c40f47..b15cbc9 100644 --- a/MyLayoutDemo/AllTest10HeaderView.m +++ b/MyLayoutDemo/AllTest10HeaderView.m @@ -57,7 +57,7 @@ -(void)createLinearRootLayout MyLinearLayout *messageLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; messageLayout.weight = 1; messageLayout.myLeading = 5; - messageLayout.subviewVSpace = 5; + messageLayout.subviewVSpacing = 5; [_rootLayout addSubview:messageLayout]; _nickNameLabel = [UILabel new]; @@ -77,8 +77,8 @@ -(void)createLinearRootLayout _nineFlowLayout = [[MyFlowLayout alloc] initWithOrientation:MyOrientation_Vert arrangedCount:3]; _nineFlowLayout.gravity = MyGravity_Horz_Fill; _nineFlowLayout.myHorzMargin = 0; - _nineFlowLayout.subviewHSpace = 5; - _nineFlowLayout.subviewVSpace = 5; + _nineFlowLayout.subviewHSpacing = 5; + _nineFlowLayout.subviewVSpacing = 5; _nineFlowLayout.myHeight = MyLayoutSize.wrap; [messageLayout addSubview:_nineFlowLayout]; @@ -187,13 +187,9 @@ - (void)setModel:(AllTest10Model *)model { //如果您的最低支持是iOS8,那么你可以重载这个方法来动态的评估cell的高度,Autolayout内部是通过这个方法来评估高度的,因此如果用MyLayout实现的话就不需要调用基类的方法,而是调用根布局视图的sizeThatFits来评估获取动态的高度。 - (CGSize)systemLayoutSizeFittingSize:(CGSize)targetSize withHorizontalFittingPriority:(UILayoutPriority)horizontalFittingPriority verticalFittingPriority:(UILayoutPriority)verticalFittingPriority { - if (@available(iOS 11.0, *)) { - //如果你的界面要支持横屏的话,因为iPhoneX的横屏左右有44的安全区域,所以这里要减去左右的安全区域的值,来作为布局宽度尺寸的评估值。 - //如果您的界面不需要支持横屏,或者延伸到安全区域外则不需要做这个特殊处理,而直接使用else部分的代码即可。 - return [self.rootLayout sizeThatFits:CGSizeMake(targetSize.width - self.safeAreaInsets.left - self.safeAreaInsets.right, targetSize.height)]; - } else { - return [self.rootLayout sizeThatFits:targetSize]; - } + //如果你的界面要支持横屏的话,因为iPhoneX的横屏左右有44的安全区域,所以这里要减去左右的安全区域的值,来作为布局宽度尺寸的评估值。 + //如果您的界面不需要支持横屏,或者延伸到安全区域外则不需要做这个特殊处理,而直接使用else部分的代码即可。 + return [self.rootLayout sizeThatFits:CGSizeMake(targetSize.width - self.safeAreaInsets.left - self.safeAreaInsets.right, targetSize.height)]; } - (NSArray *)nineImageViews { diff --git a/MyLayoutDemo/AllTest11ViewController.m b/MyLayoutDemo/AllTest11ViewController.m index e5c605b..6d818a4 100644 --- a/MyLayoutDemo/AllTest11ViewController.m +++ b/MyLayoutDemo/AllTest11ViewController.m @@ -44,8 +44,8 @@ -(void)loadView actionLayout.myHeight = MyLayoutSize.wrap; actionLayout.gravity = MyGravity_Horz_Fill; //所有子视图水平填充,也就是所有子视图的宽度相等。 actionLayout.padding = UIEdgeInsetsMake(5, 5, 5, 5); - actionLayout.subviewHSpace = 5; - actionLayout.subviewVSpace = 5; + actionLayout.subviewHSpacing = 5; + actionLayout.subviewVSpacing = 5; [rootLayout addSubview:actionLayout]; [actionLayout addSubview:[self createActionButton:NSLocalizedString(@"Identity", @"") @@ -66,7 +66,7 @@ -(void)loadView MyFlowLayout *contentLayout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Vert arrangedCount:4]; contentLayout.backgroundColor = [CFTool color:5]; contentLayout.weight = 1.0; //占用线性布局中的剩余高度。 - contentLayout.subviewSpace = 10; + contentLayout.subviewSpacing = 10; [rootLayout addSubview:contentLayout]; self.contentLayout = contentLayout; diff --git a/MyLayoutDemo/AllTest12ViewController.m b/MyLayoutDemo/AllTest12ViewController.m index cd8e31e..8b4d784 100644 --- a/MyLayoutDemo/AllTest12ViewController.m +++ b/MyLayoutDemo/AllTest12ViewController.m @@ -87,7 +87,7 @@ -(void)demo1 linelayout.orientation = MyOrientation_Vert; linelayout.mySize = CGSizeMake(MyLayoutSize.wrap, MyLayoutSize.wrap); linelayout.padding = UIEdgeInsetsMake(10, 10, 10, 10); - linelayout.subviewSpace = 10; + linelayout.subviewSpacing = 10; sbv1.mySize = CGSizeMake(100, 40); sbv2.mySize = CGSizeMake(150, 50); @@ -140,7 +140,7 @@ -(void)demo2 linelayout.orientation = MyOrientation_Vert; linelayout.myHeight = MyLayoutSize.wrap; linelayout.padding = UIEdgeInsetsMake(10, 10, 10, 10); - linelayout.subviewSpace = 10; + linelayout.subviewSpacing = 10; sbv1.mySize = CGSizeMake(100, 40); sbv2.mySize = CGSizeMake(150, 50); @@ -193,7 +193,7 @@ -(void)demo3 linelayout.orientation = MyOrientation_Horz; linelayout.myWidth = MyLayoutSize.wrap; linelayout.padding = UIEdgeInsetsMake(10, 10, 10, 10); - linelayout.subviewSpace = 10; + linelayout.subviewSpacing = 10; linelayout.gravity = MyGravity_Vert_Fill; diff --git a/MyLayoutDemo/AllTest1TableViewCell.m b/MyLayoutDemo/AllTest1TableViewCell.m index ff52df1..c8a87eb 100644 --- a/MyLayoutDemo/AllTest1TableViewCell.m +++ b/MyLayoutDemo/AllTest1TableViewCell.m @@ -97,13 +97,10 @@ - (CGSize)systemLayoutSizeFittingSize:(CGSize)targetSize withHorizontalFittingPr 因为cell的高度是自适应的,因此这里通过调用高度为wrap的布局视图的systemLayoutSizeFittingSize来获取真实的高度。 */ - if (@available(iOS 11.0, *)) { - //如果你的界面要支持横屏的话,因为iPhoneX的横屏左右有44的安全区域,所以这里要减去左右的安全区域的值,来作为布局宽度尺寸的评估值。 - //如果您的界面不需要支持横屏,或者延伸到安全区域外则不需要做这个特殊处理,而直接使用else部分的代码即可。 - return [self.rootLayout systemLayoutSizeFittingSize:CGSizeMake(targetSize.width - self.safeAreaInsets.left - self.safeAreaInsets.right, targetSize.height) withHorizontalFittingPriority:horizontalFittingPriority verticalFittingPriority:verticalFittingPriority]; - } else { - return [self.rootLayout systemLayoutSizeFittingSize:targetSize withHorizontalFittingPriority:horizontalFittingPriority verticalFittingPriority:verticalFittingPriority]; //如果使用系统自带的分割线,请记得将返回的高度height+1 - } + + //如果你的界面要支持横屏的话,因为iPhoneX的横屏左右有44的安全区域,所以这里要减去左右的安全区域的值,来作为布局宽度尺寸的评估值。 + //如果您的界面不需要支持横屏,或者延伸到安全区域外则不需要做这个特殊处理,而直接使用else部分的代码即可。 + return [self.rootLayout systemLayoutSizeFittingSize:CGSizeMake(targetSize.width - self.safeAreaInsets.left - self.safeAreaInsets.right, targetSize.height) withHorizontalFittingPriority:horizontalFittingPriority verticalFittingPriority:verticalFittingPriority]; } @@ -141,7 +138,7 @@ -(void)createLinearRootLayout MyLinearLayout *messageLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; messageLayout.weight = 1; messageLayout.myLeading = 5; //前面2行代码描述的是垂直布局占用除头像外的所有宽度,并和头像保持5个点的间距。 - messageLayout.subviewVSpace = 5; //垂直布局里面所有子视图都保持5个点的间距。 + messageLayout.subviewVSpacing = 5; //垂直布局里面所有子视图都保持5个点的间距。 [_rootLayout addSubview:messageLayout]; diff --git a/MyLayoutDemo/AllTest1TableViewCellForAutoLayout.m b/MyLayoutDemo/AllTest1TableViewCellForAutoLayout.m index 2512889..c3fc6f9 100644 --- a/MyLayoutDemo/AllTest1TableViewCellForAutoLayout.m +++ b/MyLayoutDemo/AllTest1TableViewCellForAutoLayout.m @@ -125,7 +125,7 @@ -(void)createLinearRootLayout MyLinearLayout *messageLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; messageLayout.weight = 1; messageLayout.myLeading = 5; //前面2行代码描述的是垂直布局占用除头像外的所有宽度,并和头像保持5个点的间距。 - messageLayout.subviewVSpace = 5; //垂直布局里面所有子视图都保持5个点的间距。 + messageLayout.subviewVSpacing = 5; //垂直布局里面所有子视图都保持5个点的间距。 [_rootLayout addSubview:messageLayout]; diff --git a/MyLayoutDemo/AllTest1ViewController.m b/MyLayoutDemo/AllTest1ViewController.m index 3517432..b34dd03 100644 --- a/MyLayoutDemo/AllTest1ViewController.m +++ b/MyLayoutDemo/AllTest1ViewController.m @@ -280,9 +280,10 @@ -(UIView*)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)s [headerFooterView setItemChangedAction:^(NSInteger index){ NSString *message = [NSString stringWithFormat:@"You have select index:%ld",(long)index]; - UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"" message:message delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; + UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:@"" message:message preferredStyle:UIAlertControllerStyleAlert]; - [alertView show]; + [alertVC addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:nil]]; + [self presentViewController:alertVC animated:YES completion:nil]; }]; diff --git a/MyLayoutDemo/AllTest2TableViewCell.m b/MyLayoutDemo/AllTest2TableViewCell.m index ed1b35b..ecae5a3 100644 --- a/MyLayoutDemo/AllTest2TableViewCell.m +++ b/MyLayoutDemo/AllTest2TableViewCell.m @@ -97,14 +97,14 @@ -(void)createLinearRootLayout userInfoLayout.myHeight = MyLayoutSize.wrap; //高度由子视图决定 userInfoLayout.weight = 1; //中间部分的宽度占用整个水平线性布局剩余的空间。 userInfoLayout.gravity = MyGravity_Horz_Fill; //里面的子视图宽度和布局视图相等。 - userInfoLayout.subviewVSpace = 5; //子视图间距为5。 + userInfoLayout.subviewVSpacing = 5; //子视图间距为5。 [rootLayout addSubview:userInfoLayout]; //姓名信息部分,一个水平线性布局:左边名称,后面两个操作按钮,整体底部对齐。 MyLinearLayout *userNameLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Horz]; userNameLayout.myHeight = MyLayoutSize.wrap; //高度由子视图决定 - userNameLayout.subviewHSpace = 5; //子视图之间宽度间隔是5 + userNameLayout.subviewHSpacing = 5; //子视图之间宽度间隔是5 userNameLayout.gravity = MyGravity_Vert_Bottom; //整体垂直底部对齐。 [userInfoLayout addSubview:userNameLayout]; @@ -261,8 +261,8 @@ -(void)createFloatRootLayout userInfoLayout.heightSize.equalTo(rootLayout.heightSize); userInfoLayout.weight = 1; userInfoLayout.gravity = MyGravity_Vert_Center; - userInfoLayout.subviewVSpace = 5; - userInfoLayout.subviewHSpace = 5; + userInfoLayout.subviewVSpacing = 5; + userInfoLayout.subviewHSpacing = 5; [rootLayout addSubview:userInfoLayout]; _nameLabel = [UILabel new]; diff --git a/MyLayoutDemo/AllTest3ViewController.m b/MyLayoutDemo/AllTest3ViewController.m index bafdf39..27d3218 100644 --- a/MyLayoutDemo/AllTest3ViewController.m +++ b/MyLayoutDemo/AllTest3ViewController.m @@ -30,7 +30,7 @@ @interface AllTest3ViewController () @property(nonatomic, strong) UILabel *rightFlexedLabel; //伸缩布局 -@property(nonatomic,strong) MyBaseLayout *shrinkLayout; +@property(nonatomic,strong) MyFlowLayout *shrinkLayout; @end @@ -214,7 +214,7 @@ -(void)addVisibilityTestLayout:(MyLinearLayout *)contentLayout testLayout.paddingRight = 10; testLayout.myHeight = 50; testLayout.gravity = MyGravity_Vert_Fill; - testLayout.subviewHSpace = 10; + testLayout.subviewHSpacing = 10; testLayout.myTop = 10; [contentLayout addSubview:testLayout]; @@ -266,7 +266,7 @@ -(void)addFlexedWidthLayout:(MyLinearLayout*)contentLayout testLayout.myHeight = 50; testLayout.gravity = MyGravity_Vert_Fill; testLayout.shrinkType = MySubviewsShrink_Auto; //左右2个子视图会根据自身的宽度自动调整。不会产生覆盖和重叠。 - testLayout.subviewHSpace = 10; + testLayout.subviewHSpacing = 10; [contentLayout addSubview:testLayout]; self.flexedLayout = testLayout; @@ -310,9 +310,10 @@ -(void)addShrinkLayout:(MyLinearLayout*)contentLayout testLayout.backgroundColor = [UIColor whiteColor]; testLayout.gravity = MyGravity_Horz_Fill; //尺寸相等 testLayout.padding = UIEdgeInsetsMake(10, 10, 10, 10); - testLayout.subviewHSpace = 10; - testLayout.subviewVSpace = 10; - testLayout.heightSize.equalTo(@50); + testLayout.subviewHSpacing = 10; + testLayout.subviewVSpacing = 10; + testLayout.heightSize.equalTo(@(MyLayoutSize.wrap)); + testLayout.maxLines = 2; testLayout.clipsToBounds = YES; [contentLayout addSubview:testLayout]; self.shrinkLayout = testLayout; @@ -464,9 +465,10 @@ -(MyFloatLayout*)createSegmentedLayout:(SEL)leftAction rightAction:(SEL)rightAct -(void)handleTap:(MyBaseLayout*)sender { - UIAlertView *av = [[UIAlertView alloc] initWithTitle:@"test" message:@"you touched this section" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; + UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:@"" message:@"you touched this section" preferredStyle:UIAlertControllerStyleAlert]; - [av show]; + [alertVC addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:nil]]; + [self presentViewController:alertVC animated:YES completion:nil]; } -(void)handleTouchDown:(MyBaseLayout*)sender @@ -540,11 +542,12 @@ -(void)handleRightFlexed:(UISegmentedControl*)segmented -(void)handleShrinkSwitch:(UISwitch *)sender { if (sender.isOn) - self.shrinkLayout.heightSize.equalTo(@(MyLayoutSize.wrap)); + self.shrinkLayout.maxLines = NSIntegerMax; else - self.shrinkLayout.heightSize.equalTo(@50); + self.shrinkLayout.maxLines = 2; [self.shrinkLayout layoutAnimationWithDuration:0.3]; + [((MyBaseLayout *)self.shrinkLayout.superview) layoutAnimationWithDuration:0.3]; } -(void)handleShowPopMenu:(MyBaseLayout*)sender @@ -585,8 +588,8 @@ -(void)handleShowPopMenu:(MyBaseLayout*)sender MyFlowLayout *itemLayout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Vert arrangedCount:3]; itemLayout.myHorzMargin = 0; itemLayout.gravity = MyGravity_Horz_Fill; - itemLayout.subviewHSpace = 10; - itemLayout.subviewVSpace = 10; + itemLayout.subviewHSpacing = 10; + itemLayout.subviewVSpacing = 10; itemLayout.myHeight = MyLayoutSize.wrap; [scrollView addSubview:itemLayout]; self.popmenuItemLayout = itemLayout; @@ -655,7 +658,7 @@ -(void)handleShowPopMenu:(MyBaseLayout*)sender -(void)handleAddMe:(UIButton*)sender { UIButton *button = [UIButton new]; - [button setTitle:[NSString stringWithFormat:NSLocalizedString(@"double tap remove:%d", @""), sender.superview.subviews.count ] forState:UIControlStateNormal]; + [button setTitle:[NSString stringWithFormat:NSLocalizedString(@"double tap remove:%ld", @""), sender.superview.subviews.count ] forState:UIControlStateNormal]; button.titleLabel.adjustsFontSizeToFitWidth = YES; button.backgroundColor = [CFTool color:2]; button.layer.cornerRadius = 5; diff --git a/MyLayoutDemo/AllTest4ViewController.m b/MyLayoutDemo/AllTest4ViewController.m index 7e559e4..54defba 100644 --- a/MyLayoutDemo/AllTest4ViewController.m +++ b/MyLayoutDemo/AllTest4ViewController.m @@ -136,8 +136,8 @@ -(MyFlowLayout*)createCellContainerLayout:(NSInteger)arrangedCount MyFlowLayout *containerLayout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Vert arrangedCount:arrangedCount]; containerLayout.heightSize.equalTo(@(MyLayoutSize.wrap)); containerLayout.gravity = MyGravity_Horz_Fill; //平均分配里面每个子视图的宽度或者拉伸子视图的宽度以便填充满整个布局。 - containerLayout.subviewHSpace = 5; - containerLayout.subviewVSpace = 5; + containerLayout.subviewHSpacing = 5; + containerLayout.subviewVSpacing = 5; containerLayout.padding = UIEdgeInsetsMake(5, 5, 5, 5); return containerLayout; @@ -149,7 +149,7 @@ -(UIView*)createCellLayout1:(NSString*)image title:(NSString*)title MyLinearLayout *cellLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; cellLayout.gravity = MyGravity_Horz_Fill; //里面所有子视图的宽度都跟父视图保持一致,这样子视图就不需要设置宽度了。 cellLayout.myHeight = 100; - cellLayout.subviewVSpace = 5; //设置布局视图里面子视图之间的间距为5个点。 + cellLayout.subviewVSpacing = 5; //设置布局视图里面子视图之间的间距为5个点。 cellLayout.backgroundColor = [UIColor whiteColor]; [cellLayout setTarget:self action:@selector(handleCellLayoutTap:)]; cellLayout.highlightedOpacity = 0.3; //设置触摸事件按下时的不透明度,来响应按下状态。 @@ -186,9 +186,10 @@ -(void)showClikAlert:(UIView*)sender inFlowLayout:(MyFlowLayout*)flowLayout NSString *message = [NSString stringWithFormat:@"You have select:\nSupplementaryIndex:%ld CellIndex:%ld",(long)supplementaryIndex, (long)cellIndex]; - UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:nil message:message delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; + UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:@"" message:message preferredStyle:UIAlertControllerStyleAlert]; - [alertView show]; + [alertVC addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:nil]]; + [self presentViewController:alertVC animated:YES completion:nil]; } diff --git a/MyLayoutDemo/AllTest5ViewController.m b/MyLayoutDemo/AllTest5ViewController.m index ee29453..2422658 100644 --- a/MyLayoutDemo/AllTest5ViewController.m +++ b/MyLayoutDemo/AllTest5ViewController.m @@ -29,7 +29,7 @@ -(void)loadView MyLinearLayout *rootLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; rootLayout.padding = UIEdgeInsetsMake(10, 10, 10, 10); rootLayout.gravity = MyGravity_Horz_Fill; - rootLayout.subviewSpace = 10; + rootLayout.subviewSpacing = 10; rootLayout.backgroundColor = [UIColor whiteColor]; self.view = rootLayout; @@ -56,14 +56,12 @@ -(void)loadView [rootLayout addSubview:v3]; //v3视图在其他任何iPhone设备横屏都不参与布局 - [v3 fetchLayoutSizeClass:MySizeClass_wAny | MySizeClass_hCompact].visibility = MyVisibility_Gone; + [v3 fetchLayoutTraitsInSizeClass:MySizeClass_wAny | MySizeClass_hCompact].visibility = MyVisibility_Gone; //只有iphone6Plus的横屏才参与布局 - [v3 fetchLayoutSizeClass:MySizeClass_wRegular | MySizeClass_hCompact copyFrom:MySizeClass_wAny | MySizeClass_hAny].visibility = MyVisibility_Visible; + [v3 fetchLayoutTraitsInSizeClass:MySizeClass_wRegular | MySizeClass_hCompact copyFrom:MySizeClass_wAny | MySizeClass_hAny].visibility = MyVisibility_Visible; //针对iPhone设备的所有横屏的高度都是Compact的,而宽度则是任意,因此下面的设置横屏情况下布局变为水平布局。 - //虽然fetchLayoutSizeClass方法真实返回的是MyLayoutSize或者其派生类,但是仍然可以用视图以及布局来设置其中的属性 - //但是调用lsc.backgroundColor = xx 则会崩溃,因为fetchLayoutSizeClass返回的并不是真的视图对象。 - MyLinearLayout *lsc = [rootLayout fetchLayoutSizeClass:MySizeClass_wAny | MySizeClass_hCompact copyFrom:MySizeClass_wAny | MySizeClass_hAny]; + id lsc = [rootLayout fetchLayoutTraitsInSizeClass:MySizeClass_wAny | MySizeClass_hCompact copyFrom:MySizeClass_wAny | MySizeClass_hAny]; lsc.orientation = MyOrientation_Horz; lsc.widthSize.equalTo(nil); lsc.gravity = MyGravity_Vert_Fill; diff --git a/MyLayoutDemo/AllTest6ViewController.m b/MyLayoutDemo/AllTest6ViewController.m index 138309a..77aa116 100644 --- a/MyLayoutDemo/AllTest6ViewController.m +++ b/MyLayoutDemo/AllTest6ViewController.m @@ -36,7 +36,7 @@ -(void)loadView menuLayout.gravity = MyGravity_Horz_Fill; //水平填充所有尺寸。 menuLayout.heightSize.equalTo(@(MyLayoutSize.wrap)); menuLayout.padding = UIEdgeInsetsMake(10, 10, 10, 10); - menuLayout.subviewSpace = 10; + menuLayout.subviewSpacing = 10; [rootLayout addSubview:menuLayout]; UILabel *menu1Label = [UILabel new]; @@ -105,19 +105,19 @@ -(void)loadView //下面定义iPhone设备横屏时的界面布局。 - MyLinearLayout *rootLayoutSC = [rootLayout fetchLayoutSizeClass:MySizeClass_wAny | MySizeClass_hCompact]; + id rootLayoutSC = [rootLayout fetchLayoutTraitsInSizeClass:MySizeClass_wAny | MySizeClass_hCompact]; rootLayoutSC.orientation = MyOrientation_Horz; rootLayoutSC.gravity = MyGravity_Vert_Fill; - MyFlowLayout *menuLayoutSC = [menuLayout fetchLayoutSizeClass:MySizeClass_wAny | MySizeClass_hCompact copyFrom:MySizeClass_hAny | MySizeClass_wAny]; + id menuLayoutSC = [menuLayout fetchLayoutTraitsInSizeClass:MySizeClass_wAny | MySizeClass_hCompact copyFrom:MySizeClass_hAny | MySizeClass_wAny]; menuLayoutSC.orientation = MyOrientation_Horz; menuLayoutSC.gravity = MyGravity_Vert_Fill; menuLayoutSC.widthSize.equalTo(@(MyLayoutSize.wrap)); - UILabel *menu1LabelSC = [menu1Label fetchLayoutSizeClass:MySizeClass_wAny | MySizeClass_hCompact]; - UILabel *menu2LabelSC = [menu2Label fetchLayoutSizeClass:MySizeClass_wAny | MySizeClass_hCompact]; - UILabel *menu3LabelSC = [menu3Label fetchLayoutSizeClass:MySizeClass_wAny | MySizeClass_hCompact]; + id menu1LabelSC = [menu1Label fetchLayoutTraitsInSizeClass:MySizeClass_wAny | MySizeClass_hCompact]; + id menu2LabelSC = [menu2Label fetchLayoutTraitsInSizeClass:MySizeClass_wAny | MySizeClass_hCompact]; + id menu3LabelSC = [menu3Label fetchLayoutTraitsInSizeClass:MySizeClass_wAny | MySizeClass_hCompact]; menu1LabelSC.widthSize.equalTo(menu1LabelSC.heightSize); menu2LabelSC.widthSize.equalTo(menu2LabelSC.heightSize); @@ -126,9 +126,9 @@ -(void)loadView - UILabel *func1LabelSC = [func1Label fetchLayoutSizeClass:MySizeClass_wAny | MySizeClass_hCompact]; - UILabel *func2LabelSC = [func2Label fetchLayoutSizeClass:MySizeClass_wAny | MySizeClass_hCompact]; - UILabel *func3LabelSC = [func3Label fetchLayoutSizeClass:MySizeClass_wAny | MySizeClass_hCompact]; + id func1LabelSC = [func1Label fetchLayoutTraitsInSizeClass:MySizeClass_wAny | MySizeClass_hCompact]; + id func2LabelSC = [func2Label fetchLayoutTraitsInSizeClass:MySizeClass_wAny | MySizeClass_hCompact]; + id func3LabelSC = [func3Label fetchLayoutTraitsInSizeClass:MySizeClass_wAny | MySizeClass_hCompact]; func1LabelSC.widthSize.equalTo(@[func2LabelSC.widthSize, func3LabelSC.widthSize]); func2LabelSC.leadingPos.equalTo(func1LabelSC.trailingPos); @@ -138,11 +138,11 @@ -(void)loadView func3LabelSC.heightSize.equalTo(contentLayout.heightSize); //下面是定义在iPad上设备的横屏的界面布局,因为iPad上的SizeClass都是regular,所以这里要区分横竖屏的方法是使用MySizeClass_Portrait和MySizeClass_Landscape - UILabel *menu1LabelSCForiPad = [menu1Label fetchLayoutSizeClass:MySizeClass_wRegular | MySizeClass_hRegular | MySizeClass_Landscape copyFrom:MySizeClass_wAny | MySizeClass_hAny]; + id menu1LabelSCForiPad = [menu1Label fetchLayoutTraitsInSizeClass:MySizeClass_wRegular | MySizeClass_hRegular | MySizeClass_Landscape copyFrom:MySizeClass_wAny | MySizeClass_hAny]; menu1LabelSCForiPad.heightSize.max(200); - UILabel *menu2LabelSCForiPad = [menu2Label fetchLayoutSizeClass:MySizeClass_wRegular | MySizeClass_hRegular | MySizeClass_Landscape copyFrom:MySizeClass_wAny | MySizeClass_hAny]; + id menu2LabelSCForiPad = [menu2Label fetchLayoutTraitsInSizeClass:MySizeClass_wRegular | MySizeClass_hRegular | MySizeClass_Landscape copyFrom:MySizeClass_wAny | MySizeClass_hAny]; menu2LabelSCForiPad.heightSize.max(200); - UILabel *menu3LabelSCForiPad = [menu3Label fetchLayoutSizeClass:MySizeClass_wRegular | MySizeClass_hRegular | MySizeClass_Landscape copyFrom:MySizeClass_wAny | MySizeClass_hAny]; + id menu3LabelSCForiPad = [menu3Label fetchLayoutTraitsInSizeClass:MySizeClass_wRegular | MySizeClass_hRegular | MySizeClass_Landscape copyFrom:MySizeClass_wAny | MySizeClass_hAny]; menu3LabelSCForiPad.heightSize.max(200); diff --git a/MyLayoutDemo/AllTest7ViewController.m b/MyLayoutDemo/AllTest7ViewController.m index 1c8865a..b332a10 100644 --- a/MyLayoutDemo/AllTest7ViewController.m +++ b/MyLayoutDemo/AllTest7ViewController.m @@ -84,7 +84,7 @@ -(void)createDemo1:(MyLinearLayout*)rootLayout MyLinearLayout *contentLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Horz]; contentLayout.heightSize.equalTo(@(MyLayoutSize.wrap)); contentLayout.padding = UIEdgeInsetsMake(5, 5, 5, 5); - contentLayout.subviewHSpace = 5; + contentLayout.subviewHSpacing = 5; contentLayout.backgroundColor = [CFTool color:0]; [rootLayout addSubview:contentLayout]; @@ -141,7 +141,7 @@ -(void)createDemo2:(MyLinearLayout*)rootLayout MyLinearLayout *contentLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Horz]; contentLayout.myHeight = MyLayoutSize.wrap; contentLayout.padding = UIEdgeInsetsMake(5, 5, 5, 5); - contentLayout.subviewHSpace = 5; + contentLayout.subviewHSpacing = 5; contentLayout.backgroundColor = [CFTool color:0]; [rootLayout addSubview:contentLayout]; @@ -205,7 +205,7 @@ -(void)createDemo3:(MyLinearLayout*)rootLayout MyLinearLayout *contentLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Horz]; contentLayout.myHeight = MyLayoutSize.wrap; contentLayout.padding = UIEdgeInsetsMake(5, 5, 5, 5); - contentLayout.subviewHSpace = 5; + contentLayout.subviewHSpacing = 5; contentLayout.backgroundColor = [CFTool color:0]; [rootLayout addSubview:contentLayout]; @@ -283,7 +283,7 @@ -(void)createDemo4:(MyLinearLayout*)rootLayout MyLinearLayout *contentLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Horz]; contentLayout.myHeight = MyLayoutSize.wrap; contentLayout.padding = UIEdgeInsetsMake(5, 5, 5, 5); - contentLayout.subviewHSpace = 20; + contentLayout.subviewHSpacing = 20; contentLayout.shrinkType = MySubviewsShrink_Auto; //为了实现左右两边文本的自动缩放调整,必须要将线性布局的属性设置MySubviewsShrink_Auto。当设置为Auto属性时,必要要满足当前子视图中只有2个子视图的宽度设置为等于自身内容,否则无效。这个属性用来实现左右2个子视图根据内容来占用最佳的空间的例子。 contentLayout.backgroundColor = [CFTool color:0]; [rootLayout addSubview:contentLayout]; @@ -367,8 +367,8 @@ -(void)createDemo5:(MyLinearLayout*)rootLayout contentLayout.backgroundColor = [CFTool color:0]; contentLayout.padding = UIEdgeInsetsMake(5, 5, 5, 5); contentLayout.myHeight = MyLayoutSize.wrap; - contentLayout.subviewVSpace = 5; //设置流式布局里面子视图之间的垂直间距。 - [contentLayout setSubviewsSize:subviewWidth minSpace:5 maxSpace:10]; //这里面水平间距用浮动间距,浮动间距设置为子视图固定宽度为60,最小的间距为5,最大间距为10。注意这里要求所有子视图的宽度都是60。 + contentLayout.subviewVSpacing = 5; //设置流式布局里面子视图之间的垂直间距。 + [contentLayout setSubviewsSize:subviewWidth minSpacing:5 maxSpacing:10 centered:NO]; //这里面水平间距用浮动间距,浮动间距设置为子视图固定宽度为60,最小的间距为5,最大间距为10。注意这里要求所有子视图的宽度都是60。 [rootLayout addSubview:contentLayout]; UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem]; @@ -460,8 +460,8 @@ -(void)createDemo6:(MyLinearLayout*)rootLayout MyTableLayout *contentLayout = [MyTableLayout tableLayoutWithOrientation:MyOrientation_Vert]; contentLayout.padding = UIEdgeInsetsMake(5, 5, 5, 5); - contentLayout.subviewVSpace = 5; - contentLayout.subviewHSpace = 5; //设置行间距和列间距都为5. + contentLayout.subviewVSpacing = 5; + contentLayout.subviewHSpacing = 5; //设置行间距和列间距都为5. contentLayout.backgroundColor = [CFTool color:0]; [rootLayout addSubview:contentLayout]; @@ -486,7 +486,7 @@ -(void)createDemo7:(MyLinearLayout*)rootLayout MyFlowLayout *contentLayout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Vert arrangedCount:0]; contentLayout.myHeight = MyLayoutSize.wrap; contentLayout.padding = UIEdgeInsetsMake(5, 5, 5, 5); - contentLayout.subviewSpace = 5; + contentLayout.subviewSpacing = 5; contentLayout.backgroundColor = [CFTool color:0]; [rootLayout addSubview:contentLayout]; @@ -533,7 +533,7 @@ -(void)createDemo8:(MyLinearLayout*)rootLayout contentLayout.myBottom = 0; contentLayout.gravity = MyGravity_Vert_Fill; contentLayout.padding = UIEdgeInsetsMake(5, 5, 5, 5); - contentLayout.subviewHSpace = 5; + contentLayout.subviewHSpacing = 5; contentLayout.widthSize.lBound(scrollView.widthSize,0,1); //默认水平线性布局的宽度是自适应但是最小的宽度和父视图相等,这样对于一些大尺寸屏幕因为能够容纳内容而不会产生滚动。 [scrollView addSubview:contentLayout]; @@ -590,7 +590,7 @@ -(void)createDemo9:(MyLinearLayout*)rootLayout contentLayout.myBottom = 0; contentLayout.gravity = MyGravity_Vert_Fill; contentLayout.padding = UIEdgeInsetsMake(5, 5, 5, 5); - contentLayout.subviewHSpace = 5; + contentLayout.subviewHSpacing = 5; contentLayout.widthSize.lBound(scrollView.widthSize,0,1); //默认水平线性布局的宽度是自适应的但是最小的宽度和父视图相等,这样对于一些大尺寸屏幕因为能够容纳内容而不会产生滚动。 [scrollView addSubview:contentLayout]; @@ -723,8 +723,8 @@ -(void)createDemo11:(MyLinearLayout*)rootLayout [rootLayout addSubview:contentLayout]; - contentLayout.subviewHSpace = 50; //默认设定固定间距为50 - contentLayout.shrinkType = MySubviewsShrink_Space | MySubviewsShrink_Average; //当整体内容的总宽度不超过布局视图的宽度时则正常布局,当布局视图的宽度不能容纳总体宽度时,则会平均缩小子视图之间的间距以保证一行内能容纳下所有的内容。shrinkType支持压缩尺寸和压缩间距两种压缩方法,默认是压缩尺寸。你可以选择MySubviewsShrink_Size或者MySubviewsShrink_Space来指定当布局尺寸不能容纳内容时是压缩所有子视图之间的间距还是尺寸来实现完美适配。 + contentLayout.subviewHSpacing = 50; //默认设定固定间距为50 + contentLayout.shrinkType = MySubviewsShrink_Spacing | MySubviewsShrink_Average; //当整体内容的总宽度不超过布局视图的宽度时则正常布局,当布局视图的宽度不能容纳总体宽度时,则会平均缩小子视图之间的间距以保证一行内能容纳下所有的内容。shrinkType支持压缩尺寸和压缩间距两种压缩方法,默认是压缩尺寸。你可以选择MySubviewsShrink_Size或者MySubviewsShrink_Spacing来指定当布局尺寸不能容纳内容时是压缩所有子视图之间的间距还是尺寸来实现完美适配。 UILabel *A = [self createLabel:@"Objective-C" color:5]; diff --git a/MyLayoutDemo/AllTest8ViewController.m b/MyLayoutDemo/AllTest8ViewController.m index 7d9dbc3..bb88180 100644 --- a/MyLayoutDemo/AllTest8ViewController.m +++ b/MyLayoutDemo/AllTest8ViewController.m @@ -90,7 +90,7 @@ - (void)viewDidLoad { //建立一个浮动布局,这个浮动布局不会控制父视图UIScrollView的contentSize。 MyFloatLayout *floatLayout = [MyFloatLayout floatLayoutWithOrientation:MyOrientation_Horz]; floatLayout.backgroundColor = [CFTool color:0]; - floatLayout.subviewSpace = 10; + floatLayout.subviewSpacing = 10; floatLayout.myLeading = floatLayout.myTrailing = 10; //同时设定了左边和右边边距,布局视图的宽度就决定了。 floatLayout.padding = UIEdgeInsetsMake(10, 10, 10, 10); //设置内边距。 floatLayout.myTop = 60; @@ -156,7 +156,7 @@ -(void)handleDemo1:(UIButton*)sender layout.backgroundColor = [CFTool color:14]; layout.layer.cornerRadius = 5; layout.padding = UIEdgeInsetsMake(5, 5, 5, 5); //设置布局视图的内边距。 - layout.subviewVSpace = 5; //设置视图之间的间距,这样子视图就不再需要单独设置间距了。 + layout.subviewVSpacing = 5; //设置视图之间的间距,这样子视图就不再需要单独设置间距了。 layout.gravity = MyGravity_Horz_Fill; //里面的子视图宽度和自己一样,这样就不再需要设置子视图的宽度了。 layout.myLeading = 0.2; layout.myTrailing = 0.2; //左右边距0.2表示相对边距,也就是左右边距都是父视图总宽度的20%,这样布局视图的宽度就默认为父视图的60%了。 @@ -182,8 +182,8 @@ -(void)handleDemo1:(UIButton*)sender //按钮容器。如果您想让两个按钮水平排列则只需在btnContainer初始化中把方向改为:MyOrientation_Horz 。您可以尝试看看效果。 MyLinearLayout *btnContainer = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert /*MyOrientation_Horz*/]; - btnContainer.subviewVSpace = 5; //视图之间的间距设置为5 - btnContainer.subviewHSpace = 5; //视图之间的间距设置为5 + btnContainer.subviewVSpacing = 5; //视图之间的间距设置为5 + btnContainer.subviewHSpacing = 5; //视图之间的间距设置为5 btnContainer.gravity = MyGravity_Horz_Fill; //里面的子视图的宽度水平填充,如果是垂直线性布局则里面的所有子视图的宽度都和父视图相等。如果是水平线性布局则会均分所有子视图的宽度。 [layout addSubview:btnContainer]; @@ -196,7 +196,7 @@ -(void)handleDemo1:(UIButton*)sender 您可以将下面两句代码注释掉看看横竖屏切换的结果。 */ - MyLinearLayout *btnContainerSC = [btnContainer fetchLayoutSizeClass:MySizeClass_Landscape copyFrom:MySizeClass_wAny | MySizeClass_hAny]; + id btnContainerSC = [btnContainer fetchLayoutTraitsInSizeClass: MySizeClass_Landscape copyFrom:MySizeClass_wAny | MySizeClass_hAny]; btnContainerSC.orientation = MyOrientation_Horz; diff --git a/MyLayoutDemo/AllTest9CollectionViewCell.m b/MyLayoutDemo/AllTest9CollectionViewCell.m index 2aca016..9137663 100644 --- a/MyLayoutDemo/AllTest9CollectionViewCell.m +++ b/MyLayoutDemo/AllTest9CollectionViewCell.m @@ -30,7 +30,7 @@ -(instancetype)initWithFrame:(CGRect)frame _rootLayout= [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; _rootLayout.paddingTop = 5; _rootLayout.paddingBottom = 5; - _rootLayout.subviewVSpace = 10; + _rootLayout.subviewVSpacing = 10; _rootLayout.myHorzMargin = 0; _rootLayout.gravity = MyGravity_Horz_Fill; [self.contentView addSubview:_rootLayout]; diff --git a/MyLayoutDemo/AllTest9ViewController.m b/MyLayoutDemo/AllTest9ViewController.m index 7a5dac5..ad72dbc 100644 --- a/MyLayoutDemo/AllTest9ViewController.m +++ b/MyLayoutDemo/AllTest9ViewController.m @@ -25,8 +25,8 @@ -(instancetype)init AllTest9WaterFlowLayout *layout = [[AllTest9WaterFlowLayout alloc] init]; layout.numberOfColumn = 2; - layout.vertSpace = 10; - layout.horzSpace = 10; + layout.vertSpacing = 10; + layout.horzSpacing = 10; //您可以试试内置的UICollectionViewFlowLayout效果。 /* UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; diff --git a/MyLayoutDemo/AllTest9WaterFlowLayout.h b/MyLayoutDemo/AllTest9WaterFlowLayout.h index 54b7820..bac9b51 100644 --- a/MyLayoutDemo/AllTest9WaterFlowLayout.h +++ b/MyLayoutDemo/AllTest9WaterFlowLayout.h @@ -13,8 +13,8 @@ NS_ASSUME_NONNULL_BEGIN @interface AllTest9WaterFlowLayout : UICollectionViewLayout @property (nonatomic, assign) NSUInteger numberOfColumn; -@property (nonatomic, assign) CGFloat horzSpace; -@property (nonatomic, assign) CGFloat vertSpace; +@property (nonatomic, assign) CGFloat horzSpacing; +@property (nonatomic, assign) CGFloat vertSpacing; @end diff --git a/MyLayoutDemo/AllTest9WaterFlowLayout.m b/MyLayoutDemo/AllTest9WaterFlowLayout.m index 1e6208f..64f11c9 100644 --- a/MyLayoutDemo/AllTest9WaterFlowLayout.m +++ b/MyLayoutDemo/AllTest9WaterFlowLayout.m @@ -55,7 +55,7 @@ - (void)prepareLayout if (_columnHeightArray == nil) { _columnHeightArray = [[NSMutableArray alloc] initWithCapacity:self.numberOfColumn]; for (int i = 0; i < self.numberOfColumn; i ++) { - [_columnHeightArray addObject:@(self.vertSpace)]; + [_columnHeightArray addObject:@(self.vertSpacing)]; } } @@ -64,7 +64,7 @@ - (void)prepareLayout CGFloat totalWidth = self.collectionView.frame.size.width; NSUInteger itemCount = [self.collectionView numberOfItemsInSection:0]; - CGFloat itemWidth = (totalWidth - self.horzSpace) / self.numberOfColumn; + CGFloat itemWidth = (totalWidth - self.horzSpacing) / self.numberOfColumn; for (int i = 0; i < itemCount; i ++) { NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0]; UICollectionViewLayoutAttributes *attribute = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath]; @@ -91,15 +91,14 @@ - (BOOL)shouldInvalidateLayoutForPreferredLayoutAttributes:(UICollectionViewLayo minHeightIndex = j; } } - CGFloat x = minHeightIndex * (preferredAttributes.size.width + self.horzSpace); + CGFloat x = minHeightIndex * (preferredAttributes.size.width + self.horzSpacing); CGFloat y = minHeight; attributes.frame = CGRectMake(x, y, preferredAttributes.size.width, preferredAttributes.size.height); - self.columnHeightArray[minHeightIndex] = [NSNumber numberWithFloat:minHeight + preferredAttributes.size.height + self.vertSpace]; + self.columnHeightArray[minHeightIndex] = [NSNumber numberWithFloat:minHeight + preferredAttributes.size.height + self.vertSpacing]; return YES; } //- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds { -// BOOL ok = [super shouldInvalidateLayoutForBoundsChange:newBounds]; // return YES; //} diff --git a/MyLayoutDemo/AllTestExampleViewController.m b/MyLayoutDemo/AllTestExampleViewController.m index aa1409c..e5f4cb2 100644 --- a/MyLayoutDemo/AllTestExampleViewController.m +++ b/MyLayoutDemo/AllTestExampleViewController.m @@ -109,8 +109,7 @@ -(void)example2 UIScrollView *scrollview = [[UIScrollView alloc] init]; scrollview.backgroundColor = [UIColor blueColor]; scrollview.myHorzMargin = 0; - scrollview.wrapContentHeight = YES; - scrollview.heightSize.max(400).min(280); + scrollview.heightSize.equalTo(@(MyLayoutSize.wrap)).max(400).min(280); [rootLayout addSubview:scrollview]; // 内容C视图 @@ -137,8 +136,8 @@ -(void)example3 .flex_wrap(MyFlexWrap_Wrap) .align_content(MyFlexGravity_Center) .align_items(MyFlexGravity_Flex_End) - .vert_space(10) - .horz_space(10) + .vert_spacing(10) + .horz_spacing(10) .padding(UIEdgeInsetsMake(10, 10, 10, 10)) .margin_top(50) .width(MyLayoutSize.fill) @@ -186,7 +185,7 @@ -(void)example4 MyRelativeLayout *_rootLayout = [MyRelativeLayout new]; _rootLayout.widthSize.equalTo(self.view.widthSize); - _rootLayout.wrapContentHeight = YES; + _rootLayout.heightSize.equalTo(@(MyLayoutSize.wrap)); _rootLayout.paddingTop = 15; _rootLayout.paddingBottom = 15; _rootLayout.backgroundColor = [UIColor whiteColor]; @@ -196,10 +195,9 @@ -(void)example4 MyLinearLayout *topLayout = [MyLinearLayout linearLayoutWithOrientation:(MyOrientation_Vert)]; - topLayout.wrapContentWidth = YES; topLayout.myHorzMargin = 0; - topLayout.wrapContentHeight = YES; - topLayout.subviewVSpace = 5; + topLayout.mySize = CGSizeMake(MyLayoutSize.wrap, MyLayoutSize.wrap); + topLayout.subviewVSpacing = 5; [_rootLayout addSubview:topLayout]; @@ -207,7 +205,7 @@ -(void)example4 topLable.text = @"宿州学院-皁阳火车站"; topLable.myTop = 0; topLable.myLeft = 11; - topLable.wrapContentSize = YES; + topLable.mySize = CGSizeMake(MyLayoutSize.wrap, MyLayoutSize.wrap); [topLayout addSubview:topLable]; @@ -224,7 +222,7 @@ -(void)example4 contentLabel.text = @"途径:蒙城、嘎县、阜阳师范学院、嘎县、阜阳师范学院、嘎县、阜阳师范学院"; contentLabel.myLeft = 11; - contentLabel.wrapContentHeight= YES; + contentLabel.myHeight = MyLayoutSize.wrap; contentLabel.myRight = 27; contentLabel.hidden = NO; [topLayout addSubview:contentLabel]; @@ -241,7 +239,7 @@ -(void)example5 MyLinearLayout *headerLayout = [MyLinearLayout linearLayoutWithOrientation:(MyOrientation_Horz)]; headerLayout.topPos.equalTo(rootLayout.topPos); headerLayout.leftPos.equalTo(rootLayout.leftPos); - headerLayout.wrapContentHeight = YES; + headerLayout.heightSize.equalTo(@(MyLayoutSize.wrap)); [rootLayout addSubview:headerLayout]; UIImageView *headerView = UIImageView.alloc.init; diff --git a/MyLayoutDemo/AppDelegate.m b/MyLayoutDemo/AppDelegate.m index f38774b..1798607 100644 --- a/MyLayoutDemo/AppDelegate.m +++ b/MyLayoutDemo/AppDelegate.m @@ -32,6 +32,11 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( ViewController *vc = [[ViewController alloc] init]; UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc]; + if (@available(iOS 26.0, *)) { + nav.interactiveContentPopGestureRecognizer.enabled = NO; + } else { + // Fallback on earlier versions + } self.window.rootViewController = nav; [self.window makeKeyAndVisible]; diff --git a/MyLayoutDemo/Base.lproj/FLLTest2ViewController.xib b/MyLayoutDemo/Base.lproj/FLLTest2ViewController.xib index 1cfe82b..4ab285b 100644 --- a/MyLayoutDemo/Base.lproj/FLLTest2ViewController.xib +++ b/MyLayoutDemo/Base.lproj/FLLTest2ViewController.xib @@ -1,9 +1,9 @@ - - + + - + @@ -16,7 +16,7 @@ - + @@ -38,7 +38,7 @@ - - -