feat: UITextView now support configuration with BaseTextAttributes
ReconfigurableView & ChangeableViewModel for non-destructing state update WrappedViewHolder protocol with table/collection view cell implementations
This commit is contained in:
parent
7e319dcb03
commit
88da2ab508
|
|
@ -1,5 +1,12 @@
|
|||
# Changelog
|
||||
|
||||
### 1.23.0
|
||||
|
||||
- **Update**: `UITextView` now support configuration with `BaseTextAttributes`
|
||||
- **Add**: `ReconfigurableView` & `ChangeableViewModel` for non-destructing state update
|
||||
- **Add**: `WrappedViewHolder` protocol with table/collection view cell implementations
|
||||
- **Add**: `UIViewPresenter` and `ReusableUIViewPresenter ` protocols with default implementation for proper handling view/cells reuse
|
||||
|
||||
### 1.22.0
|
||||
|
||||
- **Update**: Asynchronous request preprocessing
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = "LeadKit"
|
||||
s.version = "1.22.0"
|
||||
s.version = "1.23.0"
|
||||
s.summary = "iOS framework with a bunch of tools for rapid development"
|
||||
s.homepage = "https://github.com/TouchInstinct/LeadKit"
|
||||
s.license = "Apache License, Version 2.0"
|
||||
|
|
|
|||
24
README.md
24
README.md
|
|
@ -1,23 +1,27 @@
|
|||
# LeadKit
|
||||
|
||||
LeadKit is the iOS framework with a bunch of tools for rapid app development.
|
||||
|
||||
## Additional
|
||||
|
||||
This repository contains the following additional frameworks:
|
||||
This repository contains the following frameworks:
|
||||
|
||||
- [TISwiftUtils](TISwiftUtils) - a bunch of useful helpers for Swift development.
|
||||
- [TIFoundationUtils](TIFoundationUtils) - set of helpers for Foundation framework classes.
|
||||
- [TIUIKitCore](TIUIKitCore) - core ui elements and protocols from LeadKit.
|
||||
- [TITransitions](TITransitions) - set of custom transitions to present controller.
|
||||
- [TISwiftUICore](TISwiftUICore) Core UI elements: protocols, views and helpers.
|
||||
- [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.
|
||||
- [TIFoundationUtils](TIFoundationUtils) - Set of helpers for Foundation framework classes.
|
||||
- [TIKeychainUtils](TIKeychainUtils) - Set of helpers for Keychain classes.
|
||||
- [TITableKitUtils](TITableKitUtils) - set of helpers for TableKit classes.
|
||||
- [TIKeychainUtils](TIKeychainUtils) - set of helpers for Keychain classes.
|
||||
- [TIPagination](TIPagination) - realisation of paginating items from a data source.
|
||||
- [TINetworking](TINetworking) - Swagger-frendly networking layer helpers.
|
||||
- [TIMoyaNetworking](TIMoyaNetworking) - Moya + Swagger network service.
|
||||
- [TIAppleMapUtils](TIAppleMapUtils) - set of helpers for map objects clustering and interacting using Apple MapKit.
|
||||
- [TIGoogleMapUtils](TIGoogleMapUtils) - set of helpers for map objects clustering and interacting using Google Maps SDK.
|
||||
- [TIYandexMapUtils](TIYandexMapUtils) - set of helpers for map objects clustering and interacting using Yandex Maps SDK.
|
||||
- [TIAuth](TIAuth) - login, registration, confirmation and other related actions
|
||||
|
||||
Useful docs:
|
||||
|
||||
- [Semantic Commit Messages](docs/semantic-commit-messages.md) - commit message codestyle.
|
||||
- [Snippets](docs/snippets.md) - useful commands and scripts for development.
|
||||
|
||||
|
|
@ -51,3 +55,7 @@ pod 'TISwiftUtils', 'x.y.z'
|
|||
pod 'TIFoundationUtils', 'x.y.z'
|
||||
# ...
|
||||
```
|
||||
|
||||
## Legacy
|
||||
|
||||
Code located in root `Sources` folder and `LeadKit.podspec` should be treated as legacy and shouldn't be used in newly created projects. Please use TI* modules via SPM or CocoaPods.
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIAppleMapUtils'
|
||||
s.version = '1.22.0'
|
||||
s.version = '1.23.0'
|
||||
s.summary = 'Set of helpers for map objects clustering and interacting using Apple MapKit.'
|
||||
s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIAuth'
|
||||
s.version = '1.22.0'
|
||||
s.version = '1.23.0'
|
||||
s.summary = 'Login, registration, confirmation and other related actions'
|
||||
s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIFoundationUtils'
|
||||
s.version = '1.22.0'
|
||||
s.version = '1.23.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' }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIGoogleMapUtils'
|
||||
s.version = '1.22.0'
|
||||
s.version = '1.23.0'
|
||||
s.summary = 'Set of helpers for map objects clustering and interacting using Google Maps SDK.'
|
||||
s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIKeychainUtils'
|
||||
s.version = '1.22.0'
|
||||
s.version = '1.23.0'
|
||||
s.summary = 'Set of helpers for Keychain classes.'
|
||||
s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIMapUtils'
|
||||
s.version = '1.22.0'
|
||||
s.version = '1.23.0'
|
||||
s.summary = 'Set of helpers for map objects clustering and interacting.'
|
||||
s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIMoyaNetworking'
|
||||
s.version = '1.22.0'
|
||||
s.version = '1.23.0'
|
||||
s.summary = 'Moya + Swagger network service.'
|
||||
s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -38,11 +38,11 @@ open class DefaultTokenInterceptor<RefreshError: Error>: RequestInterceptor {
|
|||
public var defaultRetryStrategy: RetryResult = .doNotRetry
|
||||
public var requestModificationClosure: RequestModificationClosure?
|
||||
|
||||
public init(isTokenInvalidClosure: @escaping ShouldRefreshTokenClosure,
|
||||
public init(shouldRefreshTokenClosure: @escaping ShouldRefreshTokenClosure,
|
||||
refreshTokenClosure: @escaping RefreshTokenClosure,
|
||||
requestModificationClosure: RequestModificationClosure? = nil) {
|
||||
|
||||
self.shouldRefreshToken = isTokenInvalidClosure
|
||||
self.shouldRefreshToken = shouldRefreshTokenClosure
|
||||
self.refreshTokenClosure = refreshTokenClosure
|
||||
self.requestModificationClosure = requestModificationClosure
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,14 +29,14 @@ open class EndpointResponseTokenInterceptor<AE, NE>: DefaultTokenInterceptor<End
|
|||
|
||||
private let isTokenInvalidErrorResultClosure: IsTokenInvalidErrorResultClosure
|
||||
|
||||
public init(isTokenInvalidClosure: @escaping ShouldRefreshTokenClosure,
|
||||
public init(shouldRefreshTokenClosure: @escaping ShouldRefreshTokenClosure,
|
||||
refreshTokenClosure: @escaping RefreshTokenClosure,
|
||||
isTokenInvalidErrorResultClosure: @escaping IsTokenInvalidErrorResultClosure,
|
||||
requestModificationClosure: RequestModificationClosure? = nil) {
|
||||
|
||||
self.isTokenInvalidErrorResultClosure = isTokenInvalidErrorResultClosure
|
||||
|
||||
super.init(isTokenInvalidClosure: isTokenInvalidClosure,
|
||||
super.init(shouldRefreshTokenClosure: shouldRefreshTokenClosure,
|
||||
refreshTokenClosure: refreshTokenClosure,
|
||||
requestModificationClosure: requestModificationClosure)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ open class DefaultEndpointSecurityPreprocessor: EndpointRequestPreprocessor {
|
|||
switch $0 {
|
||||
case let .success(modifiedRequest):
|
||||
completion(.success(modifiedRequest))
|
||||
case let .failure(error):
|
||||
case .failure:
|
||||
completion(.failure(PreprocessError.unableToSatisfyRequirements(anyOfRequired: request.security,
|
||||
registeredPreprocessors: schemePreprocessors)))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TINetworking'
|
||||
s.version = '1.22.0'
|
||||
s.version = '1.23.0'
|
||||
s.summary = 'Swagger-frendly networking layer helpers.'
|
||||
s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TINetworkingCache'
|
||||
s.version = '1.22.0'
|
||||
s.version = '1.23.0'
|
||||
s.summary = 'Caching results of EndpointRequests.'
|
||||
s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIPagination'
|
||||
s.version = '1.22.0'
|
||||
s.version = '1.23.0'
|
||||
s.summary = 'Generic pagination component.'
|
||||
s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TISwiftUICore'
|
||||
s.version = '1.22.0'
|
||||
s.version = '1.23.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' }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TISwiftUtils'
|
||||
s.version = '1.22.0'
|
||||
s.version = '1.23.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' }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TITableKitUtils'
|
||||
s.version = '1.22.0'
|
||||
s.version = '1.23.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' }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TITransitions'
|
||||
s.version = '1.22.0'
|
||||
s.version = '1.23.0'
|
||||
s.summary = 'Set of custom transitions to present controller. '
|
||||
s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,78 @@
|
|||
//
|
||||
// Copyright (c) 2022 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import TIUIKitCore
|
||||
|
||||
open class ContainerCollectionViewCell<View: UIView>: UICollectionViewCell, InitializableViewProtocol, WrappedViewHolder {
|
||||
// MARK: - WrappedViewHolder
|
||||
|
||||
public private(set) lazy var wrappedView = createView()
|
||||
|
||||
public var contentInsets: UIEdgeInsets = .zero {
|
||||
didSet {
|
||||
contentEdgeConstraints?.update(from: contentInsets)
|
||||
}
|
||||
}
|
||||
|
||||
private var contentEdgeConstraints: EdgeConstraints?
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
initializeView()
|
||||
}
|
||||
|
||||
required public init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
|
||||
initializeView()
|
||||
}
|
||||
|
||||
// MARK: - InitializableView
|
||||
|
||||
open func addViews() {
|
||||
addSubview(wrappedView)
|
||||
}
|
||||
|
||||
open func bindViews() {
|
||||
// override in subclass
|
||||
}
|
||||
|
||||
open func configureLayout() {
|
||||
contentEdgeConstraints = configureWrappedViewLayout()
|
||||
}
|
||||
|
||||
open func configureAppearance() {
|
||||
// override in subclass
|
||||
}
|
||||
|
||||
open func localize() {
|
||||
// override in subclass
|
||||
}
|
||||
|
||||
open func createView() -> View {
|
||||
return View()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
//
|
||||
// Copyright (c) 2022 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import TIUIKitCore
|
||||
|
||||
open class ContainerTableViewCell<View: UIView>: BaseInitializableCell, WrappedViewHolder {
|
||||
// MARK: - WrappedViewHolder
|
||||
|
||||
public private(set) lazy var wrappedView = createView()
|
||||
|
||||
public var contentInsets: UIEdgeInsets = .zero {
|
||||
didSet {
|
||||
contentEdgeConstraints?.update(from: contentInsets)
|
||||
}
|
||||
}
|
||||
|
||||
private var contentEdgeConstraints: EdgeConstraints?
|
||||
|
||||
// MARK: - InitializableView
|
||||
|
||||
override open func addViews() {
|
||||
super.addViews()
|
||||
|
||||
addSubview(wrappedView)
|
||||
}
|
||||
|
||||
override open func configureLayout() {
|
||||
super.configureLayout()
|
||||
|
||||
contentEdgeConstraints = configureWrappedViewLayout()
|
||||
}
|
||||
|
||||
open func createView() -> View {
|
||||
return View()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
//
|
||||
// Copyright (c) 2022 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 EdgeConstraints {
|
||||
public let leadingConstraint: NSLayoutConstraint
|
||||
public let trailingConstraint: NSLayoutConstraint
|
||||
public let topConstraint: NSLayoutConstraint
|
||||
public let bottomConstraint: NSLayoutConstraint
|
||||
|
||||
public var allConstraints: [NSLayoutConstraint] {
|
||||
[
|
||||
leadingConstraint,
|
||||
trailingConstraint,
|
||||
topConstraint,
|
||||
bottomConstraint
|
||||
]
|
||||
}
|
||||
|
||||
public func activate() {
|
||||
NSLayoutConstraint.activate(allConstraints)
|
||||
}
|
||||
|
||||
public func deactivate() {
|
||||
NSLayoutConstraint.deactivate(allConstraints)
|
||||
}
|
||||
|
||||
public func update(from insets: UIEdgeInsets) {
|
||||
leadingConstraint.constant = insets.left
|
||||
trailingConstraint.constant = -insets.right
|
||||
topConstraint.constant = insets.top
|
||||
bottomConstraint.constant = -insets.bottom
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
//
|
||||
// Copyright (c) 2022 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import TIUIKitCore
|
||||
|
||||
open class ReusableCollectionContainerView<View: UIView>: UICollectionReusableView, InitializableViewProtocol, WrappedViewHolder {
|
||||
// MARK: - WrappedViewHolder
|
||||
|
||||
public private(set) lazy var wrappedView = createView()
|
||||
|
||||
public var contentInsets: UIEdgeInsets = .zero {
|
||||
didSet {
|
||||
contentEdgeConstraints?.update(from: contentInsets)
|
||||
}
|
||||
}
|
||||
|
||||
private var contentEdgeConstraints: EdgeConstraints?
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
initializeView()
|
||||
}
|
||||
|
||||
required public init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
|
||||
initializeView()
|
||||
}
|
||||
|
||||
// MARK: - InitializableView
|
||||
|
||||
open func addViews() {
|
||||
addSubview(wrappedView)
|
||||
}
|
||||
|
||||
open func bindViews() {
|
||||
// override in subclass
|
||||
}
|
||||
|
||||
open func configureLayout() {
|
||||
contentEdgeConstraints = configureWrappedViewLayout()
|
||||
}
|
||||
|
||||
open func configureAppearance() {
|
||||
// override in subclass
|
||||
}
|
||||
|
||||
open func localize() {
|
||||
// override in subclass
|
||||
}
|
||||
|
||||
open func createView() -> View {
|
||||
return View()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
// Copyright (c) 2022 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
|
||||
|
||||
public extension WrappedViewHolder where View: ConfigurableView {
|
||||
func configure(with viewModel: View.ViewModelType) {
|
||||
wrappedView.configure(with: viewModel)
|
||||
}
|
||||
}
|
||||
|
||||
public extension WrappedViewHolder where View: ReconfigurableView {
|
||||
func apply(change: View.ViewModelType.Change) {
|
||||
wrappedView.apply(change: change)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
//
|
||||
// Copyright (c) 2022 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 protocol WrappedViewHolder {
|
||||
associatedtype View: UIView
|
||||
|
||||
var wrappedView: View { get }
|
||||
var contentView: UIView { get }
|
||||
var contentInsets: UIEdgeInsets { get set }
|
||||
}
|
||||
|
||||
public extension WrappedViewHolder {
|
||||
func wrappedViewConstraints() -> EdgeConstraints {
|
||||
.init(leadingConstraint: wrappedView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
|
||||
trailingConstraint: wrappedView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
|
||||
topConstraint: wrappedView.topAnchor.constraint(equalTo: contentView.topAnchor),
|
||||
bottomConstraint: wrappedView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor))
|
||||
}
|
||||
|
||||
func configureWrappedViewLayout() -> EdgeConstraints {
|
||||
wrappedView.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
let contentEdgeConstraints = wrappedViewConstraints()
|
||||
contentEdgeConstraints.activate()
|
||||
|
||||
return contentEdgeConstraints
|
||||
}
|
||||
}
|
||||
|
||||
public extension WrappedViewHolder where Self: UIView {
|
||||
var contentView: UIView {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIUIElements'
|
||||
s.version = '1.22.0'
|
||||
s.version = '1.23.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' }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
//
|
||||
// Copyright (c) 2022 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 ChangeableViewModel {
|
||||
associatedtype Change
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
//
|
||||
// Copyright (c) 2022 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 ReconfigurableView: ConfigurableView where ViewModelType: ChangeableViewModel {
|
||||
func apply(change: ViewModelType.Change)
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
//
|
||||
// Copyright (c) 2022 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.
|
||||
//
|
||||
|
||||
open class DefaultUIViewPresenter<View: AnyObject>: ReusableUIViewPresenter{
|
||||
public private(set) weak var view: View?
|
||||
|
||||
public init() {}
|
||||
|
||||
// MARK: - UIViewPresenter
|
||||
|
||||
open func didCompleteConfiguration(of view: View) {
|
||||
self.view = view
|
||||
}
|
||||
|
||||
// MARK: - ReusableUIViewPresenter
|
||||
|
||||
open func willReuse(view: View) {
|
||||
if didConfigure(view: view) {
|
||||
self.view = nil
|
||||
}
|
||||
}
|
||||
|
||||
open func didConfigure(view: View) -> Bool {
|
||||
self.view === view
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
//
|
||||
// Copyright (c) 2022 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 ReusableUIViewPresenter: UIViewPresenter {
|
||||
func willReuse(view: View)
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
// Copyright (c) 2022 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 UIViewPresenter {
|
||||
associatedtype View: AnyObject // should be stored weakly
|
||||
|
||||
func didCompleteConfiguration(of view: View)
|
||||
}
|
||||
|
|
@ -20,8 +20,7 @@
|
|||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import UIKit.UIFont
|
||||
import UIKit.UIColor
|
||||
import UIKit
|
||||
|
||||
/// Base set of attributes to configure appearance of text.
|
||||
open class BaseTextAttributes {
|
||||
|
|
@ -32,6 +31,18 @@ open class BaseTextAttributes {
|
|||
public let lineHeightMultiple: CGFloat
|
||||
public let numberOfLines: Int
|
||||
|
||||
open var attributedStringAttributes: [NSAttributedString.Key : Any] {
|
||||
let paragraphStyle = NSMutableParagraphStyle()
|
||||
paragraphStyle.alignment = alignment
|
||||
paragraphStyle.lineHeightMultiple = lineHeightMultiple
|
||||
|
||||
return [
|
||||
.font: font,
|
||||
.foregroundColor: color,
|
||||
.paragraphStyle: paragraphStyle
|
||||
]
|
||||
}
|
||||
|
||||
public init(font: UIFont,
|
||||
color: UIColor,
|
||||
alignment: NSTextAlignment,
|
||||
|
|
@ -61,6 +72,10 @@ open class BaseTextAttributes {
|
|||
configure(textContainer: textField)
|
||||
}
|
||||
|
||||
open func configure(textView: UITextView) {
|
||||
configure(textContainer: textView)
|
||||
}
|
||||
|
||||
open func configure(button: UIButton, for state: UIControl.State) {
|
||||
if let buttonLabel = button.titleLabel {
|
||||
configure(label: buttonLabel)
|
||||
|
|
@ -110,6 +125,14 @@ open class BaseTextAttributes {
|
|||
attributedTextConfiguration: { textField.attributedText = $0 })
|
||||
}
|
||||
|
||||
open func configure(textView: UITextView, with string: String?) {
|
||||
configure(textContainer: textView,
|
||||
with: string,
|
||||
appearanceConfiguration: configure(textView:),
|
||||
textConfiguration: { textView.text = $0 },
|
||||
attributedTextConfiguration: { textView.attributedText = $0 })
|
||||
}
|
||||
|
||||
open func configure(button: UIButton, with string: String?, for state: UIControl.State) {
|
||||
configure(textContainer: button,
|
||||
with: string,
|
||||
|
|
@ -122,17 +145,12 @@ open class BaseTextAttributes {
|
|||
}
|
||||
|
||||
open func attributedString(for string: String) -> NSAttributedString {
|
||||
let paragraphStyle = NSMutableParagraphStyle()
|
||||
paragraphStyle.alignment = alignment
|
||||
paragraphStyle.lineHeightMultiple = lineHeightMultiple
|
||||
NSAttributedString(string: string, attributes: attributedStringAttributes)
|
||||
}
|
||||
|
||||
let attributes: [NSAttributedString.Key: Any] = [
|
||||
.font: font,
|
||||
.foregroundColor: color,
|
||||
.paragraphStyle: paragraphStyle
|
||||
]
|
||||
|
||||
return NSAttributedString(string: string, attributes: attributes)
|
||||
open func apply(in attributedString: NSMutableAttributedString, at range: NSRange? = nil) {
|
||||
attributedString.addAttributes(attributedStringAttributes,
|
||||
range: range ?? NSRange(location: 0, length: attributedString.length))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
import UIKit
|
||||
|
||||
protocol BaseTextAttributesConfigurable {
|
||||
public protocol BaseTextAttributesConfigurable {
|
||||
func set(font: UIFont)
|
||||
func set(color: UIColor)
|
||||
func set(alignment: NSTextAlignment)
|
||||
|
|
@ -55,3 +55,17 @@ extension UITextField: BaseTextAttributesConfigurable {
|
|||
textAlignment = alignment
|
||||
}
|
||||
}
|
||||
|
||||
extension UITextView: BaseTextAttributesConfigurable {
|
||||
public func set(font: UIFont) {
|
||||
self.font = font
|
||||
}
|
||||
|
||||
public func set(color: UIColor) {
|
||||
textColor = color
|
||||
}
|
||||
|
||||
public func set(alignment: NSTextAlignment) {
|
||||
textAlignment = alignment
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,6 +68,10 @@ public struct ViewText {
|
|||
attributes.configure(textField: textField, with: text)
|
||||
}
|
||||
|
||||
public func configure(textView: UITextView) {
|
||||
attributes.configure(textView: textView, with: text)
|
||||
}
|
||||
|
||||
public func configure(button: UIButton, for state: UIControl.State) {
|
||||
attributes.configure(button: button, with: text, for: state)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIUIKitCore'
|
||||
s.version = '1.22.0'
|
||||
s.version = '1.23.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' }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIYandexMapUtils'
|
||||
s.version = '1.22.0'
|
||||
s.version = '1.23.0'
|
||||
s.summary = 'Set of helpers for map objects clustering and interacting using Yandex Maps SDK.'
|
||||
s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
Loading…
Reference in New Issue