diff --git a/Code/Behavior/ELDefaultTextFieldBehavior.swift b/Code/Behavior/ELDefaultTextFieldBehavior.swift index 45ffa2b..6179383 100644 --- a/Code/Behavior/ELDefaultTextFieldBehavior.swift +++ b/Code/Behavior/ELDefaultTextFieldBehavior.swift @@ -7,6 +7,7 @@ import Foundation import UIKit /// Описывает поведение поля ввода по умолчанию +@MainActor open class ELDefaultTextFieldBehavior: NSObject, ELTextFieldBehavior { public let mask: ELTextFieldInputMask @@ -30,7 +31,7 @@ open class ELDefaultTextFieldBehavior: NSObject, ELTextFieldBehavior { private var customRightMode: ELRightViewMode? private var customLeftMode: ELLeftViewMode? - public var onAction: ((ELBehaviorAction) -> Void)? + public var onAction: (@Sendable (ELBehaviorAction) -> Void)? public let isEditable: Bool public weak var containerDelegate: ELContainerDelegate? public private(set) var textInput: (ELTextInput & ELTextInputConfigurable)? @@ -52,9 +53,9 @@ open class ELDefaultTextFieldBehavior: NSObject, ELTextFieldBehavior { /// - validation: Правило валидации поля public init( text: String? = nil, - textMapper: ((String?) -> NSAttributedString?)? = nil, + textMapper: (@Sendable (String?) -> NSAttributedString?)? = nil, placeholder: String? = nil, - placeholderMapper: ((String?) -> NSAttributedString?)? = nil, + placeholderMapper: (@Sendable (String?) -> NSAttributedString?)? = nil, isEditable: Bool = true, leftMode: ELLeftViewMode? = nil, rightMode: ELRightViewMode? = nil, @@ -78,7 +79,7 @@ open class ELDefaultTextFieldBehavior: NSObject, ELTextFieldBehavior { attributedTextMapper: textMapper ) } - + open func configure(textInput: ELTextInput & ELTextInputConfigurable) { self.textInput = textInput textInput.inputView = nil @@ -89,7 +90,7 @@ open class ELDefaultTextFieldBehavior: NSObject, ELTextFieldBehavior { textInput.configureRightItem(with: customRightMode?.initialContainer(textInput: textInput)) textInput.configureLeftItem(with: customLeftMode?.initialContainer(textInput: textInput)) } - + open func updateState(_ state: ELTextFieldState) { viewModel.state = state textInput?.configureRightItem( @@ -107,16 +108,16 @@ open class ELDefaultTextFieldBehavior: NSObject, ELTextFieldBehavior { textInput?.updateState(viewModel.state) containerDelegate?.container(self, changedState: state) } - + open func updateText(_ newText: String?) { updateText(newValue: newText) } - + public func textInputShouldClear(_: ELTextInput) -> Bool { updateText("") return true } - + open func textInputShouldBeginEditing(_: ELTextInput) -> Bool { guard isEditable else { return false @@ -131,7 +132,7 @@ open class ELDefaultTextFieldBehavior: NSObject, ELTextFieldBehavior { return true } } - + public func textInputDidBeginEditing(_ textInput: ELTextInput) { containerDelegate?.startEditing(in: self) } @@ -144,11 +145,11 @@ open class ELDefaultTextFieldBehavior: NSObject, ELTextFieldBehavior { updateState(.default) } } - + /// При вводе текста свайпом происходит рекурсивный вызов методов: /// https://stackoverflow.com/questions/58560843/ios-13-crash-with-swipekeyboard-and-textfieldshouldchangecharactersin private var lastEntry: String? - + open func textInput( _ textInput: ELTextInput & UITextInput, shouldChangeCharactersIn range: NSRange, @@ -205,7 +206,7 @@ open class ELDefaultTextFieldBehavior: NSObject, ELTextFieldBehavior { range: range ) } - + private func updateText(newValue: String?) { textInput?.enteredText = newValue viewModel.text = newValue.isNilOrEmpty ? nil : newValue @@ -225,14 +226,14 @@ open class ELDefaultTextFieldBehavior: NSObject, ELTextFieldBehavior { } return true } - + public func textInputShouldReturn(_ textInput: ELTextInput) -> Bool { onAction?(.return) containerDelegate?.return(in: self) textInput.resignFirstResponder() return true } - + open func textInput(_: ELTextInput, canPerformAction _: Selector, withSender _: Any?) -> Bool { true } @@ -266,7 +267,7 @@ private extension UITextInput { offset = range.location } } - + guard let textPosition = position(from: beginningOfDocument, offset: offset) else { return } diff --git a/Code/Behavior/ELTextFieldBehavior.swift b/Code/Behavior/ELTextFieldBehavior.swift index db2e09c..3d1916b 100644 --- a/Code/Behavior/ELTextFieldBehavior.swift +++ b/Code/Behavior/ELTextFieldBehavior.swift @@ -6,7 +6,7 @@ import Foundation /// Действия, которые могут произойти с Behavior -public enum ELBehaviorAction { +public enum ELBehaviorAction: Sendable { /// Текст был изменен case changed(newValue: String) /// Ввод завершен @@ -18,6 +18,7 @@ public enum ELBehaviorAction { } /// Хранит состояние поля ввода +@MainActor public protocol ELTextFieldBehavior: ELTextInputDelegate { /// Маска поля ввода @@ -34,10 +35,10 @@ public protocol ELTextFieldBehavior: ELTextInputDelegate { var isValid: Bool { get } /// Срабатывает при срабатывании событий поля ввода - var onAction: ((ELBehaviorAction) -> Void)? { get set } + var onAction: (@Sendable (ELBehaviorAction) -> Void)? { get set } /// Используется для обработки дополнительных событий делегата var containerDelegate: ELContainerDelegate? { get set } - + func configure(textInput: ELTextInput & ELTextInputConfigurable) func updateState(_ state: ELTextFieldState) func updateText(_ newText: String?) diff --git a/Code/Behavior/ELTextFieldState.swift b/Code/Behavior/ELTextFieldState.swift index d1bc0de..6c73f99 100644 --- a/Code/Behavior/ELTextFieldState.swift +++ b/Code/Behavior/ELTextFieldState.swift @@ -6,7 +6,7 @@ import Foundation /// Состояние TextField -public enum ELTextFieldState { +public enum ELTextFieldState: Sendable { /// Не в фокусе case `default` /// Ошибка (например, валидации) diff --git a/Code/Configuration/Configuration/ELTextInputConfigurable.swift b/Code/Configuration/Configuration/ELTextInputConfigurable.swift index ba8674a..9529cf5 100644 --- a/Code/Configuration/Configuration/ELTextInputConfigurable.swift +++ b/Code/Configuration/Configuration/ELTextInputConfigurable.swift @@ -7,6 +7,7 @@ import Foundation import UIKit /// Конфигурация представления TextInput +@MainActor public protocol ELTextInputConfigurable: AnyObject { var textInputDelegate: ELTextInputDelegate? { get set } var touchesDelegate: ELTouchesDelegate? { get set } diff --git a/Code/Configuration/Context/InputMask/ELTextFieldInputMask.swift b/Code/Configuration/Context/InputMask/ELTextFieldInputMask.swift index 1221b02..e6c8aac 100644 --- a/Code/Configuration/Context/InputMask/ELTextFieldInputMask.swift +++ b/Code/Configuration/Context/InputMask/ELTextFieldInputMask.swift @@ -6,7 +6,7 @@ import Foundation /// Маска для преобразования введенного текста в пользовательское представление и наоборот -public protocol ELTextFieldInputMask { +public protocol ELTextFieldInputMask: Sendable { /// Возвращает текст для отображения пользователю /// diff --git a/Code/Configuration/Context/InputTraits/ELTextFieldInputTraits.swift b/Code/Configuration/Context/InputTraits/ELTextFieldInputTraits.swift index 39cd205..031b6ca 100644 --- a/Code/Configuration/Context/InputTraits/ELTextFieldInputTraits.swift +++ b/Code/Configuration/Context/InputTraits/ELTextFieldInputTraits.swift @@ -7,7 +7,7 @@ import Foundation import UIKit /// Содержит все поля, необходимые для настройки ввода -public protocol ELTextFieldInputTraits { +public protocol ELTextFieldInputTraits: Sendable { var isSecureTextEntry: Bool { get set } var keyboardType: UIKeyboardType { get set } var contentType: UITextContentType? { get set } diff --git a/Code/Configuration/Context/RightView/ELRightViewContainer.swift b/Code/Configuration/Context/RightView/ELRightViewContainer.swift index ea57fc9..4ea2855 100644 --- a/Code/Configuration/Context/RightView/ELRightViewContainer.swift +++ b/Code/Configuration/Context/RightView/ELRightViewContainer.swift @@ -9,7 +9,7 @@ import Foundation import UIKit /// Содержит информацию о rightView -public struct ELRightViewContainer { +public struct ELRightViewContainer: Sendable { let view: UIView? let rightViewMode: UITextField.ViewMode let clearButtonMode: UITextField.ViewMode @@ -52,7 +52,7 @@ public struct ELRightViewContainer { } } -public struct ELLeftViewContainer { +public struct ELLeftViewContainer: Sendable { let view: UIView? let leftViewMode: UITextField.ViewMode diff --git a/Code/Configuration/Context/RightView/ELRightViewMode.swift b/Code/Configuration/Context/RightView/ELRightViewMode.swift index 40ab7b6..bdacfa2 100644 --- a/Code/Configuration/Context/RightView/ELRightViewMode.swift +++ b/Code/Configuration/Context/RightView/ELRightViewMode.swift @@ -9,6 +9,7 @@ import Foundation import UIKit /// Протокол для создания кастомного поведения правой иконки +@MainActor public protocol ELRightViewMode: AnyObject { func initialContainer(textInput: ELTextInput) -> ELRightViewContainer @@ -19,6 +20,7 @@ public protocol ELRightViewMode: AnyObject { } /// Протокол для создания кастомного поведения левой иконки +@MainActor public protocol ELLeftViewMode: AnyObject { func initialContainer(textInput: ELTextInput) -> ELLeftViewContainer diff --git a/Code/Configuration/Context/RightView/ELSystemActionRightView.swift b/Code/Configuration/Context/RightView/ELSystemActionRightView.swift index eb66f4d..a54f9d3 100644 --- a/Code/Configuration/Context/RightView/ELSystemActionRightView.swift +++ b/Code/Configuration/Context/RightView/ELSystemActionRightView.swift @@ -8,6 +8,7 @@ import Foundation import UIKit +@MainActor public final class ELSystemActionRightView: ELRightViewMode { private let view: UIView diff --git a/Code/Configuration/Context/Validation/ELDefaultTextFieldValidator.swift b/Code/Configuration/Context/Validation/ELDefaultTextFieldValidator.swift index d7ce0db..6ed320d 100644 --- a/Code/Configuration/Context/Validation/ELDefaultTextFieldValidator.swift +++ b/Code/Configuration/Context/Validation/ELDefaultTextFieldValidator.swift @@ -8,7 +8,7 @@ import Foundation /// Поведение по-умолчанию подразумевает, что поле ввода валидно, если оно не пустое -public class ELDefaultTextFieldValidator: ELTextFieldValidator { +public final class ELDefaultTextFieldValidator: ELTextFieldValidator { public func isValid(text: String?) -> Bool { text?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == false diff --git a/Code/Configuration/Context/Validation/ELMailTextFieldValidator.swift b/Code/Configuration/Context/Validation/ELMailTextFieldValidator.swift index a6cafca..e26ce69 100644 --- a/Code/Configuration/Context/Validation/ELMailTextFieldValidator.swift +++ b/Code/Configuration/Context/Validation/ELMailTextFieldValidator.swift @@ -8,7 +8,7 @@ import Foundation /// Валидатор поля e-mail -public class ELMailTextFieldValidator: ELTextFieldValidator { +public final class ELMailTextFieldValidator: ELTextFieldValidator { private enum Constants { static let matchRule = "SELF MATCHES %@" diff --git a/Code/Configuration/Context/Validation/ELTextFieldValidation.swift b/Code/Configuration/Context/Validation/ELTextFieldValidation.swift index 3fae737..00e7264 100644 --- a/Code/Configuration/Context/Validation/ELTextFieldValidation.swift +++ b/Code/Configuration/Context/Validation/ELTextFieldValidation.swift @@ -8,7 +8,7 @@ import Foundation /// Описывает валидацию ввода -public struct ELTextFieldValidation { +public struct ELTextFieldValidation: Sendable { /// Предоставляет правило валидации public let validator: ELTextFieldValidator /// Предоставляет правило срабатывания проверки валидности текста diff --git a/Code/Configuration/Context/Validation/ELTextFieldValidationRule.swift b/Code/Configuration/Context/Validation/ELTextFieldValidationRule.swift index 05d3177..9559bb3 100644 --- a/Code/Configuration/Context/Validation/ELTextFieldValidationRule.swift +++ b/Code/Configuration/Context/Validation/ELTextFieldValidationRule.swift @@ -8,7 +8,7 @@ import Foundation /// Правило срабатывания валидации поля ввода -public enum ELTextFieldValidationRule { +public enum ELTextFieldValidationRule: Sendable { /// Каждый раз при изменении текста case onChange /// При завершении редактирования diff --git a/Code/Configuration/Context/Validation/ELTextFieldValidator.swift b/Code/Configuration/Context/Validation/ELTextFieldValidator.swift index 8add651..6ed16c5 100644 --- a/Code/Configuration/Context/Validation/ELTextFieldValidator.swift +++ b/Code/Configuration/Context/Validation/ELTextFieldValidator.swift @@ -6,7 +6,7 @@ import Foundation /// Валидатор для проверки корректности введенного текста -public protocol ELTextFieldValidator { +public protocol ELTextFieldValidator: Sendable { /// Проверяет валидность текста /// diff --git a/Code/Configuration/Context/ViewModel/ELTextInputViewModel.swift b/Code/Configuration/Context/ViewModel/ELTextInputViewModel.swift index 28e6e15..4a22283 100644 --- a/Code/Configuration/Context/ViewModel/ELTextInputViewModel.swift +++ b/Code/Configuration/Context/ViewModel/ELTextInputViewModel.swift @@ -7,7 +7,7 @@ import Foundation import UIKit /// Используется для хранения данных о TextInput -public struct ELTextInputViewModel { +public struct ELTextInputViewModel: Sendable { public var text: String? public var placeholder: String? public var leftView: UIView? @@ -16,10 +16,10 @@ public struct ELTextInputViewModel { /// Маппер для преобразования текста плейсхолдера /// /// Используется в случае, когда плейсхолдер имеет кастомный шрифт - public var attributedPlaceholderMapper: ((String?) -> NSAttributedString?)? + public var attributedPlaceholderMapper: (@Sendable (String?) -> NSAttributedString?)? /// Маппер для преобразования введенного текста /// /// Используется в случае, когда текст имеет кастомный шрифт - public var attributedTextMapper: ((String?) -> NSAttributedString?)? + public var attributedTextMapper: (@Sendable (String?) -> NSAttributedString?)? } diff --git a/Code/Containers/ELContainerDelegate.swift b/Code/Containers/ELContainerDelegate.swift index 17897ae..837b7db 100644 --- a/Code/Containers/ELContainerDelegate.swift +++ b/Code/Containers/ELContainerDelegate.swift @@ -8,6 +8,7 @@ import Foundation /// Методы делегата для обработки дополнительных событий с полем ввода +@MainActor public protocol ELContainerDelegate: AnyObject { func startEditing(in behavior: ELTextFieldBehavior) diff --git a/Code/Inputs/ELTextInput.swift b/Code/Inputs/ELTextInput.swift index 6b1e168..e790643 100644 --- a/Code/Inputs/ELTextInput.swift +++ b/Code/Inputs/ELTextInput.swift @@ -6,6 +6,7 @@ import Foundation import UIKit +@MainActor public protocol ELTextInput: UIView { var attributedTextMapper: ((String?) -> NSAttributedString?)? { get set } var enteredText: String? { get set } diff --git a/Code/Inputs/ELTextInputDelegate.swift b/Code/Inputs/ELTextInputDelegate.swift index 5ba8f25..6f18dcf 100644 --- a/Code/Inputs/ELTextInputDelegate.swift +++ b/Code/Inputs/ELTextInputDelegate.swift @@ -9,6 +9,7 @@ import UIKit /// Методы делегата TextInput /// /// Является оберткой над UITextFieldDelegate и UITextViewDelegate +@MainActor public protocol ELTextInputDelegate: AnyObject { func textInputShouldClear(_ textInput: ELTextInput) -> Bool func textInputShouldBeginEditing(_ textInput: ELTextInput) -> Bool @@ -27,6 +28,7 @@ public protocol ELTextInputDelegate: AnyObject { ) -> Bool } +@MainActor public protocol ELTouchesDelegate: AnyObject { func pointInside(in textInput: ELTextInput, isInside: Bool) func touchesBegan(in textInput: ELTextInput, touches: Set, with event: UIEvent?) diff --git a/Example/ELTextFieldDemo.xcodeproj/project.pbxproj b/Example/ELTextFieldDemo.xcodeproj/project.pbxproj index 1e0312a..563a4d2 100644 --- a/Example/ELTextFieldDemo.xcodeproj/project.pbxproj +++ b/Example/ELTextFieldDemo.xcodeproj/project.pbxproj @@ -25,7 +25,6 @@ 55EA5C3129CF006A00464D65 /* ELTextField in Frameworks */ = {isa = PBXBuildFile; productRef = 55EA5C3029CF006A00464D65 /* ELTextField */; }; 55EA5C3429CF009000464D65 /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 55EA5C3329CF009000464D65 /* SnapKit */; }; 55EA5C3729CF3DCF00464D65 /* Colors.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 55EA5C3629CF3DCF00464D65 /* Colors.xcassets */; }; - 55EA5C3A29CF3E5100464D65 /* RswiftLibrary in Frameworks */ = {isa = PBXBuildFile; productRef = 55EA5C3929CF3E5100464D65 /* RswiftLibrary */; }; 55EA5C4429CF401B00464D65 /* Swissors in Frameworks */ = {isa = PBXBuildFile; productRef = 55EA5C4329CF401B00464D65 /* Swissors */; }; 55F2D65929DD3951007CBDE7 /* TLTextFieldBehavior.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55F2D65829DD3951007CBDE7 /* TLTextFieldBehavior.swift */; }; 55FC635229DDBD0C00C8BE05 /* SKTextFieldConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55FC635129DDBD0C00C8BE05 /* SKTextFieldConfiguration.swift */; }; @@ -71,7 +70,6 @@ 55EA5C3129CF006A00464D65 /* ELTextField in Frameworks */, 55EA5C4429CF401B00464D65 /* Swissors in Frameworks */, 55EA5C3429CF009000464D65 /* SnapKit in Frameworks */, - 55EA5C3A29CF3E5100464D65 /* RswiftLibrary in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -88,17 +86,9 @@ path = TL; sourceTree = ""; }; - 551DC1C329DA902E002FD40D /* Behaviors */ = { - isa = PBXGroup; - children = ( - ); - path = Behaviors; - sourceTree = ""; - }; 55614A5F29D0982D00CCFACD /* Code */ = { isa = PBXGroup; children = ( - 551DC1C329DA902E002FD40D /* Behaviors */, 55614A6129D0984900CCFACD /* Core */, 55EA5C3529CF3DAF00464D65 /* Containers */, 55EA5C1629CEF78A00464D65 /* AppDelegate.swift */, @@ -243,13 +233,11 @@ buildRules = ( ); dependencies = ( - 55EA5C3C29CF3E9000464D65 /* PBXTargetDependency */, ); name = ELTextFieldDemo; packageProductDependencies = ( 55EA5C3029CF006A00464D65 /* ELTextField */, 55EA5C3329CF009000464D65 /* SnapKit */, - 55EA5C3929CF3E5100464D65 /* RswiftLibrary */, 55EA5C4329CF401B00464D65 /* Swissors */, ); productName = ELTextFieldDemo; @@ -283,7 +271,6 @@ mainGroup = 55EA5C0A29CEF78A00464D65; packageReferences = ( 55EA5C3229CF009000464D65 /* XCRemoteSwiftPackageReference "SnapKit" */, - 55EA5C3829CF3E5100464D65 /* XCRemoteSwiftPackageReference "R.swift" */, 55EA5C4229CF401B00464D65 /* XCRemoteSwiftPackageReference "ios-Swissors" */, ); productRefGroup = 55EA5C1429CEF78A00464D65 /* Products */; @@ -337,13 +324,6 @@ }; /* End PBXSourcesBuildPhase section */ -/* Begin PBXTargetDependency section */ - 55EA5C3C29CF3E9000464D65 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - productRef = 55EA5C3B29CF3E9000464D65 /* RswiftGenerateInternalResources */; - }; -/* End PBXTargetDependency section */ - /* Begin PBXVariantGroup section */ 55EA5C2129CEF78A00464D65 /* LaunchScreen.storyboard */ = { isa = PBXVariantGroup; @@ -484,7 +464,7 @@ INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -494,7 +474,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -513,7 +493,7 @@ INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -523,7 +503,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; @@ -560,14 +540,6 @@ version = 5.6.0; }; }; - 55EA5C3829CF3E5100464D65 /* XCRemoteSwiftPackageReference "R.swift" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/mac-cain13/R.swift.git"; - requirement = { - kind = exactVersion; - version = 7.3.0; - }; - }; 55EA5C4229CF401B00464D65 /* XCRemoteSwiftPackageReference "ios-Swissors" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/elegion/ios-Swissors"; @@ -588,15 +560,6 @@ package = 55EA5C3229CF009000464D65 /* XCRemoteSwiftPackageReference "SnapKit" */; productName = SnapKit; }; - 55EA5C3929CF3E5100464D65 /* RswiftLibrary */ = { - isa = XCSwiftPackageProductDependency; - package = 55EA5C3829CF3E5100464D65 /* XCRemoteSwiftPackageReference "R.swift" */; - productName = RswiftLibrary; - }; - 55EA5C3B29CF3E9000464D65 /* RswiftGenerateInternalResources */ = { - isa = XCSwiftPackageProductDependency; - productName = "plugin:RswiftGenerateInternalResources"; - }; 55EA5C4329CF401B00464D65 /* Swissors */ = { isa = XCSwiftPackageProductDependency; package = 55EA5C4229CF401B00464D65 /* XCRemoteSwiftPackageReference "ios-Swissors" */; diff --git a/Example/ELTextFieldDemo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Example/ELTextFieldDemo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 819fee8..e5624f0 100644 --- a/Example/ELTextFieldDemo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Example/ELTextFieldDemo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -18,15 +18,6 @@ "version" : "2.5.2" } }, - { - "identity" : "r.swift", - "kind" : "remoteSourceControl", - "location" : "https://github.com/mac-cain13/R.swift.git", - "state" : { - "revision" : "589493fe3907251b62f7684c8451330ee08f4426", - "version" : "7.3.0" - } - }, { "identity" : "snapkit", "kind" : "remoteSourceControl", @@ -35,24 +26,6 @@ "revision" : "f222cbdf325885926566172f6f5f06af95473158", "version" : "5.6.0" } - }, - { - "identity" : "swift-argument-parser", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-argument-parser", - "state" : { - "revision" : "fee6933f37fde9a5e12a1e4aeaa93fe60116ff2a", - "version" : "1.2.2" - } - }, - { - "identity" : "xcodeedit", - "kind" : "remoteSourceControl", - "location" : "https://github.com/tomlokhorst/XcodeEdit", - "state" : { - "revision" : "cd466d6e8c5ffd2f2b61165d37b0646f09068e1e", - "version" : "2.9.0" - } } ], "version" : 2 diff --git a/Example/ELTextFieldDemo/Code/AppDelegate.swift b/Example/ELTextFieldDemo/Code/AppDelegate.swift index ba087a9..6e6efac 100644 --- a/Example/ELTextFieldDemo/Code/AppDelegate.swift +++ b/Example/ELTextFieldDemo/Code/AppDelegate.swift @@ -13,7 +13,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { window = UIWindow(frame: UIScreen.main.bounds) - window?.rootViewController = TutorialViewController() + window?.rootViewController = ViewController() window?.makeKeyAndVisible() return true } diff --git a/Example/ELTextFieldDemo/Code/Containers/Base/BaseTextFieldConfiguration.swift b/Example/ELTextFieldDemo/Code/Containers/Base/BaseTextFieldConfiguration.swift index 0ae04d1..99f0e14 100644 --- a/Example/ELTextFieldDemo/Code/Containers/Base/BaseTextFieldConfiguration.swift +++ b/Example/ELTextFieldDemo/Code/Containers/Base/BaseTextFieldConfiguration.swift @@ -11,8 +11,19 @@ import ELTextField enum BaseTextFieldConfiguration: ELTextFieldConfigurationProtocol { + static func caretRect() -> ELTextField.ELCaretRect { + .default + } + static func layer(for state: ELTextField.ELTextFieldState) -> ELTextField.ELTextInputLayerConfiguration { - .init(borderColor: .black, borderWidth: 1, cornerRadius: 2, tintColor: .black) + .init( + borderColor: .black, + borderWidth: 1, + cornerRadius: 2, + tintColor: .black, + caretColor: .black, + backgroundColor: .clear + ) } static func rect() -> ELTextField.ELTextInputRectConfiguration { diff --git a/Example/ELTextFieldDemo/Code/Containers/PN/FloatPlaceholder/PNFloatPlaceholderTextFieldBehavior.swift b/Example/ELTextFieldDemo/Code/Containers/PN/FloatPlaceholder/PNFloatPlaceholderTextFieldBehavior.swift index 87e5a40..a6915a2 100644 --- a/Example/ELTextFieldDemo/Code/Containers/PN/FloatPlaceholder/PNFloatPlaceholderTextFieldBehavior.swift +++ b/Example/ELTextFieldDemo/Code/Containers/PN/FloatPlaceholder/PNFloatPlaceholderTextFieldBehavior.swift @@ -28,7 +28,7 @@ class PNFloatPlaceholderTextFieldBehavior: ELDefaultTextFieldBehavior { init( text: String? = nil, - textMapper: ((String?) -> NSAttributedString?)? = nil, + textMapper: (@Sendable (String?) -> NSAttributedString?)? = nil, placeholder: String? = nil, anyValueGender: AnyValueGender, placeholderMapper _: ((String?) -> NSAttributedString?)? = nil, @@ -45,7 +45,6 @@ class PNFloatPlaceholderTextFieldBehavior: ELDefaultTextFieldBehavior { textMapper: textMapper, placeholder: nil, placeholderMapper: nil, - rightItem: nil, mask: mask, traits: traits, validation: validation diff --git a/Example/ELTextFieldDemo/Code/Containers/PN/FloatPlaceholder/PNFloatPlaceholderTextFieldContainer.swift b/Example/ELTextFieldDemo/Code/Containers/PN/FloatPlaceholder/PNFloatPlaceholderTextFieldContainer.swift index 250ef65..24b3d10 100644 --- a/Example/ELTextFieldDemo/Code/Containers/PN/FloatPlaceholder/PNFloatPlaceholderTextFieldContainer.swift +++ b/Example/ELTextFieldDemo/Code/Containers/PN/FloatPlaceholder/PNFloatPlaceholderTextFieldContainer.swift @@ -25,7 +25,7 @@ final class PNFloatPlaceholderTextFieldContainer: ELTextFieldGenericContainer ELTextField.ELCaretRect { + .default + } + static func layer(for state: ELTextField.ELTextFieldState) -> ELTextField.ELTextInputLayerConfiguration { .init(borderColor: nil, borderWidth: .zero, cornerRadius: .zero, - tintColor: R.color.grayBBBBBF()) + tintColor: .grayBBBBBF, + caretColor: .black, + backgroundColor: .clear + ) } - + static func rect() -> ELTextField.ELTextInputRectConfiguration { .init( textInset: UIEdgeInsets(top: 26, bottom: 12, horizontal: 16), - rightViewPosition: .centerHorizontally(rightInset: 20, size: CGSize(value: 40)), + rightViewPosition: .centerHorizontally(inset: 20, size: CGSize(value: 40)), containerHeight: .init(value: 60) ) } diff --git a/Example/ELTextFieldDemo/Code/Containers/PN/TopPlaceholder/PNTopPlaceholderTextFieldContainer.swift b/Example/ELTextFieldDemo/Code/Containers/PN/TopPlaceholder/PNTopPlaceholderTextFieldContainer.swift index 6df9ca7..a4e3789 100644 --- a/Example/ELTextFieldDemo/Code/Containers/PN/TopPlaceholder/PNTopPlaceholderTextFieldContainer.swift +++ b/Example/ELTextFieldDemo/Code/Containers/PN/TopPlaceholder/PNTopPlaceholderTextFieldContainer.swift @@ -11,13 +11,13 @@ import Foundation import UIKit final class PNTopPlaceholderTextFieldContainer: ELDefaultTextFieldGenericContainer { - + private let floatingLabel = UILabel() private let separatorView = SeparatorView() - - override public init(type: ELTextInputType = .singleline) { + + required init(type: ELTextInputType = .singleline) { super.init(type: type) - + addSubview(floatingLabel) addSubview(separatorView) floatingLabel.snp.makeConstraints { @@ -42,29 +42,29 @@ final class PNTopPlaceholderTextFieldContainer: ELDefaultTextFieldGenericContain $0.leading.trailing.bottom.equalToSuperview() } } - + @available(*, unavailable) required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } - + override public func setBehavior(_ behavior: ELDefaultTextFieldBehavior?) { super.setBehavior(behavior) - + updateFloatingLabelAppearance(isVisible: !(behavior?.value.isEmpty ?? true)) floatingLabel.attributedText = behavior? .placeholder? .attribute .with(font: .systemFont(ofSize: 12, weight: .regular)) - .with(foregroundColor: R.color.gray919195()).build() + .with(foregroundColor: .gray919195).build() } - + private func updateFloatingLabelAppearance(isVisible: Bool) { UIView.animate(withDuration: CATransaction.animationDuration()) { self.floatingLabel.alpha = isVisible ? 1 : .zero } } - + override func container(_ behavior: ELTextFieldBehavior, changedText text: String) { updateFloatingLabelAppearance(isVisible: !text.isEmpty) } diff --git a/Example/ELTextFieldDemo/Code/Containers/SK/SKTextFieldConfiguration.swift b/Example/ELTextFieldDemo/Code/Containers/SK/SKTextFieldConfiguration.swift index 503f796..e29f045 100644 --- a/Example/ELTextFieldDemo/Code/Containers/SK/SKTextFieldConfiguration.swift +++ b/Example/ELTextFieldDemo/Code/Containers/SK/SKTextFieldConfiguration.swift @@ -13,13 +13,26 @@ import Swissors enum SKTextFieldConfiguration: ELTextFieldConfigurationProtocol { + static func caretRect() -> ELTextField.ELCaretRect { + .default + } + + static func layer(for state: ELTextField.ELTextFieldState) -> ELTextField.ELTextInputLayerConfiguration { let color: UIColor = (state == .default) ? UIColor.gray : UIColor.blue - return .init(borderColor: color, borderWidth: 1, cornerRadius: 8, tintColor: .red) + return .init( + borderColor: color, + borderWidth: 1, + cornerRadius: 8, + tintColor: .red, + caretColor: .black, + backgroundColor: .clear + ) } static func rect() -> ELTextField.ELTextInputRectConfiguration { - .init(textInset: .init(top: 12, left: 16, bottom: 12, right: .zero), - rightViewPosition: .centerHorizontally(rightInset: 16, size: CGSize(value: 24)) ,containerHeight: .init(singleline: 48, multiline: 160)) + .init( + textInset: .init(top: 12, left: 16, bottom: 12, right: .zero), + rightViewPosition: .centerHorizontally(inset: 16, size: CGSize(value: 24)) ,containerHeight: .init(singleline: 48, multiline: 160)) } } diff --git a/Example/ELTextFieldDemo/Code/Containers/TL/TLTextFieldBehavior.swift b/Example/ELTextFieldDemo/Code/Containers/TL/TLTextFieldBehavior.swift index 5b619f4..32611ba 100644 --- a/Example/ELTextFieldDemo/Code/Containers/TL/TLTextFieldBehavior.swift +++ b/Example/ELTextFieldDemo/Code/Containers/TL/TLTextFieldBehavior.swift @@ -9,21 +9,26 @@ import Foundation import ELTextField +@MainActor class TLTextFieldBehavior: ELDefaultTextFieldBehavior { let floatingPlaceholder: String? - override init(text: String? = nil, - textMapper: ((String?) -> NSAttributedString?)? = nil, - placeholder: String? = nil, - placeholderMapper: ((String?) -> NSAttributedString?)? = nil, - isEditable: Bool = true, - rightItem: ELRightItem? = nil, - mask: ELTextFieldInputMask = ELDefaultTextMask(), - traits: ELTextFieldInputTraits = ELDefaultTextFieldInputTraits(), - validation: ELTextFieldValidation = .default) { - let mapper: (String?) -> NSAttributedString? = { - $0?.attribute.with(font: .systemFont(ofSize: 15, weight: .regular)).with(foregroundColor: R.color.black1F22229()).build() + override init( + text: String? = nil, + textMapper: ((String?) -> NSAttributedString?)? = nil, + placeholder: String? = nil, + placeholderMapper: ((String?) -> NSAttributedString?)? = nil, + isEditable: Bool = true, + leftMode: (any ELLeftViewMode)? = nil, + rightMode: (any ELRightViewMode)? = nil, + mask: any ELTextFieldInputMask = ELDefaultTextMask(), + font: ELTextInputFontConfiguration? = nil, + traits: any ELTextFieldInputTraits = ELDefaultTextFieldInputTraits(), + validation: ELTextFieldValidation = .default + ) { + let mapper: @Sendable (String?) -> NSAttributedString? = { + $0?.attribute.with(font: .systemFont(ofSize: 15, weight: .regular)).with(foregroundColor: .black1F22229).build() } self.floatingPlaceholder = placeholder super.init( @@ -31,7 +36,7 @@ class TLTextFieldBehavior: ELDefaultTextFieldBehavior { textMapper: mapper, placeholder: nil, placeholderMapper: nil, - rightItem: rightItem, + rightMode: rightMode, mask: mask, traits: traits, validation: validation diff --git a/Example/ELTextFieldDemo/Code/Containers/TL/TLTextFieldConfiguration.swift b/Example/ELTextFieldDemo/Code/Containers/TL/TLTextFieldConfiguration.swift index b7ee2c2..d959746 100644 --- a/Example/ELTextFieldDemo/Code/Containers/TL/TLTextFieldConfiguration.swift +++ b/Example/ELTextFieldDemo/Code/Containers/TL/TLTextFieldConfiguration.swift @@ -12,26 +12,37 @@ import UIKit enum TLTextFieldConfiguration: ELTextFieldConfigurationProtocol { + static func caretRect() -> ELTextField.ELCaretRect { + .default + } + static func layer(for state: ELTextField.ELTextFieldState) -> ELTextField.ELTextInputLayerConfiguration { let borderColor: UIColor? let tintColor: UIColor? switch state { case .default, .disabled: - borderColor = R.color.gray919195() - tintColor = R.color.black1F22229() + borderColor = .gray919195 + tintColor = .black1F22229 case .error: borderColor = .red tintColor = .red case .editing: borderColor = .black - tintColor = R.color.black1F22229() + tintColor = .black1F22229 } - return .init(borderColor: borderColor, borderWidth: 1, cornerRadius: 8, tintColor: tintColor) + return .init( + borderColor: borderColor, + borderWidth: 1, + cornerRadius: 8, + tintColor: tintColor, + caretColor: .black, + backgroundColor: .clear + ) } static func rect() -> ELTextField.ELTextInputRectConfiguration { .init(textInset: UIEdgeInsets(top: 30, bottom: 8, horizontal: 16), - rightViewPosition: .centerHorizontally(rightInset: 16, size: CGSize(value: 32)), + rightViewPosition: .centerHorizontally(inset: 16, size: CGSize(value: 32)), containerHeight: .init(singleline: 60, multiline: 120)) } } diff --git a/Example/ELTextFieldDemo/Code/Containers/TL/TLTextFieldContainer.swift b/Example/ELTextFieldDemo/Code/Containers/TL/TLTextFieldContainer.swift index f2b286e..cf4e3de 100644 --- a/Example/ELTextFieldDemo/Code/Containers/TL/TLTextFieldContainer.swift +++ b/Example/ELTextFieldDemo/Code/Containers/TL/TLTextFieldContainer.swift @@ -31,18 +31,18 @@ final class TLTextFieldContainer: ELTextFieldGenericContainer NSAttributedString? = { $0?.attribute.with(font: .systemFont(ofSize: 15, weight: .bold)).build() } - let rightImage = ELRightItem.image(image: UIImage(systemName: "checkmark"), mode: .always) var traits = ELDefaultTextFieldInputTraits() traits.keyboardType = .phonePad super.init(text: mail, textMapper: mapper, placeholder: "Почта", placeholderMapper: mapper, - rightItem: rightImage, + rightMode: ELSystemImageRightView( + image: UIImage(systemName: "checkmark"), + viewMode: .always + ), traits: traits, validation: .init(validator: ELMailTextFieldValidator(), rule: .onEndEditing)) diff --git a/Example/ELTextFieldDemo/Code/Core/Configurable.swift b/Example/ELTextFieldDemo/Code/Core/Configurable.swift index 0530311..4922953 100644 --- a/Example/ELTextFieldDemo/Code/Core/Configurable.swift +++ b/Example/ELTextFieldDemo/Code/Core/Configurable.swift @@ -8,6 +8,7 @@ import Foundation +@MainActor protocol Configurable { associatedtype Model diff --git a/Example/ELTextFieldDemo/Code/Core/PasswordBehavior.swift b/Example/ELTextFieldDemo/Code/Core/PasswordBehavior.swift index d428964..409ac07 100644 --- a/Example/ELTextFieldDemo/Code/Core/PasswordBehavior.swift +++ b/Example/ELTextFieldDemo/Code/Core/PasswordBehavior.swift @@ -15,10 +15,12 @@ class PasswordBehavior: ELDefaultTextFieldBehavior { init() { var traits = ELDefaultTextFieldInputTraits() traits.isSecureTextEntry = true - super.init(placeholder: "Пароль", - rightItem: .secure(showImage: UIImage(systemName: "eye.fill"), - hideImage: UIImage(systemName: "eye.slash.fill"), - mode: .always), - traits: traits) + super.init( + placeholder: "Пароль", + rightMode: ELSecureTextRightViewMode( + showTextImage: UIImage(systemName: "eye.fill"), + hideTextImage: UIImage(systemName: "eye.slash.fill") + ) + ) } } diff --git a/Example/ELTextFieldDemo/Code/Core/Views/SeparatorView.swift b/Example/ELTextFieldDemo/Code/Core/Views/SeparatorView.swift index c463af7..3783e90 100644 --- a/Example/ELTextFieldDemo/Code/Core/Views/SeparatorView.swift +++ b/Example/ELTextFieldDemo/Code/Core/Views/SeparatorView.swift @@ -18,7 +18,7 @@ final class SeparatorView: UIView { override init(frame: CGRect) { super.init(frame: frame) - backgroundColor = R.color.grayCCCCCE() + backgroundColor = .grayCCCCCE } @available(*, unavailable) diff --git a/Example/ELTextFieldDemo/Code/ViewController.swift b/Example/ELTextFieldDemo/Code/ViewController.swift index 1acc95c..c5c3957 100644 --- a/Example/ELTextFieldDemo/Code/ViewController.swift +++ b/Example/ELTextFieldDemo/Code/ViewController.swift @@ -19,23 +19,29 @@ class ViewController: UIViewController { case skMultiline(model: ELDefaultTextFieldBehavior) case search(model: PNFloatPlaceholderTextFieldBehavior) case tl(model: TLTextFieldBehavior) -// case tlMultiline(model: TLTextFieldBehavior) + // case tlMultiline(model: TLTextFieldBehavior) } private let items: [Items] = [ .topPlaceholder(model: MailBehavior()), .topPlaceholder(model: PasswordBehavior()), - .sk(model: .init(text: "allo", - placeholder: "Привет", - rightItem: .action(image: UIImage(systemName: "pencil"), - mode: .always, - behavior: .delete))), + .sk( + model: .init( + text: "allo", + placeholder: "Привет", + rightMode: ELSystemActionRightView( + image: UIImage(systemName: "pencil"), + viewMode: .always, + behavior: .delete + ) + ) + ), .skMultiline(model: .init(textMapper: { $0?.attribute.with(foregroundColor: .black).with(font: .systemFont(ofSize: 24)).build() }, placeholder: "allo", placeholderMapper: { $0?.attribute.with(foregroundColor: .gray).build() })), -// .sk(model: ), + // .sk(model: ), .search(model: PNFloatPlaceholderTextFieldBehavior(placeholder: "Имя", anyValueGender: .female)), .search(model: PNFloatPlaceholderTextFieldBehavior(placeholder: "Фамилия", anyValueGender: .female)), .search(model: PNFloatPlaceholderTextFieldBehavior(placeholder: "Почта", anyValueGender: .female)), @@ -46,13 +52,15 @@ class ViewController: UIViewController { outputMask: "$##########"))), .tl(model: MailBehavior()), .tl(model: .init(placeholder: "Телефончик", - rightItem: .action(image: UIImage(systemName: "xmark.circle"), - mode: .whileEditing, - behavior: .delete), + rightMode: ELSystemActionRightView( + image: UIImage(systemName: "xmark.circle"), + viewMode: .whileEditing, + behavior: .delete + ), mask: ELPhoneTextMask(phoneCode: "+7", inputMask: "$ (###) ### ## ##", outputMask: "$##########"))), -// .tlMultiline(model: MailBehavior()), + // .tlMultiline(model: MailBehavior()), ] private lazy var tableView: UITableView = { @@ -105,10 +113,10 @@ class ViewController: UIViewController { let cell = tableView.dequeueCell(of: AbstractTableViewCell.self, for: indexPath) cell.set(model: model) return cell -// case let .tlMultiline(model): -// let cell = tableView.dequeueCell(of: MultilineTextFieldTableViewCell.self, for: indexPath) -// cell.set(model: model) -// return cell + // case let .tlMultiline(model): + // let cell = tableView.dequeueCell(of: MultilineTextFieldTableViewCell.self, for: indexPath) + // cell.set(model: model) + // return cell } } } diff --git a/Example/ELTextFieldDemo/Colors.xcassets/Contents.json b/Example/ELTextFieldDemo/Colors.xcassets/Contents.json index 73c0059..25a219f 100644 --- a/Example/ELTextFieldDemo/Colors.xcassets/Contents.json +++ b/Example/ELTextFieldDemo/Colors.xcassets/Contents.json @@ -2,5 +2,8 @@ "info" : { "author" : "xcode", "version" : 1 + }, + "properties" : { + "generate-swift-asset-symbol-extensions" : "enabled" } }