From ef59898e794541404e3fe99fb148781345c4aedf Mon Sep 17 00:00:00 2001 From: Nikita Vasilev <nv3212@gmail.com> Date: Mon, 27 Jan 2025 17:11:25 +0100 Subject: [PATCH 1/2] Implement adding an action to a `UITextField` --- .../Classes/Extensions/FlexUI+UIButton.swift | 38 ++----------- .../Extensions/FlexUI+UITextField.swift | 53 ++++++++++++++++++- Sources/FlexUI/Classes/Model/Command.swift | 34 ++++++++++++ 3 files changed, 90 insertions(+), 35 deletions(-) create mode 100644 Sources/FlexUI/Classes/Model/Command.swift diff --git a/Sources/FlexUI/Classes/Extensions/FlexUI+UIButton.swift b/Sources/FlexUI/Classes/Extensions/FlexUI+UIButton.swift index 71f11ae..a2013cf 100644 --- a/Sources/FlexUI/Classes/Extensions/FlexUI+UIButton.swift +++ b/Sources/FlexUI/Classes/Extensions/FlexUI+UIButton.swift @@ -1,41 +1,11 @@ // // flex-ui -// Copyright © 2024 Space Code. All rights reserved. +// Copyright © 2025 Space Code. All rights reserved. // import UIKit -// MARK: - ButtonCommand - -/// A private class that wraps a closure to be executed as an action for a `UIButton`. -/// This class allows associating arbitrary closure actions with buttons, and is used internally -/// to manage custom button actions in the `FlexUI` extension. -private final class ButtonCommand { - // MARK: Properties - - /// The closure that will be executed when the button action is triggered. - let block: () -> Void - - // MARK: Initialization - - /// Initializes a new instance of `ButtonCommand` with the provided closure. - /// - /// - Parameter block: The closure to be executed when the button is tapped. - init(block: @escaping () -> Void) { - self.block = block - } - - // MARK: Actions - - /// The action method that is triggered when the associated button's event occurs. - /// It executes the stored closure. - @objc - func action() { - block() - } -} - -@MainActor private let kMapTable = NSMapTable<AnyObject, ButtonCommand>.weakToStrongObjects() +@MainActor private let kMapTable = NSMapTable<AnyObject, Command>.weakToStrongObjects() /// An extension to `FlexUI` that adds helper methods for configuring `UIButton` properties. /// These methods allow for fluent configuration of button properties such as title, image, alignment, and actions. @@ -251,7 +221,7 @@ public extension FlexUI where Component: UIButton { return self } - let buttonCommand = ButtonCommand(block: command) + let buttonCommand = Command(block: command) component.removeTarget(nil, action: nil, for: event) component.addTarget(buttonCommand, action: #selector(buttonCommand.action), for: event) kMapTable.setObject(buttonCommand, forKey: component) @@ -271,7 +241,7 @@ public extension FlexUI where Component: UIButton { return self } - let buttonCommand = ButtonCommand { [weak component] in + let buttonCommand = Command { [weak component] in if let component = component { command(component) } diff --git a/Sources/FlexUI/Classes/Extensions/FlexUI+UITextField.swift b/Sources/FlexUI/Classes/Extensions/FlexUI+UITextField.swift index acc54c1..bdb612d 100644 --- a/Sources/FlexUI/Classes/Extensions/FlexUI+UITextField.swift +++ b/Sources/FlexUI/Classes/Extensions/FlexUI+UITextField.swift @@ -1,10 +1,12 @@ // // flex-ui -// Copyright © 2024 Space Code. All rights reserved. +// Copyright © 2025 Space Code. All rights reserved. // import UIKit +@MainActor private let kMapTable = NSMapTable<AnyObject, Command>.weakToStrongObjects() + public extension FlexUI where Component: UITextField { /// Sets the font of the text field. /// @@ -140,4 +142,53 @@ public extension FlexUI where Component: UITextField { component.adjustsFontSizeToFitWidth = true return self } + + /// Adds a command to be executed when a specified event occurs on the UIControl component. + /// The method is annotated with `@discardableResult` to allow the return value to be ignored, + /// and `@MainActor` to ensure it runs on the main thread. + /// + /// - Parameters: + /// - command: A closure to be executed when the event occurs. Can be `nil`, in which case no action is added. + /// - event: The `UIControl.Event` that triggers the command. + /// - Returns: The instance of `Self` to allow method chaining. + @discardableResult + @MainActor + func add(command: (() -> Void)?, event: UIControl.Event) -> Self { + guard let command = command else { + return self + } + + let buttonCommand = Command(block: command) + component.removeTarget(nil, action: nil, for: event) + component.addTarget(buttonCommand, action: #selector(buttonCommand.action), for: event) + kMapTable.setObject(buttonCommand, forKey: component) + return self + } + + /// Adds a command to be executed when a specified event occurs on the `UITextField` component. + /// The method is annotated with `@discardableResult` to allow the return value to be ignored, + /// and `@MainActor` to ensure it runs on the main thread. + /// + /// - Parameters: + /// - command: A closure that receives the `UITextField` as a parameter when the event occurs. Can be `nil`. + /// - event: The `UIControl.Event` that triggers the command. + /// - Returns: The instance of `Self` to allow method chaining. + @discardableResult + @MainActor + func add(command: ((UITextField) -> Void)?, event: UIControl.Event) -> Self { + guard let command = command else { + return self + } + + let buttonCommand = Command { [weak component] in + if let component = component { + command(component) + } + } + + component.removeTarget(nil, action: nil, for: event) + component.addTarget(buttonCommand, action: #selector(buttonCommand.action), for: event) + kMapTable.setObject(buttonCommand, forKey: component) + return self + } } diff --git a/Sources/FlexUI/Classes/Model/Command.swift b/Sources/FlexUI/Classes/Model/Command.swift new file mode 100644 index 0000000..19ff61f --- /dev/null +++ b/Sources/FlexUI/Classes/Model/Command.swift @@ -0,0 +1,34 @@ +// +// flex-ui +// Copyright © 2025 Space Code. All rights reserved. +// + +import Foundation + +/// A private class that wraps a closure to be executed as an action for a `UIControl`. +/// This class allows associating arbitrary closure actions with buttons, and is used internally +/// to manage custom button actions in the `FlexUI` extension. +final class Command { + // MARK: Properties + + /// The closure that will be executed when the button action is triggered. + let block: () -> Void + + // MARK: Initialization + + /// Initializes a new instance of `Command` with the provided closure. + /// + /// - Parameter block: The closure to be executed when the button is tapped. + init(block: @escaping () -> Void) { + self.block = block + } + + // MARK: Actions + + /// The action method that is triggered when the associated button's event occurs. + /// It executes the stored closure. + @objc + func action() { + block() + } +} From 4e3df9d8dde3b7adad332c118db71998f3dae16c Mon Sep 17 00:00:00 2001 From: Nikita Vasilev <nv3212@gmail.com> Date: Mon, 27 Jan 2025 17:16:05 +0100 Subject: [PATCH 2/2] Update `CHANGELOG.md` --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f4eca9f..e350ec9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,17 @@ # Change Log All notable changes to this project will be documented in this file. +#### 1.x Releases +- `1.1.x` Releases - [1.1.0](#110) +- `1.0.x` Releases - [1.0.0](#100) + +## [1.1.0](https://github.com/space-code/flare/releases/tag/1.1.0) +Released on 2025-01-27. + +#### Added +- Implement adding an action to a `UITextField`. + - Added in Pull Request [#3](https://github.com/space-code/flex-ui/pull/3). + ## [1.0.0](https://github.com/space-code/flex-ui/releases/tag/1.0.0) Released on 2025-01-07.