diff --git a/Sources/Core/Extensions/CGFloat+RAK.swift b/Sources/Core/Extensions/BinaryFloatingPoint+RAK.swift similarity index 50% rename from Sources/Core/Extensions/CGFloat+RAK.swift rename to Sources/Core/Extensions/BinaryFloatingPoint+RAK.swift index f883b27..02229a0 100644 --- a/Sources/Core/Extensions/CGFloat+RAK.swift +++ b/Sources/Core/Extensions/BinaryFloatingPoint+RAK.swift @@ -1,5 +1,5 @@ // -// CGFloat+RAK.swift +// BinaryFloatingPoint+RAK.swift // RakuyoKit // // Created by Rakuyo on 2024/4/9. @@ -8,27 +8,18 @@ import UIKit -extension Extendable where Base == CGFloat { +extension Extendable where Base: BinaryFloatingPoint { /// Get 0.5pt value - public static var halfPoint: Base { Base(1).rak.scale } + public static var halfPoint: CGFloat { 1.rak.scale } /// `float / scale` - public var scale: Base { - let _scale: Base = { - #if os(watchOS) - return 1 - #else - return UIScreen.rak.scale - #endif - }() - return base / _scale - } + public var scale: CGFloat { .init(base) / _scale } /// Align floating point values to the pixels of the current device /// /// - Parameter rule: A rule for rounding a floating-point number /// - Returns: The result after alignment - public func alignPixel(with rule: FloatingPointRoundingRule = .toNearestOrAwayFromZero) -> Base { - { (base * $0).rounded(rule) / $0 }(scale) + public func alignPixel(with rule: FloatingPointRoundingRule = .toNearestOrAwayFromZero) -> CGFloat { + { (.init(base) * $0).rounded(rule) / $0 }(scale) } } diff --git a/Sources/Core/Extensions/BinaryInteger+RAK.swift b/Sources/Core/Extensions/BinaryInteger+RAK.swift new file mode 100644 index 0000000..9acc488 --- /dev/null +++ b/Sources/Core/Extensions/BinaryInteger+RAK.swift @@ -0,0 +1,22 @@ +// +// BinaryInteger+RAK.swift +// RakuyoKit +// +// Created by Rakuyo on 2024/6/13. +// Copyright © 2024 RakuyoKit. All rights reserved. +// + +import UIKit + +extension Extendable where Base: BinaryInteger { + /// `float / scale` + public var scale: CGFloat { .init(base) / _scale } + + /// Align floating point values to the pixels of the current device + /// + /// - Parameter rule: A rule for rounding a floating-point number + /// - Returns: The result after alignment + public func alignPixel(with rule: FloatingPointRoundingRule = .toNearestOrAwayFromZero) -> CGFloat { + { (.init(base) * $0).rounded(rule) / $0 }(scale) + } +} diff --git a/Sources/Core/Extensions/Character+RAK.swift b/Sources/Core/Extensions/Character+RAK.swift new file mode 100644 index 0000000..52c2518 --- /dev/null +++ b/Sources/Core/Extensions/Character+RAK.swift @@ -0,0 +1,22 @@ +// +// Character+RAK.swift +// RakuyoKit +// +// Created by Rakuyo on 2024/6/13. +// Copyright © 2024 RakuyoKit. All rights reserved. +// + +import Foundation + +extension Extendable where Base == Character { + /// An emoji is either a 2-byte unicode character or a regular UTF8 character with the emoji modifier attached, like 3️⃣. + /// `0x203C` is the first instance of a UTF16 emoji that doesn't require a modifier. + /// + /// `Unicode.Scalar.Properties.isEmoji` returns `true` for any character that can be turned into an emoji by adding a modifier + /// (such as the number "3"). + /// To avoid this situation, we will first check whether it is greater than `0x203C`. + public var isEmoji: Bool { + guard let scalar = base.unicodeScalars.first else { return false } + return (scalar.value >= 0x203C || base.unicodeScalars.count > 1) && scalar.properties.isEmoji + } +} diff --git a/Sources/Core/Extensions/Numeric+RAK.swift b/Sources/Core/Extensions/Numeric+RAK.swift new file mode 100644 index 0000000..96a36ce --- /dev/null +++ b/Sources/Core/Extensions/Numeric+RAK.swift @@ -0,0 +1,22 @@ +// +// Numeric+RAK.swift +// RakuyoKit +// +// Created by Rakuyo on 2024/4/9. +// Copyright © 2024 RakuyoKit. All rights reserved. +// + +import UIKit + +extension Extendable where Base: Numeric { + /// `float / scale` + /// + /// For use within the framework only + var _scale: CGFloat { + #if os(watchOS) + return 1 + #else + return UIScreen.rak.scale + #endif + } +} diff --git a/Sources/Core/Extensions/String+RAK.swift b/Sources/Core/Extensions/String+RAK.swift index ab12a5c..98ac36b 100644 --- a/Sources/Core/Extensions/String+RAK.swift +++ b/Sources/Core/Extensions/String+RAK.swift @@ -8,6 +8,18 @@ import UIKit +// MARK: - Judgment + +extension Extendable where Base == String { + public func isContainsEmoji() -> Bool { + base.contains { $0.rak.isEmoji } + } + + public func isOnlyContainsEmojis() -> Bool { + !base.isEmpty && !base.contains { !$0.rak.isEmoji } + } +} + // MARK: - Conversion extension Extendable where Base == String { diff --git a/Sources/Epoxy/CollectionView/WaterfallLayout/WaterfallCompositionalLayout+Config.swift b/Sources/Epoxy/CollectionView/WaterfallLayout/WaterfallCompositionalLayout+Config.swift index 02b81fa..bf79b5b 100644 --- a/Sources/Epoxy/CollectionView/WaterfallLayout/WaterfallCompositionalLayout+Config.swift +++ b/Sources/Epoxy/CollectionView/WaterfallLayout/WaterfallCompositionalLayout+Config.swift @@ -11,6 +11,7 @@ import UIKit extension WaterfallCompositionalLayout { + public typealias ItemWidthProvider = (_ index: Int, _ collectionWidth: CGFloat) -> CGFloat public typealias ItemHeightProvider = (_ index: Int, _ itemWidth: CGFloat) -> CGFloat public typealias ItemCountProvider = () -> Int @@ -54,8 +55,9 @@ extension WaterfallCompositionalLayout { public let arrangementStyle: ArrangementStyle public let interItemSpacing: CGFloat public let contentInsets: SectionEdgeInsets? - public let itemHeightProvider: ItemHeightProvider public let itemCountProvider: ItemCountProvider + public let itemWidthProvider: ItemWidthProvider? + public let itemHeightProvider: ItemHeightProvider /// Initialization for configuration of waterfall compositional layout section /// @@ -72,6 +74,7 @@ extension WaterfallCompositionalLayout { interItemSpacing: CGFloat = 0, contentInsets: SectionEdgeInsets? = nil, itemCountProvider: @escaping ItemCountProvider, + itemWidthProvider: ItemWidthProvider? = nil, itemHeightProvider: @escaping ItemHeightProvider ) { self.columnCount = columnCount @@ -79,6 +82,7 @@ extension WaterfallCompositionalLayout { self.interItemSpacing = interItemSpacing self.contentInsets = contentInsets self.itemCountProvider = itemCountProvider + self.itemWidthProvider = itemWidthProvider self.itemHeightProvider = itemHeightProvider } } diff --git a/Sources/Epoxy/CollectionView/WaterfallLayout/WaterfallCompositionalLayout+LayoutBuilder.swift b/Sources/Epoxy/CollectionView/WaterfallLayout/WaterfallCompositionalLayout+LayoutBuilder.swift index 4855527..5a4ad92 100644 --- a/Sources/Epoxy/CollectionView/WaterfallLayout/WaterfallCompositionalLayout+LayoutBuilder.swift +++ b/Sources/Epoxy/CollectionView/WaterfallLayout/WaterfallCompositionalLayout+LayoutBuilder.swift @@ -18,6 +18,7 @@ extension WaterfallCompositionalLayout { private let columnCount: CGFloat private let arrangementStyle: Configuration.ArrangementStyle private var columnHeights: [CGFloat] + private let itemWidthProvider: ItemWidthProvider? private let itemHeightProvider: ItemHeightProvider private let interItemSpacing: CGFloat private let contentInsets: NSDirectionalEdgeInsets @@ -27,6 +28,7 @@ extension WaterfallCompositionalLayout { columnCount = CGFloat(config.columnCount) arrangementStyle = config.arrangementStyle columnHeights = [CGFloat](repeating: 0, count: config.columnCount) + itemWidthProvider = config.itemWidthProvider itemHeightProvider = config.itemHeightProvider interItemSpacing = config.interItemSpacing contentInsets = config.contentInsets.edgeInsets @@ -53,7 +55,7 @@ extension WaterfallCompositionalLayout.LayoutBuilder { } fileprivate func frame(for row: Int) -> CGRect { - let width = columnWidth + let width = itemWidthProvider?(row, collectionWidth) ?? columnWidth let height = itemHeightProvider(row, width) let size = CGSize(width: width, height: height) let origin = itemOrigin(for: row, width: size.width)