From 65bdb5c1b46459c35626a36f8fa3ccb6df5f6496 Mon Sep 17 00:00:00 2001
From: Nikita Vasilev
Date: Mon, 30 Dec 2024 17:38:06 +0100
Subject: [PATCH] Implement the `flex-ui` package
---
.github/workflows/ci.yml | 42 +--
.../contents.xcworkspacedata | 7 +
Makefile | 2 +-
Package.swift | 17 +-
README.md | 7 -
.../FlexUI/Classes/Core/FlexCompatible.swift | 34 +++
Sources/FlexUI/Classes/Core/FlexUI.swift | 20 ++
.../Classes/Extensions/FlexUI+StackView.swift | 82 +++++
.../Classes/Extensions/FlexUI+UIButton.swift | 285 ++++++++++++++++++
.../Extensions/FlexUI+UICollectionView.swift | 143 +++++++++
.../Extensions/FlexUI+UIImageView.swift | 55 ++++
.../Classes/Extensions/FlexUI+UILabel.swift | 74 +++++
.../Extensions/FlexUI+UIScrollView.swift | 97 ++++++
.../Extensions/FlexUI+UITextField.swift | 143 +++++++++
.../Extensions/FlexUI+UITextView.swift | 151 ++++++++++
Sources/flex-ui/Classes/flex-ui.swift | 6 -
.../FlexUITests.swift} | 2 +-
hooks/pre-commit | 0
18 files changed, 1099 insertions(+), 68 deletions(-)
create mode 100644 .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
create mode 100644 Sources/FlexUI/Classes/Core/FlexCompatible.swift
create mode 100644 Sources/FlexUI/Classes/Core/FlexUI.swift
create mode 100644 Sources/FlexUI/Classes/Extensions/FlexUI+StackView.swift
create mode 100644 Sources/FlexUI/Classes/Extensions/FlexUI+UIButton.swift
create mode 100644 Sources/FlexUI/Classes/Extensions/FlexUI+UICollectionView.swift
create mode 100644 Sources/FlexUI/Classes/Extensions/FlexUI+UIImageView.swift
create mode 100644 Sources/FlexUI/Classes/Extensions/FlexUI+UILabel.swift
create mode 100644 Sources/FlexUI/Classes/Extensions/FlexUI+UIScrollView.swift
create mode 100644 Sources/FlexUI/Classes/Extensions/FlexUI+UITextField.swift
create mode 100644 Sources/FlexUI/Classes/Extensions/FlexUI+UITextView.swift
delete mode 100644 Sources/flex-ui/Classes/flex-ui.swift
rename Tests/{flex-uiTests/flex-uiTests.swift => FlexUITests/FlexUITests.swift} (67%)
mode change 100644 => 100755 hooks/pre-commit
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 85b786a..ce5b6dc 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -23,22 +23,12 @@ jobs:
with:
args: --strict
env:
-
DIFF_BASE: ${{ github.base_ref }}
-
-
-
-
-
iOS:
-
name: ${{ matrix.name }}
runs-on: ${{ matrix.runsOn }}
-
env:
-
DEVELOPER_DIR: "/Applications/${{ matrix.xcode }}.app/Contents/Developer"
-
timeout-minutes: 20
strategy:
fail-fast: false
@@ -54,31 +44,17 @@ jobs:
runsOn: macos-13
steps:
- uses: actions/checkout@v3
-
- name: ${{ matrix.name }}
run: xcodebuild test -scheme "{{ cookiecutter.name }}" -destination "${{ matrix.destination }}" clean -enableCodeCoverage YES -resultBundlePath "test_output/${{ matrix.name }}.xcresult" || exit 1
-
- uses: actions/upload-artifact@v4
with:
-
name: ${{ matrix.name }}
path: test_output
-
-
-
-
-
-
-
spm:
-
name: ${{ matrix.name }}
runs-on: ${{ matrix.runsOn }}
-
env:
-
DEVELOPER_DIR: "/Applications/${{ matrix.xcode }}.app/Contents/Developer"
-
timeout-minutes: 20
strategy:
fail-fast: false
@@ -92,28 +68,12 @@ jobs:
runsOn: macos-13
steps:
- uses: actions/checkout@v3
-
- name: ${{ matrix.name }}
run: swift build -c release --target "{{ cookiecutter.name }}"
-
- merge-test-reports:
- needs: [iOS, macOS, watchOS, tvOS]
- runs-on: macos-13
- steps:
- - name: Download artifacts
- uses: actions/download-artifact@v4
- with:
- path: test_output
- - run: xcrun xcresulttool merge test_output/**/*.xcresult --output-path test_output/final/final.xcresult
- - name: Upload Merged Artifact
- uses: actions/upload-artifact@v4
- with:
- name: MergedResult
- path: test_output/final
discover-typos:
name: Discover Typos
- runs-on: macOS-12
+ runs-on: macOS-14
env:
DEVELOPER_DIR: /Applications/Xcode_14.1.app/Contents/Developer
steps:
diff --git a/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..919434a
--- /dev/null
+++ b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/Makefile b/Makefile
index 3edc6d6..856d64b 100644
--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,7 @@ bootstrap: hook
mint bootstrap
hook:
- ln -sf .git/hooks/pre-commit ../../hooks/pre-commit
+ ln -sf ../../hooks/pre-commit .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit
mint:
diff --git a/Package.swift b/Package.swift
index 93e9fc5..e332b00 100644
--- a/Package.swift
+++ b/Package.swift
@@ -5,20 +5,13 @@ import PackageDescription
let package = Package(
name: "flex-ui",
+ defaultLocalization: "en",
+ platforms: [.iOS(.v14)],
products: [
- // Products define the executables and libraries a package produces, making them visible to other packages.
- .library(
- name: "flex-ui",
- targets: ["flex-ui"]),
+ .library(name: "FlexUI", targets: ["FlexUI"]),
],
targets: [
- // Targets are the basic building blocks of a package, defining a module or a test suite.
- // Targets can depend on other targets in this package and products from dependencies.
- .target(
- name: "flex-ui"),
- .testTarget(
- name: "flex-uiTests",
- dependencies: ["flex-ui"]
- ),
+ .target(name: "FlexUI"),
+ .testTarget(name: "FlexUITests", dependencies: ["FlexUI"]),
]
)
diff --git a/README.md b/README.md
index aa9a6a0..7fd2588 100644
--- a/README.md
+++ b/README.md
@@ -5,13 +5,6 @@
-
-
-
-
-
-
-
diff --git a/Sources/FlexUI/Classes/Core/FlexCompatible.swift b/Sources/FlexUI/Classes/Core/FlexCompatible.swift
new file mode 100644
index 0000000..7d1fd84
--- /dev/null
+++ b/Sources/FlexUI/Classes/Core/FlexCompatible.swift
@@ -0,0 +1,34 @@
+//
+// flex-ui
+// Copyright © 2024 Space Code. All rights reserved.
+//
+
+import Foundation
+
+// MARK: - FlexCompatible
+
+/// A protocol that adds flexibility to any type by allowing it to be wrapped in a `FlexUI` component.
+/// Types conforming to this protocol can use the `flex` property to access a `FlexUI` wrapper for further customization.
+public protocol FlexCompatible {
+ /// The associated type representing the component wrapped by `FlexUI`.
+ associatedtype ComponentType
+
+ /// A computed property that returns a `FlexUI` wrapper for the current instance.
+ /// This allows for easy configuration and manipulation of the component.
+ var flex: FlexUI { get }
+}
+
+public extension FlexCompatible {
+ /// Default implementation of the `flex` property for types conforming to `FlexCompatible`.
+ /// This wraps the current instance in a `FlexUI` component, allowing for configuration.
+ var flex: FlexUI {
+ FlexUI(component: self)
+ }
+}
+
+// MARK: - NSObject + FlexCompatible
+
+/// Extension to make `NSObject` conform to `FlexCompatible`.
+/// This enables all `NSObject` instances to be wrapped with a `FlexUI` component,
+/// providing them with the flexibility to be configured easily.
+extension NSObject: FlexCompatible {}
diff --git a/Sources/FlexUI/Classes/Core/FlexUI.swift b/Sources/FlexUI/Classes/Core/FlexUI.swift
new file mode 100644
index 0000000..39027b1
--- /dev/null
+++ b/Sources/FlexUI/Classes/Core/FlexUI.swift
@@ -0,0 +1,20 @@
+//
+// flex-ui
+// Copyright © 2024 Space Code. All rights reserved.
+//
+
+/// A generic class that represents a flexible UI component.
+/// This class allows you to wrap any component and provide additional flexibility for customization or configuration.
+public final class FlexUI {
+ /// The wrapped UI component of the specified type.
+ /// This component is passed during initialization and can be accessed for further customization.
+ public let component: Component
+
+ /// Initializes a new instance of `FlexUI` with the provided component.
+ ///
+ /// - Parameter component: The component to be wrapped by `FlexUI`.
+ /// This allows you to configure the component later as needed.
+ public init(component: Component) {
+ self.component = component
+ }
+}
diff --git a/Sources/FlexUI/Classes/Extensions/FlexUI+StackView.swift b/Sources/FlexUI/Classes/Extensions/FlexUI+StackView.swift
new file mode 100644
index 0000000..5137259
--- /dev/null
+++ b/Sources/FlexUI/Classes/Extensions/FlexUI+StackView.swift
@@ -0,0 +1,82 @@
+//
+// flex-ui
+// Copyright © 2024 Space Code. All rights reserved.
+//
+
+import UIKit
+
+/// An extension to `FlexUI` that adds helper methods for configuring `UIStackView` properties.
+/// These methods enable easy configuration of common `UIStackView` properties using a fluent interface.
+extension FlexUI where Component: UIStackView {
+ /// Sets the axis along which the arranged views in the stack are laid out.
+ ///
+ /// - Parameter axis: The axis for layout (either `.horizontal` or `.vertical`).
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func axis(_ axis: NSLayoutConstraint.Axis) -> Self {
+ component.axis = axis
+ return self
+ }
+
+ /// Sets the distribution method for the arranged views in the stack.
+ ///
+ /// - Parameter distribution: The distribution method (e.g., `.fill`, `.equalSpacing`, etc.).
+ ///
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func distribution(_ distribution: UIStackView.Distribution) -> Self {
+ component.distribution = distribution
+ return self
+ }
+
+ /// Sets the spacing between the arranged views in the stack.
+ ///
+ /// - Parameter space: The spacing value (in points) between the arranged views.
+ ///
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func spacing(_ space: CGFloat) -> Self {
+ component.spacing = space
+ return self
+ }
+
+ /// Sets the alignment of the arranged views along the stack's axis.
+ ///
+ /// - Parameter alignment: The alignment option (e.g., `.fill`, `.leading`, `.center`, etc.).
+ ///
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func alignment(_ alignment: UIStackView.Alignment) -> Self {
+ component.alignment = alignment
+ return self
+ }
+
+ /// Adds an array of subviews to the stack view's arranged subviews.
+ ///
+ /// - Parameter subviews: The array of `UIView` instances to be added to the stack view.
+ ///
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func addArrangeSubviews(_ subviews: [UIView]) -> Self {
+ subviews.forEach { component.addArrangedSubview($0) }
+ return self
+ }
+
+ /// Sets the layout margins for the stack view and enables layout margins relative arrangement.
+ ///
+ /// - Parameter value: The directional layout margins (insets) to apply to the stack view.
+ ///
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func layoutMargins(_ value: NSDirectionalEdgeInsets) -> Self {
+ component.isLayoutMarginsRelativeArrangement = true
+ component.directionalLayoutMargins = value
+ return self
+ }
+}
diff --git a/Sources/FlexUI/Classes/Extensions/FlexUI+UIButton.swift b/Sources/FlexUI/Classes/Extensions/FlexUI+UIButton.swift
new file mode 100644
index 0000000..e8d9e6b
--- /dev/null
+++ b/Sources/FlexUI/Classes/Extensions/FlexUI+UIButton.swift
@@ -0,0 +1,285 @@
+//
+// flex-ui
+// Copyright © 2024 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.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.
+public extension FlexUI where Component: UIButton {
+ /// Adds an action to the button for a specified event.
+ ///
+ /// - Parameters:
+ /// - target: The object that receives the action message.
+ /// - selector: The selector representing the action method.
+ /// - event: The event to associate the action with (e.g., `.touchUpInside`).
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func action(_ target: Any?, _ selector: Selector, _ event: UIControl.Event) -> Self {
+ component.addTarget(target, action: selector, for: event)
+ return self
+ }
+
+ /// Sets the title for a specific button state.
+ ///
+ /// - Parameters:
+ /// - title: The title to display on the button.
+ /// - state: The state for which to set the title (default is `.normal`).
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func title(_ title: String, for state: UIControl.State = .normal) -> Self {
+ component.setTitle(title, for: state)
+ return self
+ }
+
+ /// Sets the attributed title for a specific button state.
+ ///
+ /// - Parameters:
+ /// - title: The attributed string to display on the button.
+ /// - state: The state for which to set the title (default is `.normal`).
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func attributedTitle(_ title: NSAttributedString?, for state: UIControl.State = .normal) -> Self {
+ component.setAttributedTitle(title, for: state)
+ return self
+ }
+
+ /// Sets the title color for a specific button state.
+ ///
+ /// - Parameters:
+ /// - color: The color to set for the title text.
+ /// - state: The state for which to set the title color (default is `.normal`).
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func titleColor(_ color: UIColor, for state: UIControl.State = .normal) -> Self {
+ component.setTitleColor(color, for: state)
+ return self
+ }
+
+ /// Sets the text alignment of the button's title.
+ ///
+ /// - Parameter aligment: The text alignment to apply (e.g., `.center`, `.left`, `.right`).
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func textAlignment(_ aligment: NSTextAlignment) -> Self {
+ component.titleLabel?.textAlignment = aligment
+ return self
+ }
+
+ /// Sets the number of lines allowed for the button's title.
+ ///
+ /// - Parameter number: The maximum number of lines for the title (default is 1).
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func numberOfLines(_ number: Int) -> Self {
+ component.titleLabel?.numberOfLines = number
+ return self
+ }
+
+ /// Sets the minimum scale factor for the button's title.
+ ///
+ /// - Parameter value: The minimum scale factor to apply (e.g., 0.5).
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func minimumScaleFactor(_ value: CGFloat) -> Self {
+ component.titleLabel?.minimumScaleFactor = value
+ return self
+ }
+
+ /// Sets the line break mode for the button's title.
+ ///
+ /// - Parameter mode: The line break mode to apply (e.g., `.byWordWrapping`, `.byTruncatingTail`).
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func lineBreakMode(_ mode: NSLineBreakMode) -> Self {
+ component.titleLabel?.lineBreakMode = mode
+ return self
+ }
+
+ /// Sets the content mode for the button's image view.
+ ///
+ /// - Parameter contentMode: The content mode to apply to the image view (e.g., `.scaleAspectFit`).
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func imageContentMode(_ contentMode: UIView.ContentMode) -> Self {
+ component.imageView?.contentMode = contentMode
+ return self
+ }
+
+ /// Sets the horizontal content alignment for the button.
+ ///
+ /// - Parameter adjustment: The horizontal alignment to apply (e.g., `.center`, `.left`, `.right`).
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func contentHorizontalAlignment(_ adjustment: UIControl.ContentHorizontalAlignment) -> Self {
+ component.contentHorizontalAlignment = adjustment
+ return self
+ }
+
+ /// Sets the vertical content alignment for the button.
+ ///
+ /// - Parameter adjustment: The vertical alignment to apply (e.g., `.center`, `.top`, `.bottom`).
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func contentVerticalAlignment(_ adjustment: UIControl.ContentVerticalAlignment) -> Self {
+ component.contentVerticalAlignment = adjustment
+ return self
+ }
+
+ /// Sets the image edge insets for the button.
+ ///
+ /// - Parameter insets: The insets to apply to the image view.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func imageEdgeInsets(_ insets: UIEdgeInsets) -> Self {
+ component.imageEdgeInsets = insets
+ return self
+ }
+
+ /// Sets the title edge insets for the button.
+ ///
+ /// - Parameter insets: The insets to apply to the title label.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func titleEdgeInsets(_ insets: UIEdgeInsets) -> Self {
+ component.titleEdgeInsets = insets
+ return self
+ }
+
+ /// Sets the content edge insets for the button.
+ ///
+ /// - Parameter inset: The insets to apply to the entire button's content.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func contentEdgeInsets(_ inset: UIEdgeInsets) -> Self {
+ component.contentEdgeInsets = inset
+ return self
+ }
+
+ /// Sets the semantic content attribute for the button.
+ ///
+ /// - Parameter atrib: The semantic content attribute to apply (e.g., `.unspecified`, `.forceLeftToRight`).
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func semanticContentAttribute(_ atrib: UISemanticContentAttribute) -> Self {
+ component.semanticContentAttribute = atrib
+ return self
+ }
+
+ /// Sets the background image for a specific button state.
+ ///
+ /// - Parameters:
+ /// - image: The image to set as the background.
+ /// - controlState: The state for which to set the background image (default is `.normal`).
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func backgroundImage(_ image: UIImage?, for controlState: UIControl.State = .normal) -> Self {
+ component.setBackgroundImage(image, for: controlState)
+ return self
+ }
+
+ /// Enables or disables the button.
+ ///
+ /// - Parameter isEnable: A boolean indicating whether the button should be enabled or disabled.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func enable(_ isEnable: Bool) -> Self {
+ component.isEnabled = isEnable
+ return self
+ }
+
+ /// Adds a custom command block to be executed when the button is tapped.
+ ///
+ /// - Parameters:
+ /// - command: The closure to be executed.
+ /// - event: The event to associate the command with (default is `.touchUpInside`).
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func add(command: (() -> Void)?, event: UIControl.Event = .touchUpInside) -> Self {
+ guard let command = command else {
+ return self
+ }
+
+ let buttonCommand = ButtonCommand(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 custom command block to be executed when the button is tapped, with access to the button itself.
+ ///
+ /// - Parameters:
+ /// - command: The closure to be executed with the button as the parameter.
+ /// - event: The event to associate the command with.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func add(command: ((UIButton) -> Void)?, event: UIControl.Event) -> Self {
+ guard let command = command else {
+ return self
+ }
+
+ let buttonCommand = ButtonCommand { [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/Extensions/FlexUI+UICollectionView.swift b/Sources/FlexUI/Classes/Extensions/FlexUI+UICollectionView.swift
new file mode 100644
index 0000000..bcb38a1
--- /dev/null
+++ b/Sources/FlexUI/Classes/Extensions/FlexUI+UICollectionView.swift
@@ -0,0 +1,143 @@
+//
+// flex-ui
+// Copyright © 2024 Space Code. All rights reserved.
+//
+
+import UIKit
+
+/// An extension to `FlexUI` that adds helper methods for configuring `UICollectionView` properties.
+/// These methods allow for fluent configuration of properties such as scroll indicators, selection,
+/// and registration of cells and headers.
+public extension FlexUI where Component: UICollectionView {
+ /// Toggles the visibility of vertical and horizontal scroll indicators.
+ ///
+ /// - Parameter isShow: A boolean indicating whether to show the scroll indicators.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func showScollIndicators(_ isShow: Bool) -> Self {
+ component.showsVerticalScrollIndicator = isShow
+ component.showsHorizontalScrollIndicator = isShow
+ return self
+ }
+
+ /// Enables or disables scrolling for the collection view.
+ ///
+ /// - Parameter isEnabled: A boolean indicating whether scrolling is enabled or disabled.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func isScrollEnabled(_ isEnabled: Bool) -> Self {
+ component.isScrollEnabled = isEnabled
+ return self
+ }
+
+ /// Allows or disallows selection of items in the collection view.
+ ///
+ /// - Parameter isAllowSelection: A boolean indicating whether selection is allowed.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func allowSelection(_ isAllowSelection: Bool) -> Self {
+ component.allowsSelection = isAllowSelection
+ return self
+ }
+
+ /// Toggles the vertical bounce behavior of the collection view.
+ ///
+ /// - Parameter isAlwaysBounceVertically: A boolean indicating whether vertical bounce should always be allowed.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func alwaysBounceVertical(_ isAlwaysBounceVertically: Bool) -> Self {
+ component.alwaysBounceVertical = isAlwaysBounceVertically
+ return self
+ }
+
+ /// Registers a class for use as a collection view cell.
+ ///
+ /// - Parameters:
+ /// - cellClass: The class of the cell to register.
+ /// - identifier: The reuse identifier for the cell.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func register(_ cellClass: AnyClass?, with identifier: String) -> Self {
+ component.register(cellClass, forCellWithReuseIdentifier: identifier)
+ return self
+ }
+
+ /// Registers a nib for use as a collection view cell.
+ ///
+ /// - Parameters:
+ /// - nib: The nib to register.
+ /// - identifier: The reuse identifier for the cell.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func register(_ nib: UINib?, with identifier: String) -> Self {
+ component.register(nib, forCellWithReuseIdentifier: identifier)
+ return self
+ }
+
+ /// Registers a class for use as a collection view header.
+ ///
+ /// - Parameters:
+ /// - headerClass: The class of the header to register.
+ /// - identifier: The reuse identifier for the header.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func register(_ headerClass: AnyClass?, _ identifier: String) -> Self {
+ component.register(
+ headerClass,
+ forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader,
+ withReuseIdentifier: identifier
+ )
+ return self
+ }
+
+ /// Sets the delegate for the collection view.
+ ///
+ /// - Parameter delegate: The delegate object that conforms to `UICollectionViewDelegate`.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func delegate(_ delegate: UICollectionViewDelegate?) -> Self {
+ component.delegate = delegate
+ return self
+ }
+
+ /// Sets the data source for the collection view.
+ ///
+ /// - Parameter dataSource: The data source object that conforms to `UICollectionViewDataSource`.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func dataSource(_ dataSource: UICollectionViewDataSource?) -> Self {
+ component.dataSource = dataSource
+ return self
+ }
+
+ /// Sets the content inset for the collection view.
+ ///
+ /// - Parameter inset: The insets to apply to the content of the collection view.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func contentInset(_ inset: UIEdgeInsets) -> Self {
+ component.contentInset = inset
+ return self
+ }
+
+ /// Enables or disables paging for the collection view.
+ ///
+ /// - Parameter isPagingEnabled: A boolean indicating whether paging is enabled.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func isPagingEnabled(_ isPagingEnabled: Bool) -> Self {
+ component.isPagingEnabled = isPagingEnabled
+ return self
+ }
+}
diff --git a/Sources/FlexUI/Classes/Extensions/FlexUI+UIImageView.swift b/Sources/FlexUI/Classes/Extensions/FlexUI+UIImageView.swift
new file mode 100644
index 0000000..eee146b
--- /dev/null
+++ b/Sources/FlexUI/Classes/Extensions/FlexUI+UIImageView.swift
@@ -0,0 +1,55 @@
+//
+// flex-ui
+// Copyright © 2024 Space Code. All rights reserved.
+//
+
+import UIKit
+
+/// An extension to `FlexUI` that adds helper methods for configuring `UIImageView` properties.
+/// These methods allow for fluent configuration of properties such as content mode, clips to bounds,
+/// image, and accessibility adjustments.
+public extension FlexUI where Component: UIImageView {
+ /// Sets the content mode of the image view.
+ ///
+ /// - Parameter contentMode: The content mode that determines how the image is scaled or positioned within the image view.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func contentMode(_ contentMode: UIView.ContentMode) -> Self {
+ component.contentMode = contentMode
+ return self
+ }
+
+ /// Toggles whether the image view clips its content to its bounds.
+ ///
+ /// - Parameter clipsToBounds: A boolean indicating whether the content of the image view should be clipped to the bounds.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func clipsToBounds(_ clipsToBounds: Bool) -> Self {
+ component.clipsToBounds = clipsToBounds
+ return self
+ }
+
+ /// Sets the image to be displayed in the image view.
+ ///
+ /// - Parameter image: The image to display in the image view.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func image(_ image: UIImage?) -> Self {
+ component.image = image
+ return self
+ }
+
+ /// Toggles whether the image view adjusts its image size based on the current accessibility content size category.
+ ///
+ /// - Parameter value: A boolean indicating whether to adjust the image size for accessibility.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func adjustsImageSizeForAccessibilityContentSizeCategory(_ value: Bool) -> Self {
+ component.adjustsImageSizeForAccessibilityContentSizeCategory = value
+ return self
+ }
+}
diff --git a/Sources/FlexUI/Classes/Extensions/FlexUI+UILabel.swift b/Sources/FlexUI/Classes/Extensions/FlexUI+UILabel.swift
new file mode 100644
index 0000000..cb26eb4
--- /dev/null
+++ b/Sources/FlexUI/Classes/Extensions/FlexUI+UILabel.swift
@@ -0,0 +1,74 @@
+//
+// flex-ui
+// Copyright © 2024 Space Code. All rights reserved.
+//
+
+import UIKit
+
+public extension FlexUI where Component: UILabel {
+ /// Sets the text of the label.
+ ///
+ /// - Parameter text: The string to display as the label's text.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func text(_ text: String) -> Self {
+ component.text = text
+ return self
+ }
+
+ /// Sets the attributed text of the label.
+ ///
+ /// - Parameter attributedText: The attributed string to display as the label's text.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func attributedText(_ attributedText: NSAttributedString?) -> Self {
+ component.attributedText = attributedText
+ return self
+ }
+
+ /// Sets the font of the label.
+ ///
+ /// - Parameter font: The font to be applied to the label's text.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func font(_ font: UIFont) -> Self {
+ component.font = font
+ return self
+ }
+
+ /// Sets the text color of the label.
+ ///
+ /// - Parameter color: The color to apply to the label's text.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func textColor(_ color: UIColor) -> Self {
+ component.textColor = color
+ return self
+ }
+
+ /// Sets the number of lines for the label's text.
+ ///
+ /// - Parameter numberOfLines: The number of lines to which the label's text can wrap.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func numberOfLines(_ numberOfLines: Int) -> Self {
+ component.numberOfLines = numberOfLines
+ return self
+ }
+
+ /// Sets the text alignment of the label.
+ ///
+ /// - Parameter textAlignment: The alignment of the text within the label.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func textAlignment(_ textAlignment: NSTextAlignment) -> Self {
+ component.textAlignment = textAlignment
+ return self
+ }
+}
diff --git a/Sources/FlexUI/Classes/Extensions/FlexUI+UIScrollView.swift b/Sources/FlexUI/Classes/Extensions/FlexUI+UIScrollView.swift
new file mode 100644
index 0000000..e7ba5ad
--- /dev/null
+++ b/Sources/FlexUI/Classes/Extensions/FlexUI+UIScrollView.swift
@@ -0,0 +1,97 @@
+//
+// flex-ui
+// Copyright © 2024 Space Code. All rights reserved.
+//
+
+import UIKit
+
+public extension FlexUI where Component: UIScrollView {
+ /// Toggles the visibility of the scroll indicators for both vertical and horizontal scroll views.
+ ///
+ /// - Parameter isVisible: A boolean indicating whether the scroll indicators should be visible.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func showScrollIndicators(_ isVisible: Bool) -> Self {
+ component.showsVerticalScrollIndicator = isVisible
+ component.showsHorizontalScrollIndicator = isVisible
+ return self
+ }
+
+ /// Sets the insets for the scroll indicators.
+ ///
+ /// - Parameter inset: The inset to apply to the scroll indicators.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func scrollIndicatorInsets(_ inset: UIEdgeInsets) -> Self {
+ component.scrollIndicatorInsets = inset
+ return self
+ }
+
+ /// Sets the behavior for adjusting the content inset.
+ ///
+ /// - Parameter behavior: The content inset adjustment behavior to apply to the scroll view.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func contentInsetAdjustmentBehavior(_ behavior: UIScrollView.ContentInsetAdjustmentBehavior) -> Self {
+ component.contentInsetAdjustmentBehavior = behavior
+ return self
+ }
+
+ /// Sets the content inset for the scroll view.
+ ///
+ /// - Parameter inset: The content inset to apply, defaulting to `.zero` if not provided.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func contentInset(_ inset: UIEdgeInsets = .zero) -> Self {
+ component.contentInset = inset
+ return self
+ }
+
+ /// Sets the style of the scroll indicators.
+ ///
+ /// - Parameter style: The indicator style to apply to the scroll view.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func indicatorStyle(_ style: UIScrollView.IndicatorStyle) -> Self {
+ component.indicatorStyle = style
+ return self
+ }
+
+ /// Enables or disables scrolling for the scroll view.
+ ///
+ /// - Parameter isScrollEnable: A boolean indicating whether scrolling should be enabled.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func enableScrolling(_ isScrollEnable: Bool) -> Self {
+ component.isScrollEnabled = isScrollEnable
+ return self
+ }
+
+ /// Sets the delegate for the scroll view.
+ ///
+ /// - Parameter delegate: The delegate to assign to the scroll view.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func delegate(_ delegate: UIScrollViewDelegate) -> Self {
+ component.delegate = delegate
+ return self
+ }
+
+ /// Toggles the bouncing effect when scrolling reaches the edge of the content.
+ ///
+ /// - Parameter isEnable: A boolean indicating whether the bouncing effect should be enabled.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func bounce(_ isEnable: Bool) -> Self {
+ component.bounces = isEnable
+ return self
+ }
+}
diff --git a/Sources/FlexUI/Classes/Extensions/FlexUI+UITextField.swift b/Sources/FlexUI/Classes/Extensions/FlexUI+UITextField.swift
new file mode 100644
index 0000000..f2dff81
--- /dev/null
+++ b/Sources/FlexUI/Classes/Extensions/FlexUI+UITextField.swift
@@ -0,0 +1,143 @@
+//
+// flex-ui
+// Copyright © 2024 Space Code. All rights reserved.
+//
+
+import UIKit
+
+public extension FlexUI where Component: UITextField {
+ /// Sets the font of the text field.
+ ///
+ /// - Parameter font: The font to apply to the text field's text.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func font(_ font: UIFont) -> Self {
+ component.font = font
+ return self
+ }
+
+ /// Sets the placeholder text for the text field.
+ ///
+ /// - Parameter placeholder: The string to display as the placeholder text.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func placeholder(_ placeholder: String) -> Self {
+ component.placeholder = placeholder
+ return self
+ }
+
+ /// Sets the text color of the text field.
+ ///
+ /// - Parameter color: The color to apply to the text in the text field.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func textColor(_ color: UIColor) -> Self {
+ component.textColor = color
+ return self
+ }
+
+ /// Sets the text of the text field.
+ ///
+ /// - Parameter text: The string to display as the text in the text field.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func text(_ text: String?) -> Self {
+ component.text = text
+ return self
+ }
+
+ /// Sets the delegate of the text field.
+ ///
+ /// - Parameter delegate: The delegate to assign to the text field.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func delegate(_ delegate: UITextFieldDelegate) -> Self {
+ component.delegate = delegate
+ return self
+ }
+
+ /// Sets the attributed placeholder text with custom attributes for the text field.
+ ///
+ /// - Parameters:
+ /// - placeholder: The placeholder text to display.
+ /// - attributes: The attributes to apply to the placeholder text.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func attributedPlaceholder(_ placeholder: String, _ attributes: [NSAttributedString.Key: Any]) -> Self {
+ component.attributedPlaceholder = NSAttributedString(string: placeholder, attributes: attributes)
+ return self
+ }
+
+ /// Sets the keyboard type for the text field.
+ ///
+ /// - Parameter type: The keyboard type to use for the text field.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func keyboardType(_ type: UIKeyboardType) -> Self {
+ component.keyboardType = type
+ return self
+ }
+
+ /// Sets the text alignment for the text field.
+ ///
+ /// - Parameter aligment: The text alignment to apply to the text field.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func textAligment(_ aligment: NSTextAlignment) -> Self {
+ component.textAlignment = aligment
+ return self
+ }
+
+ /// Sets the return key type for the text field.
+ ///
+ /// - Parameter type: The return key type to use for the text field.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func returnKeyType(_ type: UIReturnKeyType) -> Self {
+ component.returnKeyType = type
+ return self
+ }
+
+ /// Sets the autocorrection type for the text field.
+ ///
+ /// - Parameter type: The autocorrection type to use for the text field.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func autocorrectionType(_ type: UITextAutocorrectionType) -> Self {
+ component.autocorrectionType = type
+ return self
+ }
+
+ /// Sets the text content type for the text field.
+ ///
+ /// - Parameter type: The text content type to use for the text field.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func textContentType(_ type: UITextContentType) -> Self {
+ component.textContentType = type
+ return self
+ }
+
+ /// Sets the minimum font size for the text field's text.
+ ///
+ /// - Parameter size: The minimum font size for the text field's text.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func minFontSize(_ size: CGFloat) -> Self {
+ component.minimumFontSize = size
+ component.adjustsFontSizeToFitWidth = true
+ return self
+ }
+}
diff --git a/Sources/FlexUI/Classes/Extensions/FlexUI+UITextView.swift b/Sources/FlexUI/Classes/Extensions/FlexUI+UITextView.swift
new file mode 100644
index 0000000..7ff1487
--- /dev/null
+++ b/Sources/FlexUI/Classes/Extensions/FlexUI+UITextView.swift
@@ -0,0 +1,151 @@
+//
+// flex-ui
+// Copyright © 2024 Space Code. All rights reserved.
+//
+
+import UIKit
+
+public extension FlexUI where Component: UITextView {
+ /// Sets the text color of the text view.
+ ///
+ /// - Parameter color: The color to apply to the text in the text view.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func textColor(_ color: UIColor) -> Self {
+ component.textColor = color
+ return self
+ }
+
+ /// Enables or disables scrolling for the text view.
+ ///
+ /// - Parameter isEnable: A boolean indicating whether scrolling should be enabled.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func isScrollEnabled(_ isEnable: Bool) -> Self {
+ component.isScrollEnabled = isEnable
+ return self
+ }
+
+ /// Sets the font of the text view.
+ ///
+ /// - Parameter font: The font to apply to the text view's text.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func font(_ font: UIFont) -> Self {
+ component.font = font
+ return self
+ }
+
+ /// Sets the text of the text view.
+ ///
+ /// - Parameter text: The string to display as the text in the text view.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func text(_ text: String) -> Self {
+ component.text = text
+ return self
+ }
+
+ /// Sets the line break mode for the text view.
+ ///
+ /// - Parameter mode: The line break mode to use for the text view's text.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func lineBreakMode(_ mode: NSLineBreakMode) -> Self {
+ component.textContainer.lineBreakMode = mode
+ return self
+ }
+
+ /// Sets the keyboard type for the text view.
+ ///
+ /// - Parameter type: The keyboard type to use for the text view.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func keyboardType(_ type: UIKeyboardType) -> Self {
+ component.keyboardType = type
+ return self
+ }
+
+ /// Sets the text alignment for the text view.
+ ///
+ /// - Parameter alignment: The text alignment to apply to the text view.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func textAlignment(_ alignment: NSTextAlignment) -> Self {
+ component.textAlignment = alignment
+ return self
+ }
+
+ /// Sets the attributed text for the text view.
+ ///
+ /// - Parameter text: The attributed string to display in the text view.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func attributedText(_ text: NSAttributedString) -> Self {
+ component.attributedText = text
+ return self
+ }
+
+ /// Enables or disables editing for the text view.
+ ///
+ /// - Parameter isEditable: A boolean indicating whether editing should be enabled.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func isEditable(_ isEditable: Bool) -> Self {
+ component.isEditable = isEditable
+ return self
+ }
+
+ /// Enables or disables text selection for the text view.
+ ///
+ /// - Parameter selectable: A boolean indicating whether text selection should be enabled.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func isSelectable(_ selectable: Bool) -> Self {
+ component.isSelectable = selectable
+ return self
+ }
+
+ /// Sets the autocorrection type for the text view.
+ ///
+ /// - Parameter type: The autocorrection type to use for the text view.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func autocorrectionType(_ type: UITextAutocorrectionType) -> Self {
+ component.autocorrectionType = type
+ return self
+ }
+
+ /// Sets the text content type for the text view.
+ ///
+ /// - Parameter type: The text content type to use for the text view.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func textContentType(_ type: UITextContentType) -> Self {
+ component.textContentType = type
+ return self
+ }
+
+ /// Sets the delegate for the text view.
+ ///
+ /// - Parameter target: The delegate to assign to the text view.
+ /// - Returns: The current instance of `FlexUI` for further configuration.
+ @discardableResult
+ @MainActor
+ func delegate(_ target: UITextViewDelegate) -> Self {
+ component.delegate = target
+ return self
+ }
+}
diff --git a/Sources/flex-ui/Classes/flex-ui.swift b/Sources/flex-ui/Classes/flex-ui.swift
deleted file mode 100644
index 6c8e157..0000000
--- a/Sources/flex-ui/Classes/flex-ui.swift
+++ /dev/null
@@ -1,6 +0,0 @@
-//
-// flex-ui
-// Copyright © 2024 Space Code. All rights reserved.
-//
-
-final class flex - ui {}
diff --git a/Tests/flex-uiTests/flex-uiTests.swift b/Tests/FlexUITests/FlexUITests.swift
similarity index 67%
rename from Tests/flex-uiTests/flex-uiTests.swift
rename to Tests/FlexUITests/FlexUITests.swift
index 3b39c86..a44f869 100644
--- a/Tests/flex-uiTests/flex-uiTests.swift
+++ b/Tests/FlexUITests/FlexUITests.swift
@@ -5,4 +5,4 @@
import XCTest
-final class flex - uiTests: XCTestCase {}
+final class FlexUITests: XCTestCase {}
diff --git a/hooks/pre-commit b/hooks/pre-commit
old mode 100644
new mode 100755