diff --git a/README.md b/README.md index 89ad1a34..08a799a7 100644 --- a/README.md +++ b/README.md @@ -99,59 +99,112 @@ HFlow(itemSpacing: 4, rowSpacing: 20) { ![HFlow](Resources/hflow-spacing.png) -## Justified +## Distribute items -Justify by stretching items, the spaces between them, or both. +Distribute items evenly by minimizing the empty spaces left in each row. +Implements the Knuth-Plass line breaking algorithm. ```swift -HFlow(justification: .stretchItems) { +HFlow(distributeItemsEvenly: true) { ForEach(colors, id: \.description) { color in RoundedRectangle(cornerRadius: 10) .fill(color.gradient) - .frame(height: 50) - .frame(minWidth: 35) + .frame(width: 65, height: 50) } } -.frame(width: 300) +.frame(width: 300, alignment: .leading) ``` -![HFlow](Resources/hflow-justified.png) - ---- +![HFlow](Resources/hflow-distributed.png) -Distribute items evenly by minimizing the empty spaces left in each row. -Implements the Knuth-Plass line breaking algorithm. +## Justified ```swift -HFlow(distributeItemsEvenly: true) { +HFlow(justified: true) { ForEach(colors, id: \.description) { color in RoundedRectangle(cornerRadius: 10) .fill(color.gradient) - .frame(width: 65, height: 50) + .frame(width: 50, height: 50) } } -.frame(width: 300, alignment: .leading) +.frame(width: 300) ``` -![HFlow](Resources/hflow-distributed.png) +![HFlow](Resources/hflow-justified.png) + +## Flexibility + +```swift +HFlow { // distributes flexible items proportionally + RoundedRectangle(cornerRadius: 10) + .fill(.red) + .frame(minWidth: 50, maxWidth: .infinity) + .frame(height: 50) + .flexibility(.minimum) // takes as little space as possible, rigid + RoundedRectangle(cornerRadius: 10) + .fill(.green) + .frame(minWidth: 50, maxWidth: .infinity) + .frame(height: 50) + .flexibility(.natural) // expands + RoundedRectangle(cornerRadius: 10) + .fill(.blue) + .frame(minWidth: 50, maxWidth: .infinity) + .frame(height: 50) + .flexibility(.natural) // expands + RoundedRectangle(cornerRadius: 10) + .fill(.yellow) + .frame(minWidth: 50, maxWidth: .infinity) + .frame(height: 50) // takes as much space as possible + .flexibility(.maximum) +} +.frame(width: 300) +``` ---- +![HFlow](Resources/hflow-flexibility.png) -Distribute and justify for visually pleasing UI. +## Line breaks ```swift -HFlow(justification: .stretchItems, distributeItemsEvenly: true) { - ForEach(colors, id: \.description) { color in - RoundedRectangle(cornerRadius: 10) - .fill(color.gradient) - .frame(height: 50) - .frame(minWidth: 60) - } +HFlow { + RoundedRectangle(cornerRadius: 10) + .fill(.red) + .frame(width: 50, height: 50) + RoundedRectangle(cornerRadius: 10) + .fill(.green) + .frame(width: 50, height: 50) + RoundedRectangle(cornerRadius: 10) + .fill(.blue) + .frame(width: 50, height: 50) + LineBreak() + RoundedRectangle(cornerRadius: 10) + .fill(.yellow) + .frame(width: 50, height: 50) +} +.frame(width: 300) +``` + +![HFlow](Resources/hflow-linebreak.png) + +```swift +HFlow { + RoundedRectangle(cornerRadius: 10) + .fill(.red) + .frame(width: 50, height: 50) + RoundedRectangle(cornerRadius: 10) + .fill(.green) + .frame(width: 50, height: 50) + .startInNewLine() + RoundedRectangle(cornerRadius: 10) + .fill(.blue) + .frame(width: 50, height: 50) + RoundedRectangle(cornerRadius: 10) + .fill(.yellow) + .frame(width: 50, height: 50) } .frame(width: 300) ``` -![HFlow](Resources/hflow-justified-and-distributed.png) +![HFlow](Resources/hflow-newline.png) ## RTL diff --git a/Resources/hflow-flexibility.png b/Resources/hflow-flexibility.png new file mode 100644 index 00000000..12146ea0 Binary files /dev/null and b/Resources/hflow-flexibility.png differ diff --git a/Resources/hflow-justified-and-distributed.png b/Resources/hflow-justified-and-distributed.png deleted file mode 100644 index 9b357b7f..00000000 Binary files a/Resources/hflow-justified-and-distributed.png and /dev/null differ diff --git a/Resources/hflow-justified.png b/Resources/hflow-justified.png index 4325dcd2..ceba2e55 100644 Binary files a/Resources/hflow-justified.png and b/Resources/hflow-justified.png differ diff --git a/Resources/hflow-linebreak.png b/Resources/hflow-linebreak.png new file mode 100644 index 00000000..04233336 Binary files /dev/null and b/Resources/hflow-linebreak.png differ diff --git a/Resources/hflow-newline.png b/Resources/hflow-newline.png new file mode 100644 index 00000000..a6ee4ac0 Binary files /dev/null and b/Resources/hflow-newline.png differ diff --git a/Sources/Flow/Example/ContentView.swift b/Sources/Flow/Example/ContentView.swift index 39ff0d1a..6b0f5ede 100644 --- a/Sources/Flow/Example/ContentView.swift +++ b/Sources/Flow/Example/ContentView.swift @@ -7,7 +7,7 @@ struct ContentView: View { @State private var height: CGFloat = 400 @State private var itemSpacing: CGFloat? = nil @State private var lineSpacing: CGFloat? = nil - @State private var justified: Justified = .none + @State private var justified: Bool = false @State private var horizontalAlignment: HAlignment = .leading @State private var verticalAlignment: VAlignment = .top @State private var distributeItemsEvenly: Bool = false @@ -75,7 +75,7 @@ struct ContentView: View { stepper("Line", $lineSpacing) } Section(header: Text("Extras")) { - picker($justified, style: .inline) + Toggle("Justified", isOn: $justified) Toggle("Distibute evenly", isOn: $distributeItemsEvenly.animation()) } } @@ -134,7 +134,7 @@ struct ContentView: View { verticalAlignment: verticalAlignment.value, horizontalSpacing: itemSpacing, verticalSpacing: lineSpacing, - justification: justified.justification, + justified: justified, distributeItemsEvenly: distributeItemsEvenly ) ) @@ -145,7 +145,7 @@ struct ContentView: View { verticalAlignment: verticalAlignment.value, horizontalSpacing: lineSpacing, verticalSpacing: itemSpacing, - justification: justified.justification, + justified: justified, distributeItemsEvenly: distributeItemsEvenly ) ) @@ -160,24 +160,6 @@ enum Contents: String, CustomStringConvertible, CaseIterable { var description: String { rawValue } } -enum Justified: String, CustomStringConvertible, CaseIterable { - case none = "no justification" - case stretchItems = "stretch items" - case stretchSpaces = "stretch spaces" - case stretchItemsAndSpaces = "stretch both" - - var description: String { rawValue } - - var justification: Justification? { - switch self { - case .none: nil - case .stretchItems: .stretchItems - case .stretchSpaces: .stretchSpaces - case .stretchItemsAndSpaces: .stretchItemsAndSpaces - } - } -} - #Preview { ContentView() } diff --git a/Sources/Flow/Internal/LineBreaking.swift b/Sources/Flow/Internal/LineBreaking.swift index a1e1025f..599521b5 100644 --- a/Sources/Flow/Internal/LineBreaking.swift +++ b/Sources/Flow/Internal/LineBreaking.swift @@ -162,7 +162,7 @@ func sizes(of items: IndexedLineBreakingInput, availableSpace: CGFloat) -> SizeC return nil } // Calculate total size - var totalSizeOfItems = items.sum(of: \.element.size.lowerBound) + items.dropFirst().sum(of: \.element.spacing) + let totalSizeOfItems = items.sum(of: \.element.size.lowerBound) + items.dropFirst().sum(of: \.element.spacing) if totalSizeOfItems > availableSpace { return nil } diff --git a/Sources/Flow/Support.swift b/Sources/Flow/Support.swift index 0690de73..41d81b5d 100644 --- a/Sources/Flow/Support.swift +++ b/Sources/Flow/Support.swift @@ -85,8 +85,8 @@ public struct LineBreak: View { extension View { /// Allows flow layout elements to be started on new lines, allowing precise control over line breaking. - public func startInNewLine() -> some View { - layoutValue(key: ShouldStartInNewLineLayoutValueKey.self, value: true) + public func startInNewLine(_ enabled: Bool = true) -> some View { + layoutValue(key: ShouldStartInNewLineLayoutValueKey.self, value: enabled) } /// Allows modifying the flexibility behavior of views so that flow can layout them accordingly.