diff --git a/InsetGrouped/InsetGrouped/ViewController.swift b/InsetGrouped/InsetGrouped/ViewController.swift index fd58d26..0e6cdd4 100644 --- a/InsetGrouped/InsetGrouped/ViewController.swift +++ b/InsetGrouped/InsetGrouped/ViewController.swift @@ -11,7 +11,7 @@ import Jenga class ViewController: UIViewController, DSLAutoTable { @TableBuilder - var tableContents: [Sectionable] { + var tableContents: [Section] { TableSection { NavigationRow("设置样式") diff --git a/Jenga.xcodeproj/project.pbxproj b/Jenga.xcodeproj/project.pbxproj index e08795e..fa8280e 100644 --- a/Jenga.xcodeproj/project.pbxproj +++ b/Jenga.xcodeproj/project.pbxproj @@ -13,40 +13,37 @@ B37BD5F227F47538009C2244 /* AnyLocation.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37BD5BC27F47538009C2244 /* AnyLocation.swift */; }; B37BD5F327F47538009C2244 /* BindingWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37BD5BD27F47538009C2244 /* BindingWrapper.swift */; }; B37BD5F427F47538009C2244 /* State.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37BD5BE27F47538009C2244 /* State.swift */; }; - B37BD5F527F47538009C2244 /* BindingExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37BD5BF27F47538009C2244 /* BindingExtension.swift */; }; B37BD5F627F47538009C2244 /* ResultBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37BD5C027F47538009C2244 /* ResultBuilder.swift */; }; B37BD5F727F47538009C2244 /* BindingConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37BD5C127F47538009C2244 /* BindingConvertible.swift */; }; B37BD5F827F47538009C2244 /* TapActionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37BD5C327F47538009C2244 /* TapActionCell.swift */; }; - B37BD5F927F47538009C2244 /* SeparatorCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37BD5C427F47538009C2244 /* SeparatorCell.swift */; }; + B37BD5F927F47538009C2244 /* SpacerCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37BD5C427F47538009C2244 /* SpacerCell.swift */; }; B37BD5FA27F47538009C2244 /* ToggleCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37BD5C527F47538009C2244 /* ToggleCell.swift */; }; B37BD5FB27F47538009C2244 /* Jenga.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37BD5C627F47538009C2244 /* Jenga.swift */; }; B37BD5FC27F47538009C2244 /* BacicSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37BD5C827F47538009C2244 /* BacicSection.swift */; }; B37BD5FD27F47538009C2244 /* RadioSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37BD5C927F47538009C2244 /* RadioSection.swift */; }; B37BD5FE27F47538009C2244 /* TableSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37BD5CA27F47538009C2244 /* TableSection.swift */; }; - B37BD5FF27F47538009C2244 /* Sectionable.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37BD5CB27F47538009C2244 /* Sectionable.swift */; }; + B37BD5FF27F47538009C2244 /* Section.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37BD5CB27F47538009C2244 /* Section.swift */; }; B37BD60027F47538009C2244 /* UIView.Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37BD5CD27F47538009C2244 /* UIView.Extension.swift */; }; B37BD60127F47538009C2244 /* Collection.Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37BD5CE27F47538009C2244 /* Collection.Extension.swift */; }; - B37BD60227F47538009C2244 /* NSObject.Runtime.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37BD5CF27F47538009C2244 /* NSObject.Runtime.swift */; }; + B37BD60227F47538009C2244 /* Runtime.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37BD5CF27F47538009C2244 /* Runtime.swift */; }; B37BD60327F47538009C2244 /* UILabel.Inset.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37BD5D027F47538009C2244 /* UILabel.Inset.swift */; }; - B37BD60427F47538009C2244 /* TableKit.Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37BD5D227F47538009C2244 /* TableKit.Log.swift */; }; + B37BD60427F47538009C2244 /* Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37BD5D227F47538009C2244 /* Log.swift */; }; B37BD60527F47538009C2244 /* TableCellHeightCalculator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37BD5D327F47538009C2244 /* TableCellHeightCalculator.swift */; }; B37BD60627F47538009C2244 /* TableCellRegisterer.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37BD5D427F47538009C2244 /* TableCellRegisterer.swift */; }; - B37BD60727F47538009C2244 /* TableRowAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37BD5D527F47538009C2244 /* TableRowAction.swift */; }; B37BD60827F47538009C2244 /* TableRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37BD5D727F47538009C2244 /* TableRow.swift */; }; B37BD60927F47538009C2244 /* BasicRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37BD5D827F47538009C2244 /* BasicRow.swift */; }; B37BD60A27F47538009C2244 /* Configurable.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37BD5D927F47538009C2244 /* Configurable.swift */; }; B37BD60B27F47538009C2244 /* Reusable.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37BD5DA27F47538009C2244 /* Reusable.swift */; }; - B37BD60C27F47538009C2244 /* RowSystemable.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37BD5DC27F47538009C2244 /* RowSystemable.swift */; }; + B37BD60C27F47538009C2244 /* RowSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37BD5DC27F47538009C2244 /* RowSystem.swift */; }; B37BD60D27F47538009C2244 /* Row.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37BD5DD27F47538009C2244 /* Row.swift */; }; - B37BD60E27F47538009C2244 /* RowCompatible.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37BD5DE27F47538009C2244 /* RowCompatible.swift */; }; - B37BD60F27F47538009C2244 /* SeparatorRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37BD5E027F47538009C2244 /* SeparatorRow.swift */; }; + B37BD60F27F47538009C2244 /* SpacerRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37BD5E027F47538009C2244 /* SpacerRow.swift */; }; B37BD61027F47538009C2244 /* OptionRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37BD5E127F47538009C2244 /* OptionRow.swift */; }; B37BD61127F47538009C2244 /* TapActionRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37BD5E227F47538009C2244 /* TapActionRow.swift */; }; B37BD61227F47538009C2244 /* NavigationRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37BD5E327F47538009C2244 /* NavigationRow.swift */; }; B37BD61327F47538009C2244 /* ToggleRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37BD5E427F47538009C2244 /* ToggleRow.swift */; }; B37BD61427F47538009C2244 /* Icon.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37BD5E727F47538009C2244 /* Icon.swift */; }; B37BD61527F47538009C2244 /* AsyncImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37BD5E827F47538009C2244 /* AsyncImage.swift */; }; - B37BD61627F47538009C2244 /* Icon.Image.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37BD5E927F47538009C2244 /* Icon.Image.swift */; }; + B37BD61627F47538009C2244 /* Image.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37BD5E927F47538009C2244 /* Image.swift */; }; B37BD61727F47538009C2244 /* Text.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37BD5EA27F47538009C2244 /* Text.swift */; }; B37BD61827F47538009C2244 /* DetailText.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37BD5EB27F47538009C2244 /* DetailText.swift */; }; B37BD61927F47538009C2244 /* DSLTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37BD5EC27F47538009C2244 /* DSLTable.swift */; }; @@ -62,40 +59,37 @@ B37BD5BC27F47538009C2244 /* AnyLocation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnyLocation.swift; sourceTree = ""; }; B37BD5BD27F47538009C2244 /* BindingWrapper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BindingWrapper.swift; sourceTree = ""; }; B37BD5BE27F47538009C2244 /* State.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = State.swift; sourceTree = ""; }; - B37BD5BF27F47538009C2244 /* BindingExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BindingExtension.swift; sourceTree = ""; }; B37BD5C027F47538009C2244 /* ResultBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ResultBuilder.swift; sourceTree = ""; }; B37BD5C127F47538009C2244 /* BindingConvertible.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BindingConvertible.swift; sourceTree = ""; }; B37BD5C327F47538009C2244 /* TapActionCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TapActionCell.swift; sourceTree = ""; }; - B37BD5C427F47538009C2244 /* SeparatorCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SeparatorCell.swift; sourceTree = ""; }; + B37BD5C427F47538009C2244 /* SpacerCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SpacerCell.swift; sourceTree = ""; }; B37BD5C527F47538009C2244 /* ToggleCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ToggleCell.swift; sourceTree = ""; }; B37BD5C627F47538009C2244 /* Jenga.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Jenga.swift; sourceTree = ""; }; B37BD5C827F47538009C2244 /* BacicSection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BacicSection.swift; sourceTree = ""; }; B37BD5C927F47538009C2244 /* RadioSection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RadioSection.swift; sourceTree = ""; }; B37BD5CA27F47538009C2244 /* TableSection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableSection.swift; sourceTree = ""; }; - B37BD5CB27F47538009C2244 /* Sectionable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Sectionable.swift; sourceTree = ""; }; + B37BD5CB27F47538009C2244 /* Section.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Section.swift; sourceTree = ""; }; B37BD5CD27F47538009C2244 /* UIView.Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIView.Extension.swift; sourceTree = ""; }; B37BD5CE27F47538009C2244 /* Collection.Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Collection.Extension.swift; sourceTree = ""; }; - B37BD5CF27F47538009C2244 /* NSObject.Runtime.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSObject.Runtime.swift; sourceTree = ""; }; + B37BD5CF27F47538009C2244 /* Runtime.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Runtime.swift; sourceTree = ""; }; B37BD5D027F47538009C2244 /* UILabel.Inset.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UILabel.Inset.swift; sourceTree = ""; }; - B37BD5D227F47538009C2244 /* TableKit.Log.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableKit.Log.swift; sourceTree = ""; }; + B37BD5D227F47538009C2244 /* Log.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Log.swift; sourceTree = ""; }; B37BD5D327F47538009C2244 /* TableCellHeightCalculator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableCellHeightCalculator.swift; sourceTree = ""; }; B37BD5D427F47538009C2244 /* TableCellRegisterer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableCellRegisterer.swift; sourceTree = ""; }; - B37BD5D527F47538009C2244 /* TableRowAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableRowAction.swift; sourceTree = ""; }; B37BD5D727F47538009C2244 /* TableRow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableRow.swift; sourceTree = ""; }; B37BD5D827F47538009C2244 /* BasicRow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BasicRow.swift; sourceTree = ""; }; B37BD5D927F47538009C2244 /* Configurable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Configurable.swift; sourceTree = ""; }; B37BD5DA27F47538009C2244 /* Reusable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reusable.swift; sourceTree = ""; }; - B37BD5DC27F47538009C2244 /* RowSystemable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RowSystemable.swift; sourceTree = ""; }; + B37BD5DC27F47538009C2244 /* RowSystem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RowSystem.swift; sourceTree = ""; }; B37BD5DD27F47538009C2244 /* Row.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Row.swift; sourceTree = ""; }; - B37BD5DE27F47538009C2244 /* RowCompatible.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RowCompatible.swift; sourceTree = ""; }; - B37BD5E027F47538009C2244 /* SeparatorRow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SeparatorRow.swift; sourceTree = ""; }; + B37BD5E027F47538009C2244 /* SpacerRow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SpacerRow.swift; sourceTree = ""; }; B37BD5E127F47538009C2244 /* OptionRow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OptionRow.swift; sourceTree = ""; }; B37BD5E227F47538009C2244 /* TapActionRow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TapActionRow.swift; sourceTree = ""; }; B37BD5E327F47538009C2244 /* NavigationRow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationRow.swift; sourceTree = ""; }; B37BD5E427F47538009C2244 /* ToggleRow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ToggleRow.swift; sourceTree = ""; }; B37BD5E727F47538009C2244 /* Icon.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Icon.swift; sourceTree = ""; }; B37BD5E827F47538009C2244 /* AsyncImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AsyncImage.swift; sourceTree = ""; }; - B37BD5E927F47538009C2244 /* Icon.Image.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Icon.Image.swift; sourceTree = ""; }; + B37BD5E927F47538009C2244 /* Image.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Image.swift; sourceTree = ""; }; B37BD5EA27F47538009C2244 /* Text.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Text.swift; sourceTree = ""; }; B37BD5EB27F47538009C2244 /* DetailText.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetailText.swift; sourceTree = ""; }; B37BD5EC27F47538009C2244 /* DSLTable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DSLTable.swift; sourceTree = ""; }; @@ -114,6 +108,31 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + B32FAB2027F54959006A37BE /* Core */ = { + isa = PBXGroup; + children = ( + B37BD5BA27F47538009C2244 /* Binding */, + B37BD5C227F47538009C2244 /* Cell */, + B37BD5C627F47538009C2244 /* Jenga.swift */, + B37BD5C727F47538009C2244 /* Section */, + B37BD5CC27F47538009C2244 /* Extension */, + B37BD5D127F47538009C2244 /* Utils */, + B37BD5D627F47538009C2244 /* Row */, + B37BD5E527F47538009C2244 /* Model */, + B37BD5ED27F47538009C2244 /* TableDirector.swift */, + ); + path = Core; + sourceTree = ""; + }; + B32FAB2127F54965006A37BE /* DSL */ = { + isa = PBXGroup; + children = ( + B37BD5EC27F47538009C2244 /* DSLTable.swift */, + B37BD5EE27F47538009C2244 /* DSLAutoTable.swift */, + ); + path = DSL; + sourceTree = ""; + }; B37BD49F27F4606B009C2244 = { isa = PBXGroup; children = ( @@ -143,43 +162,32 @@ B37BD5B927F47538009C2244 /* Jenga */ = { isa = PBXGroup; children = ( - B37BD5BA27F47538009C2244 /* UIBinding */, - B37BD5C227F47538009C2244 /* Cells */, - B37BD5C627F47538009C2244 /* Jenga.swift */, - B37BD5C727F47538009C2244 /* Section */, - B37BD5CC27F47538009C2244 /* Extension */, - B37BD5D127F47538009C2244 /* Utils */, - B37BD5D627F47538009C2244 /* Row */, - B37BD5E527F47538009C2244 /* Model */, - B37BD5EC27F47538009C2244 /* DSLTable.swift */, - B37BD5ED27F47538009C2244 /* TableDirector.swift */, - B37BD5EE27F47538009C2244 /* DSLAutoTable.swift */, + B32FAB2127F54965006A37BE /* DSL */, + B32FAB2027F54959006A37BE /* Core */, ); path = Jenga; sourceTree = ""; }; - B37BD5BA27F47538009C2244 /* UIBinding */ = { + B37BD5BA27F47538009C2244 /* Binding */ = { isa = PBXGroup; children = ( B37BD5BB27F47538009C2244 /* Binding.swift */, B37BD5BC27F47538009C2244 /* AnyLocation.swift */, B37BD5BD27F47538009C2244 /* BindingWrapper.swift */, B37BD5BE27F47538009C2244 /* State.swift */, - B37BD5BF27F47538009C2244 /* BindingExtension.swift */, - B37BD5C027F47538009C2244 /* ResultBuilder.swift */, B37BD5C127F47538009C2244 /* BindingConvertible.swift */, ); - path = UIBinding; + path = Binding; sourceTree = ""; }; - B37BD5C227F47538009C2244 /* Cells */ = { + B37BD5C227F47538009C2244 /* Cell */ = { isa = PBXGroup; children = ( B37BD5C327F47538009C2244 /* TapActionCell.swift */, - B37BD5C427F47538009C2244 /* SeparatorCell.swift */, + B37BD5C427F47538009C2244 /* SpacerCell.swift */, B37BD5C527F47538009C2244 /* ToggleCell.swift */, ); - path = Cells; + path = Cell; sourceTree = ""; }; B37BD5C727F47538009C2244 /* Section */ = { @@ -188,7 +196,7 @@ B37BD5C827F47538009C2244 /* BacicSection.swift */, B37BD5C927F47538009C2244 /* RadioSection.swift */, B37BD5CA27F47538009C2244 /* TableSection.swift */, - B37BD5CB27F47538009C2244 /* Sectionable.swift */, + B37BD5CB27F47538009C2244 /* Section.swift */, ); path = Section; sourceTree = ""; @@ -198,7 +206,6 @@ children = ( B37BD5CD27F47538009C2244 /* UIView.Extension.swift */, B37BD5CE27F47538009C2244 /* Collection.Extension.swift */, - B37BD5CF27F47538009C2244 /* NSObject.Runtime.swift */, B37BD5D027F47538009C2244 /* UILabel.Inset.swift */, ); path = Extension; @@ -207,10 +214,11 @@ B37BD5D127F47538009C2244 /* Utils */ = { isa = PBXGroup; children = ( - B37BD5D227F47538009C2244 /* TableKit.Log.swift */, + B37BD5C027F47538009C2244 /* ResultBuilder.swift */, + B37BD5D227F47538009C2244 /* Log.swift */, + B37BD5CF27F47538009C2244 /* Runtime.swift */, B37BD5D327F47538009C2244 /* TableCellHeightCalculator.swift */, B37BD5D427F47538009C2244 /* TableCellRegisterer.swift */, - B37BD5D527F47538009C2244 /* TableRowAction.swift */, ); path = Utils; sourceTree = ""; @@ -222,26 +230,17 @@ B37BD5D827F47538009C2244 /* BasicRow.swift */, B37BD5D927F47538009C2244 /* Configurable.swift */, B37BD5DA27F47538009C2244 /* Reusable.swift */, - B37BD5DB27F47538009C2244 /* Protocol */, + B37BD5DD27F47538009C2244 /* Row.swift */, B37BD5DF27F47538009C2244 /* System */, ); path = Row; sourceTree = ""; }; - B37BD5DB27F47538009C2244 /* Protocol */ = { - isa = PBXGroup; - children = ( - B37BD5DC27F47538009C2244 /* RowSystemable.swift */, - B37BD5DD27F47538009C2244 /* Row.swift */, - B37BD5DE27F47538009C2244 /* RowCompatible.swift */, - ); - path = Protocol; - sourceTree = ""; - }; B37BD5DF27F47538009C2244 /* System */ = { isa = PBXGroup; children = ( - B37BD5E027F47538009C2244 /* SeparatorRow.swift */, + B37BD5DC27F47538009C2244 /* RowSystem.swift */, + B37BD5E027F47538009C2244 /* SpacerRow.swift */, B37BD5E127F47538009C2244 /* OptionRow.swift */, B37BD5E227F47538009C2244 /* TapActionRow.swift */, B37BD5E327F47538009C2244 /* NavigationRow.swift */, @@ -255,7 +254,7 @@ children = ( B37BD5E727F47538009C2244 /* Icon.swift */, B37BD5E827F47538009C2244 /* AsyncImage.swift */, - B37BD5E927F47538009C2244 /* Icon.Image.swift */, + B37BD5E927F47538009C2244 /* Image.swift */, B37BD5EA27F47538009C2244 /* Text.swift */, B37BD5EB27F47538009C2244 /* DetailText.swift */, ); @@ -342,28 +341,27 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - B37BD60427F47538009C2244 /* TableKit.Log.swift in Sources */, - B37BD60227F47538009C2244 /* NSObject.Runtime.swift in Sources */, + B37BD60427F47538009C2244 /* Log.swift in Sources */, + B37BD60227F47538009C2244 /* Runtime.swift in Sources */, B37BD61127F47538009C2244 /* TapActionRow.swift in Sources */, B37BD60027F47538009C2244 /* UIView.Extension.swift in Sources */, B37BD60527F47538009C2244 /* TableCellHeightCalculator.swift in Sources */, - B37BD60E27F47538009C2244 /* RowCompatible.swift in Sources */, B37BD5FC27F47538009C2244 /* BacicSection.swift in Sources */, B37BD5F827F47538009C2244 /* TapActionCell.swift in Sources */, B37BD61827F47538009C2244 /* DetailText.swift in Sources */, B37BD60A27F47538009C2244 /* Configurable.swift in Sources */, B37BD5F227F47538009C2244 /* AnyLocation.swift in Sources */, - B37BD61627F47538009C2244 /* Icon.Image.swift in Sources */, + B37BD61627F47538009C2244 /* Image.swift in Sources */, B37BD61527F47538009C2244 /* AsyncImage.swift in Sources */, B37BD61B27F47538009C2244 /* DSLAutoTable.swift in Sources */, B37BD61A27F47538009C2244 /* TableDirector.swift in Sources */, - B37BD60C27F47538009C2244 /* RowSystemable.swift in Sources */, + B37BD60C27F47538009C2244 /* RowSystem.swift in Sources */, B37BD60927F47538009C2244 /* BasicRow.swift in Sources */, B37BD60327F47538009C2244 /* UILabel.Inset.swift in Sources */, B37BD61427F47538009C2244 /* Icon.swift in Sources */, B37BD5FB27F47538009C2244 /* Jenga.swift in Sources */, B37BD5EF27F47538009C2244 /* Jenga.docc in Sources */, - B37BD5FF27F47538009C2244 /* Sectionable.swift in Sources */, + B37BD5FF27F47538009C2244 /* Section.swift in Sources */, B37BD61927F47538009C2244 /* DSLTable.swift in Sources */, B37BD61327F47538009C2244 /* ToggleRow.swift in Sources */, B37BD5F127F47538009C2244 /* Binding.swift in Sources */, @@ -373,16 +371,14 @@ B37BD5F727F47538009C2244 /* BindingConvertible.swift in Sources */, B37BD60627F47538009C2244 /* TableCellRegisterer.swift in Sources */, B37BD61727F47538009C2244 /* Text.swift in Sources */, - B37BD5F927F47538009C2244 /* SeparatorCell.swift in Sources */, - B37BD5F527F47538009C2244 /* BindingExtension.swift in Sources */, - B37BD60F27F47538009C2244 /* SeparatorRow.swift in Sources */, + B37BD5F927F47538009C2244 /* SpacerCell.swift in Sources */, + B37BD60F27F47538009C2244 /* SpacerRow.swift in Sources */, B37BD60B27F47538009C2244 /* Reusable.swift in Sources */, B37BD5FD27F47538009C2244 /* RadioSection.swift in Sources */, B37BD5FA27F47538009C2244 /* ToggleCell.swift in Sources */, B37BD5FE27F47538009C2244 /* TableSection.swift in Sources */, B37BD61227F47538009C2244 /* NavigationRow.swift in Sources */, B37BD5F427F47538009C2244 /* State.swift in Sources */, - B37BD60727F47538009C2244 /* TableRowAction.swift in Sources */, B37BD5F327F47538009C2244 /* BindingWrapper.swift in Sources */, B37BD60D27F47538009C2244 /* Row.swift in Sources */, B37BD5F627F47538009C2244 /* ResultBuilder.swift in Sources */, diff --git a/JengaExample/JengaExample/Examples/CustomTableViewController.swift b/JengaExample/JengaExample/Examples/CustomTableViewController.swift index 0ca46ec..30a15e9 100644 --- a/JengaExample/JengaExample/Examples/CustomTableViewController.swift +++ b/JengaExample/JengaExample/Examples/CustomTableViewController.swift @@ -40,7 +40,7 @@ class CustomTableViewController: UIViewController, DSLTable { extension CustomTableViewController { @TableBuilder - var tableContents: [Sectionable] { + var tableContents: [Section] { TableSection(binding: $array) { TableRow() diff --git a/JengaExample/JengaExample/Examples/CustomViewController.swift b/JengaExample/JengaExample/Examples/CustomViewController.swift index e45c08a..c67c42a 100644 --- a/JengaExample/JengaExample/Examples/CustomViewController.swift +++ b/JengaExample/JengaExample/Examples/CustomViewController.swift @@ -11,7 +11,7 @@ import Jenga class CustomViewController: UIViewController, DSLAutoTable { @State var emojis: [String] = [] - + override func viewDidLoad() { super.viewDidLoad() setup() @@ -30,7 +30,7 @@ class CustomViewController: UIViewController, DSLAutoTable { extension CustomViewController { @TableBuilder - var tableContents: [Sectionable] { + var tableContents: [Section] { TableSection { @@ -40,7 +40,7 @@ extension CustomViewController { cell.delegate = self } - SeparatorRow(10) + SpacerRow(10) TableRow() .height(1540 / 2078 * (UIScreen.main.bounds.width - 32)) @@ -58,6 +58,25 @@ extension CustomViewController { } .headerHeight(UITableView.automaticDimension) + TableSection(binding: $emojis.bindEnumerated(), on: self) { (self, emoji) in + TableRow() + .data(emoji.map { "\($0.element) \($0.offset)"}) + .onTap(on: self) { (self) in + + } + } + .rowHeight(44) + .headerHeight(12) + .hiddenWithEmpty(true) + + TableSection(binding: $emojis) { + + TableRow() + .data($0) + .height(44) + } + .headerHeight(UITableView.automaticDimension) + TableSection { TapActionRow("Random") .onTap(on: self) { (self) in diff --git a/JengaExample/JengaExample/Examples/SettingViewController.swift b/JengaExample/JengaExample/Examples/SettingViewController.swift index 2598296..69c5f85 100644 --- a/JengaExample/JengaExample/Examples/SettingViewController.swift +++ b/JengaExample/JengaExample/Examples/SettingViewController.swift @@ -52,7 +52,7 @@ class SettingViewController: UIViewController, DSLAutoTable { extension SettingViewController { @TableBuilder - var tableContents: [Sectionable] { + var tableContents: [Section] { TableSection { NavigationRow("账号与安全") .onTap { diff --git a/JengaExample/JengaExample/Examples/TableViewController.swift b/JengaExample/JengaExample/Examples/TableViewController.swift index 6075c84..066c0b3 100644 --- a/JengaExample/JengaExample/Examples/TableViewController.swift +++ b/JengaExample/JengaExample/Examples/TableViewController.swift @@ -40,7 +40,7 @@ class TableViewController: UIViewController, DSLAutoTable { extension TableViewController { @TableBuilder - var tableContents: [Sectionable] { + var tableContents: [Section] { TableSection { NavigationRow("用户协议") @@ -125,7 +125,7 @@ extension TableViewController { self.reloadTable() } - SeparatorRow(30) + SpacerRow(30) TapActionRow("切换开关, reload isOn binding") .onTap(on: self) { (self) in diff --git a/JengaExample/JengaExample/Extension/Icon.AsyncImage.swift b/JengaExample/JengaExample/Extension/Icon.AsyncImage.swift index 5bd9677..44ea478 100644 --- a/JengaExample/JengaExample/Extension/Icon.AsyncImage.swift +++ b/JengaExample/JengaExample/Extension/Icon.AsyncImage.swift @@ -9,53 +9,49 @@ import Jenga import UIKit import Kingfisher -extension Icon { +public struct AsyncImage: Jenga.AsyncImage { - public struct AsyncImage: Equatable, Jenga.AsyncImage { - - public static func == (lhs: Icon.AsyncImage, rhs: Icon.AsyncImage) -> Bool { - lhs.source.cacheKey == rhs.source.cacheKey - } - - /// The initializer is kept private until v2.0 when `methodSignature` is removed. - public init(_ source: Resource, placeholder: Placeholder? = .none, options: KingfisherOptionsInfo = []) { - self.source = source - self.placeholder = placeholder - self.options = options - } - - var options: KingfisherOptionsInfo = [] - - /// The image for the normal state. - let source: Resource - - /// The image for the highlighted state. - var placeholder: Placeholder? - - var processor = RoundCornerImageProcessor(cornerRadius: 0) { - didSet { - options.append(.processor(processor)) - } + public var downloadURL: URL { source.downloadURL } + + + /// The initializer is kept private until v2.0 when `methodSignature` is removed. + public init(_ source: Resource, placeholder: Placeholder? = .none, options: KingfisherOptionsInfo = []) { + self.source = source + self.placeholder = placeholder + self.options = options + } + + var options: KingfisherOptionsInfo = [] + + /// The image for the normal state. + let source: Resource + + /// The image for the highlighted state. + var placeholder: Placeholder? + + var processor = RoundCornerImageProcessor(cornerRadius: 0) { + didSet { + options.append(.processor(processor)) } - - public func loadImage(with imageView: UIImageView?, _ completion: @escaping (Bool) -> Void) { - imageView?.kf.setImage( - with: source, - placeholder: placeholder, - options: options - ) { result in - switch result { - case .success: return completion(true) - case .failure: return completion(false) - } + } + + public func loadImage(with imageView: UIImageView?, _ completion: @escaping (Bool) -> Void) { + imageView?.kf.setImage( + with: source, + placeholder: placeholder, + options: options + ) { result in + switch result { + case .success: return completion(true) + case .failure: return completion(false) } } } } -public extension Icon.AsyncImage { +public extension AsyncImage { - static func async(_ source: Resource, placeholder: Placeholder? = .none, options: KingfisherOptionsInfo = []) -> Icon.AsyncImage { + static func async(_ source: Resource, placeholder: Placeholder? = .none, options: KingfisherOptionsInfo = []) -> Self { return .init(source, placeholder: placeholder, options: options) } @@ -92,7 +88,7 @@ public extension Icon.AsyncImage { } } -public extension Icon.AsyncImage { +public extension AsyncImage { func by(cornerRadius value: CGFloat? = nil) -> Self { var temp = self @@ -107,14 +103,14 @@ public extension Icon.AsyncImage { } } -public extension RowSystemable { +public extension RowSystem { - func icon(_ value: Binding) -> Self { + func icon(_ value: Binding) -> Self { icon = value.map { .async($0) } return self } - func icon(_ value: Icon.AsyncImage) -> Self { + func icon(_ value: AsyncImage) -> Self { icon = .constant(.async(value)) return self } diff --git a/JengaExample/JengaExample/ViewController.swift b/JengaExample/JengaExample/ViewController.swift index 3a17dc3..d57260e 100644 --- a/JengaExample/JengaExample/ViewController.swift +++ b/JengaExample/JengaExample/ViewController.swift @@ -11,7 +11,7 @@ import Jenga class ViewController: UIViewController, DSLAutoTable { @TableBuilder - var tableContents: [Sectionable] { + var tableContents: [Section] { TableSection { NavigationRow("设置样式") .onTap(on: self) { (self) in diff --git a/Sources/Jenga/UIBinding/AnyLocation.swift b/Sources/Jenga/Core/Binding/AnyLocation.swift similarity index 100% rename from Sources/Jenga/UIBinding/AnyLocation.swift rename to Sources/Jenga/Core/Binding/AnyLocation.swift diff --git a/Sources/Jenga/UIBinding/Binding.swift b/Sources/Jenga/Core/Binding/Binding.swift similarity index 100% rename from Sources/Jenga/UIBinding/Binding.swift rename to Sources/Jenga/Core/Binding/Binding.swift diff --git a/Sources/Jenga/UIBinding/BindingConvertible.swift b/Sources/Jenga/Core/Binding/BindingConvertible.swift similarity index 98% rename from Sources/Jenga/UIBinding/BindingConvertible.swift rename to Sources/Jenga/Core/Binding/BindingConvertible.swift index e39bc22..a91b413 100644 --- a/Sources/Jenga/UIBinding/BindingConvertible.swift +++ b/Sources/Jenga/Core/Binding/BindingConvertible.swift @@ -231,9 +231,9 @@ extension Binding where Value == CGFloat { } } -extension Binding where Value: Sequence { +public extension Binding where Value: Sequence { - func enumerated() -> Binding> { + func bindEnumerated() -> Binding> { return self.map { $0.enumerated() } } } diff --git a/Sources/Jenga/UIBinding/BindingWrapper.swift b/Sources/Jenga/Core/Binding/BindingWrapper.swift similarity index 100% rename from Sources/Jenga/UIBinding/BindingWrapper.swift rename to Sources/Jenga/Core/Binding/BindingWrapper.swift diff --git a/Sources/Jenga/UIBinding/State.swift b/Sources/Jenga/Core/Binding/State.swift similarity index 100% rename from Sources/Jenga/UIBinding/State.swift rename to Sources/Jenga/Core/Binding/State.swift diff --git a/Sources/Jenga/Cells/SeparatorCell.swift b/Sources/Jenga/Core/Cell/SpacerCell.swift similarity index 69% rename from Sources/Jenga/Cells/SeparatorCell.swift rename to Sources/Jenga/Core/Cell/SpacerCell.swift index 739dfe0..1325cc9 100644 --- a/Sources/Jenga/Cells/SeparatorCell.swift +++ b/Sources/Jenga/Core/Cell/SpacerCell.swift @@ -7,6 +7,6 @@ import UIKit -public class SeparatorCell: UITableViewCell { +public class SpacerCell: UITableViewCell { } diff --git a/Sources/Jenga/Cells/TapActionCell.swift b/Sources/Jenga/Core/Cell/TapActionCell.swift similarity index 76% rename from Sources/Jenga/Cells/TapActionCell.swift rename to Sources/Jenga/Core/Cell/TapActionCell.swift index 3c2d273..540e78c 100644 --- a/Sources/Jenga/Cells/TapActionCell.swift +++ b/Sources/Jenga/Core/Cell/TapActionCell.swift @@ -8,34 +8,22 @@ open class TapActionCell: UITableViewCell, ConfigurableCell { setUpAppearance() } - /** - Overrides the designated initializer that returns an object initialized from data in a given unarchiver. - - - parameter aDecoder: An unarchiver object. - - - returns: `self`, initialized using the data in decoder. - */ public required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) setUpAppearance() } - // MARK: UIView - open override func tintColorDidChange() { super.tintColorDidChange() textLabel?.textColor = row?.text.color.wrappedValue ?? tintColor } - // MARK: Private Methods - private func setUpAppearance() { textLabel?.numberOfLines = 0 textLabel?.textAlignment = .center } private weak var row: TapActionRowCompatible? - open func configure(with row: Row) { guard let row = row as? TapActionRowCompatible else { return diff --git a/Sources/Jenga/Cells/ToggleCell.swift b/Sources/Jenga/Core/Cell/ToggleCell.swift similarity index 90% rename from Sources/Jenga/Cells/ToggleCell.swift rename to Sources/Jenga/Core/Cell/ToggleCell.swift index 6abfd55..29f45b9 100644 --- a/Sources/Jenga/Cells/ToggleCell.swift +++ b/Sources/Jenga/Core/Cell/ToggleCell.swift @@ -1,6 +1,5 @@ import UIKit -/// A `UITableViewCell` subclass that shows a `UISwitch` as the `accessoryView`. open class ToggleCell: UITableViewCell, ConfigurableCell { public private(set) lazy var switchControl = UISwitch() @@ -26,5 +25,4 @@ open class ToggleCell: UITableViewCell, ConfigurableCell { textLabel?.numberOfLines = 0 detailTextLabel?.numberOfLines = 0 } - } diff --git a/Sources/Jenga/Extension/Collection.Extension.swift b/Sources/Jenga/Core/Extension/Collection.Extension.swift similarity index 100% rename from Sources/Jenga/Extension/Collection.Extension.swift rename to Sources/Jenga/Core/Extension/Collection.Extension.swift diff --git a/Sources/Jenga/Extension/UILabel.Inset.swift b/Sources/Jenga/Core/Extension/UILabel.Inset.swift similarity index 91% rename from Sources/Jenga/Extension/UILabel.Inset.swift rename to Sources/Jenga/Core/Extension/UILabel.Inset.swift index 0fcd7c1..1c902a6 100644 --- a/Sources/Jenga/Extension/UILabel.Inset.swift +++ b/Sources/Jenga/Core/Extension/UILabel.Inset.swift @@ -12,12 +12,12 @@ extension UILabel { var edgeInsets: UIEdgeInsets { get { - let wrapper: Wrapper? = associated.get(&AssociateKey.edgeInsets) + let wrapper: Wrapper? = get(&AssociateKey.edgeInsets) return wrapper?.value ?? .zero } set { let wrapper = Wrapper(newValue) - associated.set(retain: &AssociateKey.edgeInsets, wrapper) + set(retain: &AssociateKey.edgeInsets, wrapper) UILabel.swizzled } } diff --git a/Sources/Jenga/Extension/UIView.Extension.swift b/Sources/Jenga/Core/Extension/UIView.Extension.swift similarity index 100% rename from Sources/Jenga/Extension/UIView.Extension.swift rename to Sources/Jenga/Core/Extension/UIView.Extension.swift diff --git a/Sources/Jenga/Jenga.swift b/Sources/Jenga/Core/Jenga.swift similarity index 62% rename from Sources/Jenga/Jenga.swift rename to Sources/Jenga/Core/Jenga.swift index 0cee362..f287d32 100644 --- a/Sources/Jenga/Jenga.swift +++ b/Sources/Jenga/Core/Jenga.swift @@ -7,6 +7,9 @@ import UIKit +public typealias RowBuilder = ArrayBuilder +public typealias TableBuilder = ArrayBuilder
+ public enum JengaProvider { } extension JengaProvider { @@ -18,7 +21,7 @@ extension JengaProvider { } // 全局样式配置 不配置使用系统样式 - public typealias DefaultCellHandle = (UITableViewCell, _ style: RowSystemable) -> Void + public typealias DefaultCellHandle = (UITableViewCell, _ style: RowSystem) -> Void public typealias TableViewHandle = (CGRect) -> UITableView internal static var defaultHandle: DefaultCellHandle? @@ -53,43 +56,3 @@ extension JengaProvider { return tableView } } - -public enum TableRowActionType { - - case click - case clickDelete - case select - case deselect - case willSelect - case willDeselect - case willDisplay - case didEndDisplaying - case shouldHighlight - case shouldBeginMultipleSelection - case didBeginMultipleSelection - case height - case canEdit - case configure - case canDelete - case canMove - case canMoveTo - case move - case showContextMenu - case accessoryButtonTap - case custom(String) - - var key: String { - - switch (self) { - case .custom(let key): - return key - default: - return "_\(self)" - } - } -} - -//func `deinit`(item: Any...) { -// let result = "deinit" + item.map { "\($0)" }.joined(separator: "\t") -// print(result, separator: "\t") -//} diff --git a/Sources/Jenga/Model/AsyncImage.swift b/Sources/Jenga/Core/Model/AsyncImage.swift similarity index 86% rename from Sources/Jenga/Model/AsyncImage.swift rename to Sources/Jenga/Core/Model/AsyncImage.swift index dfea46f..c10ca95 100644 --- a/Sources/Jenga/Model/AsyncImage.swift +++ b/Sources/Jenga/Core/Model/AsyncImage.swift @@ -10,5 +10,7 @@ import UIKit public protocol AsyncImage { + var downloadURL: URL { get } + func loadImage(with imageView: UIImageView?, _ completion: @escaping (Bool) -> Void) } diff --git a/Sources/Jenga/Model/DetailText.swift b/Sources/Jenga/Core/Model/DetailText.swift similarity index 75% rename from Sources/Jenga/Model/DetailText.swift rename to Sources/Jenga/Core/Model/DetailText.swift index b7b0606..cdd754a 100644 --- a/Sources/Jenga/Model/DetailText.swift +++ b/Sources/Jenga/Core/Model/DetailText.swift @@ -7,17 +7,12 @@ public struct DetailText: Equatable { var text: Text public enum `Type` { - /// Does not show a detail text in `UITableViewCell.CellStyle.default`. case none - /// Shows the detail text in `UITableViewCell.CellStyle.subtitle`. case subtitle - /// Shows the detail text in `UITableViewCell.CellStyle.value1`. case value1 - /// Shows the detail text in `UITableViewCell.CellStyle.value2`. case value2 } - /// Returns the corresponding table view cell style. public var style: UITableViewCell.CellStyle { switch type { case .none: return .default diff --git a/Sources/Jenga/Core/Model/Icon.swift b/Sources/Jenga/Core/Model/Icon.swift new file mode 100644 index 0000000..5ac4ff1 --- /dev/null +++ b/Sources/Jenga/Core/Model/Icon.swift @@ -0,0 +1,45 @@ +import UIKit +/// A enum that represents the image used in a row. +public enum Icon { + case image(Image) + case async(AsyncImage) +} + +extension Icon: Hashable { + + public static func == (lhs: Icon, rhs: Icon) -> Bool { + switch (lhs, rhs) { + case (.image(let r1), .image(let r2)): return r1.image == r2.image && r1.highlightedImage == r2.highlightedImage + case (.async(let r1), .async(let r2)): return r1.downloadURL == r2.downloadURL + case (.image, .async): return false + case (.async, .image): return false + } + } + + public func hash(into hasher: inout Hasher) { + switch self { + case .image(let r): + hasher.combine(r.image) + hasher.combine(r.highlightedImage) + case .async(let p): + hasher.combine(p.downloadURL) + } + } +} + +extension Icon { + + public var asImage: Image? { + switch self { + case .image(let value): return value + default: return nil + } + } + + public var asAsync: AsyncImage? { + switch self { + case .async(let value): return value + default: return nil + } + } +} diff --git a/Sources/Jenga/Core/Model/Image.swift b/Sources/Jenga/Core/Model/Image.swift new file mode 100644 index 0000000..afadf60 --- /dev/null +++ b/Sources/Jenga/Core/Model/Image.swift @@ -0,0 +1,88 @@ +// +// Icon.Image.swift +// TableKit +// +// Created by 方林威 on 2022/3/23. +// + +import UIKit + +public protocol Image { + + var image: UIImage? { get } + + var highlightedImage: UIImage? { get } + + func loadImage(with imageView: UIImageView?, _ completion: @escaping (Bool) -> Void) +} + +public struct IconImage: Image { + + private let signature: String + + public var image: UIImage? + + public let highlightedImage: UIImage? + + public func loadImage(with imageView: UIImageView?, _ completion: @escaping (Bool) -> Void) { + imageView?.image = image + imageView?.highlightedImage = highlightedImage + completion(true) + } + + private init(signature: String, image: UIImage?, highlightedImage: UIImage?) { + self.signature = signature + self.image = image + self.highlightedImage = highlightedImage + } + + public static func named(_ name: String, in bundle: Bundle? = .none, compatibleWith traitCollection: UITraitCollection? = .none) -> Self { + return IconImage( + signature: "\(#function):\(name)", + image: UIImage(named: name, in: bundle, compatibleWith: traitCollection), + highlightedImage: UIImage(named: name + "-highlighted", in: bundle, compatibleWith: traitCollection) + ) + } + + public static func images(normal: UIImage?, highlighted: UIImage?) -> Self { + return IconImage(signature: #function, image: normal, highlightedImage: highlighted) + } + + @available(iOS 13.0, tvOS 13.0, *) + public static func sfSymbol(_ name: String, withConfiguration configuration: UIImage.Configuration? = .none) -> Self { + let fallback = UIImage.SymbolConfiguration(textStyle: .body) + return IconImage( + signature: "\(#function):\(name)", + image: UIImage(systemName: name, withConfiguration: configuration ?? fallback), + highlightedImage: nil + ) + } +} + +public extension IconImage { + + static func image(_ image: UIImage?) -> Self { + return .images(normal: image, highlighted: nil) + } + + static func image(named name: String, in bundle: Bundle? = .none, compatibleWith traitCollection: UITraitCollection? = .none) -> Self { + return .named(name, in: bundle, compatibleWith: traitCollection) + } + + static func image(_ image: UIImage?, highlighted: UIImage?) -> Self { + return .images(normal: image, highlighted: highlighted) + } +} + +public extension RowSystem { + + func icon(_ value: Binding) -> Self { + icon = value.map { .image($0) } + return self + } + + func icon(_ value: IconImage) -> Self { + icon = .constant(.image(value)) + return self + } +} diff --git a/Sources/Jenga/Model/Text.swift b/Sources/Jenga/Core/Model/Text.swift similarity index 100% rename from Sources/Jenga/Model/Text.swift rename to Sources/Jenga/Core/Model/Text.swift diff --git a/Sources/Jenga/Row/BasicRow.swift b/Sources/Jenga/Core/Row/BasicRow.swift similarity index 92% rename from Sources/Jenga/Row/BasicRow.swift rename to Sources/Jenga/Core/Row/BasicRow.swift index 7750554..319d16a 100644 --- a/Sources/Jenga/Row/BasicRow.swift +++ b/Sources/Jenga/Core/Row/BasicRow.swift @@ -7,12 +7,8 @@ import UIKit -open class BasicRow: RowSystemable, RowConfigurable { +open class BasicRow: RowSystem, RowConfigurable { - // MARK: - Initializer - - /// Initializes an `OptionRow` with a text, a selection state and an action closure. - /// The detail text, icon, and the customization closure are optional. public init(_ binding: Binding) { self.text = binding.map { .init(string: $0) } } @@ -87,6 +83,14 @@ open class BasicRow: RowSystemable, RowConfigurable { deinit { log("deinit", "SystemRow", cellType, text.string.wrappedValue ?? text.attributedString.wrappedValue?.string ?? "") } } +public extension BasicRow { + + func customize(_ value: @escaping ((T) -> Void)) -> Self { + customize = value + return self + } +} + extension BasicRow { internal func defaultSetup(with cell: UITableViewCell) { @@ -94,7 +98,7 @@ extension BasicRow { text.append(observer: self) { [weak cell] change in guard let cell = cell else { return } let text = change.new - + text.string.map { cell.textLabel?.text = $0 } text.attributedString.map { cell.textLabel?.attributedText = $0 } text.color.map { cell.textLabel?.textColor = $0 } @@ -109,7 +113,7 @@ extension BasicRow { switch change.new.type { case .none: cell.detailTextLabel?.text = nil - + case .subtitle, .value1, .value2: change.new.text.string.map { cell.detailTextLabel?.text = $0 } change.new.text.attributedString.map { cell.detailTextLabel?.attributedText = $0 } @@ -124,9 +128,12 @@ extension BasicRow { guard let cell = cell else { return } switch changed.new { case .image(let value): - cell.imageView?.image = value.image - cell.imageView?.highlightedImage = value.highlightedImage - + value.loadImage(with: cell.imageView) { [weak cell] result in + guard result else { return } + // https://www.cnblogs.com/lisa090818/p/3508390.html + cell?.setNeedsLayout() + } + case .async(let value): value.loadImage(with: cell.imageView) { [weak cell] result in guard result else { return } @@ -141,14 +148,6 @@ extension BasicRow { } } -public extension BasicRow { - - func customize(_ value: @escaping ((T) -> Void)) -> Self { - customize = value - return self - } -} - internal extension UITableViewCell.CellStyle { var stringValue: String { diff --git a/Sources/Jenga/Core/Row/Configurable.swift b/Sources/Jenga/Core/Row/Configurable.swift new file mode 100644 index 0000000..a2c61e2 --- /dev/null +++ b/Sources/Jenga/Core/Row/Configurable.swift @@ -0,0 +1,30 @@ +import UIKit + +public protocol RowConfigurable { + + /// 配置 对应 cellForRow 设置内容 添加监听等 + /// - Parameter cell: cell + func configure(_ cell: UITableViewCell) + + /// 恢复 对应 didEndDisplaying 移除监听等操作 + /// - Parameter cell: cell + func recovery(_ cell: UITableViewCell) +} + +public protocol ConfigurableCell: UITableViewCell { + associatedtype CellData + + /// 配置 对应 cellForRow 设置内容 添加监听等 + /// - Parameter cell: cell + func configure(with _: CellData) + + /// 恢复 对应 didEndDisplaying 移除监听等操作 + /// - Parameter cell: cell + func recovery(_ cell: CellData) +} + +extension ConfigurableCell { + + public func recovery(_ cell: CellData) { + } +} diff --git a/Sources/Jenga/Core/Row/Reusable.swift b/Sources/Jenga/Core/Row/Reusable.swift new file mode 100644 index 0000000..7c830d3 --- /dev/null +++ b/Sources/Jenga/Core/Row/Reusable.swift @@ -0,0 +1,46 @@ +import UIKit + +extension UITableViewCell: Reusable {} + +internal protocol Reusable { + static var reuseIdentifier: String { get } +} + +internal extension Reusable { + + static var reuseIdentifier: String { + let type = String(describing: self) + return type.matches(of: String.typeDescriptionPattern).last ?? type + } +} + +internal extension String { + + static var typeDescriptionPattern: String { + // For the types in the format of "(CustomCell in _B5334F301B8CC6AA00C64A6D)" + return "^\\(([\\w\\d]+)\\sin\\s_[0-9A-F]+\\)$" + } + + func matches(of pattern: String) -> [String] { + let regex = try? NSRegularExpression(pattern: pattern, options: .caseInsensitive) +#if swift(>=3.2) + let fullText = NSRange(location: 0, length: count) +#else + let fullText = NSRange(location: 0, length: characters.count) +#endif + + guard let matches = regex?.matches(in: self, options: [], range: fullText) else { + return [] + } + + return matches.reduce([]) { accumulator, match in + accumulator + (0..=4) + return (self as NSString).substring(with: match.range(at: $0)) +#else + return (self as NSString).substring(with: match.rangeAt($0)) +#endif + } + } + } +} diff --git a/Sources/Jenga/Row/Protocol/Row.swift b/Sources/Jenga/Core/Row/Row.swift similarity index 53% rename from Sources/Jenga/Row/Protocol/Row.swift rename to Sources/Jenga/Core/Row/Row.swift index 454eff5..951ef51 100644 --- a/Sources/Jenga/Row/Protocol/Row.swift +++ b/Sources/Jenga/Core/Row/Row.swift @@ -1,30 +1,3 @@ -// -// Row.swift -// QuickTableViewController -// -// Created by Ben on 01/09/2015. -// Copyright (c) 2015 bcylin. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// - -import Foundation import UIKit public protocol RowActionable: AnyObject { @@ -44,13 +17,10 @@ extension RowHashable { public var hashValue: Int { ObjectIdentifier(self).hashValue } } -/// Any type that conforms to this protocol is capable of representing a row in a table view. public protocol Row: RowHashable, RowActionable { - /// The type of the table view cell to display the row. var cellType: UITableViewCell.Type { get } - /// The reuse identifier of the table view cell to display the row. var reuseIdentifier: String { get } var selectionStyle: UITableViewCell.SelectionStyle { get set } diff --git a/Sources/Jenga/Row/System/NavigationRow.swift b/Sources/Jenga/Core/Row/System/NavigationRow.swift similarity index 67% rename from Sources/Jenga/Row/System/NavigationRow.swift rename to Sources/Jenga/Core/Row/System/NavigationRow.swift index a0aa0bc..004c96f 100644 --- a/Sources/Jenga/Row/System/NavigationRow.swift +++ b/Sources/Jenga/Core/Row/System/NavigationRow.swift @@ -1,13 +1,9 @@ import UIKit -/// A class that represents a row that triggers certain navigation when selected. open class NavigationRow: BasicRow, NavigationRowCompatible, Equatable { - /// A closure that will be invoked when the accessory button is selected. public var accessoryButtonAction: RowAction? - /// Returns the accessory type with the disclosure indicator when `action` is not nil, - /// and with the detail button when `accessoryButtonAction` is not nil. public override var accessoryType: UITableViewCell.AccessoryType { get { if let accessory = _accessoryType { return accessory } @@ -23,17 +19,15 @@ open class NavigationRow: BasicRow, NavigationRowCompatib private var _accessoryType: UITableViewCell.AccessoryType? - /// The `NavigationRow` is selectable when action is not nil. public override var isSelectable: Bool { get { action != nil } set {} } - /// Returns true iff `lhs` and `rhs` have equal titles, detail texts and icons. public static func == (lhs: NavigationRow, rhs: NavigationRow) -> Bool { return lhs.text == rhs.text && - lhs.detailText == rhs.detailText -// (lhs.icon as? Equatable) == (rhs.icon as? Equatable) + lhs.detailText == rhs.detailText && + lhs.icon == rhs.icon } } diff --git a/Sources/Jenga/Row/System/OptionRow.swift b/Sources/Jenga/Core/Row/System/OptionRow.swift similarity index 64% rename from Sources/Jenga/Row/System/OptionRow.swift rename to Sources/Jenga/Core/Row/System/OptionRow.swift index d6b2070..5ccb4fa 100644 --- a/Sources/Jenga/Row/System/OptionRow.swift +++ b/Sources/Jenga/Core/Row/System/OptionRow.swift @@ -3,18 +3,11 @@ import UIKit /// TODO: 功能待开发 open class OptionRow: BasicRow, OptionRowCompatible, Equatable { - // MARK: - Initializer - - /// Initializes an `OptionRow` with a text, a selection state and an action closure. - /// The detail text, icon, and the customization closure are optional. public init(text: Binding, isSelected: Bool = false) { super.init(text) self.isSelected = isSelected } - // MARK: - OptionRowCompatible - - /// The state of selection. public var isSelected: Bool = false { didSet { guard isSelected != oldValue else { @@ -26,18 +19,16 @@ open class OptionRow: BasicRow, OptionRowCompatible, Equa } } - /// Returns `.checkmark` when the row is selected, otherwise returns `.none`. public override var accessoryType: UITableViewCell.AccessoryType { get { isSelected ? .checkmark : .none } set {} } - /// Returns true iff `lhs` and `rhs` have equal titles, detail texts, selection states, and icons. public static func == (lhs: OptionRow, rhs: OptionRow) -> Bool { return lhs.text == rhs.text && lhs.detailText == rhs.detailText && - lhs.isSelected == rhs.isSelected -// lhs.icon == rhs.icon + lhs.isSelected == rhs.isSelected && + lhs.icon == rhs.icon } } diff --git a/Sources/Jenga/Row/Protocol/RowSystemable.swift b/Sources/Jenga/Core/Row/System/RowSystem.swift similarity index 62% rename from Sources/Jenga/Row/Protocol/RowSystemable.swift rename to Sources/Jenga/Core/Row/System/RowSystem.swift index 6d6e438..6eb7299 100644 --- a/Sources/Jenga/Row/Protocol/RowSystemable.swift +++ b/Sources/Jenga/Core/Row/System/RowSystem.swift @@ -1,33 +1,7 @@ -// -// RowStyle.swift -// QuickTableViewController -// -// Created by Ben on 30/07/2017. -// Copyright © 2017 bcylin. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// - import UIKit // 系统样式 -public protocol RowSystemable: Row { +public protocol RowSystem: Row { /// The text of the row. var text: Binding { get set } @@ -44,28 +18,18 @@ public protocol RowSystemable: Row { var accessoryType: UITableViewCell.AccessoryType { get } } -public extension RowSystemable { +public extension RowSystem { func icon(_ value: Binding) -> Self { icon = value return self } - func icon(_ value: Binding) -> Self { - icon = value.map { .image($0) } - return self - } - func icon(_ value: Icon) -> Self { icon = .constant(value) return self } - func icon(_ value: Icon.Image) -> Self { - icon = .constant(.image(value)) - return self - } - func detailText(_ value: Binding) -> Self { detailText = value return self @@ -125,3 +89,30 @@ public extension RowSystemable { return self } } + +public protocol NavigationRowCompatible: RowSystem { + + var accessoryButtonAction: RowAction? { get } +} + +public protocol BadgeRowCompatible: RowSystem { + + var badgeValue: Binding { get set } + var badgeColor: Binding? { get set } +} + +public protocol TapActionRowCompatible: RowSystem { + + var textAlignment: NSTextAlignment { get set} +} + +public protocol OptionRowCompatible: RowSystem { + + var isSelected: Bool { get set } +} + +public protocol ToggleRowCompatible: RowSystem { + + var isOn: Binding { get set } + var onTap: ((Bool) -> Void)? { get } +} diff --git a/Sources/Jenga/Row/System/SeparatorRow.swift b/Sources/Jenga/Core/Row/System/SpacerRow.swift similarity index 60% rename from Sources/Jenga/Row/System/SeparatorRow.swift rename to Sources/Jenga/Core/Row/System/SpacerRow.swift index 48ba999..24f10e9 100644 --- a/Sources/Jenga/Row/System/SeparatorRow.swift +++ b/Sources/Jenga/Core/Row/System/SpacerRow.swift @@ -1,5 +1,5 @@ // -// SeparatorRow.swift +// SpacerRow.swift // Zunion // // Created by 方林威 on 2022/3/7. @@ -7,8 +7,7 @@ import UIKit -/// A class that represents a row that triggers certain navigation when selected. -open class SeparatorRow: Row { +open class SpacerRow: Row { let color: UIColor @@ -20,26 +19,19 @@ open class SeparatorRow: Row { public var action: RowAction? - // MARK: - RowStyle - - /// The type of the table view cell to display the row. public let cellType: UITableViewCell.Type = T.self - /// Returns the reuse identifier of the table view cell to display the row. public var reuseIdentifier: String { T.reuseIdentifier } public var selectionStyle: UITableViewCell.SelectionStyle = .none - /// Returns the table view cell style for the specified detail text. public var cellStyle: UITableViewCell.CellStyle = .default public var height: RowHeight? public var estimatedHeight: RowHeight? - /// `OptionRow` is always selectable. public var isSelectable: Bool = false - /// Returns `.checkmark` when the row is selected, otherwise returns `.none`. public var accessoryType: UITableViewCell.AccessoryType = .none } diff --git a/Sources/Jenga/Row/System/TapActionRow.swift b/Sources/Jenga/Core/Row/System/TapActionRow.swift similarity index 76% rename from Sources/Jenga/Row/System/TapActionRow.swift rename to Sources/Jenga/Core/Row/System/TapActionRow.swift index 321b779..fbff2f4 100644 --- a/Sources/Jenga/Row/System/TapActionRow.swift +++ b/Sources/Jenga/Core/Row/System/TapActionRow.swift @@ -1,9 +1,7 @@ import UIKit -/// A class that represents a row that triggers certain action when selected. open class TapActionRow: BasicRow, TapActionRowCompatible, Equatable { - /// The `NavigationRow` is selectable when action is not nil. public override var isSelectable: Bool { get { action != nil } set {} @@ -16,7 +14,6 @@ open class TapActionRow: BasicRow, TapActionRowCompatible, (cell as? T)?.configure(with: self) } - /// Returns true iff `lhs` and `rhs` have equal titles and detail texts. public static func == (lhs: TapActionRow, rhs: TapActionRow) -> Bool { return lhs.text == rhs.text && lhs.detailText == rhs.detailText } diff --git a/Sources/Jenga/Core/Row/System/ToggleRow.swift b/Sources/Jenga/Core/Row/System/ToggleRow.swift new file mode 100644 index 0000000..1b3cbaa --- /dev/null +++ b/Sources/Jenga/Core/Row/System/ToggleRow.swift @@ -0,0 +1,59 @@ +import UIKit + +/// A class that represents a row with a switch. +open class ToggleRow: BasicRow, ToggleRowCompatible, Equatable { + + public init(_ text: Binding, isOn: Binding) { + self.isOn = isOn + super.init(text) + } + + public init(_ text: String, isOn: Binding) { + self.isOn = isOn + super.init(text) + } + + public var isOn: Binding + + public var onTap: ((Bool) -> Void)? + + open override func configure(_ cell: UITableViewCell) { + super.configure(cell) + (cell as? Cell)?.configure(with: (isOn, onTap)) + } + + public override var isSelectable: Bool { + get { false } + set {} + } + + public static func == (lhs: ToggleRow, rhs: ToggleRow) -> Bool { + return lhs.text == rhs.text && + lhs.detailText == rhs.detailText && + lhs.isOn == rhs.isOn && + lhs.icon == rhs.icon + } +} + +public extension ToggleRow { + + func isOn(_ value: Bool) -> Self { + self.isOn = .constant(value) + return self + } + + /// Toggle click + func onTap(_ value: @escaping (Bool) -> Void) -> Self { + self.onTap = value + return self + } + + func onTap(on target: S, _ value: @escaping (S, Bool) -> Void) -> Self where S: AnyObject { + self.onTap = { [weak target] isOn in + guard let target = target else { return } + value(target, isOn) + } + return self + } + +} diff --git a/Sources/Jenga/Row/TableRow.swift b/Sources/Jenga/Core/Row/TableRow.swift similarity index 99% rename from Sources/Jenga/Row/TableRow.swift rename to Sources/Jenga/Core/Row/TableRow.swift index 21bf1cf..9d01bf6 100644 --- a/Sources/Jenga/Row/TableRow.swift +++ b/Sources/Jenga/Core/Row/TableRow.swift @@ -8,7 +8,7 @@ import UIKit // 自定义cell open class TableRow: Row, RowConfigurable { - + public init(_ data: Cell.CellData) { self.item = .constant(data) } @@ -60,7 +60,7 @@ public extension TableRow { self.isSelectable = value return self } - + func data(_ data: Cell.CellData) -> Self { item = .constant(data) return self diff --git a/Sources/Jenga/Section/BacicSection.swift b/Sources/Jenga/Core/Section/BacicSection.swift similarity index 91% rename from Sources/Jenga/Section/BacicSection.swift rename to Sources/Jenga/Core/Section/BacicSection.swift index 9202696..b3b780a 100644 --- a/Sources/Jenga/Section/BacicSection.swift +++ b/Sources/Jenga/Core/Section/BacicSection.swift @@ -7,7 +7,7 @@ import UIKit -open class BacicSection: Sectionable { +open class BacicSection: Section { public init(_ rows: [Row] = []) { self.rows = rows diff --git a/Sources/Jenga/Section/RadioSection.swift b/Sources/Jenga/Core/Section/RadioSection.swift similarity index 72% rename from Sources/Jenga/Section/RadioSection.swift rename to Sources/Jenga/Core/Section/RadioSection.swift index 1b150ff..0bc1b50 100644 --- a/Sources/Jenga/Section/RadioSection.swift +++ b/Sources/Jenga/Core/Section/RadioSection.swift @@ -3,9 +3,6 @@ import Foundation /// TODO: 功能待开发 open class RadioSection: BacicSection { - // MARK: - Initializer - - /// Initializes a section with a title, containing rows and an optional footer. public init(@ArrayBuilder builder: ArrayBuilder.ContentBlock) { self.options = builder() super.init() @@ -28,9 +25,6 @@ open class RadioSection: BacicSection { } } - // MARK: - RadioSection - - /// A boolean that indicates whether there's always one option selected. open var alwaysSelectsOneOption: Bool = false { didSet { if alwaysSelectsOneOption && selectedOption == nil { @@ -39,15 +33,12 @@ open class RadioSection: BacicSection { } } - /// The array of options in the section. It's identical to the `rows`. open private(set) var options: [OptionRowCompatible] - /// Returns the selected index, or nil when nothing is selected. open var indexOfSelectedOption: Int? { return options.firstIndex { $0.isSelected } } - /// Returns the selected option row, or nil when nothing is selected. open var selectedOption: OptionRowCompatible? { if let index = indexOfSelectedOption { return options[index] @@ -56,11 +47,6 @@ open class RadioSection: BacicSection { } } - /// Toggle the selection of the given option and keep options mutually exclusive. - /// If `alwaysSelectOneOption` is set to true, it will not deselect the current selection. - /// - /// - Parameter option: The option to flip the `isSelected` state. - /// - Returns: The indexes of changed options. open func toggle(_ option: OptionRowCompatible) -> IndexSet { if option.isSelected && alwaysSelectsOneOption { return [] @@ -71,7 +57,6 @@ open class RadioSection: BacicSection { } if option.isSelected { - // Deselect the selected option. return options.firstIndex(where: { $0 === option }).map { [$0] } ?? [] } diff --git a/Sources/Jenga/Section/Sectionable.swift b/Sources/Jenga/Core/Section/Section.swift similarity index 95% rename from Sources/Jenga/Section/Sectionable.swift rename to Sources/Jenga/Core/Section/Section.swift index c6a5cfd..2cdcd16 100644 --- a/Sources/Jenga/Section/Sectionable.swift +++ b/Sources/Jenga/Core/Section/Section.swift @@ -1,5 +1,5 @@ // -// Sectionable.swift +// Section.swift // Zunion // // Created by 方林威 on 2022/3/2. @@ -39,7 +39,7 @@ public struct HeaderFooter { } } -public protocol Sectionable: AnyObject { +public protocol Section: AnyObject { var rows: [Row] { get set } @@ -52,14 +52,14 @@ public protocol Sectionable: AnyObject { var hiddenWithEmpty: Bool { get set } } -public extension Sectionable { +public extension Section { var numberOfRows: Int { rows.count } var isEmpty: Bool { rows.isEmpty } } -public extension Sectionable { +public extension Section { func header(_ value: @autoclosure () -> (HeaderFooter)) -> Self { header = value() diff --git a/Sources/Jenga/Core/Section/TableSection.swift b/Sources/Jenga/Core/Section/TableSection.swift new file mode 100644 index 0000000..3f0fd39 --- /dev/null +++ b/Sources/Jenga/Core/Section/TableSection.swift @@ -0,0 +1,96 @@ +import Foundation +import UIKit + +open class TableSection: BacicSection { + + public var didUpdate: ((TableSection) -> Void)? + + public init() { + super.init() + } + + public init(@RowBuilder builder: RowBuilder.ContentBlock) { + super.init(builder()) + } + + public init(_ array: [T], content: @escaping (Binding) -> Row) { + super.init(array.map { content(.constant($0)) }) + } +} + +public extension TableSection { + + /// forEach + convenience init(binding: Binding<[T]>, builder: @escaping (Binding) -> Row) { + self.init() + binding.append(observer: self) { [weak self] changed in + guard let self = self else { return } + self.rows = changed.new.map { builder(.constant($0)) } + self.didUpdate?(self) + } + } + + /// forEach on target + convenience init( + binding: Binding<[T]>, + on target: S, + builder: @escaping (S, Binding) -> Row) { + self.init() + binding.append(observer: self) { [weak self, weak target] changed in + guard let self = self else { return } + guard let target = target else { return } + self.rows = changed.new.map { builder(target, .constant($0)) } + self.didUpdate?(self) + } + } + + /// map T + convenience init(binding: Binding, @RowBuilder builder: @escaping (T) -> [Row]) { + self.init() + binding.append(observer: self) { [weak self] changed in + guard let self = self else { return } + self.rows = builder(changed.new) + self.didUpdate?(self) + } + } + + /// map T on target + convenience init( + binding: Binding, + on target: S, + @RowBuilder builder: @escaping (S, T) -> [Row]) where S: AnyObject { + self.init() + binding.append(observer: self) { [weak self, weak target] changed in + guard let self = self else { return } + guard let target = target else { return } + self.rows = builder(target, changed.new) + self.didUpdate?(self) + } + } + + /// map binding EnumeratedSequence + convenience init(binding: Binding>, + builder: @escaping (Binding.Iterator.Element>) -> Row) { + self.init() + binding.append(observer: self) { [weak self] changed in + guard let self = self else { return } + self.rows = changed.new.map { builder(.constant($0)) } + self.didUpdate?(self) + } + } + + /// map binding EnumeratedSequence on target + convenience init( + binding: Binding>, + on target: S, + builder: @escaping (S, Binding.Iterator.Element>) -> Row) where S: AnyObject { + self.init() + binding.append(observer: self) { [weak self, weak target] changed in + guard let self = self else { return } + guard let target = target else { return } + self.rows = changed.new.map { builder(target, .constant($0)) } + self.didUpdate?(self) + } + } +} + diff --git a/Sources/Jenga/TableDirector.swift b/Sources/Jenga/Core/TableDirector.swift similarity index 96% rename from Sources/Jenga/TableDirector.swift rename to Sources/Jenga/Core/TableDirector.swift index 4f2d1c1..71c8bb2 100644 --- a/Sources/Jenga/TableDirector.swift +++ b/Sources/Jenga/Core/TableDirector.swift @@ -8,8 +8,6 @@ import UIKit public class TableDirector: NSObject { - typealias Section = Sectionable - public let tableView: UITableView public private(set) weak var delegate: UIScrollViewDelegate? @@ -60,8 +58,8 @@ public class TableDirector: NSObject { ) } - private var content: [Sectionable]? - public func set(sections: [Sectionable]) { + private var content: [Section]? + public func set(sections: [Section]) { self.content = sections self.sections = sections.filter { !($0.isEmpty && $0.hiddenWithEmpty) } reload() @@ -116,7 +114,7 @@ extension TableDirector: UITableViewDataSource { public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { func cell(for row: Row) -> UITableViewCell { - if let row = row as? RowSystemable { + if let row = row as? RowSystem { let cell = tableView.dequeueReusableCell(withIdentifier: row.reuseIdentifier) ?? row.cellType.init(style: row.cellStyle, reuseIdentifier: row.reuseIdentifier) @@ -187,7 +185,7 @@ extension TableDirector: UITableViewDelegate { let section = sections[indexPath.section] let row = section.rows[indexPath.row] - if !(row is RowSystemable), rowHeightCalculator != nil { + if !(row is RowSystem), rowHeightCalculator != nil { cellRegisterer?.register(cellType: row.cellType, forCellReuseIdentifier: row.reuseIdentifier) } @@ -200,7 +198,7 @@ extension TableDirector: UITableViewDelegate { public func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { let section = sections[indexPath.section] let row = section.rows[indexPath.row] - if !(row is RowSystemable), rowHeightCalculator != nil { + if !(row is RowSystem), rowHeightCalculator != nil { cellRegisterer?.register(cellType: row.cellType, forCellReuseIdentifier: row.reuseIdentifier) } diff --git a/Sources/Jenga/Utils/TableKit.Log.swift b/Sources/Jenga/Core/Utils/Log.swift similarity index 83% rename from Sources/Jenga/Utils/TableKit.Log.swift rename to Sources/Jenga/Core/Utils/Log.swift index fad7875..c2d608b 100644 --- a/Sources/Jenga/Utils/TableKit.Log.swift +++ b/Sources/Jenga/Core/Utils/Log.swift @@ -7,11 +7,9 @@ import Foundation -func log(_ items: Any..., separator: String = " ") { +internal func log(_ items: Any..., separator: String = " ") { guard JengaProvider.isEnabledLog else { return } let content = String(items.map { "\($0)" }.joined(separator: " ")) Swift.print("[TableKit]", content , separator:separator, terminator: "\n") } - - diff --git a/Sources/Jenga/UIBinding/ResultBuilder.swift b/Sources/Jenga/Core/Utils/ResultBuilder.swift similarity index 86% rename from Sources/Jenga/UIBinding/ResultBuilder.swift rename to Sources/Jenga/Core/Utils/ResultBuilder.swift index 57cc508..793576d 100644 --- a/Sources/Jenga/UIBinding/ResultBuilder.swift +++ b/Sources/Jenga/Core/Utils/ResultBuilder.swift @@ -12,7 +12,7 @@ public struct ArrayBuilder { public extension ArrayBuilder { - // MARK: 组合全部表达式的返回值 + /// 组合全部表达式的返回值 static func buildBlock(_ component: [Component]...) -> [Component] { component.flatMap { $0 } } @@ -21,17 +21,17 @@ public extension ArrayBuilder { component.flatMap { $0 ?? [] } } - // MARK: 处理空白block + /// 处理空白block static func buildOptional(_ component: [T]?) -> [Component] { [] } - // MARK: 处理不包含else的if语句 + /// 处理不包含else的if语句 static func buildOptional(_ component: [Component]?) -> [Component] { component ?? [] } - // MARK: 处理每一行表达式的返回值 + /// 处理每一行表达式的返回值 static func buildExpression(_ expression: Component) -> [Component] { [expression] } @@ -52,12 +52,12 @@ public extension ArrayBuilder { expression } - // MARK: 处理for循环 + /// 处理for循环 static func buildArray(_ components: [[Component]]) -> [Component] { components.flatMap { $0 } } - // MARK: 处理if...else...(必须包含else) + /// 处理if...else...(必须包含else) static func buildEither(first: [Component]) -> [Component] { first } diff --git a/Sources/Jenga/Core/Utils/Runtime.swift b/Sources/Jenga/Core/Utils/Runtime.swift new file mode 100644 index 0000000..d16eb3a --- /dev/null +++ b/Sources/Jenga/Core/Utils/Runtime.swift @@ -0,0 +1,52 @@ +// +// NSObject.Runtime.swift +// Jenga +// +// Created by 方林威 on 2022/3/30. +// Copyright © 2022 LEE. All rights reserved. +// + +import Foundation + +extension NSObject { + + func get(_ key: UnsafeRawPointer) -> T? { + return objc_getAssociatedObject(self, key) as? T + } + + func set(retain key: UnsafeRawPointer, _ value: T) { + objc_setAssociatedObject(self, key, value, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } +} + + +extension NSObject { + + static func swizzled_method(_ originalSelector: Selector, _ swizzledSelector: Selector) { + guard + let originalMethod = class_getInstanceMethod(Self.self, originalSelector), + let swizzledMethod = class_getInstanceMethod(Self.self, swizzledSelector) else { + return + } + + // 在进行 Swizzling 的时候,需要用 class_addMethod 先进行判断一下原有类中是否有要替换方法的实现 + let didAddMethod: Bool = class_addMethod( + Self.self, + originalSelector, + method_getImplementation(swizzledMethod), + method_getTypeEncoding(swizzledMethod) + ) + // 如果 class_addMethod 返回 yes,说明当前类中没有要替换方法的实现,所以需要在父类中查找,这时候就用到 method_getImplemetation 去获取 class_getInstanceMethod 里面的方法实现,然后再进行 class_replaceMethod 来实现 Swizzing + if didAddMethod { + class_replaceMethod( + Self.self, + swizzledSelector, + method_getImplementation(originalMethod), + method_getTypeEncoding(originalMethod) + ) + + } else { + method_exchangeImplementations(originalMethod, swizzledMethod) + } + } +} diff --git a/Sources/Jenga/Utils/TableCellHeightCalculator.swift b/Sources/Jenga/Core/Utils/TableCellHeightCalculator.swift similarity index 100% rename from Sources/Jenga/Utils/TableCellHeightCalculator.swift rename to Sources/Jenga/Core/Utils/TableCellHeightCalculator.swift diff --git a/Sources/Jenga/Core/Utils/TableCellRegisterer.swift b/Sources/Jenga/Core/Utils/TableCellRegisterer.swift new file mode 100644 index 0000000..f0132cd --- /dev/null +++ b/Sources/Jenga/Core/Utils/TableCellRegisterer.swift @@ -0,0 +1,30 @@ +import UIKit + +class TableCellRegisterer { + + private var registeredIds = Set() + private weak var tableView: UITableView? + + init(tableView: UITableView?) { + self.tableView = tableView + } + + func register(cellType: AnyClass, forCellReuseIdentifier reuseIdentifier: String) { + guard !registeredIds.contains(reuseIdentifier) else { + return + } + guard tableView?.dequeueReusableCell(withIdentifier: reuseIdentifier) == nil else { + registeredIds.insert(reuseIdentifier) + return + } + + let bundle = Bundle(for: cellType) + if let _ = bundle.path(forResource: reuseIdentifier, ofType: "nib") { + tableView?.register(UINib(nibName: reuseIdentifier, bundle: bundle), forCellReuseIdentifier: reuseIdentifier) + } else { + tableView?.register(cellType, forCellReuseIdentifier: reuseIdentifier) + } + + registeredIds.insert(reuseIdentifier) + } +} diff --git a/Sources/Jenga/DSLAutoTable.swift b/Sources/Jenga/DSL/DSLAutoTable.swift similarity index 77% rename from Sources/Jenga/DSLAutoTable.swift rename to Sources/Jenga/DSL/DSLAutoTable.swift index 3c0f64d..40314c9 100644 --- a/Sources/Jenga/DSLAutoTable.swift +++ b/Sources/Jenga/DSL/DSLAutoTable.swift @@ -14,26 +14,26 @@ public extension DSLAutoTable where Self: UIViewController { var tableView: UITableView { get { - guard let value: UITableView = associated.get(&TableViewKey) else { + guard let value: UITableView = get(&TableViewKey) else { let temp = JengaProvider.autoTable(.zero) - associated.set(retain: &TableViewKey, temp) + set(retain: &TableViewKey, temp) return temp } return value } - set { associated.set(retain: &TableViewKey, newValue) } + set { set(retain: &TableViewKey, newValue) } } var table: TableDirector { get { - guard let value: TableDirector = associated.get(&TableKey) else { + guard let value: TableDirector = get(&TableKey) else { let temp = TableDirector(tableView) - associated.set(retain: &TableKey, temp) + set(retain: &TableKey, temp) return temp } return value } - set { associated.set(retain: &TableKey, newValue) } + set { set(retain: &TableKey, newValue) } } func didLayoutTable() { diff --git a/Sources/Jenga/DSLTable.swift b/Sources/Jenga/DSL/DSLTable.swift similarity index 87% rename from Sources/Jenga/DSLTable.swift rename to Sources/Jenga/DSL/DSLTable.swift index 1018abb..03a51cc 100644 --- a/Sources/Jenga/DSLTable.swift +++ b/Sources/Jenga/DSL/DSLTable.swift @@ -11,7 +11,7 @@ public protocol DSLTable { var table: TableDirector { get } - var tableContents: [Sectionable] { get } + var tableContents: [Section] { get } func reloadTable() } diff --git a/Sources/Jenga/Extension/NSObject.Runtime.swift b/Sources/Jenga/Extension/NSObject.Runtime.swift deleted file mode 100644 index 57ec893..0000000 --- a/Sources/Jenga/Extension/NSObject.Runtime.swift +++ /dev/null @@ -1,105 +0,0 @@ -// -// NSObject.Runtime.swift -// Jenga -// -// Created by 方林威 on 2022/3/30. -// Copyright © 2022 LEE. All rights reserved. -// - -import Foundation - -class AssociatedWrapper { - let base: Base - init(_ base: Base) { - self.base = base - } -} - -protocol AssociatedCompatible { - associatedtype AssociatedCompatibleType - var associated: AssociatedCompatibleType { get } -} - -extension AssociatedCompatible { - - public var associated: AssociatedWrapper { - get { return AssociatedWrapper(self) } - } -} - -extension NSObject: AssociatedCompatible { } - -extension AssociatedWrapper where Base: NSObject { - - enum Policy { - case nonatomic - case atomic - } - - /// 获取关联值 - func get(_ key: UnsafeRawPointer) -> T? { - objc_getAssociatedObject(base, key) as? T - } - - /// 设置关联值 OBJC_ASSOCIATION_ASSIGN - @discardableResult - func set(assign key: UnsafeRawPointer, _ value: T) -> T { - objc_setAssociatedObject(base, key, value, .OBJC_ASSOCIATION_ASSIGN) - return value - } - - /// 设置关联值 OBJC_ASSOCIATION_RETAIN_NONATOMIC / OBJC_ASSOCIATION_RETAIN - @discardableResult - func set(retain key: UnsafeRawPointer, _ value: T?, _ policy: Policy = .nonatomic) -> T? { - switch policy { - case .nonatomic: - objc_setAssociatedObject(base, key, value, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) - case .atomic: - objc_setAssociatedObject(base, key, value, .OBJC_ASSOCIATION_RETAIN) - } - return value - } - - /// 设置关联值 OBJC_ASSOCIATION_COPY_NONATOMIC / OBJC_ASSOCIATION_COPY - @discardableResult - func set(copy key: UnsafeRawPointer, _ value: T?, _ policy: Policy = .nonatomic) -> T? { - switch policy { - case .nonatomic: - objc_setAssociatedObject(base, key, value, .OBJC_ASSOCIATION_COPY_NONATOMIC) - case .atomic: - objc_setAssociatedObject(base, key, value, .OBJC_ASSOCIATION_COPY) - } - return value - } -} - -extension NSObject { - - static func swizzled_method(_ originalSelector: Selector, _ swizzledSelector: Selector) { - guard - let originalMethod = class_getInstanceMethod(Self.self, originalSelector), - let swizzledMethod = class_getInstanceMethod(Self.self, swizzledSelector) else { - return - } - - // 在进行 Swizzling 的时候,需要用 class_addMethod 先进行判断一下原有类中是否有要替换方法的实现 - let didAddMethod: Bool = class_addMethod( - Self.self, - originalSelector, - method_getImplementation(swizzledMethod), - method_getTypeEncoding(swizzledMethod) - ) - // 如果 class_addMethod 返回 yes,说明当前类中没有要替换方法的实现,所以需要在父类中查找,这时候就用到 method_getImplemetation 去获取 class_getInstanceMethod 里面的方法实现,然后再进行 class_replaceMethod 来实现 Swizzing - if didAddMethod { - class_replaceMethod( - Self.self, - swizzledSelector, - method_getImplementation(originalMethod), - method_getTypeEncoding(originalMethod) - ) - - } else { - method_exchangeImplementations(originalMethod, swizzledMethod) - } - } -} diff --git a/Sources/Jenga/Model/Icon.Image.swift b/Sources/Jenga/Model/Icon.Image.swift deleted file mode 100644 index 32f2275..0000000 --- a/Sources/Jenga/Model/Icon.Image.swift +++ /dev/null @@ -1,87 +0,0 @@ -// -// Icon.Image.swift -// TableKit -// -// Created by 方林威 on 2022/3/23. -// - -import UIKit - -extension Icon { - - public struct Image: Equatable { - /// The initializer is kept private until v2.0 when `methodSignature` is removed. - private init(methodSignature: String, image: UIImage?, highlightedImage: UIImage?) { - self.methodSignature = methodSignature - self.image = image - self.highlightedImage = highlightedImage - } - - // MARK: - Properties - - /// A string to keep track of how the struct is initialized. - /// It's used internally to avoid the breaking Equatable changes of Icon (as enum) until v2.0. - private let methodSignature: String - - /// The image for the normal state. - public var image: UIImage? - - /// The image for the highlighted state. - public let highlightedImage: UIImage? - - // MARK: - - - /// Returns `Icon` with an image of the given name for the normal state. - /// The "-highlighted" suffix is appended to the name for the highlighted image. - /// - /// - Parameters: - /// - name: The name of the image asset. - /// - bundle: The bundle containing the image file or asset catalog. Specify nil to search the app’s main bundle. - /// - traitCollection: The traits associated with the intended environment for the image. Specify nil to use the traits associated with the main screen. - public static func named(_ name: String, in bundle: Bundle? = .none, compatibleWith traitCollection: UITraitCollection? = .none) -> Self { - return Image( - methodSignature: "\(#function):\(name)", - image: UIImage(named: name, in: bundle, compatibleWith: traitCollection), - highlightedImage: UIImage(named: name + "-highlighted", in: bundle, compatibleWith: traitCollection) - ) - } - - /// Returns `Icon` with images for the normal and highlighted states. - /// A method to provide backward compatiblility with the previous enum `case images(normal: UIImage, highlighted: UIImage)`. - public static func images(normal: UIImage?, highlighted: UIImage?) -> Self { - return Image(methodSignature: #function, image: normal, highlightedImage: highlighted) - } - - /// Returns `Icon` with the specified SF Symbol as the image for the normal state. - /// The image for the highlighted state is nil. - /// - /// - Parameters: - /// - name: The name of the system symbol image. - /// - configuration: The configuration to specify traits and other details that define the variant of image. - @available(iOS 13.0, tvOS 13.0, *) - public static func sfSymbol(_ name: String, withConfiguration configuration: UIImage.Configuration? = .none) -> Self { - // Make sure the image scales with the Dynamic Type settings. - let fallback = UIImage.SymbolConfiguration(textStyle: .body) - return Image( - methodSignature: "\(#function):\(name)", - image: UIImage(systemName: name, withConfiguration: configuration ?? fallback), - highlightedImage: nil - ) - } - } -} - -public extension Icon.Image { - - static func image(_ image: UIImage?) -> Self { - return .images(normal: image, highlighted: nil) - } - - static func image(named name: String, in bundle: Bundle? = .none, compatibleWith traitCollection: UITraitCollection? = .none) -> Self { - return .named(name, in: bundle, compatibleWith: traitCollection) - } - - static func image(_ image: UIImage?, highlighted: UIImage?) -> Self { - return .images(normal: image, highlighted: highlighted) - } -} diff --git a/Sources/Jenga/Model/Icon.swift b/Sources/Jenga/Model/Icon.swift deleted file mode 100644 index 6887195..0000000 --- a/Sources/Jenga/Model/Icon.swift +++ /dev/null @@ -1,20 +0,0 @@ -import UIKit -/// A enum that represents the image used in a row. -public enum Icon { - case image(Image) - case async(AsyncImage) - - public var image: Image? { - switch self { - case .image(let value): return value - default: return nil - } - } - - public var async: AsyncImage? { - switch self { - case .async(let value): return value - default: return nil - } - } -} diff --git a/Sources/Jenga/Row/Configurable.swift b/Sources/Jenga/Row/Configurable.swift deleted file mode 100644 index fe6b643..0000000 --- a/Sources/Jenga/Row/Configurable.swift +++ /dev/null @@ -1,56 +0,0 @@ -// -// Configurable.swift -// QuickTableViewController -// -// Created by Ben on 30/07/2017. -// Copyright © 2017 bcylin. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// - -import UIKit - -public protocol RowConfigurable { - - /// 配置 对应 cellForRow 设置内容 添加监听等 - /// - Parameter cell: cell - func configure(_ cell: UITableViewCell) - - /// 恢复 对应 didEndDisplaying 移除监听等操作 - /// - Parameter cell: cell - func recovery(_ cell: UITableViewCell) -} - -public protocol ConfigurableCell: UITableViewCell { - associatedtype CellData - - /// 配置 对应 cellForRow 设置内容 添加监听等 - /// - Parameter cell: cell - func configure(with _: CellData) - - /// 恢复 对应 didEndDisplaying 移除监听等操作 - /// - Parameter cell: cell - func recovery(_ cell: CellData) -} - -extension ConfigurableCell { - - public func recovery(_ cell: CellData) { - } -} diff --git a/Sources/Jenga/Row/Protocol/RowCompatible.swift b/Sources/Jenga/Row/Protocol/RowCompatible.swift deleted file mode 100644 index 91583f1..0000000 --- a/Sources/Jenga/Row/Protocol/RowCompatible.swift +++ /dev/null @@ -1,61 +0,0 @@ -// -// RowCompatible.swift -// QuickTableViewController -// -// Created by Ben on 10/12/2017. -// Copyright © 2017 bcylin. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// - -import Foundation -import UIKit - -/// This protocol defines the compatible interface of a `NavigationRow` regardless of its associated cell type. -public protocol NavigationRowCompatible: RowSystemable { - - var accessoryButtonAction: RowAction? { get } -} - -public protocol BadgeRowCompatible: RowSystemable { - - var badgeValue: Binding { get set } - var badgeColor: Binding? { get set } -} - -/// This protocol defines the compatible interface of a `TapActionRow` regardless of its associated cell type. -public protocol TapActionRowCompatible: RowSystemable { - - var textAlignment: NSTextAlignment { get set} -} - - -/// This protocol defines the compatible interface of an `OptionRow` regardless of its associated cell type. -public protocol OptionRowCompatible: RowSystemable { - /// The state of selection. - var isSelected: Bool { get set } -} - -/// This protocol defines the compatible interface of a `SwitchRow` regardless of its associated cell type. -public protocol ToggleRowCompatible: RowSystemable { - /// The state of the switch. - var isOn: Binding { get set } - var onTap: ((Bool) -> Void)? { get } -} - diff --git a/Sources/Jenga/Row/Reusable.swift b/Sources/Jenga/Row/Reusable.swift deleted file mode 100644 index 6c77f27..0000000 --- a/Sources/Jenga/Row/Reusable.swift +++ /dev/null @@ -1,77 +0,0 @@ -// -// Reusable.swift -// QuickTableViewController -// -// Created by Ben on 21/08/2017. -// Copyright © 2017 bcylin. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// - -import UIKit - -extension UITableViewCell: Reusable {} - - -internal protocol Reusable { - static var reuseIdentifier: String { get } -} - - -internal extension Reusable { - - static var reuseIdentifier: String { - let type = String(describing: self) - return type.matches(of: String.typeDescriptionPattern).last ?? type - } - -} - - -internal extension String { - - static var typeDescriptionPattern: String { - // For the types in the format of "(CustomCell in _B5334F301B8CC6AA00C64A6D)" - return "^\\(([\\w\\d]+)\\sin\\s_[0-9A-F]+\\)$" - } - - func matches(of pattern: String) -> [String] { - let regex = try? NSRegularExpression(pattern: pattern, options: .caseInsensitive) - #if swift(>=3.2) - let fullText = NSRange(location: 0, length: count) - #else - let fullText = NSRange(location: 0, length: characters.count) - #endif - - guard let matches = regex?.matches(in: self, options: [], range: fullText) else { - return [] - } - - return matches.reduce([]) { accumulator, match in - accumulator + (0..=4) - return (self as NSString).substring(with: match.range(at: $0)) - #else - return (self as NSString).substring(with: match.rangeAt($0)) - #endif - } - } - } - -} diff --git a/Sources/Jenga/Row/System/ToggleRow.swift b/Sources/Jenga/Row/System/ToggleRow.swift deleted file mode 100644 index acb7e5a..0000000 --- a/Sources/Jenga/Row/System/ToggleRow.swift +++ /dev/null @@ -1,96 +0,0 @@ -// -// ToggleRow.swift -// QuickTableViewController -// -// Created by Ben on 01/09/2015. -// Copyright (c) 2015 bcylin. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// - -import UIKit - -/// A class that represents a row with a switch. -open class ToggleRow: BasicRow, ToggleRowCompatible, Equatable { - - // MARK: - Initializer - - /// Initializes a `ToggleRow` with a title, a switch state and an action closure. - /// The detail text, icon and the customization closure are optional. - public init(_ text: Binding, isOn: Binding) { - self.isOn = isOn - super.init(text) - } - - public init(_ text: String, isOn: Binding) { - self.isOn = isOn - super.init(text) - } - - // MARK: - SwitchRowCompatible - - /// The state of the switch. - public var isOn: Binding - - public var onTap: ((Bool) -> Void)? - - open override func configure(_ cell: UITableViewCell) { - super.configure(cell) - (cell as? Cell)?.configure(with: (isOn, onTap)) - } - - /// The `SwitchRow` should not be selectable. - public override var isSelectable: Bool { - get { false } - set {} - } - - // MARK: - Equatable - - /// Returns true iff `lhs` and `rhs` have equal titles, detail texts, switch values, and icons. - public static func == (lhs: ToggleRow, rhs: ToggleRow) -> Bool { - return lhs.text == rhs.text && - lhs.detailText == rhs.detailText && - lhs.isOn == rhs.isOn -// lhs.icon == rhs.icon - } -} - -public extension ToggleRow { - - func isOn(_ value: Bool) -> Self { - self.isOn = .constant(value) - return self - } - - /// Toggle click - func onTap(_ value: @escaping (Bool) -> Void) -> Self { - self.onTap = value - return self - } - - func onTap(on target: S, _ value: @escaping (S, Bool) -> Void) -> Self where S: AnyObject { - self.onTap = { [weak target] isOn in - guard let target = target else { return } - value(target, isOn) - } - return self - } - -} diff --git a/Sources/Jenga/Section/TableSection.swift b/Sources/Jenga/Section/TableSection.swift deleted file mode 100644 index dc5d495..0000000 --- a/Sources/Jenga/Section/TableSection.swift +++ /dev/null @@ -1,94 +0,0 @@ -import Foundation -import UIKit - -open class TableSection: BacicSection { - - public var didUpdate: ((TableSection) -> Void)? - - public init(@RowBuilder builder: RowBuilder.ContentBlock) { - super.init(builder()) - } - - public init(_ array: [T], content: @escaping (Binding) -> Row) { - super.init(array.map { content(.constant($0)) }) - } - - /// forEach - public init(binding: Binding<[T]>, content: @escaping (Binding) -> Row) { - super.init() - binding.append(observer: self) { [weak self] changed in - guard let self = self else { return } - self.rows = changed.new.map { content(.constant($0)) } - self.didUpdate?(self) - } - } - - /// forEach on target - public init(binding: Binding<[T]>, on target: S, builder: @escaping (S, Binding) -> Row) { - super.init() - binding.append(observer: self) { [weak self, weak target] changed in - guard let self = self else { return } - guard let target = target else { return } - self.rows = changed.new.map { builder(target, .constant($0)) } - self.didUpdate?(self) - } - } - - /// map T - public init(binding: Binding, @RowBuilder builder: @escaping (T) -> [Row]) { - super.init() - binding.append(observer: self) { [weak self] changed in - guard let self = self else { return } - self.rows = builder(changed.new) - self.didUpdate?(self) - } - } - - /// map T on target - public init(binding: Binding, on target: S, @RowBuilder builder: @escaping (S, T) -> [Row]) where S: AnyObject { - super.init() - binding.append(observer: self) { [weak self, weak target] changed in - guard let self = self else { return } - guard let target = target else { return } - self.rows = builder(target, changed.new) - self.didUpdate?(self) - } - } - - /// map binding on target - public init(binding: Binding, on target: S, @RowBuilder builder: @escaping (S, Binding) -> [Row]) where S: AnyObject { - super.init() - binding.append(observer: self) { [weak self, weak target] changed in - guard let self = self else { return } - guard let target = target else { return } - self.rows = builder(target, .constant(changed.new)) - self.didUpdate?(self) - } - } - - /// map binding EnumeratedSequence - public init(binding: Binding>, content: @escaping (Binding.Iterator.Element>) -> Row) { - super.init() - binding.append(observer: self) { [weak self] changed in - guard let self = self else { return } - self.rows = changed.new.map { content(.constant($0)) } - self.didUpdate?(self) - } - } - - /// map binding EnumeratedSequence on target - public init(binding: Binding>, on target: S, content: @escaping (S, Binding.Iterator.Element>) -> Row) where S: AnyObject { - super.init() - binding.append(observer: self) { [weak self, weak target] changed in - guard let self = self else { return } - guard let target = target else { return } - self.rows = changed.new.map { content(target, .constant($0)) } - self.didUpdate?(self) - } - } -} - -public typealias RowBuilder = ArrayBuilder -public typealias SectionBuilder = ArrayBuilder -public typealias TableBuilder = ArrayBuilder - diff --git a/Sources/Jenga/UIBinding/BindingExtension.swift b/Sources/Jenga/UIBinding/BindingExtension.swift deleted file mode 100644 index 8985515..0000000 --- a/Sources/Jenga/UIBinding/BindingExtension.swift +++ /dev/null @@ -1,3 +0,0 @@ -import Foundation -import CoreGraphics - diff --git a/Sources/Jenga/Utils/TableCellRegisterer.swift b/Sources/Jenga/Utils/TableCellRegisterer.swift deleted file mode 100644 index a31ad01..0000000 --- a/Sources/Jenga/Utils/TableCellRegisterer.swift +++ /dev/null @@ -1,58 +0,0 @@ -// -// Copyright (c) 2015 Max Sokolov https://twitter.com/max_sokolov -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -// the Software, and to permit persons to whom the Software is furnished to do so, -// subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -import UIKit - -class TableCellRegisterer { - - private var registeredIds = Set() - private weak var tableView: UITableView? - - init(tableView: UITableView?) { - self.tableView = tableView - } - - func register(cellType: AnyClass, forCellReuseIdentifier reuseIdentifier: String) { - - if registeredIds.contains(reuseIdentifier) { - return - } - - // check if cell is already registered, probably cell has been registered by storyboard - if tableView?.dequeueReusableCell(withIdentifier: reuseIdentifier) != nil { - - registeredIds.insert(reuseIdentifier) - return - } - - let bundle = Bundle(for: cellType) - - // we hope that cell's xib file has name that equals to cell's class name - // in that case we could register nib - if let _ = bundle.path(forResource: reuseIdentifier, ofType: "nib") { - tableView?.register(UINib(nibName: reuseIdentifier, bundle: bundle), forCellReuseIdentifier: reuseIdentifier) - // otherwise, register cell class - } else { - tableView?.register(cellType, forCellReuseIdentifier: reuseIdentifier) - } - - registeredIds.insert(reuseIdentifier) - } -} diff --git a/Sources/Jenga/Utils/TableRowAction.swift b/Sources/Jenga/Utils/TableRowAction.swift deleted file mode 100644 index 15afc62..0000000 --- a/Sources/Jenga/Utils/TableRowAction.swift +++ /dev/null @@ -1,70 +0,0 @@ -// -// TableRowAction.swift -// Zunion -// -// Created by 方林威 on 2022/2/25. -// - -import UIKit - -open class TableRowActionOptions { - - public let item: CellType.CellData - public let cell: CellType? - public let indexPath: IndexPath - public let userInfo: [AnyHashable: Any]? - - init(item: CellType.CellData, cell: CellType?, path: IndexPath, userInfo: [AnyHashable: Any]?) { - - self.item = item - self.cell = cell - self.indexPath = path - self.userInfo = userInfo - } -} - -private enum TableRowActionHandler { - - case voidAction((TableRowActionOptions) -> Void) - case action((TableRowActionOptions) -> Any?) - - func invoke(withOptions options: TableRowActionOptions) -> Any? { - - switch self { - case .voidAction(let handler): - return handler(options) - case .action(let handler): - return handler(options) - } - } -} - -open class TableRowAction { - - open var id: String? - public let type: TableRowActionType - private let handler: TableRowActionHandler - - public init(_ type: TableRowActionType, handler: @escaping (_ options: TableRowActionOptions) -> Void) { - - self.type = type - self.handler = .voidAction(handler) - } - - public init(_ key: String, handler: @escaping (_ options: TableRowActionOptions) -> Void) { - - self.type = .custom(key) - self.handler = .voidAction(handler) - } - - public init(_ type: TableRowActionType, handler: @escaping (_ options: TableRowActionOptions) -> T) { - - self.type = type - self.handler = .action(handler) - } - - public func invokeActionOn(cell: UITableViewCell?, item: CellType.CellData, path: IndexPath, userInfo: [AnyHashable: Any]?) -> Any? { - - return handler.invoke(withOptions: TableRowActionOptions(item: item, cell: cell as? CellType, path: path, userInfo: userInfo)) - } -}