Merge branch 'master' into feature/web_view

# Conflicts:
#	CHANGELOG.md
#	LeadKit.podspec
#	TIAppleMapUtils/TIAppleMapUtils.podspec
#	TIAuth/TIAuth.podspec
#	TIEcommerce/TIEcommerce.podspec
#	TIFoundationUtils/TIFoundationUtils.podspec
#	TIGoogleMapUtils/TIGoogleMapUtils.podspec
#	TIKeychainUtils/TIKeychainUtils.podspec
#	TILogging/TILogging.podspec
#	TIMapUtils/TIMapUtils.podspec
#	TIMoyaNetworking/TIMoyaNetworking.podspec
#	TINetworking/TINetworking.podspec
#	TINetworkingCache/TINetworkingCache.podspec
#	TIPagination/TIPagination.podspec
#	TISwiftUICore/TISwiftUICore.podspec
#	TISwiftUtils/TISwiftUtils.podspec
#	TITableKitUtils/TITableKitUtils.podspec
#	TITransitions/TITransitions.podspec
#	TIUIElements/TIUIElements.podspec
#	TIUIKitCore/TIUIKitCore.podspec
#	TIYandexMapUtils/TIYandexMapUtils.podspec
This commit is contained in:
Nikita Semenov 2023-01-30 11:37:24 +03:00
commit a67c42886d
12 changed files with 319 additions and 7 deletions

View File

@ -4,6 +4,10 @@
- **Added**: `BaseInitializableWebView`with navigation and error handling api.
### 1.31.0
- **Added**: `URLInteractiveTextView` for terms and conditions hints in login flow
### 1.30.0
- **Added**: Base classes for encryption and decryption user token with pin code or biometry

View File

@ -26,7 +26,7 @@ import OSLog
import UIKit
@available(iOS 15, *)
open class LogsListViewController: BaseInitializeableViewController,
open class LogsListViewController: BaseInitializableViewController,
LogsListViewOutput,
AlertPresentationContext,
UISearchBarDelegate,

View File

@ -24,7 +24,7 @@ import TIUIKitCore
import UIKit
@available(iOS 15, *)
open class LoggingTogglingViewController: BaseInitializeableViewController {
open class LoggingTogglingViewController: BaseInitializableViewController {
private var initialCenter = CGPoint()

View File

@ -0,0 +1,60 @@
//
// 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.UITextView
import TIUIKitCore
open class BaseInitializableTextView: UITextView, InitializableViewProtocol {
public override init(frame: CGRect, textContainer: NSTextContainer?) {
super.init(frame: frame, textContainer: textContainer)
initializeView()
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initializeView()
}
// MARK: - InitializableView
open func addViews() {
// override in subclass
}
open func configureLayout() {
// override in subclass
}
open func bindViews() {
// override in subclass
}
open func configureAppearance() {
// override in subclass
}
open func localize() {
// override in subclass
}
}

View File

@ -0,0 +1,42 @@
//
// 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.UITextView
import TISwiftUtils
open class DefaultUITextViewURLInteractionHandler: NSObject, UITextViewURLInteractionHandler {
public var urlInteractionHandler: ParameterClosure<URL>?
public init(urlInteractionHandler: ParameterClosure<URL>?) {
self.urlInteractionHandler = urlInteractionHandler
}
open func textView(_ textView: UITextView,
shouldInteractWith URL: URL,
in characterRange: NSRange,
interaction: UITextItemInteraction) -> Bool {
urlInteractionHandler?(URL)
return false
}
}

View File

@ -0,0 +1,67 @@
//
// 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.UITextView
import TIUIKitCore
public extension UITextView {
private typealias InteractiveRange = (range: Range<String.Index>, url: URL)
struct InteractivePart {
public let text: String
public let url: URL?
public init(text: String, url: URL?) {
self.text = text
self.url = url
}
}
func set(text: String,
primaryTextStyle: BaseTextAttributes,
interactiveParts: [InteractivePart],
interactivePartsStyle: BaseTextAttributes) {
let mutableAttributedText = NSMutableAttributedString(string: text, attributes: primaryTextStyle.attributedStringAttributes)
let interactiveAttributes = interactivePartsStyle.attributedStringAttributes
interactiveParts
.compactMap { interactivePart -> InteractiveRange? in
guard let range = text.range(of: interactivePart.text),
let url = interactivePart.url else {
return nil
}
return InteractiveRange(range: range, url: url)
}
.forEach { range, url in
var partLinkAttributes = interactiveAttributes
partLinkAttributes.updateValue(url, forKey: .link)
mutableAttributedText.addAttributes(partLinkAttributes, range: NSRange(range, in: text))
}
attributedText = mutableAttributedText
}
}

View File

@ -0,0 +1,28 @@
//
// 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.UITextView
import TISwiftUtils
public protocol UITextViewURLInteractionHandler: UITextViewDelegate {
var urlInteractionHandler: ParameterClosure<URL>? { get set }
}

View File

@ -0,0 +1,52 @@
//
// 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.UIGeometry
import TIUIKitCore
open class URLInteractiveTextView: BaseInitializableTextView, ConfigurableView {
public var interactionHandler: UITextViewURLInteractionHandler? {
didSet {
delegate = interactionHandler
}
}
open override func configureAppearance() {
super.configureAppearance()
isEditable = false
textContainerInset = UIEdgeInsets(top: .zero,
left: -textContainer.lineFragmentPadding,
bottom: .zero,
right: -textContainer.lineFragmentPadding)
}
public func configure(with viewModel: URLInteractiveTextViewModel) {
set(text: viewModel.text,
primaryTextStyle: viewModel.primaryTextStyle,
interactiveParts: viewModel.interactiveParts,
interactivePartsStyle: viewModel.interactivePartsStyle)
interactionHandler = viewModel.interactionHandler
}
}

View File

@ -0,0 +1,59 @@
//
// 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 TISwiftUtils
import TIUIKitCore
import UIKit.UITextView
open class URLInteractiveTextViewModel {
public var text: String
public var primaryTextStyle: BaseTextAttributes
public var interactiveParts: [UITextView.InteractivePart]
public var interactivePartsStyle: BaseTextAttributes
public var interactionHandler: UITextViewURLInteractionHandler?
public init(text: String,
primaryTextStyle: BaseTextAttributes,
interactiveParts: [UITextView.InteractivePart],
interactivePartsStyle: BaseTextAttributes,
interactionHandler: UITextViewURLInteractionHandler?) {
self.text = text
self.primaryTextStyle = primaryTextStyle
self.interactiveParts = interactiveParts
self.interactivePartsStyle = interactivePartsStyle
self.interactionHandler = interactionHandler
}
convenience init(text: String,
primaryTextStyle: BaseTextAttributes,
interactiveParts: [UITextView.InteractivePart],
interactivePartsStyle: BaseTextAttributes,
urlInteractionHandler: ParameterClosure<URL>?) {
self.init(text: text,
primaryTextStyle: primaryTextStyle,
interactiveParts: interactiveParts,
interactivePartsStyle: interactivePartsStyle,
interactionHandler: DefaultUITextViewURLInteractionHandler(urlInteractionHandler: urlInteractionHandler))
}
}

View File

@ -20,12 +20,12 @@
// THE SOFTWARE.
//
public protocol InitializeableViewController: InitializableViewProtocol {
public protocol InitializableViewController: InitializableViewProtocol {
func configureBarButtons()
}
public extension InitializeableViewController {
public extension InitializableViewController {
func initializeView() {
assertionFailure("Use \(String(describing: initializeController)) for UIViewController instead!")
}

View File

@ -22,7 +22,7 @@
import UIKit
open class BaseCustomViewController<View: UIView>: BaseInitializeableViewController {
open class BaseCustomViewController<View: UIView>: BaseInitializableViewController {
public private(set) lazy var customView = createView()

View File

@ -22,7 +22,7 @@
import UIKit.UIViewController
open class BaseInitializeableViewController: UIViewController, InitializeableViewController {
open class BaseInitializableViewController: UIViewController, InitializableViewController {
override open func viewDidLoad() {
super.viewDidLoad()
@ -34,7 +34,7 @@ open class BaseInitializeableViewController: UIViewController, InitializeableVie
initializeController()
}
// MARK: - InitializeableController
// MARK: - InitializableController
open func addViews() {
// override in subclass