diff --git a/CHANGELOG.md b/CHANGELOG.md index f5683a58..32a6b3e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,4 +18,11 @@ ## 0.5.10 -- **Fix**: `Public` modifier for `SpinnerView` \ No newline at end of file +- **Fix**: `Public` modifier for `SpinnerView` + +## 0.5.11 + +- **[Breaking Change]**: rename initializer from `init(initialFrom:)` to `init(resetFrom:)` in `ResettableType` +- **Add**: `SeparatorCell` with `SeparatorCellViewModel` +- **Add**: `AnyBaseTableRow` for type-erasure +- **Add**: `EmptyCellRow` for empty cell with static height \ No newline at end of file diff --git a/LeadKit.podspec b/LeadKit.podspec index dba0d228..d282fe81 100644 --- a/LeadKit.podspec +++ b/LeadKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "LeadKit" - s.version = "0.5.10" + s.version = "0.5.11" s.summary = "iOS framework with a bunch of tools for rapid development" s.homepage = "https://github.com/TouchInstinct/LeadKit" s.license = "Apache License, Version 2.0" @@ -41,8 +41,11 @@ Pod::Spec.new do |s| ss.source_files = "Sources/**/*.swift" ss.watchos.exclude_files = [ "Sources/Classes/Pagination/PaginationTableViewWrapper.swift", - "Sources/Classes/Views/XibView.swift", - "Sources/Classes/Views/SpinnerView.swift", + "Sources/Classes/Views/AnyBaseTableRow/*", + "Sources/Classes/Views/SeparatorCell/*", + "Sources/Classes/Views/EmptyCell/*", + "Sources/Classes/Views/XibView/*", + "Sources/Classes/Views/SpinnerView/*", "Sources/Extensions/CABasicAnimation/*", "Sources/Extensions/CGFloat/CGFloat+Pixels.swift", "Sources/Extensions/NetworkService/NetworkService+ActivityIndicator.swift", @@ -50,7 +53,8 @@ Pod::Spec.new do |s| "Sources/Extensions/PaginationTableViewWrapperDelegate/PaginationTableViewWrapperDelegate+DefaultImplementation.swift", "Sources/Extensions/StoryboardProtocol/*", "Sources/Extensions/Support/UIScrollView+Support.swift", - "Sources/Extensions/TableDirector/TableDirector+Extensions.swift", + "Sources/Extensions/TableDirector/*", + "Sources/Extensions/Array/Array+AnyBaseTableRowExtensions.swift", "Sources/Extensions/UIActivityIndicatorView/*", "Sources/Extensions/UIAlertcontroller/*", "Sources/Extensions/UICollectionView/*", @@ -68,12 +72,16 @@ Pod::Spec.new do |s| "Sources/Structures/DrawingOperations/BorderDrawingOperation.swift", ] ss.tvos.exclude_files = [ + "Sources/Classes/Views/AnyBaseTableRow/*", + "Sources/Classes/Views/SeparatorCell/*", + "Sources/Classes/Views/EmptyCell/*", "Sources/Classes/Pagination/PaginationTableViewWrapper.swift", "Sources/Structures/Drawing/CALayerDrawingOperation.swift", "Sources/Extensions/NetworkService/NetworkService+ActivityIndicator.swift", "Sources/Extensions/PaginationTableViewWrapperDelegate/PaginationTableViewWrapperDelegate+DefaultImplementation.swift", "Sources/Extensions/Support/UIScrollView+Support.swift", - "Sources/Extensions/TableDirector/TableDirector+Extensions.swift", + "Sources/Extensions/TableDirector/*", + "Sources/Extensions/Array/Array+AnyBaseTableRowExtensions.swift" ] ss.dependency "CocoaLumberjack/Swift", '~> 3.1.0' @@ -92,10 +100,14 @@ Pod::Spec.new do |s| ss.source_files = "Sources/**/*.swift" ss.exclude_files = [ + "Sources/Classes/Views/AnyBaseTableRow/*", + "Sources/Classes/Views/SeparatorCell/*", + "Sources/Classes/Views/EmptyCell/*", "Sources/Classes/Pagination/PaginationTableViewWrapper.swift", "Sources/Extensions/NetworkService/NetworkService+ActivityIndicator.swift", "Sources/Extensions/PaginationTableViewWrapperDelegate/PaginationTableViewWrapperDelegate+DefaultImplementation.swift", - "Sources/Extensions/TableDirector/TableDirector+Extensions.swift", + "Sources/Extensions/TableDirector/*", + "Sources/Extensions/Array/Array+AnyBaseTableRowExtensions.swift" ] ss.dependency "CocoaLumberjack/Swift", '~> 3.1.0' diff --git a/LeadKit.xcodeproj/project.pbxproj b/LeadKit.xcodeproj/project.pbxproj index 32475e1f..ed16ed59 100644 --- a/LeadKit.xcodeproj/project.pbxproj +++ b/LeadKit.xcodeproj/project.pbxproj @@ -384,6 +384,30 @@ 67E6C2371EBB32F5007842A6 /* SingleLoadCursor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E6C2341EBB32F5007842A6 /* SingleLoadCursor.swift */; }; 67E6C2381EBB32F5007842A6 /* SingleLoadCursor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E6C2341EBB32F5007842A6 /* SingleLoadCursor.swift */; }; 82F8BB181F5DDED100C1061B /* Single+DeferredJust.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82F8BB171F5DDED100C1061B /* Single+DeferredJust.swift */; }; + A658E54D1F8CD7790093527A /* TableRow+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A658E54C1F8CD7790093527A /* TableRow+Extensions.swift */; }; + A658E54E1F8CD7790093527A /* TableRow+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A658E54C1F8CD7790093527A /* TableRow+Extensions.swift */; }; + A658E5501F8CD9350093527A /* Array+AnyBaseTableRowExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A658E54F1F8CD9350093527A /* Array+AnyBaseTableRowExtensions.swift */; }; + A658E5511F8CD9350093527A /* Array+AnyBaseTableRowExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A658E54F1F8CD9350093527A /* Array+AnyBaseTableRowExtensions.swift */; }; + A6C9A4FA1F8BBCF2009311CC /* EmptyCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6AF3B371F8B956F00CDB971 /* EmptyCell.swift */; }; + A6C9A4FB1F8BBCF2009311CC /* EmptyCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6AF3B361F8B956E00CDB971 /* EmptyCellViewModel.swift */; }; + A6C9A5041F8BC78F009311CC /* CellSeparatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6E0DDEE1F8A6C57002CA74E /* CellSeparatorType.swift */; }; + A6C9A5051F8BC78F009311CC /* SeparatorConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6E0DDF01F8A6C80002CA74E /* SeparatorConfiguration.swift */; }; + A6C9A50C1F8BC799009311CC /* EmptyCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6AF3B371F8B956F00CDB971 /* EmptyCell.swift */; }; + A6C9A50D1F8BC799009311CC /* EmptyCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6AF3B361F8B956E00CDB971 /* EmptyCellViewModel.swift */; }; + A6C9A50E1F8BC799009311CC /* EmptyCellRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = A66428A71F8A654600C6308D /* EmptyCellRow.swift */; }; + A6C9A50F1F8BC79D009311CC /* Comparable+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6D10EAA1F8A9278003E69DD /* Comparable+Extensions.swift */; }; + A6C9A5101F8BC79D009311CC /* Comparable+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6D10EAA1F8A9278003E69DD /* Comparable+Extensions.swift */; }; + A6C9A5111F8BC79D009311CC /* Comparable+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6D10EAA1F8A9278003E69DD /* Comparable+Extensions.swift */; }; + A6D10EAB1F8A9278003E69DD /* Comparable+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6D10EAA1F8A9278003E69DD /* Comparable+Extensions.swift */; }; + A6E0DDDE1F8A696F002CA74E /* EmptyCellRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = A66428A71F8A654600C6308D /* EmptyCellRow.swift */; }; + A6E0DDDF1F8A696F002CA74E /* SeparatorCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A66428A61F8A653600C6308D /* SeparatorCell.swift */; }; + A6E0DDE01F8A696F002CA74E /* SeparatorCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A66428A51F8A653500C6308D /* SeparatorCellViewModel.swift */; }; + A6E0DDE11F8A696F002CA74E /* AnyBaseTableRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = A66428A81F8A655600C6308D /* AnyBaseTableRow.swift */; }; + A6E0DDE31F8A696F002CA74E /* SeparatorCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A66428A61F8A653600C6308D /* SeparatorCell.swift */; }; + A6E0DDE41F8A696F002CA74E /* SeparatorCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A66428A51F8A653500C6308D /* SeparatorCellViewModel.swift */; }; + A6E0DDE51F8A696F002CA74E /* AnyBaseTableRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = A66428A81F8A655600C6308D /* AnyBaseTableRow.swift */; }; + A6E0DDEF1F8A6C57002CA74E /* CellSeparatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6E0DDEE1F8A6C57002CA74E /* CellSeparatorType.swift */; }; + A6E0DDF11F8A6C80002CA74E /* SeparatorConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6E0DDF01F8A6C80002CA74E /* SeparatorConfiguration.swift */; }; A6F32C081F6EBDAA00AC08EE /* String+LocalizedComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6F32C071F6EBDAA00AC08EE /* String+LocalizedComponent.swift */; }; A6F32C0A1F6EBE5B00AC08EE /* String+LocalizedComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6F32C071F6EBDAA00AC08EE /* String+LocalizedComponent.swift */; }; A6F32C0B1F6EBE5C00AC08EE /* String+LocalizedComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6F32C071F6EBDAA00AC08EE /* String+LocalizedComponent.swift */; }; @@ -576,6 +600,17 @@ 887F99C5326BD220C2811BD6 /* Pods_LeadKit_LeadKit_iOS_Extensions.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_LeadKit_LeadKit_iOS_Extensions.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 95E457F1241D136396FC2420 /* Pods_LeadKitTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_LeadKitTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 9966FB938D114F79F71AE037 /* Pods-LeadKit-LeadKit iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LeadKit-LeadKit iOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-LeadKit-LeadKit iOS/Pods-LeadKit-LeadKit iOS.release.xcconfig"; sourceTree = ""; }; + A658E54C1F8CD7790093527A /* TableRow+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TableRow+Extensions.swift"; sourceTree = ""; }; + A658E54F1F8CD9350093527A /* Array+AnyBaseTableRowExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+AnyBaseTableRowExtensions.swift"; sourceTree = ""; }; + A66428A51F8A653500C6308D /* SeparatorCellViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeparatorCellViewModel.swift; sourceTree = ""; }; + A66428A61F8A653600C6308D /* SeparatorCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeparatorCell.swift; sourceTree = ""; }; + A66428A71F8A654600C6308D /* EmptyCellRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyCellRow.swift; sourceTree = ""; }; + A66428A81F8A655600C6308D /* AnyBaseTableRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyBaseTableRow.swift; sourceTree = ""; }; + A6AF3B361F8B956E00CDB971 /* EmptyCellViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyCellViewModel.swift; sourceTree = ""; }; + A6AF3B371F8B956F00CDB971 /* EmptyCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyCell.swift; sourceTree = ""; }; + A6D10EAA1F8A9278003E69DD /* Comparable+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Comparable+Extensions.swift"; sourceTree = ""; }; + A6E0DDEE1F8A6C57002CA74E /* CellSeparatorType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CellSeparatorType.swift; sourceTree = ""; }; + A6E0DDF01F8A6C80002CA74E /* SeparatorConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeparatorConfiguration.swift; sourceTree = ""; }; A6F32C071F6EBDAA00AC08EE /* String+LocalizedComponent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+LocalizedComponent.swift"; sourceTree = ""; }; A6F32C0E1F6EBE8E00AC08EE /* StringExtensionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StringExtensionTests.swift; sourceTree = ""; }; A854A36EB179651E6D0CC9FB /* Pods_LeadKit_tvOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_LeadKit_tvOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -718,8 +753,11 @@ 671461D41EB3396E00EAB194 /* Views */ = { isa = PBXGroup; children = ( - 67051ADA1EBC7C36008EADC0 /* SpinnerView.swift */, - 671461D51EB3396E00EAB194 /* XibView.swift */, + A6AF3B381F8B957400CDB971 /* AnyBaseTableRow */, + A66428A41F8A651700C6308D /* SeparatorCell */, + A6C9A4F71F8BBC0F009311CC /* EmptyCell */, + A6C9A4F81F8BBC27009311CC /* SpinnerView */, + A6C9A4F91F8BBCCE009311CC /* XibView */, ); path = Views; sourceTree = ""; @@ -737,6 +775,7 @@ 671461DA1EB3396E00EAB194 /* Extensions */ = { isa = PBXGroup; children = ( + A6D10EA91F8A9269003E69DD /* Comparable */, 671461DB1EB3396E00EAB194 /* Alamofire */, EFBE57CE1EC35ED90040E00A /* Array */, 67A1FF921EBCA64A00D6C89F /* CABasicAnimation */, @@ -906,6 +945,7 @@ isa = PBXGroup; children = ( 671462011EB3396E00EAB194 /* TableDirector+Extensions.swift */, + A658E54C1F8CD7790093527A /* TableRow+Extensions.swift */, ); path = TableDirector; sourceTree = ""; @@ -1222,12 +1262,64 @@ path = Single; sourceTree = ""; }; + A66428A41F8A651700C6308D /* SeparatorCell */ = { + isa = PBXGroup; + children = ( + A66428A61F8A653600C6308D /* SeparatorCell.swift */, + A66428A51F8A653500C6308D /* SeparatorCellViewModel.swift */, + A6E0DDEE1F8A6C57002CA74E /* CellSeparatorType.swift */, + A6E0DDF01F8A6C80002CA74E /* SeparatorConfiguration.swift */, + ); + path = SeparatorCell; + sourceTree = ""; + }; + A6AF3B381F8B957400CDB971 /* AnyBaseTableRow */ = { + isa = PBXGroup; + children = ( + A66428A81F8A655600C6308D /* AnyBaseTableRow.swift */, + ); + path = AnyBaseTableRow; + sourceTree = ""; + }; + A6C9A4F71F8BBC0F009311CC /* EmptyCell */ = { + isa = PBXGroup; + children = ( + A6AF3B371F8B956F00CDB971 /* EmptyCell.swift */, + A6AF3B361F8B956E00CDB971 /* EmptyCellViewModel.swift */, + A66428A71F8A654600C6308D /* EmptyCellRow.swift */, + ); + path = EmptyCell; + sourceTree = ""; + }; + A6C9A4F81F8BBC27009311CC /* SpinnerView */ = { + isa = PBXGroup; + children = ( + 67051ADA1EBC7C36008EADC0 /* SpinnerView.swift */, + ); + path = SpinnerView; + sourceTree = ""; + }; + A6C9A4F91F8BBCCE009311CC /* XibView */ = { + isa = PBXGroup; + children = ( + 671461D51EB3396E00EAB194 /* XibView.swift */, + ); + path = XibView; + sourceTree = ""; + }; + A6D10EA91F8A9269003E69DD /* Comparable */ = { + isa = PBXGroup; + children = ( + A6D10EAA1F8A9278003E69DD /* Comparable+Extensions.swift */, + ); + path = Comparable; + sourceTree = ""; + }; A6F32C0D1F6EBE7700AC08EE /* Extensions */ = { isa = PBXGroup; children = ( A6F32C0E1F6EBE8E00AC08EE /* StringExtensionTests.swift */, ); - name = Extensions; path = Extensions; sourceTree = ""; }; @@ -1276,6 +1368,7 @@ isa = PBXGroup; children = ( EFBE57CF1EC35EF20040E00A /* Array+Extensions.swift */, + A658E54F1F8CD9350093527A /* Array+AnyBaseTableRowExtensions.swift */, ); path = Array; sourceTree = ""; @@ -2011,6 +2104,7 @@ 671463201EB3396E00EAB194 /* Any+Cast.swift in Sources */, EFBE57DB1EC361620040E00A /* UIView+Layout.swift in Sources */, 6714639E1EB33AEB00EAB194 /* NetworkService+ActivityIndicator.swift in Sources */, + A6E0DDEF1F8A6C57002CA74E /* CellSeparatorType.swift in Sources */, 6714634C1EB3396E00EAB194 /* ReuseIdentifierProtocol.swift in Sources */, 671462F01EB3396E00EAB194 /* UIImage+SupportExtensions.swift in Sources */, 6771DFDE1EE99F6F002DCDAE /* DateFormattingArguments.swift in Sources */, @@ -2028,6 +2122,7 @@ 671462D01EB3396E00EAB194 /* UIScrollView+Support.swift in Sources */, 671463901EB3396E00EAB194 /* TemplateDrawingOperation.swift in Sources */, 6714630C1EB3396E00EAB194 /* UIViewController+DefaultStoryboardIdentifier.swift in Sources */, + A658E54D1F8CD7790093527A /* TableRow+Extensions.swift in Sources */, 671462981EB3396E00EAB194 /* CGSize+Resize.swift in Sources */, 671462F81EB3396E00EAB194 /* UIView+DefaultReuseIdentifier.swift in Sources */, 67051ADB1EBC7C36008EADC0 /* SpinnerView.swift in Sources */, @@ -2036,12 +2131,15 @@ 82F8BB181F5DDED100C1061B /* Single+DeferredJust.swift in Sources */, 671463301EB3396E00EAB194 /* CursorType.swift in Sources */, 6714624C1EB3396E00EAB194 /* MapCursor.swift in Sources */, + A6C9A4FA1F8BBCF2009311CC /* EmptyCell.swift in Sources */, 671463241EB3396E00EAB194 /* Any+TypeName.swift in Sources */, 671463881EB3396E00EAB194 /* RoundDrawingOperation.swift in Sources */, + A6D10EAB1F8A9278003E69DD /* Comparable+Extensions.swift in Sources */, 671463801EB3396E00EAB194 /* PaddingDrawingOperation.swift in Sources */, 671462E01EB3396E00EAB194 /* UICollectionView+CellRegistration.swift in Sources */, 671462601EB3396E00EAB194 /* PaginationTableViewWrapper.swift in Sources */, 671463281EB3396E00EAB194 /* BaseViewModel.swift in Sources */, + A6E0DDDF1F8A696F002CA74E /* SeparatorCell.swift in Sources */, 671462AC1EB3396E00EAB194 /* Observable+DeferredJust.swift in Sources */, 671463001EB3396E00EAB194 /* UIView+LoadFromNib.swift in Sources */, 6714627C1EB3396E00EAB194 /* AlamofireManager+Extensions.swift in Sources */, @@ -2050,10 +2148,15 @@ 671462581EB3396E00EAB194 /* Log.swift in Sources */, 671462B41EB3396E00EAB194 /* PaginationTableViewWrapperDelegate+DefaultImplementation.swift in Sources */, 671462781EB3396E00EAB194 /* ResizeContentMode.swift in Sources */, + A6E0DDE11F8A696F002CA74E /* AnyBaseTableRow.swift in Sources */, + A6E0DDDE1F8A696F002CA74E /* EmptyCellRow.swift in Sources */, + A6C9A4FB1F8BBCF2009311CC /* EmptyCellViewModel.swift in Sources */, 671463041EB3396E00EAB194 /* UIView+LoadingIndicator.swift in Sources */, EFBE57D61EC3603E0040E00A /* UIAlertController+Extensions.swift in Sources */, 671463701EB3396E00EAB194 /* ApiRequestParameters.swift in Sources */, + A658E5501F8CD9350093527A /* Array+AnyBaseTableRowExtensions.swift in Sources */, 671462EC1EB3396E00EAB194 /* UIImage+Extensions.swift in Sources */, + A6E0DDF11F8A6C80002CA74E /* SeparatorConfiguration.swift in Sources */, 6714636C1EB3396E00EAB194 /* XibNameProtocol.swift in Sources */, 6771DFEA1EEA7CB8002DCDAE /* DateFormattingService+MappingTransform.swift in Sources */, 671462A41EB3396E00EAB194 /* IndexPath+ImmutableIndexPath.swift in Sources */, @@ -2075,6 +2178,7 @@ A6F32C081F6EBDAA00AC08EE /* String+LocalizedComponent.swift in Sources */, 671462881EB3396E00EAB194 /* CGFloat+Pixels.swift in Sources */, 671462941EB3396E00EAB194 /* CGSize+CGContextSize.swift in Sources */, + A6E0DDE01F8A696F002CA74E /* SeparatorCellViewModel.swift in Sources */, 671463641EB3396E00EAB194 /* ViewHeightProtocol.swift in Sources */, 671462481EB3396E00EAB194 /* FixedPageCursor.swift in Sources */, 671462C81EB3396E00EAB194 /* String+Localization.swift in Sources */, @@ -2107,14 +2211,25 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + A6C9A5051F8BC78F009311CC /* SeparatorConfiguration.swift in Sources */, + A6C9A50D1F8BC799009311CC /* EmptyCellViewModel.swift in Sources */, 671463CA1EB34B1E00EAB194 /* TestView.swift in Sources */, 671463C71EB34B1E00EAB194 /* PaginationViewModelTests.swift in Sources */, + A6C9A50E1F8BC799009311CC /* EmptyCellRow.swift in Sources */, + A6C9A50C1F8BC799009311CC /* EmptyCell.swift in Sources */, 671463B81EB34B1E00EAB194 /* StubCursor.swift in Sources */, + A658E54E1F8CD7790093527A /* TableRow+Extensions.swift in Sources */, + A6E0DDE41F8A696F002CA74E /* SeparatorCellViewModel.swift in Sources */, + A658E5511F8CD9350093527A /* Array+AnyBaseTableRowExtensions.swift in Sources */, 671463BB1EB34B1E00EAB194 /* CursorTests.swift in Sources */, A6F32C101F6EBE9600AC08EE /* StringExtensionTests.swift in Sources */, 671463C11EB34B1E00EAB194 /* MappableUserDefaultsTests.swift in Sources */, 671463BE1EB34B1E00EAB194 /* LoadFromNibTests.swift in Sources */, + A6E0DDE31F8A696F002CA74E /* SeparatorCell.swift in Sources */, + A6C9A50F1F8BC79D009311CC /* Comparable+Extensions.swift in Sources */, 671463C41EB34B1E00EAB194 /* Post.swift in Sources */, + A6E0DDE51F8A696F002CA74E /* AnyBaseTableRow.swift in Sources */, + A6C9A5041F8BC78F009311CC /* CellSeparatorType.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2382,6 +2497,7 @@ 6771DFD91EE99EBA002DCDAE /* DateFormattingService.swift in Sources */, 671462951EB3396E00EAB194 /* CGSize+CGContextSize.swift in Sources */, 671463651EB3396E00EAB194 /* ViewHeightProtocol.swift in Sources */, + A6C9A5101F8BC79D009311CC /* Comparable+Extensions.swift in Sources */, 671462491EB3396E00EAB194 /* FixedPageCursor.swift in Sources */, EFBE57E11EC362470040E00A /* AlertRepresentable.swift in Sources */, 671462C91EB3396E00EAB194 /* String+Localization.swift in Sources */, @@ -2418,6 +2534,7 @@ buildActionMask = 2147483647; files = ( 671463C21EB34B1E00EAB194 /* MappableUserDefaultsTests.swift in Sources */, + A6C9A5111F8BC79D009311CC /* Comparable+Extensions.swift in Sources */, 671463BF1EB34B1E00EAB194 /* LoadFromNibTests.swift in Sources */, 671463C51EB34B1E00EAB194 /* Post.swift in Sources */, 671463CB1EB34B1E00EAB194 /* TestView.swift in Sources */, diff --git a/Sources/Classes/Cursors/FixedPageCursor.swift b/Sources/Classes/Cursors/FixedPageCursor.swift index 22a6a1b8..8bc726f5 100644 --- a/Sources/Classes/Cursors/FixedPageCursor.swift +++ b/Sources/Classes/Cursors/FixedPageCursor.swift @@ -80,7 +80,7 @@ public class ResettableFixedPageCursor: FixedPageC super.init(cursor: cursor, pageSize: pageSize) } - public required init(initialFrom other: ResettableFixedPageCursor) { + public required init(resetFrom other: ResettableFixedPageCursor) { super.init(cursor: other.cursor.reset(), pageSize: other.pageSize) } diff --git a/Sources/Classes/Cursors/MapCursor.swift b/Sources/Classes/Cursors/MapCursor.swift index bc83026d..69647bc9 100644 --- a/Sources/Classes/Cursors/MapCursor.swift +++ b/Sources/Classes/Cursors/MapCursor.swift @@ -95,7 +95,7 @@ public class ResettableMapCursor: MapCursor: ResettableCursorType { self.loadingObservable = loadingObservable } - public required init(initialFrom other: SingleLoadCursor) { + public required init(resetFrom other: SingleLoadCursor) { self.loadingObservable = other.loadingObservable } diff --git a/Sources/Classes/Cursors/StaticCursor.swift b/Sources/Classes/Cursors/StaticCursor.swift index 144cba0c..fca71d1b 100644 --- a/Sources/Classes/Cursors/StaticCursor.swift +++ b/Sources/Classes/Cursors/StaticCursor.swift @@ -34,7 +34,7 @@ public class StaticCursor: ResettableCursorType { self.content = content } - public required init(initialFrom other: StaticCursor) { + public required init(resetFrom other: StaticCursor) { self.content = other.content } diff --git a/Sources/Classes/Views/AnyBaseTableRow/AnyBaseTableRow.swift b/Sources/Classes/Views/AnyBaseTableRow/AnyBaseTableRow.swift new file mode 100644 index 00000000..70810c57 --- /dev/null +++ b/Sources/Classes/Views/AnyBaseTableRow/AnyBaseTableRow.swift @@ -0,0 +1,65 @@ +// +// Copyright (c) 2017 Touch Instinct +// +// 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 TableKit + +/// Class used as general placeholder for type-erasured in TableRow +private class AnyBaseTableRowHolder { + let row: Row + + init(tableRow: Row) { + row = tableRow + } +} + +/// Class used to hold resolved TableRow +private final class AnyBaseTableRowBox: AnyBaseTableRowHolder + where T: SeparatorCell, T.T: SeparatorCellViewModel { + + init(tableRow: TableRow) { + super.init(tableRow: tableRow) + } + +} + +/// Class that used to configure separators when multiply cells presented in one section +/// Holds TableRow with any model inherited from BaseCellViewModel +public final class AnyBaseTableRow { + + private let anyTableRow: AnyBaseTableRowHolder + + /// Row `item`, that typed as BaseCellViewModel + public let viewModel: SeparatorCellViewModel + + /// TableRow that typed to generic Row + public var row: Row { + return anyTableRow.row + } + + /// Initialize AnyBaseTableRow with tableRow + /// - parameter tableRow: TableRow which `item` conforms to BaseCellViewModel + public init(tableRow: TableRow) where T: SeparatorCell, T.T: SeparatorCellViewModel { + anyTableRow = AnyBaseTableRowBox(tableRow: tableRow) + viewModel = tableRow.item + } + +} diff --git a/Sources/Classes/Views/EmptyCell/EmptyCell.swift b/Sources/Classes/Views/EmptyCell/EmptyCell.swift new file mode 100644 index 00000000..b140c792 --- /dev/null +++ b/Sources/Classes/Views/EmptyCell/EmptyCell.swift @@ -0,0 +1,38 @@ +// +// Copyright (c) 2017 Touch Instinct +// +// 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 +import TableKit + +/// Empty cell class. Do not use it directly. +/// - see: `EmptyCellRow` +public final class EmptyCell: SeparatorCell, ConfigurableCell { + + public func configure(with viewModel: EmptyCellViewModel) { + backgroundColor = .clear + contentView.backgroundColor = viewModel.color + selectionStyle = .none + + configureSeparator(with: viewModel) + } + +} diff --git a/Sources/Classes/Views/EmptyCell/EmptyCellRow.swift b/Sources/Classes/Views/EmptyCell/EmptyCellRow.swift new file mode 100644 index 00000000..edb3da7b --- /dev/null +++ b/Sources/Classes/Views/EmptyCell/EmptyCellRow.swift @@ -0,0 +1,58 @@ +// +// Copyright (c) 2017 Touch Instinct +// +// 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 TableKit + +/// Class that +/// - Simulates spacing with no-breaking constraints +/// - Can end editing on click +public final class EmptyCellRow: TableRow { + + /// Provide height with color to create row + /// - parameter height: Height of row + /// - parameter color: Color of row + /// - parameter endEditingOnClick: Will cell end editing for neighbour currently active UIControl subclasses + /// - returns: Fully configured EmptyCellRow + public convenience init(height: CGFloat, + color: UIColor = .clear, + endEditingOnClick: Bool = false) { + + self.init(item: EmptyCellViewModel(height: height, color: color)) + + if endEditingOnClick { + self.on(.click) { options in + options.cell?.window?.endEditing(true) + } + } + } + + /// Used for set custom height to each cell, not for each cell type + override public var defaultHeight: CGFloat? { + return item.height + } + + /// EmptyCellRow typed as AnyBaseTableRow + public var anyRow: AnyBaseTableRow { + return AnyBaseTableRow(tableRow: self) + } + +} diff --git a/Sources/Classes/Views/EmptyCell/EmptyCellViewModel.swift b/Sources/Classes/Views/EmptyCell/EmptyCellViewModel.swift new file mode 100644 index 00000000..1c7d7281 --- /dev/null +++ b/Sources/Classes/Views/EmptyCell/EmptyCellViewModel.swift @@ -0,0 +1,39 @@ +// +// Copyright (c) 2017 Touch Instinct +// +// 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 + +/// ViewModel for EmptyCell +public final class EmptyCellViewModel: SeparatorCellViewModel { + + let color: UIColor + let height: CGFloat + + /// Returns configured ViewModel for cell + /// - parameter height: Height of cell + /// - parameter color: Fill color of cell + /// - returns: Configured ViewModel + public init(height: CGFloat, color: UIColor = .clear) { + self.color = color + self.height = height + } +} diff --git a/Sources/Classes/Views/SeparatorCell/CellSeparatorType.swift b/Sources/Classes/Views/SeparatorCell/CellSeparatorType.swift new file mode 100644 index 00000000..4b9cf952 --- /dev/null +++ b/Sources/Classes/Views/SeparatorCell/CellSeparatorType.swift @@ -0,0 +1,63 @@ +// +// Copyright (c) 2017 Touch Instinct +// +// 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 + +/// Cell self-descriptive separator type +public enum CellSeparatorType { + + /// All separators for cell hidden + case none + + /// Show only top separator + case top(SeparatorConfiguration) + + /// Show only bottom separator + case bottom(SeparatorConfiguration) + + /// First configuration for top, second for bottom + case full(SeparatorConfiguration, SeparatorConfiguration) +} + +public extension CellSeparatorType { + + /// Determine if bottom separator is hidden + var bottomIsHidden: Bool { + switch self { + case .top, .none: + return true + case .bottom, .full: + return false + } + } + + /// Determine if top separator is hidden + var topIsHidden: Bool { + switch self { + case .bottom, .none: + return true + case .top, .full: + return false + } + } + +} diff --git a/Sources/Classes/Views/SeparatorCell/SeparatorCell.swift b/Sources/Classes/Views/SeparatorCell/SeparatorCell.swift new file mode 100644 index 00000000..918713e1 --- /dev/null +++ b/Sources/Classes/Views/SeparatorCell/SeparatorCell.swift @@ -0,0 +1,168 @@ +// +// Copyright (c) 2017 Touch Instinct +// +// 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 +import TableKit + +private enum Constants { + static let defaultSeparatorHeight = CGFloat(pixels: 1) +} + +/// Base cell that provides separator support +/// Take note that: +/// - in `configure(with:)` you must call `configureSeparator(with:)` +/// - separators are simple views, that located on `contentView`. +/// - if you hide that with another view that fully hide you can use that method `moveSeparators(to:)` +open class SeparatorCell: UITableViewCell { + + // MARK: - Public + + /// Configure separator with viewModel + /// - parameter viewModel: ViewModel of cell, that inherits from BaseCellViewModel + public func configureSeparator(with viewModel: SeparatorCellViewModel) { + configureInterface(with: viewModel) + } + + /// Move separator upward in hierarchy + public func bringSeparatorsToFront() { + contentView.bringSubview(toFront: topView) + contentView.bringSubview(toFront: bottomView) + } + + /// Move separator backward in hierarchy + public func sendSeparatorsToBack() { + contentView.sendSubview(toBack: topView) + contentView.sendSubview(toBack: bottomView) + } + + // MARK: - Private + + private var topView: UIView! + private var bottomView: UIView! + + // top separator constraints + private var topViewLeftConstraint: NSLayoutConstraint! + private var topViewRightConstraint: NSLayoutConstraint! + private var topViewTopConstraint: NSLayoutConstraint! + private var topViewHeightConstraint: NSLayoutConstraint! + + // bottom separator constraints + private var bottomViewLeftConstraint: NSLayoutConstraint! + private var bottomViewRightConstraint: NSLayoutConstraint! + private var bottomViewBottomConstraint: NSLayoutConstraint! + private var bottomViewHeightConstraint: NSLayoutConstraint! + + private var topSeparatorInsets = UIEdgeInsets.zero + private var bottomSeparatorInsets = UIEdgeInsets.zero + + private var topSeparatorHeight = Constants.defaultSeparatorHeight + private var bottomSeparatorHeight = Constants.defaultSeparatorHeight + + // MARK: - Initialization + + public required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + + configureLineViews() + } + + public override init(style: UITableViewCellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + + configureLineViews() + } + + override open func updateConstraints() { + topViewTopConstraint.constant = topSeparatorInsets.top + topViewLeftConstraint.constant = topSeparatorInsets.left + topViewRightConstraint.constant = topSeparatorInsets.right + topViewHeightConstraint.constant = topSeparatorHeight + + bottomViewLeftConstraint.constant = bottomSeparatorInsets.left + bottomViewRightConstraint.constant = bottomSeparatorInsets.right + bottomViewBottomConstraint.constant = bottomSeparatorInsets.bottom + bottomViewHeightConstraint.constant = bottomSeparatorHeight + + super.updateConstraints() + } + + // MARK: - Private + + private func configureInterface(with viewModel: SeparatorCellViewModel) { + topView.isHidden = viewModel.separatorType.topIsHidden + bottomView.isHidden = viewModel.separatorType.bottomIsHidden + + topView.backgroundColor = viewModel.topSeparatorConfiguration?.color + topSeparatorHeight = viewModel.topSeparatorConfiguration?.height ?? Constants.defaultSeparatorHeight + topSeparatorInsets = viewModel.topSeparatorConfiguration?.insets ?? .zero + + bottomView.backgroundColor = viewModel.bottomSeparatorConfiguration?.color + bottomSeparatorHeight = viewModel.bottomSeparatorConfiguration?.height ?? Constants.defaultSeparatorHeight + bottomSeparatorInsets = viewModel.bottomSeparatorConfiguration?.insets ?? .zero + } + + private func configureLineViews() { + topView = createSeparatorView() + bottomView = createSeparatorView() + + createConstraints() + } + + private func createSeparatorView() -> UIView { + let view = UIView() + view.isHidden = true + view.backgroundColor = .black + view.translatesAutoresizingMaskIntoConstraints = false + contentView.addSubview(view) + return view + } + + private func createConstraints() { + // height + topViewHeightConstraint = topView.heightAnchor.constraint(equalToConstant: topSeparatorHeight) + topViewHeightConstraint.isActive = true + + bottomViewHeightConstraint = bottomView.heightAnchor.constraint(equalToConstant: bottomSeparatorHeight) + bottomViewHeightConstraint.isActive = true + + // top separator + topViewTopConstraint = topView.topAnchor.constraint(equalTo: contentView.topAnchor) + topViewTopConstraint.isActive = true + + topViewRightConstraint = contentView.rightAnchor.constraint(equalTo: topView.rightAnchor) + topViewRightConstraint.isActive = true + + topViewLeftConstraint = topView.leftAnchor.constraint(equalTo: contentView.leftAnchor) + topViewLeftConstraint.isActive = true + + // bottom separator + bottomViewRightConstraint = contentView.rightAnchor.constraint(equalTo: bottomView.rightAnchor) + bottomViewRightConstraint.isActive = true + + bottomViewLeftConstraint = bottomView.leftAnchor.constraint(equalTo: contentView.leftAnchor) + bottomViewLeftConstraint.isActive = true + + bottomViewBottomConstraint = bottomView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor) + bottomViewBottomConstraint.isActive = true + } + +} diff --git a/Sources/Classes/Views/SeparatorCell/SeparatorCellViewModel.swift b/Sources/Classes/Views/SeparatorCell/SeparatorCellViewModel.swift new file mode 100644 index 00000000..1fac48ec --- /dev/null +++ b/Sources/Classes/Views/SeparatorCell/SeparatorCellViewModel.swift @@ -0,0 +1,68 @@ +// +// Copyright (c) 2017 Touch Instinct +// +// 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 + +/// By default this class does not provide any separators +open class SeparatorCellViewModel { + + internal var separatorType = CellSeparatorType.none + + /// Configuration for topSeparator + /// - Important: Bottom dimension is ignored + internal var topSeparatorConfiguration: SeparatorConfiguration? + + /// Configuration for topSeparator + /// - Important: Top dimension is ignored + internal var bottomSeparatorConfiguration: SeparatorConfiguration? + + public init() {} + +} + +public extension SeparatorCellViewModel { + + func with(separatorType: CellSeparatorType) -> Self { + set(separatorType: separatorType) + return self + } + + func set(separatorType: CellSeparatorType) { + self.separatorType = separatorType + + switch separatorType { + case .top(let configuration): + topSeparatorConfiguration = configuration + bottomSeparatorConfiguration = nil + case .bottom(let configuration): + topSeparatorConfiguration = nil + bottomSeparatorConfiguration = configuration + case .full(let top, let bottom): + topSeparatorConfiguration = top + bottomSeparatorConfiguration = bottom + case .none: + topSeparatorConfiguration = nil + bottomSeparatorConfiguration = nil + } + } + +} diff --git a/Sources/Classes/Views/SeparatorCell/SeparatorConfiguration.swift b/Sources/Classes/Views/SeparatorCell/SeparatorConfiguration.swift new file mode 100644 index 00000000..a6ebee80 --- /dev/null +++ b/Sources/Classes/Views/SeparatorCell/SeparatorConfiguration.swift @@ -0,0 +1,42 @@ +// +// Copyright (c) 2017 Touch Instinct +// +// 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 + +/// Separator configuration. Supports positioning, color and height per each separator +public struct SeparatorConfiguration { + let color: UIColor + let insets: UIEdgeInsets? + let height: CGFloat + + /// Initialize configuration with parameters + /// - parameter color: Color must be provided + /// - parameter insets: Insets for separator. Default is no insets + /// - parameter height: Height for separator. Default is 1 pixel + /// - returns: Ready to use separator configuration + public init(color: UIColor, insets: UIEdgeInsets? = .zero, height: CGFloat = CGFloat(pixels: 1)) { + self.color = color + self.insets = insets + self.height = height + } + +} diff --git a/Sources/Classes/Views/SpinnerView.swift b/Sources/Classes/Views/SpinnerView/SpinnerView.swift similarity index 100% rename from Sources/Classes/Views/SpinnerView.swift rename to Sources/Classes/Views/SpinnerView/SpinnerView.swift diff --git a/Sources/Classes/Views/XibView.swift b/Sources/Classes/Views/XibView/XibView.swift similarity index 100% rename from Sources/Classes/Views/XibView.swift rename to Sources/Classes/Views/XibView/XibView.swift diff --git a/Sources/Extensions/Array/Array+AnyBaseTableRowExtensions.swift b/Sources/Extensions/Array/Array+AnyBaseTableRowExtensions.swift new file mode 100644 index 00000000..2a66cc04 --- /dev/null +++ b/Sources/Extensions/Array/Array+AnyBaseTableRowExtensions.swift @@ -0,0 +1,32 @@ +// +// Copyright (c) 2017 Touch Instinct +// +// 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 TableKit + +public extension Array where Element == AnyBaseTableRow { + + /// Create rows from anyBaseTableRow array + var rows: [Row] { + return map { $0.row } + } + +} diff --git a/Sources/Extensions/Comparable/Comparable+Extensions.swift b/Sources/Extensions/Comparable/Comparable+Extensions.swift new file mode 100644 index 00000000..4c49811e --- /dev/null +++ b/Sources/Extensions/Comparable/Comparable+Extensions.swift @@ -0,0 +1,34 @@ +// +// Copyright (c) 2017 Touch Instinct +// +// 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 + +public extension Comparable { + + /// Use this function to restrict comparable with lower and upper values + /// - parameter bounds: Lower and uppper bounds tuple + /// - returns: Current value if it fits range, otherwise lower if value is too small or upper if value is too big + func `in`(bounds: (lower: Self, upper: Self)) -> Self { + return min(max(bounds.lower, self), bounds.upper) + } + +} diff --git a/Sources/Extensions/TableDirector/TableDirector+Extensions.swift b/Sources/Extensions/TableDirector/TableDirector+Extensions.swift index bc00d2aa..2010e557 100644 --- a/Sources/Extensions/TableDirector/TableDirector+Extensions.swift +++ b/Sources/Extensions/TableDirector/TableDirector+Extensions.swift @@ -112,3 +112,30 @@ public extension TableDirector { } } + +// MARK: - Separator Configuration + +public extension TableDirector { + + /// Configure separators for bunch of rows in array + /// - parameter rows: Rows for configuration + /// - parameter extreme: Configuration that will be used for extreme values, for first or last row + /// - parameter middle: Configuration for intermediate rows + func configure(rows: [AnyBaseTableRow], + extreme extremeSeparatorConfiguration: SeparatorConfiguration, + middle middleSeparatorConfiguration: SeparatorConfiguration) { + + if rows.isEmpty { + return + } + + switch rows.count { + case 1: + rows.first?.viewModel.set(separatorType: .full(extremeSeparatorConfiguration, extremeSeparatorConfiguration)) + default: + rows.forEach { $0.viewModel.set(separatorType: .full(middleSeparatorConfiguration, middleSeparatorConfiguration))} + rows.first?.viewModel.set(separatorType: .top(extremeSeparatorConfiguration)) + rows.last?.viewModel.set(separatorType: .bottom(extremeSeparatorConfiguration)) + } + } +} diff --git a/Sources/Extensions/TableDirector/TableRow+Extensions.swift b/Sources/Extensions/TableDirector/TableRow+Extensions.swift new file mode 100644 index 00000000..3ee4f421 --- /dev/null +++ b/Sources/Extensions/TableDirector/TableRow+Extensions.swift @@ -0,0 +1,36 @@ +// +// Copyright (c) 2017 Touch Instinct +// +// 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 TableKit + +public extension TableRow where CellType.T: SeparatorCellViewModel { + + func with(separatorType: CellSeparatorType) -> Self { + item.set(separatorType: separatorType) + return self + } + + func set(separatorType: CellSeparatorType) { + item.set(separatorType: separatorType) + } + +} diff --git a/Sources/Protocols/ResettableType.swift b/Sources/Protocols/ResettableType.swift index c100df98..223ae00a 100644 --- a/Sources/Protocols/ResettableType.swift +++ b/Sources/Protocols/ResettableType.swift @@ -28,7 +28,7 @@ public protocol ResettableType { /// Initializer with other instance parameter. /// /// - Parameter other: Other instance of specific type. - init(initialFrom other: Self) + init(resetFrom other: Self) } @@ -38,7 +38,7 @@ public extension ResettableType { /// /// - Returns: resseted instance of self func reset() -> Self { - return Self(initialFrom: self) + return Self(resetFrom: self) } } diff --git a/Tests/Cursors/StubCursor.swift b/Tests/Cursors/StubCursor.swift index 28210ff1..90b30cb8 100644 --- a/Tests/Cursors/StubCursor.swift +++ b/Tests/Cursors/StubCursor.swift @@ -50,7 +50,7 @@ class StubCursor: ResettableCursorType { self.requestDelay = requestDelay } - required init(initialFrom other: StubCursor) { + required init(resetFrom other: StubCursor) { self.maxItemsCount = other.maxItemsCount self.requestDelay = other.requestDelay }