From 55977e39f5f623534b97160cff56a6638bc5df05 Mon Sep 17 00:00:00 2001 From: Ivan Smolin Date: Wed, 21 Oct 2020 22:06:54 +0300 Subject: [PATCH 01/12] add TableKitUtils with BaseSeparatorCell --- .gitignore | 77 ++++++---- Package.resolved | 16 +++ Package.swift | 5 + .../Extensions/Array+SeparatorRowBox.swift | 66 +++++++++ .../Extensions/TableRow+Separators.swift | 53 +++++++ .../Sources/Separators/SeparatorRowBox.swift | 41 ++++++ .../Separators/BaseSeparatorCell.swift | 133 ++++++++++++++++++ .../Separators/SeparatorConfigurable.swift | 27 ++++ .../Separators/SeparatorConfiguration.swift | 36 +++++ .../Separators/ViewSeparatorType.swift | 71 ++++++++++ .../Views/Cells/BaseInitializableCell.swift | 60 ++++++++ 11 files changed, 559 insertions(+), 26 deletions(-) create mode 100644 Package.resolved create mode 100644 TITableKitUtils/Sources/Extensions/Array+SeparatorRowBox.swift create mode 100644 TITableKitUtils/Sources/Extensions/TableRow+Separators.swift create mode 100644 TITableKitUtils/Sources/Separators/SeparatorRowBox.swift create mode 100644 TIUIElements/Sources/Separators/BaseSeparatorCell.swift create mode 100644 TIUIElements/Sources/Separators/SeparatorConfigurable.swift create mode 100644 TIUIElements/Sources/Separators/SeparatorConfiguration.swift create mode 100644 TIUIElements/Sources/Separators/ViewSeparatorType.swift create mode 100644 TIUIElements/Sources/Views/Cells/BaseInitializableCell.swift diff --git a/.gitignore b/.gitignore index 9004ef49..835c021b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,16 +1,18 @@ -# ================ -# Swift.gitignore -# ================ - # Xcode # # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore -## Build generated -build/ -DerivedData +## User settings +xcuserdata/ -## Various settings +## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) +*.xcscmblueprint +*.xccheckout + +## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) +build/ +DerivedData/ +*.moved-aside *.pbxuser !default.pbxuser *.mode1v3 @@ -19,17 +21,14 @@ DerivedData !default.mode2v3 *.perspectivev3 !default.perspectivev3 -xcuserdata - -## Other -*.xccheckout -*.moved-aside -*.xcuserstate -*.xcscmblueprint ## Obj-C/Swift specific *.hmap + +## App packaging *.ipa +*.dSYM.zip +*.dSYM ## Playgrounds timeline.xctimeline @@ -39,6 +38,14 @@ playground.xcworkspace # # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. # Packages/ +# Package.pins +# Package.resolved +# *.xcodeproj +# +# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata +# hence it is not needed unless you have added a package configuration file to your project +.swiftpm + .build/ # CocoaPods @@ -48,33 +55,51 @@ playground.xcworkspace # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control # Pods/ +# +# Add this line if you want to avoid checking in source code from the Xcode workspace +# *.xcworkspace # Carthage # # Add this line if you want to avoid checking in source code from Carthage dependencies. Carthage/Checkouts -Carthage/Build +Carthage/Build/ + +# Accio dependency management +Dependencies/ +.accio/ # fastlane # -# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the -# screenshots whenever they are needed. +# It is recommended to not store the screenshots in the git repo. +# Instead, use fastlane to re-generate the screenshots whenever they are needed. # For more information about the recommended setup visit: -# https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md +# https://docs.fastlane.tools/best-practices/source-control/#source-control fastlane/report.xml -fastlane/screenshots +fastlane/Preview.html +fastlane/screenshots/**/*.png +fastlane/test_output +# Code Injection +# +# After new code Injection tools there's a generated folder /iOSInjectionProject +# https://github.com/johnno1962/injectionforxcode -# AppCode -# https://intellij-support.jetbrains.com/hc/en-us/articles/206544839-How-to-manage-projects-under-Version-Control-Systems +iOSInjectionProject/ -.idea/workspace.xml -.idea/tasks.xml +# homebrew-bundle +Brewfile.lock.json -cpd-output.xml +# Node.js +# Dependency directories +node_modules/ # Touch Instinct custom Downloads/ - +fastlane/README.md +Templates/ +cpd-output.xml +*.swp +*IDEWorkspaceChecks.plist diff --git a/Package.resolved b/Package.resolved new file mode 100644 index 00000000..7c8c1236 --- /dev/null +++ b/Package.resolved @@ -0,0 +1,16 @@ +{ + "object": { + "pins": [ + { + "package": "TableKit", + "repositoryURL": "https://github.com/maxsokolov/TableKit.git", + "state": { + "branch": null, + "revision": "8bf4840d9d0475a92352f02f368f88b74eced447", + "version": "2.11.0" + } + } + ] + }, + "version": 1 +} diff --git a/Package.swift b/Package.swift index 698d8992..7e976793 100644 --- a/Package.swift +++ b/Package.swift @@ -12,14 +12,19 @@ let package = Package( .library(name: "TISwiftUtils", targets: ["TISwiftUtils"]), .library(name: "TIFoundationUtils", targets: ["TIFoundationUtils"]), .library(name: "TIUIElements", targets: ["TIUIElements"]), + .library(name: "TITableKitUtils", targets: ["TITableKitUtils"]), .library(name: "OTPSwiftView", targets: ["OTPSwiftView"]) ], + dependencies: [ + .package(url: "https://github.com/maxsokolov/TableKit.git", from: "2.11.0") + ], targets: [ .target(name: "TITransitions", path: "TITransitions/Sources"), .target(name: "TIUIKitCore", path: "TIUIKitCore/Sources"), .target(name: "TISwiftUtils", path: "TISwiftUtils/Sources"), .target(name: "TIFoundationUtils", dependencies: ["TISwiftUtils"], path: "TIFoundationUtils/Sources"), .target(name: "TIUIElements", dependencies: ["TIUIKitCore"], path: "TIUIElements/Sources"), + .target(name: "TITableKitUtils", dependencies: ["TIUIElements", "TableKit"], path: "TITableKitUtils/Sources"), .target(name: "OTPSwiftView", dependencies: ["TIUIKitCore", "TISwiftUtils"], path: "OTPSwiftView/Sources") ] ) diff --git a/TITableKitUtils/Sources/Extensions/Array+SeparatorRowBox.swift b/TITableKitUtils/Sources/Extensions/Array+SeparatorRowBox.swift new file mode 100644 index 00000000..fa212ca2 --- /dev/null +++ b/TITableKitUtils/Sources/Extensions/Array+SeparatorRowBox.swift @@ -0,0 +1,66 @@ +// +// Copyright (c) 2020 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 +import TIUIElements + +public extension Array where Element == SeparatorRowBox { + + /// Create rows from SeparatorRowBox array + var rows: [Row] { + return map { $0.row } + } + + /// Configure separators from SeparatorRowBox array + /// - parameter extreme: Configuration that will be used for extreme values, for first or last row + /// - parameter middle: Configuration for intermediate rows + func configureSeparators(extreme extremeSeparatorConfiguration: SeparatorConfiguration, + middle middleSeparatorConfiguration: SeparatorConfiguration) { + + configureSeparators(first: extremeSeparatorConfiguration, + middle: middleSeparatorConfiguration, + last: extremeSeparatorConfiguration) + } + + /// Configure separators from SeparatorRowBox array + /// - parameter first: Configuration of the top separator of the first row + /// - parameter middle: Configuration of the separators between the rows + /// - parameter last: Configuration of the bottom separator of the last row + func configureSeparators(first firstSeparatorConfiguration: SeparatorConfiguration, + middle middleSeparatorConfiguration: SeparatorConfiguration, + last lastSeparatorConfiguration: SeparatorConfiguration) { + + if isEmpty { + return + } + + switch count { + case 1: + first?.set(separatorType: .full(firstSeparatorConfiguration, lastSeparatorConfiguration)) + + default: + dropFirst().dropLast().forEach { $0.set(separatorType: .bottom(middleSeparatorConfiguration)) } + first?.set(separatorType: .full(firstSeparatorConfiguration, middleSeparatorConfiguration)) + last?.set(separatorType: .bottom(lastSeparatorConfiguration)) + } + } +} diff --git a/TITableKitUtils/Sources/Extensions/TableRow+Separators.swift b/TITableKitUtils/Sources/Extensions/TableRow+Separators.swift new file mode 100644 index 00000000..e4903641 --- /dev/null +++ b/TITableKitUtils/Sources/Extensions/TableRow+Separators.swift @@ -0,0 +1,53 @@ +// +// Copyright (c) 2020 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 +import TIUIElements + +private let configureSeparatorActionId = "TableRowConfigureSeparatorActionId" + +public extension TableRow where CellType: SeparatorConfigurable { + + func with(separatorType: ViewSeparatorType) -> Self { + set(separatorType: separatorType) + return self + } + + func set(separatorType: ViewSeparatorType) { + removeAction(forActionId: configureSeparatorActionId) + + let action = TableRowAction(.configure) { + $0.cell?.configure(with: separatorType) + } + + action.id = configureSeparatorActionId + on(action) + } +} + +public extension TableRow where CellType: SeparatorConfigurable { + + /// TableRow typed as SeparatorRowBox + var separatorRowBox: SeparatorRowBox { + return SeparatorRowBox(row: self) + } +} diff --git a/TITableKitUtils/Sources/Separators/SeparatorRowBox.swift b/TITableKitUtils/Sources/Separators/SeparatorRowBox.swift new file mode 100644 index 00000000..6109c4da --- /dev/null +++ b/TITableKitUtils/Sources/Separators/SeparatorRowBox.swift @@ -0,0 +1,41 @@ +// +// Copyright (c) 2020 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 +import TIUIElements +import TISwiftUtils + +/// Class that used to configure separators when multiply cells presented in one section +public final class SeparatorRowBox { + private let setSeparatorHandler: ParameterClosure + + public func set(separatorType: ViewSeparatorType) { + setSeparatorHandler(separatorType) + } + + public let row: Row + + public init(row: TableRow) where T: SeparatorConfigurable { + self.row = row + setSeparatorHandler = row.set + } +} diff --git a/TIUIElements/Sources/Separators/BaseSeparatorCell.swift b/TIUIElements/Sources/Separators/BaseSeparatorCell.swift new file mode 100644 index 00000000..e29b8067 --- /dev/null +++ b/TIUIElements/Sources/Separators/BaseSeparatorCell.swift @@ -0,0 +1,133 @@ +// +// Copyright (c) 2020 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 + +open class BaseSeparatorCell: BaseInitializableCell, SeparatorConfigurable { + private lazy var topSeparatorView = createTopSeparator() + private lazy var bottomSeparatorView = UIView() + + private var topViewLeftConstraint: NSLayoutConstraint? + private var topViewRightConstraint: NSLayoutConstraint? + private var topViewTopConstraint: NSLayoutConstraint? + private var topViewHeightConstraint: NSLayoutConstraint? + + private var bottomViewLeftConstraint: NSLayoutConstraint? + private var bottomViewRightConstraint: NSLayoutConstraint? + private var bottomViewBottomConstraint: NSLayoutConstraint? + private var bottomViewHeightConstraint: NSLayoutConstraint? + + open func createTopSeparator() -> UIView { + .init() + } + + open func createBottomSeparator() -> UIView { + .init() + } + + public func configure(with separatorType: ViewSeparatorType) { + topSeparatorView.isHidden = separatorType.topIsHidden + bottomSeparatorView.isHidden = separatorType.bottomIsHidden + + switch separatorType { + case .none: + break + + case let .bottom(configuration): + updateBottomSeparator(with: configuration) + + case let .top(configuration): + updateTopSeparator(with: configuration) + + case let .full(topConfiguration, bottomConfiguration): + updateTopSeparator(with: topConfiguration) + updateBottomSeparator(with: bottomConfiguration) + } + } + + open override func prepareForReuse() { + super.prepareForReuse() + configure(with: .none) + } + + // MARK: - InitializableView + + open override func addViews() { + super.addViews() + + contentView.addSubviews(topSeparatorView, bottomSeparatorView) + } + + open override func configureLayout() { + super.configureLayout() + + topViewTopConstraint = topSeparatorView.topAnchor.constraint(equalTo: contentView.topAnchor) + topViewRightConstraint = contentView.rightAnchor.constraint(equalTo: topSeparatorView.rightAnchor) + topViewLeftConstraint = topSeparatorView.leftAnchor.constraint(equalTo: contentView.leftAnchor) + topViewHeightConstraint = topSeparatorView.heightAnchor.constraint(equalToConstant: 1) + + bottomViewRightConstraint = contentView.rightAnchor.constraint(equalTo: bottomSeparatorView.rightAnchor) + bottomViewLeftConstraint = bottomSeparatorView.leftAnchor.constraint(equalTo: contentView.leftAnchor) + bottomViewBottomConstraint = bottomSeparatorView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor) + bottomViewHeightConstraint = bottomSeparatorView.heightAnchor.constraint(equalToConstant: 1) + + NSLayoutConstraint.activate([ + topViewTopConstraint, + topViewRightConstraint, + topViewLeftConstraint, + topViewHeightConstraint, + bottomViewRightConstraint, + bottomViewLeftConstraint, + bottomViewBottomConstraint, + bottomViewHeightConstraint + ].compactMap { $0 }) + } + + open override func configureAppearance() { + super.configureAppearance() + + [topSeparatorView, bottomSeparatorView].forEach { + $0.isHidden = true + $0.backgroundColor = .black + $0.translatesAutoresizingMaskIntoConstraints = false + } + } +} + +private extension BaseSeparatorCell { + func updateTopSeparator(with configuration: SeparatorConfiguration) { + topSeparatorView.backgroundColor = configuration.color + topViewHeightConstraint?.constant = configuration.height + topViewTopConstraint?.constant = configuration.insets.top + topViewLeftConstraint?.constant = configuration.insets.left + topViewRightConstraint?.constant = configuration.insets.right + + } + + func updateBottomSeparator(with configuration: SeparatorConfiguration) { + bottomSeparatorView.backgroundColor = configuration.color + bottomViewHeightConstraint?.constant = configuration.height + bottomViewBottomConstraint?.constant = configuration.insets.bottom + bottomViewLeftConstraint?.constant = configuration.insets.left + bottomViewRightConstraint?.constant = configuration.insets.right + } +} diff --git a/TIUIElements/Sources/Separators/SeparatorConfigurable.swift b/TIUIElements/Sources/Separators/SeparatorConfigurable.swift new file mode 100644 index 00000000..9311f585 --- /dev/null +++ b/TIUIElements/Sources/Separators/SeparatorConfigurable.swift @@ -0,0 +1,27 @@ +// +// Copyright (c) 2020 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 protocol SeparatorConfigurable { + func configure(with separatorType: ViewSeparatorType) +} diff --git a/TIUIElements/Sources/Separators/SeparatorConfiguration.swift b/TIUIElements/Sources/Separators/SeparatorConfiguration.swift new file mode 100644 index 00000000..0a44231c --- /dev/null +++ b/TIUIElements/Sources/Separators/SeparatorConfiguration.swift @@ -0,0 +1,36 @@ +// +// Copyright (c) 2020 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 + +public struct SeparatorConfiguration { + + public let color: UIColor + public let insets: UIEdgeInsets + public let height: CGFloat + + public init(color: UIColor, insets: UIEdgeInsets = .zero, height: CGFloat = 1) { + self.color = color + self.insets = insets + self.height = height + } +} diff --git a/TIUIElements/Sources/Separators/ViewSeparatorType.swift b/TIUIElements/Sources/Separators/ViewSeparatorType.swift new file mode 100644 index 00000000..9158ff2e --- /dev/null +++ b/TIUIElements/Sources/Separators/ViewSeparatorType.swift @@ -0,0 +1,71 @@ +// +// Copyright (c) 2020 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. +// + +public enum ViewSeparatorType { + + /// All separators for view is 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 ViewSeparatorType { + + /// Determine if bottom separator is hidden. + var bottomIsHidden: Bool { + return bottomConfiguration == nil + } + + /// Determine if top separator is hidden. + var topIsHidden: Bool { + return topConfiguration == nil + } + + /// Returns top configuration if type is top or full. + var topConfiguration: SeparatorConfiguration? { + switch self { + case let .top(configuration), let .full(configuration, _): + return configuration + + default: + return nil + } + } + + /// Returns bottom configuration if type is bottom or full. + var bottomConfiguration: SeparatorConfiguration? { + switch self { + case let .bottom(configuration), let .full(_, configuration): + return configuration + + default: + return nil + } + } +} diff --git a/TIUIElements/Sources/Views/Cells/BaseInitializableCell.swift b/TIUIElements/Sources/Views/Cells/BaseInitializableCell.swift new file mode 100644 index 00000000..8c646d95 --- /dev/null +++ b/TIUIElements/Sources/Views/Cells/BaseInitializableCell.swift @@ -0,0 +1,60 @@ +// +// Copyright (c) 2020 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 TIUIKitCore +import UIKit + +open class BaseInitializableCell: UITableViewCell, InitializableView { + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: .default, reuseIdentifier: reuseIdentifier) + + initializeView() + } + + @available(*, unavailable) + required public init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + + // MARK: - InitializableView + + open func addViews() { + // empty for subclasses overriding + } + + open func bindViews() { + // empty for subclasses overriding + } + + open func configureLayout() { + // empty for subclasses overriding + } + + open func configureAppearance() { + selectionStyle = .none + backgroundColor = .clear + } + + open func localize() { + // empty for subclasses overriding + } +} From 6fdb6af732ccc6268f8dedafa99732115dee1400 Mon Sep 17 00:00:00 2001 From: Ivan Smolin Date: Wed, 21 Oct 2020 22:34:45 +0300 Subject: [PATCH 02/12] add ViewText and BaseTextAttributes --- .../Sources/ViewText/BaseTextAttributes.swift | 47 +++++++ TIUIKitCore/Sources/ViewText/ViewText.swift | 33 +++++ .../UIButton+ViewTextConfigurable.swift | 121 ++++++++++++++++++ .../UILabel+ViewTextConfigurable.swift | 44 +++++++ .../UITextField+ViewTextConfigurable.swift | 44 +++++++ .../ViewTextConfigurable.swift | 43 +++++++ 6 files changed, 332 insertions(+) create mode 100644 TIUIKitCore/Sources/ViewText/BaseTextAttributes.swift create mode 100644 TIUIKitCore/Sources/ViewText/ViewText.swift create mode 100644 TIUIKitCore/Sources/ViewText/ViewTextConfigurable/UIButton+ViewTextConfigurable.swift create mode 100644 TIUIKitCore/Sources/ViewText/ViewTextConfigurable/UILabel+ViewTextConfigurable.swift create mode 100644 TIUIKitCore/Sources/ViewText/ViewTextConfigurable/UITextField+ViewTextConfigurable.swift create mode 100644 TIUIKitCore/Sources/ViewText/ViewTextConfigurable/ViewTextConfigurable.swift diff --git a/TIUIKitCore/Sources/ViewText/BaseTextAttributes.swift b/TIUIKitCore/Sources/ViewText/BaseTextAttributes.swift new file mode 100644 index 00000000..e51c6abc --- /dev/null +++ b/TIUIKitCore/Sources/ViewText/BaseTextAttributes.swift @@ -0,0 +1,47 @@ +// +// Copyright (c) 2020 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.UIFont +import UIKit.UIColor + +/// Base set of attributes to configure appearance of text. +open class BaseTextAttributes { + + /// Text font. + public let font: UIFont + /// Text color. + public let color: UIColor + /// Text alignment. + public let alignment: NSTextAlignment + + /// Memberwise initializer. + /// + /// - Parameters: + /// - font: Text font. + /// - color: Text color. + /// - alignment: Text alignment. + public init(font: UIFont, color: UIColor, alignment: NSTextAlignment = .natural) { + self.font = font + self.color = color + self.alignment = alignment + } +} diff --git a/TIUIKitCore/Sources/ViewText/ViewText.swift b/TIUIKitCore/Sources/ViewText/ViewText.swift new file mode 100644 index 00000000..da7f0822 --- /dev/null +++ b/TIUIKitCore/Sources/ViewText/ViewText.swift @@ -0,0 +1,33 @@ +// +// Copyright (c) 2020 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.NSAttributedString + +/// Enum that describes text with appearance options. +/// +/// - string: Regular string with common and often-used text attributes. +/// - attributedString: Attributed string. +public enum ViewText { + + case string(String, textAttributes: BaseTextAttributes) + case attributedString(NSAttributedString) +} diff --git a/TIUIKitCore/Sources/ViewText/ViewTextConfigurable/UIButton+ViewTextConfigurable.swift b/TIUIKitCore/Sources/ViewText/ViewTextConfigurable/UIButton+ViewTextConfigurable.swift new file mode 100644 index 00000000..de0e5e5f --- /dev/null +++ b/TIUIKitCore/Sources/ViewText/ViewTextConfigurable/UIButton+ViewTextConfigurable.swift @@ -0,0 +1,121 @@ +// +// Copyright (c) 2020 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.UIButton + +extension UIButton: ViewTextConfigurable { + + public var textFont: UIFont? { + get { + return titleLabel?.font + } + set { + titleLabel?.font = newValue + } + } + + public var titleColor: UIColor? { + get { + return currentTitleColor + } + set { + setTitleColor(newValue, for: []) + } + } + + public var textAlignment: NSTextAlignment { + get { + return contentHorizontalAlignment.textAlignment + } + set { + contentHorizontalAlignment = .init(textAlignment: newValue) + } + } + + public var text: String? { + get { + return currentTitle + } + set { + setTitle(newValue, for: []) + } + } + + public var attributedText: NSAttributedString? { + get { + return currentAttributedTitle + } + set { + setAttributedTitle(newValue, for: []) + } + } +} + +private extension UIControl.ContentHorizontalAlignment { + + init(textAlignment: NSTextAlignment) { + switch textAlignment { + case .left: + self = .leading + + case .right: + self = .trailing + + case .center: + self = .center + + case .justified: + self = .fill + + case .natural: + self = .leading + + @unknown default: + self = .leading + } + } + + var textAlignment: NSTextAlignment { + switch self { + case .left: + return .left + + case .right: + return .right + + case .center: + return .center + + case .fill: + return .justified + + case .leading: + return .natural + + case .trailing: + return .right + + @unknown default: + return .natural + } + } +} diff --git a/TIUIKitCore/Sources/ViewText/ViewTextConfigurable/UILabel+ViewTextConfigurable.swift b/TIUIKitCore/Sources/ViewText/ViewTextConfigurable/UILabel+ViewTextConfigurable.swift new file mode 100644 index 00000000..8418ffa4 --- /dev/null +++ b/TIUIKitCore/Sources/ViewText/ViewTextConfigurable/UILabel+ViewTextConfigurable.swift @@ -0,0 +1,44 @@ +// +// Copyright (c) 2020 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.UILabel + +extension UILabel: ViewTextConfigurable { + + public var textFont: UIFont? { + get { + return font + } + set { + font = newValue + } + } + + public var titleColor: UIColor? { + get { + return textColor + } + set { + textColor = newValue + } + } +} diff --git a/TIUIKitCore/Sources/ViewText/ViewTextConfigurable/UITextField+ViewTextConfigurable.swift b/TIUIKitCore/Sources/ViewText/ViewTextConfigurable/UITextField+ViewTextConfigurable.swift new file mode 100644 index 00000000..eaa2ce79 --- /dev/null +++ b/TIUIKitCore/Sources/ViewText/ViewTextConfigurable/UITextField+ViewTextConfigurable.swift @@ -0,0 +1,44 @@ +// +// Copyright (c) 2020 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.UITextField + +extension UITextField: ViewTextConfigurable { + + public var textFont: UIFont? { + get { + return font + } + set { + font = newValue + } + } + + public var titleColor: UIColor? { + get { + return textColor + } + set { + textColor = newValue + } + } +} diff --git a/TIUIKitCore/Sources/ViewText/ViewTextConfigurable/ViewTextConfigurable.swift b/TIUIKitCore/Sources/ViewText/ViewTextConfigurable/ViewTextConfigurable.swift new file mode 100644 index 00000000..ee2b5f9a --- /dev/null +++ b/TIUIKitCore/Sources/ViewText/ViewTextConfigurable/ViewTextConfigurable.swift @@ -0,0 +1,43 @@ +// +// Copyright (c) 2020 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.UIFont +import UIKit.UIColor + +/// Protocol that represents text object with appearance attributes. +public protocol ViewTextConfigurable: AnyObject { + + /// Font of text object. + var textFont: UIFont? { get set } + + /// Text color of text object. + var titleColor: UIColor? { get set } + + /// Text alignment of text object. + var textAlignment: NSTextAlignment { get set } + + /// Text itself of text object. + var text: String? { get set } + + /// Attributed text of text object. + var attributedText: NSAttributedString? { get set } +} From 4af10aa1075263fb05fac9a9729858f9bd7838d6 Mon Sep 17 00:00:00 2001 From: Ivan Smolin Date: Wed, 21 Oct 2020 22:37:20 +0300 Subject: [PATCH 03/12] add ConfigurableView --- .../ConfigurableView/ConfigurableView.swift | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 TIUIKitCore/Sources/Protocols/ConfigurableView/ConfigurableView.swift diff --git a/TIUIKitCore/Sources/Protocols/ConfigurableView/ConfigurableView.swift b/TIUIKitCore/Sources/Protocols/ConfigurableView/ConfigurableView.swift new file mode 100644 index 00000000..cd5fc1ab --- /dev/null +++ b/TIUIKitCore/Sources/Protocols/ConfigurableView/ConfigurableView.swift @@ -0,0 +1,27 @@ +// +// Copyright (c) 2020 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. +// + +public protocol ConfigurableView { + associatedtype ViewModelType + + func configure(with _: ViewModelType) +} From 64cb76654286778ec000f7f0a874fcfe2e4ef57e Mon Sep 17 00:00:00 2001 From: Ivan Smolin Date: Wed, 21 Oct 2020 22:55:14 +0300 Subject: [PATCH 04/12] add missing ViewTextConfigurable extensions --- .../Sources/ViewText/BaseTextAttributes.swift | 10 ++++ TIUIKitCore/Sources/ViewText/ViewText.swift | 60 ++++++++++++++++++- .../ViewTextConfigurable.swift | 26 ++++++++ 3 files changed, 95 insertions(+), 1 deletion(-) diff --git a/TIUIKitCore/Sources/ViewText/BaseTextAttributes.swift b/TIUIKitCore/Sources/ViewText/BaseTextAttributes.swift index e51c6abc..d9d5a26e 100644 --- a/TIUIKitCore/Sources/ViewText/BaseTextAttributes.swift +++ b/TIUIKitCore/Sources/ViewText/BaseTextAttributes.swift @@ -45,3 +45,13 @@ open class BaseTextAttributes { self.alignment = alignment } } + +public extension BaseTextAttributes { + + /// Configures text appearance of given ViewTextConfigurable instance. + /// + /// - Parameter view: ViewTextConfigurable instance to configure with BaseTextAttributes. + func configureBaseApperance(of view: ViewTextConfigurable) { + view.configureBaseAppearance(with: self) + } +} diff --git a/TIUIKitCore/Sources/ViewText/ViewText.swift b/TIUIKitCore/Sources/ViewText/ViewText.swift index da7f0822..b84ea370 100644 --- a/TIUIKitCore/Sources/ViewText/ViewText.swift +++ b/TIUIKitCore/Sources/ViewText/ViewText.swift @@ -20,7 +20,7 @@ // THE SOFTWARE. // -import Foundation.NSAttributedString +import UIKit /// Enum that describes text with appearance options. /// @@ -31,3 +31,61 @@ public enum ViewText { case string(String, textAttributes: BaseTextAttributes) case attributedString(NSAttributedString) } + +public extension ViewText { + + /// Convenient initializer for .string case with default alignment parameter. + /// + /// - Parameters: + /// - string: Text to use. + /// - font: Font to use. + /// - color: Color to use. + /// - alignment: Alignment to use. Default is natural. + init(string: String, font: UIFont, color: UIColor, alignment: NSTextAlignment = .natural) { + self = .string(string, textAttributes: BaseTextAttributes(font: font, + color: color, + alignment: alignment)) + } + + /// Attributed string created using text attributes. + var attributedString: NSAttributedString { + switch self { + case let .string(title, textAttributes): + + let paragraphStyle = NSMutableParagraphStyle() + paragraphStyle.alignment = textAttributes.alignment + + let attributes: [NSAttributedString.Key: Any] = [ + .font: textAttributes.font, + .foregroundColor: textAttributes.color, + .paragraphStyle: paragraphStyle + ] + + return NSAttributedString(string: title, attributes: attributes) + + case .attributedString(let attributedTitle): + return attributedTitle + } + } + + /// Method that calculates size of view text using given max width and height arguments. + /// + /// - Parameters: + /// - maxWidth: The width constraint to apply when computing the string’s bounding rectangle. + /// - maxHeight: The width constraint to apply when computing the string’s bounding rectangle. + /// - Returns: Returns the size required to draw the text. + func size(maxWidth: CGFloat = CGFloat.greatestFiniteMagnitude, + maxHeight: CGFloat = CGFloat.greatestFiniteMagnitude) -> CGSize { + + return attributedString.boundingRect(with: CGSize(width: maxWidth, height: maxHeight), + options: [.usesLineFragmentOrigin, .usesFontLeading], + context: nil).size + } + + /// Configures given ViewTextConfigurable instance. + /// + /// - Parameter view: ViewTextConfigurable instance to configure with ViewText. + func configure(view: ViewTextConfigurable) { + view.configure(with: self) + } +} diff --git a/TIUIKitCore/Sources/ViewText/ViewTextConfigurable/ViewTextConfigurable.swift b/TIUIKitCore/Sources/ViewText/ViewTextConfigurable/ViewTextConfigurable.swift index ee2b5f9a..dfd7256c 100644 --- a/TIUIKitCore/Sources/ViewText/ViewTextConfigurable/ViewTextConfigurable.swift +++ b/TIUIKitCore/Sources/ViewText/ViewTextConfigurable/ViewTextConfigurable.swift @@ -41,3 +41,29 @@ public protocol ViewTextConfigurable: AnyObject { /// Attributed text of text object. var attributedText: NSAttributedString? { get set } } + +public extension ViewTextConfigurable { + + /// Configures text and text appearance of view using ViewText object. + /// + /// - Parameter viewText: ViewText object with text and text appearance. + func configure(with viewText: ViewText) { + switch viewText { + case let .string(text, textAttributes): + self.text = text + self.configureBaseAppearance(with: textAttributes) + + case .attributedString(let attributedString): + self.attributedText = attributedString + } + } + + /// Configures text appearance of view. + /// + /// - Parameter baseTextAttributes: Set of attributes to configure appearance of text. + func configureBaseAppearance(with baseTextAttributes: BaseTextAttributes) { + textFont = baseTextAttributes.font + titleColor = baseTextAttributes.color + textAlignment = baseTextAttributes.alignment + } +} From e92b46eb511af07d633db16b411b0e88582864f7 Mon Sep 17 00:00:00 2001 From: Ivan Smolin Date: Thu, 22 Oct 2020 12:44:42 +0300 Subject: [PATCH 05/12] rename InitializableView -> InitializableViewProtocol, move base elements to TIUIElements --- .../Sources/Views/BaseInitializableControl.swift | 3 ++- .../Sources/Views/BaseInitializableView.swift | 3 ++- TIUIElements/Sources/Views/Cells/BaseInitializableCell.swift | 2 +- .../InitializableView/InitializableView+Extensions.swift | 2 +- .../InitializableViewProtocol.swift} | 2 +- 5 files changed, 7 insertions(+), 5 deletions(-) rename {TIUIKitCore => TIUIElements}/Sources/Views/BaseInitializableControl.swift (95%) rename {TIUIKitCore => TIUIElements}/Sources/Views/BaseInitializableView.swift (95%) rename TIUIKitCore/Sources/Protocols/{InitializableView/InitializableView.swift => InitializableViewProtocol/InitializableViewProtocol.swift} (97%) diff --git a/TIUIKitCore/Sources/Views/BaseInitializableControl.swift b/TIUIElements/Sources/Views/BaseInitializableControl.swift similarity index 95% rename from TIUIKitCore/Sources/Views/BaseInitializableControl.swift rename to TIUIElements/Sources/Views/BaseInitializableControl.swift index c5cc67f0..7d852d5d 100644 --- a/TIUIKitCore/Sources/Views/BaseInitializableControl.swift +++ b/TIUIElements/Sources/Views/BaseInitializableControl.swift @@ -1,6 +1,7 @@ import UIKit +import TIUIKitCore -open class BaseInitializableControl: UIControl, InitializableView { +open class BaseInitializableControl: UIControl, InitializableViewProtocol { override public init(frame: CGRect) { super.init(frame: frame) diff --git a/TIUIKitCore/Sources/Views/BaseInitializableView.swift b/TIUIElements/Sources/Views/BaseInitializableView.swift similarity index 95% rename from TIUIKitCore/Sources/Views/BaseInitializableView.swift rename to TIUIElements/Sources/Views/BaseInitializableView.swift index 1fb7d960..d7192498 100644 --- a/TIUIKitCore/Sources/Views/BaseInitializableView.swift +++ b/TIUIElements/Sources/Views/BaseInitializableView.swift @@ -21,8 +21,9 @@ // import UIKit +import TIUIKitCore -open class BaseInitializableView: UIView, InitializableView { +open class BaseInitializableView: UIView, InitializableViewProtocol { override public init(frame: CGRect) { super.init(frame: frame) diff --git a/TIUIElements/Sources/Views/Cells/BaseInitializableCell.swift b/TIUIElements/Sources/Views/Cells/BaseInitializableCell.swift index 8c646d95..7f45b55f 100644 --- a/TIUIElements/Sources/Views/Cells/BaseInitializableCell.swift +++ b/TIUIElements/Sources/Views/Cells/BaseInitializableCell.swift @@ -23,7 +23,7 @@ import TIUIKitCore import UIKit -open class BaseInitializableCell: UITableViewCell, InitializableView { +open class BaseInitializableCell: UITableViewCell, InitializableViewProtocol { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: .default, reuseIdentifier: reuseIdentifier) diff --git a/TIUIKitCore/Sources/Extensions/InitializableView/InitializableView+Extensions.swift b/TIUIKitCore/Sources/Extensions/InitializableView/InitializableView+Extensions.swift index b5026ee6..2c10e7c9 100644 --- a/TIUIKitCore/Sources/Extensions/InitializableView/InitializableView+Extensions.swift +++ b/TIUIKitCore/Sources/Extensions/InitializableView/InitializableView+Extensions.swift @@ -20,7 +20,7 @@ // THE SOFTWARE. // -public extension InitializableView { +public extension InitializableViewProtocol { func initializeView() { addViews() diff --git a/TIUIKitCore/Sources/Protocols/InitializableView/InitializableView.swift b/TIUIKitCore/Sources/Protocols/InitializableViewProtocol/InitializableViewProtocol.swift similarity index 97% rename from TIUIKitCore/Sources/Protocols/InitializableView/InitializableView.swift rename to TIUIKitCore/Sources/Protocols/InitializableViewProtocol/InitializableViewProtocol.swift index bbf07d29..766256fb 100644 --- a/TIUIKitCore/Sources/Protocols/InitializableView/InitializableView.swift +++ b/TIUIKitCore/Sources/Protocols/InitializableViewProtocol/InitializableViewProtocol.swift @@ -21,7 +21,7 @@ // /// Protocol with methods that should be called in constructor methods of view. -public protocol InitializableView { +public protocol InitializableViewProtocol { /// Main method that should call other methods in particular order. func initializeView() From dfbe18feddf26f720f6b5f430f2b8f8d2b07da8f Mon Sep 17 00:00:00 2001 From: Ivan Smolin Date: Fri, 23 Oct 2020 10:24:22 +0300 Subject: [PATCH 06/12] update READMEs --- README.md | 21 +++++++++++++++++++ TISwiftUtils/README.md | 4 ---- .../Optional/Optional+Extensions.swift | 2 -- .../Substring/Substring+Extensions.swift | 2 -- TISwiftUtils/Sources/Helpers/Typealias.swift | 2 -- TITableKitUtils/README.md | 4 ++++ TIUIKitCore/README.md | 4 ---- 7 files changed, 25 insertions(+), 14 deletions(-) create mode 100644 TITableKitUtils/README.md diff --git a/README.md b/README.md index 6cda623b..73a91c28 100644 --- a/README.md +++ b/README.md @@ -8,4 +8,25 @@ This repository contains the following additional frameworks: - [TIUIElements](TIUIElements) - bunch of of useful protocols and views. - [OTPSwiftView](OTPSwiftView) - a fully customizable OTP view. - [TISwiftUtils](TISwiftUtils) - a bunch of useful helpers for development. +- [TITableKitUtils](TITableKitUtils) - Set of helpers for TableKit classes. + +## Installation + +### SPM + +```swift +dependencies: [ + .package(url: "https://github.com/TouchInstinct/LeadKit.git", from: "x.y.z"), +], +``` + +### Cocoapods + +```ruby +source 'https://github.com/TouchInstinct/Podspecs.git' + +pod 'TISwiftUtils', 'x.y.z' +pod 'TIFoundationUtils', 'x.y.z' +# ... +``` diff --git a/TISwiftUtils/README.md b/TISwiftUtils/README.md index 993c9cfe..1a9f1a94 100644 --- a/TISwiftUtils/README.md +++ b/TISwiftUtils/README.md @@ -18,7 +18,3 @@ final class ViewModel { var hasFinishedOnboarding: Bool } ``` - -# Installation via SPM - -You can install this framework as a target of LeadKit. diff --git a/TISwiftUtils/Sources/Extensions/Optional/Optional+Extensions.swift b/TISwiftUtils/Sources/Extensions/Optional/Optional+Extensions.swift index 0bddd3bb..af793186 100644 --- a/TISwiftUtils/Sources/Extensions/Optional/Optional+Extensions.swift +++ b/TISwiftUtils/Sources/Extensions/Optional/Optional+Extensions.swift @@ -20,8 +20,6 @@ // THE SOFTWARE. // -import UIKit - public extension Optional where Wrapped == String { var orEmpty: String { self ?? "" diff --git a/TISwiftUtils/Sources/Extensions/Substring/Substring+Extensions.swift b/TISwiftUtils/Sources/Extensions/Substring/Substring+Extensions.swift index 61d0812f..e68fc6f3 100644 --- a/TISwiftUtils/Sources/Extensions/Substring/Substring+Extensions.swift +++ b/TISwiftUtils/Sources/Extensions/Substring/Substring+Extensions.swift @@ -20,8 +20,6 @@ // THE SOFTWARE. // -import Foundation - public extension Substring { var string: String { String(self) diff --git a/TISwiftUtils/Sources/Helpers/Typealias.swift b/TISwiftUtils/Sources/Helpers/Typealias.swift index 897d1f09..3ece5296 100644 --- a/TISwiftUtils/Sources/Helpers/Typealias.swift +++ b/TISwiftUtils/Sources/Helpers/Typealias.swift @@ -20,8 +20,6 @@ // THE SOFTWARE. // -import UIKit - /// Closure with custom arguments and return value. public typealias Closure = (Input) -> Output diff --git a/TITableKitUtils/README.md b/TITableKitUtils/README.md new file mode 100644 index 00000000..1401a1b5 --- /dev/null +++ b/TITableKitUtils/README.md @@ -0,0 +1,4 @@ +# TITableKitUtils + +Set of helpers for TableKit classes. + diff --git a/TIUIKitCore/README.md b/TIUIKitCore/README.md index 9c46b9b3..96d028a7 100644 --- a/TIUIKitCore/README.md +++ b/TIUIKitCore/README.md @@ -12,7 +12,3 @@ Core UI elements: protocols, views and helpers. # Views - [BaseInitializableView](BaseInitializableView/BaseInitializableView.swift) - UIView conformance to InitializableView. - -# Installation via SPM - -You can install this framework as a target of LeadKit. From 281f0b72b9a0b8fd5cad1d499db98321e2c1d5ac Mon Sep 17 00:00:00 2001 From: Ivan Smolin Date: Fri, 23 Oct 2020 10:24:34 +0300 Subject: [PATCH 07/12] add podspecs --- TIFoundationUtils/TIFoundationUtils.podspec | 16 ++++++++++++++++ TISwiftUtils/TISwiftUtils.podspec | 14 ++++++++++++++ TITableKitUtils/TITableKitUtils.podspec | 18 ++++++++++++++++++ TIUIElements/TIUIElements.podspec | 16 ++++++++++++++++ TIUIKitCore/TIUIKitCore.podspec | 15 +++++++++++++++ 5 files changed, 79 insertions(+) create mode 100644 TIFoundationUtils/TIFoundationUtils.podspec create mode 100644 TISwiftUtils/TISwiftUtils.podspec create mode 100644 TITableKitUtils/TITableKitUtils.podspec create mode 100644 TIUIElements/TIUIElements.podspec create mode 100644 TIUIKitCore/TIUIKitCore.podspec diff --git a/TIFoundationUtils/TIFoundationUtils.podspec b/TIFoundationUtils/TIFoundationUtils.podspec new file mode 100644 index 00000000..7d1ecf00 --- /dev/null +++ b/TIFoundationUtils/TIFoundationUtils.podspec @@ -0,0 +1,16 @@ +Pod::Spec.new do |s| + s.name = 'TIFoundationUtils' + s.version = '0.10.99' + s.summary = 'Set of helpers for Foundation framework classes.' + s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name + s.license = { :type => 'MIT', :file => 'LICENSE' } + s.author = { 'petropavel13' => 'ivan.smolin@touchin.ru' } + s.source = { :git => 'https://github.com/TouchInstinct/LeadKit.git', :tag => s.version.to_s } + + s.ios.deployment_target = '11.0' + s.swift_versions = ['5.3'] + + s.source_files = s.name + '/Sources/**/*' + + s.dependency 'TISwiftUtils', s.version.to_s +end diff --git a/TISwiftUtils/TISwiftUtils.podspec b/TISwiftUtils/TISwiftUtils.podspec new file mode 100644 index 00000000..a1325f65 --- /dev/null +++ b/TISwiftUtils/TISwiftUtils.podspec @@ -0,0 +1,14 @@ +Pod::Spec.new do |s| + s.name = 'TISwiftUtils' + s.version = '0.10.99' + s.summary = 'Bunch of useful helpers for Swift development.' + s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name + s.license = { :type => 'MIT', :file => 'LICENSE' } + s.author = { 'petropavel13' => 'ivan.smolin@touchin.ru' } + s.source = { :git => 'https://github.com/TouchInstinct/LeadKit.git', :tag => s.version.to_s } + + s.ios.deployment_target = '9.0' + s.swift_versions = ['5.3'] + + s.source_files = s.name + '/Sources/**/*' +end diff --git a/TITableKitUtils/TITableKitUtils.podspec b/TITableKitUtils/TITableKitUtils.podspec new file mode 100644 index 00000000..97866c0c --- /dev/null +++ b/TITableKitUtils/TITableKitUtils.podspec @@ -0,0 +1,18 @@ +Pod::Spec.new do |s| + s.name = 'TITableKitUtils' + s.version = '0.10.99' + s.summary = 'Set of helpers for TableKit classes.' + s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name + s.license = { :type => 'MIT', :file => 'LICENSE' } + s.author = { 'petropavel13' => 'ivan.smolin@touchin.ru' } + s.source = { :git => 'https://github.com/TouchInstinct/LeadKit.git', :tag => s.version.to_s } + + s.ios.deployment_target = '11.0' + s.swift_versions = ['5.3'] + + s.source_files = s.name + '/Sources/**/*' + + s.dependency 'TIUIElements', s.version.to_s + s.dependency 'TISwiftUtils', s.version.to_s + s.dependency 'TableKit', '2.11.0' +end diff --git a/TIUIElements/TIUIElements.podspec b/TIUIElements/TIUIElements.podspec new file mode 100644 index 00000000..8f864637 --- /dev/null +++ b/TIUIElements/TIUIElements.podspec @@ -0,0 +1,16 @@ +Pod::Spec.new do |s| + s.name = 'TIUIElements' + s.version = '0.10.99' + s.summary = 'Bunch of useful protocols and views.' + s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name + s.license = { :type => 'MIT', :file => 'LICENSE' } + s.author = { 'petropavel13' => 'ivan.smolin@touchin.ru' } + s.source = { :git => 'https://github.com/TouchInstinct/LeadKit.git', :tag => s.version.to_s } + + s.ios.deployment_target = '11.0' + s.swift_versions = ['5.3'] + + s.source_files = s.name + '/Sources/**/*' + + s.dependency 'TIUIKitCore', s.version.to_s +end diff --git a/TIUIKitCore/TIUIKitCore.podspec b/TIUIKitCore/TIUIKitCore.podspec new file mode 100644 index 00000000..45014cf1 --- /dev/null +++ b/TIUIKitCore/TIUIKitCore.podspec @@ -0,0 +1,15 @@ +Pod::Spec.new do |s| + s.name = 'TIUIKitCore' + s.version = '0.10.99' + s.summary = 'Core UI elements: protocols, views and helpers.' + s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name + s.license = { :type => 'MIT', :file => 'LICENSE' } + s.author = { 'petropavel13' => 'ivan.smolin@touchin.ru' } + s.source = { :git => 'https://github.com/TouchInstinct/LeadKit.git', :tag => s.version.to_s } + + s.ios.deployment_target = '11.0' + s.swift_versions = ['5.3'] + + s.source_files = s.name + '/Sources/**/*' + s.framework = 'UIKit' +end From 0a7f68bd23b8bc855c7161bd26e0947e834a27d3 Mon Sep 17 00:00:00 2001 From: Ivan Smolin Date: Fri, 23 Oct 2020 12:41:36 +0300 Subject: [PATCH 08/12] update podspec and add missing extensions for TableKit --- TIFoundationUtils/TIFoundationUtils.podspec | 1 + .../TableDirector+Extensions.swift | 243 ++++++++++++++++++ .../TableSection+Extensions.swift | 39 +++ .../Array}/Array+SeparatorRowBox.swift | 0 .../TableRow}/TableRow+Separators.swift | 0 .../Separators/SeparatorConfigurable.swift | 2 - 6 files changed, 283 insertions(+), 2 deletions(-) create mode 100644 TITableKitUtils/Sources/Extensions/TableDirector/TableDirector+Extensions.swift create mode 100644 TITableKitUtils/Sources/Extensions/TableSection/TableSection+Extensions.swift rename TITableKitUtils/Sources/{Extensions => Separators/Extensions/Array}/Array+SeparatorRowBox.swift (100%) rename TITableKitUtils/Sources/{Extensions => Separators/Extensions/TableRow}/TableRow+Separators.swift (100%) diff --git a/TIFoundationUtils/TIFoundationUtils.podspec b/TIFoundationUtils/TIFoundationUtils.podspec index 7d1ecf00..000de78d 100644 --- a/TIFoundationUtils/TIFoundationUtils.podspec +++ b/TIFoundationUtils/TIFoundationUtils.podspec @@ -13,4 +13,5 @@ Pod::Spec.new do |s| s.source_files = s.name + '/Sources/**/*' s.dependency 'TISwiftUtils', s.version.to_s + s.framework = 'Foundation' end diff --git a/TITableKitUtils/Sources/Extensions/TableDirector/TableDirector+Extensions.swift b/TITableKitUtils/Sources/Extensions/TableDirector/TableDirector+Extensions.swift new file mode 100644 index 00000000..4156c963 --- /dev/null +++ b/TITableKitUtils/Sources/Extensions/TableDirector/TableDirector+Extensions.swift @@ -0,0 +1,243 @@ +// +// Copyright (c) 2020 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 +import UIKit.UITableView + +public extension TableDirector { + + /** + method replaces current table director's section at index and reloads it + + - parameter section: new section + - parameter index: current replaced section index + - parameter reload: is reloaded after replace + + - returns: self + */ + @discardableResult + func replace(section: TableSection, atIndex index: Int, reload: Bool = true) -> Self { + if index < sections.count { + remove(sectionAt: index) + } + insert(section: section, atIndex: index) + if reload { + self.reload(sectionAtIndex: index) + } + return self + } + + /** + method reloads section at index with animation + + - parameter index: current reloaded section index + - parameter animation: reloading animation. Default .none + + - returns: self + */ + @discardableResult + func reload(sectionAtIndex index: Int, with animation: UITableView.RowAnimation = .none) -> Self { + let action = { [tableView] in + guard let tableView = tableView else { + return + } + + if index < tableView.numberOfSections { + tableView.reloadSections([index], with: animation) + } else { + tableView.reloadData() + } + } + if animation == .none { + UIView.performWithoutAnimation(action) + } else { + action() + } + return self + } + + /** + method replaces current table director's state with sections + + - parameter sections: new sections + + - returns: self + */ + @discardableResult + func replace(withSections sections: [TableSection]) -> Self { + clear().append(sections: sections).reload() + return self + } + + /** + method replaces current table director's state with section + + - parameter section: new section + + - returns: self + */ + @discardableResult + func replace(withSection section: TableSection) -> Self { + return replace(withSections: [section]) + } + + /** + method replaces current table director's state with rows + + - parameter rows: new rows + + - returns: self + */ + @discardableResult + func replace(withRows rows: [Row]) -> Self { + return replace(withSection: TableSection(rows: rows)) + } + + /// Clear table view and reload it within empty section + func safeClear() { + clear().append(section: TableSection(onlyRows: [])).reload() + } + + /// Inserts rows into table without complete reload. + /// + /// - Parameters: + /// - rows: Rows to insert. + /// - indexPath: Position of first row. + /// - animation: The type of animation when rows are inserted + /// - manualBeginEndUpdates: Don't call beginUpdates() & endUpdates() inside. + func insert(rows: [Row], + at indexPath: IndexPath, + with animation: UITableView.RowAnimation, + manualBeginEndUpdates: Bool = false) { + + sections[indexPath.section].insert(rows: rows, at: indexPath.row) + let indexPaths: [IndexPath] = rows.indices.map { + IndexPath(row: indexPath.row + $0, section: indexPath.section) + } + + if manualBeginEndUpdates { + tableView?.insertRows(at: indexPaths, with: animation) + } else { + tableView?.beginUpdates() + tableView?.insertRows(at: indexPaths, with: animation) + tableView?.endUpdates() + } + } + + /// Removes rows from table without complete reload. + /// + /// - Parameters: + /// - rowsCount: Number of rows to remove. + /// - indexPath: Position of first row to remove. + /// - animation: The type of animation when rows are deleted + /// - manualBeginEndUpdates: Don't call beginUpdates() & endUpdates() inside. + func remove(rowsCount: Int, + startingAt indexPath: IndexPath, + with animation: UITableView.RowAnimation, + manualBeginEndUpdates: Bool = false) { + + var indexPaths = [IndexPath]() + for index in indexPath.row ..< indexPath.row + rowsCount { + indexPaths.append(IndexPath(row: index, section: indexPath.section)) + } + + indexPaths.reversed().forEach { + sections[$0.section].remove(rowAt: $0.row) + } + + if manualBeginEndUpdates { + tableView?.deleteRows(at: indexPaths, with: animation) + } else { + tableView?.beginUpdates() + tableView?.deleteRows(at: indexPaths, with: animation) + tableView?.endUpdates() + } + } + + /// Method inserts section with animation. + /// + /// - Parameters: + /// - section: Section to insert + /// - index: Position to insert + /// - animation: The type of insert animation + /// - manualBeginEndUpdates: Don't call beginUpdates() & endUpdates() inside. + /// - Returns: self + @discardableResult + func insert(section: TableSection, + at index: Int, + with animation: UITableView.RowAnimation, + manualBeginEndUpdates: Bool = false) -> Self { + + insert(section: section, atIndex: index) + if manualBeginEndUpdates { + tableView?.insertSections([index], with: animation) + } else { + tableView?.beginUpdates() + tableView?.insertSections([index], with: animation) + tableView?.endUpdates() + } + + return self + } + + /// Method removes section with animation. + /// + /// - Parameters: + /// - index: Position to remove + /// - animation: The type of remove animation + /// - manualBeginEndUpdates: Don't call beginUpdates() & endUpdates() inside. + /// - Returns: self + @discardableResult + func remove(at index: Int, + with animation: UITableView.RowAnimation, + manualBeginEndUpdates: Bool = false) -> Self { + + delete(sectionAt: index) + if manualBeginEndUpdates { + tableView?.deleteSections([index], with: animation) + } else { + tableView?.beginUpdates() + tableView?.deleteSections([index], with: animation) + tableView?.endUpdates() + } + + return self + } + + /// Method replace section with animation. + /// + /// - Parameters: + /// - section: Section to replace + /// - index: Position to replace + /// - animation: The type of replace animation + /// - manualBeginEndUpdates: Don't call beginUpdates() & endUpdates() inside. + /// - Returns: self + @discardableResult + func replace(with section: TableSection, + at index: Int, + with animation: UITableView.RowAnimation, + manualBeginEndUpdates: Bool = false) -> Self { + + remove(at: index, with: animation, manualBeginEndUpdates: manualBeginEndUpdates) + return insert(section: section, at: index, with: animation, manualBeginEndUpdates: manualBeginEndUpdates) + } +} diff --git a/TITableKitUtils/Sources/Extensions/TableSection/TableSection+Extensions.swift b/TITableKitUtils/Sources/Extensions/TableSection/TableSection+Extensions.swift new file mode 100644 index 00000000..eda00808 --- /dev/null +++ b/TITableKitUtils/Sources/Extensions/TableSection/TableSection+Extensions.swift @@ -0,0 +1,39 @@ +// +// Copyright (c) 2020 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 TableSection { + + /// Initializes section with rows and zero height footer and header. + /// + /// - Parameter rows: Rows to insert into section. + convenience init(onlyRows rows: [Row]) { + self.init(rows: rows) + + self.headerView = nil + self.footerView = nil + + self.headerHeight = .leastNonzeroMagnitude + self.footerHeight = .leastNonzeroMagnitude + } +} diff --git a/TITableKitUtils/Sources/Extensions/Array+SeparatorRowBox.swift b/TITableKitUtils/Sources/Separators/Extensions/Array/Array+SeparatorRowBox.swift similarity index 100% rename from TITableKitUtils/Sources/Extensions/Array+SeparatorRowBox.swift rename to TITableKitUtils/Sources/Separators/Extensions/Array/Array+SeparatorRowBox.swift diff --git a/TITableKitUtils/Sources/Extensions/TableRow+Separators.swift b/TITableKitUtils/Sources/Separators/Extensions/TableRow/TableRow+Separators.swift similarity index 100% rename from TITableKitUtils/Sources/Extensions/TableRow+Separators.swift rename to TITableKitUtils/Sources/Separators/Extensions/TableRow/TableRow+Separators.swift diff --git a/TIUIElements/Sources/Separators/SeparatorConfigurable.swift b/TIUIElements/Sources/Separators/SeparatorConfigurable.swift index 9311f585..7730cbd0 100644 --- a/TIUIElements/Sources/Separators/SeparatorConfigurable.swift +++ b/TIUIElements/Sources/Separators/SeparatorConfigurable.swift @@ -20,8 +20,6 @@ // THE SOFTWARE. // -import Foundation - public protocol SeparatorConfigurable { func configure(with separatorType: ViewSeparatorType) } From 6433233d9eedaaffb9a7e35eb80e2ca3c60bd97a Mon Sep 17 00:00:00 2001 From: Ivan Smolin Date: Fri, 23 Oct 2020 15:57:24 +0300 Subject: [PATCH 09/12] rename configure(with:) -> configureSeparators(with:) --- .../Separators/Extensions/TableRow/TableRow+Separators.swift | 2 +- TIUIElements/Sources/Separators/BaseSeparatorCell.swift | 4 ++-- TIUIElements/Sources/Separators/SeparatorConfigurable.swift | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/TITableKitUtils/Sources/Separators/Extensions/TableRow/TableRow+Separators.swift b/TITableKitUtils/Sources/Separators/Extensions/TableRow/TableRow+Separators.swift index e4903641..05ae4742 100644 --- a/TITableKitUtils/Sources/Separators/Extensions/TableRow/TableRow+Separators.swift +++ b/TITableKitUtils/Sources/Separators/Extensions/TableRow/TableRow+Separators.swift @@ -36,7 +36,7 @@ public extension TableRow where CellType: SeparatorConfigurable { removeAction(forActionId: configureSeparatorActionId) let action = TableRowAction(.configure) { - $0.cell?.configure(with: separatorType) + $0.cell?.configureSeparators(with: separatorType) } action.id = configureSeparatorActionId diff --git a/TIUIElements/Sources/Separators/BaseSeparatorCell.swift b/TIUIElements/Sources/Separators/BaseSeparatorCell.swift index e29b8067..f0154e73 100644 --- a/TIUIElements/Sources/Separators/BaseSeparatorCell.swift +++ b/TIUIElements/Sources/Separators/BaseSeparatorCell.swift @@ -44,7 +44,7 @@ open class BaseSeparatorCell: BaseInitializableCell, SeparatorConfigurable { .init() } - public func configure(with separatorType: ViewSeparatorType) { + public func configureSeparators(with separatorType: ViewSeparatorType) { topSeparatorView.isHidden = separatorType.topIsHidden bottomSeparatorView.isHidden = separatorType.bottomIsHidden @@ -66,7 +66,7 @@ open class BaseSeparatorCell: BaseInitializableCell, SeparatorConfigurable { open override func prepareForReuse() { super.prepareForReuse() - configure(with: .none) + configureSeparators(with: .none) } // MARK: - InitializableView diff --git a/TIUIElements/Sources/Separators/SeparatorConfigurable.swift b/TIUIElements/Sources/Separators/SeparatorConfigurable.swift index 7730cbd0..6e58a390 100644 --- a/TIUIElements/Sources/Separators/SeparatorConfigurable.swift +++ b/TIUIElements/Sources/Separators/SeparatorConfigurable.swift @@ -21,5 +21,5 @@ // public protocol SeparatorConfigurable { - func configure(with separatorType: ViewSeparatorType) + func configureSeparators(with separatorType: ViewSeparatorType) } From 18d7e5c5b6cc1d1cb27ce264660e436ba41faa19 Mon Sep 17 00:00:00 2001 From: Ivan Smolin Date: Fri, 23 Oct 2020 17:24:26 +0300 Subject: [PATCH 10/12] update BaseSeparatorCell layout configuration --- .../Separators/BaseSeparatorCell.swift | 36 +++++++++++++------ 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/TIUIElements/Sources/Separators/BaseSeparatorCell.swift b/TIUIElements/Sources/Separators/BaseSeparatorCell.swift index f0154e73..003f7dd0 100644 --- a/TIUIElements/Sources/Separators/BaseSeparatorCell.swift +++ b/TIUIElements/Sources/Separators/BaseSeparatorCell.swift @@ -44,6 +44,14 @@ open class BaseSeparatorCell: BaseInitializableCell, SeparatorConfigurable { .init() } + open func add(topSeparatorView: UIView) { + contentView.addSubview(topSeparatorView) + } + + open func add(bottomSeparatorView: UIView) { + contentView.addSubview(bottomSeparatorView) + } + public func configureSeparators(with separatorType: ViewSeparatorType) { topSeparatorView.isHidden = separatorType.topIsHidden bottomSeparatorView.isHidden = separatorType.bottomIsHidden @@ -74,20 +82,27 @@ open class BaseSeparatorCell: BaseInitializableCell, SeparatorConfigurable { open override func addViews() { super.addViews() - contentView.addSubviews(topSeparatorView, bottomSeparatorView) + add(topSeparatorView: topSeparatorView) + add(bottomSeparatorView: bottomSeparatorView) } open override func configureLayout() { super.configureLayout() - topViewTopConstraint = topSeparatorView.topAnchor.constraint(equalTo: contentView.topAnchor) - topViewRightConstraint = contentView.rightAnchor.constraint(equalTo: topSeparatorView.rightAnchor) - topViewLeftConstraint = topSeparatorView.leftAnchor.constraint(equalTo: contentView.leftAnchor) + if let separatorSuperview = topSeparatorView.superview { + topViewTopConstraint = topSeparatorView.topAnchor.constraint(equalTo: separatorSuperview.topAnchor) + topViewRightConstraint = separatorSuperview.rightAnchor.constraint(equalTo: topSeparatorView.rightAnchor) + topViewLeftConstraint = topSeparatorView.leftAnchor.constraint(equalTo: separatorSuperview.leftAnchor) + } + topViewHeightConstraint = topSeparatorView.heightAnchor.constraint(equalToConstant: 1) - bottomViewRightConstraint = contentView.rightAnchor.constraint(equalTo: bottomSeparatorView.rightAnchor) - bottomViewLeftConstraint = bottomSeparatorView.leftAnchor.constraint(equalTo: contentView.leftAnchor) - bottomViewBottomConstraint = bottomSeparatorView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor) + if let separatorSuperview = topSeparatorView.superview { + bottomViewRightConstraint = separatorSuperview.rightAnchor.constraint(equalTo: bottomSeparatorView.rightAnchor) + bottomViewLeftConstraint = bottomSeparatorView.leftAnchor.constraint(equalTo: separatorSuperview.leftAnchor) + bottomViewBottomConstraint = bottomSeparatorView.bottomAnchor.constraint(equalTo: separatorSuperview.bottomAnchor) + } + bottomViewHeightConstraint = bottomSeparatorView.heightAnchor.constraint(equalToConstant: 1) NSLayoutConstraint.activate([ @@ -119,15 +134,14 @@ private extension BaseSeparatorCell { topViewHeightConstraint?.constant = configuration.height topViewTopConstraint?.constant = configuration.insets.top topViewLeftConstraint?.constant = configuration.insets.left - topViewRightConstraint?.constant = configuration.insets.right - + topViewRightConstraint?.constant = -configuration.insets.right } func updateBottomSeparator(with configuration: SeparatorConfiguration) { bottomSeparatorView.backgroundColor = configuration.color bottomViewHeightConstraint?.constant = configuration.height - bottomViewBottomConstraint?.constant = configuration.insets.bottom + bottomViewBottomConstraint?.constant = -configuration.insets.bottom bottomViewLeftConstraint?.constant = configuration.insets.left - bottomViewRightConstraint?.constant = configuration.insets.right + bottomViewRightConstraint?.constant = -configuration.insets.right } } From 53e25e3a13e5025dcb5f5d17ae128153fdbcb123 Mon Sep 17 00:00:00 2001 From: Ivan Smolin Date: Fri, 23 Oct 2020 17:25:30 +0300 Subject: [PATCH 11/12] update podspec version to 0.11.0 --- TIFoundationUtils/TIFoundationUtils.podspec | 2 +- TISwiftUtils/TISwiftUtils.podspec | 2 +- TITableKitUtils/TITableKitUtils.podspec | 2 +- TIUIElements/TIUIElements.podspec | 2 +- TIUIKitCore/TIUIKitCore.podspec | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/TIFoundationUtils/TIFoundationUtils.podspec b/TIFoundationUtils/TIFoundationUtils.podspec index 000de78d..384ae080 100644 --- a/TIFoundationUtils/TIFoundationUtils.podspec +++ b/TIFoundationUtils/TIFoundationUtils.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIFoundationUtils' - s.version = '0.10.99' + s.version = '0.11.0' s.summary = 'Set of helpers for Foundation framework classes.' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TISwiftUtils/TISwiftUtils.podspec b/TISwiftUtils/TISwiftUtils.podspec index a1325f65..4a256658 100644 --- a/TISwiftUtils/TISwiftUtils.podspec +++ b/TISwiftUtils/TISwiftUtils.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TISwiftUtils' - s.version = '0.10.99' + s.version = '0.11.0' s.summary = 'Bunch of useful helpers for Swift development.' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TITableKitUtils/TITableKitUtils.podspec b/TITableKitUtils/TITableKitUtils.podspec index 97866c0c..3cacb356 100644 --- a/TITableKitUtils/TITableKitUtils.podspec +++ b/TITableKitUtils/TITableKitUtils.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TITableKitUtils' - s.version = '0.10.99' + s.version = '0.11.0' s.summary = 'Set of helpers for TableKit classes.' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TIUIElements/TIUIElements.podspec b/TIUIElements/TIUIElements.podspec index 8f864637..74097f9d 100644 --- a/TIUIElements/TIUIElements.podspec +++ b/TIUIElements/TIUIElements.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIUIElements' - s.version = '0.10.99' + s.version = '0.11.0' s.summary = 'Bunch of useful protocols and views.' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TIUIKitCore/TIUIKitCore.podspec b/TIUIKitCore/TIUIKitCore.podspec index 45014cf1..bd49a66e 100644 --- a/TIUIKitCore/TIUIKitCore.podspec +++ b/TIUIKitCore/TIUIKitCore.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIUIKitCore' - s.version = '0.10.99' + s.version = '0.11.0' s.summary = 'Core UI elements: protocols, views and helpers.' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } From 8b1e20d7901a24141306c9c48d33a3e4da3ea3e1 Mon Sep 17 00:00:00 2001 From: Ivan Smolin Date: Fri, 23 Oct 2020 17:25:50 +0300 Subject: [PATCH 12/12] update README --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 80043749..1e06c8f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +### 0.11.0 +- **Add**: Cocoapods support for TI-family libraries. +- **Add**: `SeparatorConfigurable` and all helper types for separator configuration. +- **Add**: `BaseSeparatorCell` - `BaseInitializeableCell` subclass with separators support. +- **Add**: `TITableKitUtils` - set of helpers for TableKit classes. +- **Add**: `BaseTextAttributes` and `ViewText` implementation form LeadKit. +- **Update**: `BaseInitializableView` and `BaseInitializableControl` are moved to `TIUIElements` from `TIUIKitCore`. + ### 0.10.9 - **Fix**: `change presentedOrTopViewController to open`.