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 @@ Swift Compatibility Platform Compatibility CI - -Number of GitHub contributors -Number of GitHub issues that are open -Number of GitHub closed issues -Number of GitHub stars -Number of GitHub pull requests that are open - GitHub release; latest by date

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