Merge pull request #254 from TouchInstinct/feature/separators

Feature/separators
This commit is contained in:
Ivan Smolin 2020-10-23 18:34:00 +03:00 committed by GitHub
commit 6b3bf25f0a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 1425 additions and 44 deletions

77
.gitignore vendored
View File

@ -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

View File

@ -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`.

16
Package.resolved Normal file
View File

@ -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
}

View File

@ -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")
]
)

View File

@ -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'
# ...
```

View File

@ -0,0 +1,17 @@
Pod::Spec.new do |s|
s.name = 'TIFoundationUtils'
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' }
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
s.framework = 'Foundation'
end

View File

@ -18,7 +18,3 @@ final class ViewModel {
var hasFinishedOnboarding: Bool
}
```
# Installation via SPM
You can install this framework as a target of LeadKit.

View File

@ -20,8 +20,6 @@
// THE SOFTWARE.
//
import UIKit
public extension Optional where Wrapped == String {
var orEmpty: String {
self ?? ""

View File

@ -20,8 +20,6 @@
// THE SOFTWARE.
//
import Foundation
public extension Substring {
var string: String {
String(self)

View File

@ -20,8 +20,6 @@
// THE SOFTWARE.
//
import UIKit
/// Closure with custom arguments and return value.
public typealias Closure<Input, Output> = (Input) -> Output

View File

@ -0,0 +1,14 @@
Pod::Spec.new do |s|
s.name = 'TISwiftUtils'
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' }
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

View File

@ -0,0 +1,4 @@
# TITableKitUtils
Set of helpers for TableKit classes.

View File

@ -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)
}
}

View File

@ -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
}
}

View File

@ -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))
}
}
}

View File

@ -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<CellType>(.configure) {
$0.cell?.configureSeparators(with: separatorType)
}
action.id = configureSeparatorActionId
on(action)
}
}
public extension TableRow where CellType: SeparatorConfigurable {
/// TableRow typed as SeparatorRowBox
var separatorRowBox: SeparatorRowBox {
return SeparatorRowBox(row: self)
}
}

View File

@ -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<ViewSeparatorType>
public func set(separatorType: ViewSeparatorType) {
setSeparatorHandler(separatorType)
}
public let row: Row
public init<T>(row: TableRow<T>) where T: SeparatorConfigurable {
self.row = row
setSeparatorHandler = row.set
}
}

View File

@ -0,0 +1,18 @@
Pod::Spec.new do |s|
s.name = 'TITableKitUtils'
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' }
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

View File

@ -0,0 +1,147 @@
//
// 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()
}
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
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()
configureSeparators(with: .none)
}
// MARK: - InitializableView
open override func addViews() {
super.addViews()
add(topSeparatorView: topSeparatorView)
add(bottomSeparatorView: bottomSeparatorView)
}
open override func configureLayout() {
super.configureLayout()
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)
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([
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
}
}

View File

@ -0,0 +1,25 @@
//
// 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 SeparatorConfigurable {
func configureSeparators(with separatorType: ViewSeparatorType)
}

View File

@ -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
}
}

View File

@ -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
}
}
}

View File

@ -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)

View File

@ -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)

View File

@ -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, InitializableViewProtocol {
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
}
}

View File

@ -0,0 +1,16 @@
Pod::Spec.new do |s|
s.name = 'TIUIElements'
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' }
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

View File

@ -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.

View File

@ -20,7 +20,7 @@
// THE SOFTWARE.
//
public extension InitializableView {
public extension InitializableViewProtocol {
func initializeView() {
addViews()

View File

@ -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)
}

View File

@ -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()

View File

@ -0,0 +1,57 @@
//
// 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
}
}
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)
}
}

View File

@ -0,0 +1,91 @@
//
// 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
/// 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)
}
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 strings bounding rectangle.
/// - maxHeight: The width constraint to apply when computing the strings 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)
}
}

View File

@ -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
}
}
}

View File

@ -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
}
}
}

View File

@ -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
}
}
}

View File

@ -0,0 +1,69 @@
//
// 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 }
}
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
}
}

View File

@ -0,0 +1,15 @@
Pod::Spec.new do |s|
s.name = 'TIUIKitCore'
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' }
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