diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/WrappingStack.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/WrappingStack.xcscheme new file mode 100644 index 0000000..b693472 --- /dev/null +++ b/.swiftpm/xcode/xcshareddata/xcschemes/WrappingStack.xcscheme @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Docs/Resources/wrapping-hstack-macos.png b/Docs/Resources/wrapping-hstack-macos.png new file mode 100644 index 0000000..85eadcd Binary files /dev/null and b/Docs/Resources/wrapping-hstack-macos.png differ diff --git a/Readme.md b/Readme.md index 2958531..102b354 100644 --- a/Readme.md +++ b/Readme.md @@ -1,7 +1,67 @@ -# Swiftui WrappingStack +# SwiftUI WrappingStack -![Swift 5.3](https://img.shields.io/badge/Swift-5.3-FA5B2C) ![Xcode 12.5](https://img.shields.io/badge/Xcode-12-44B3F6) ![iOS 9.0](https://img.shields.io/badge/iOS-8.0-178DF6) ![iPadOS 9.0](https://img.shields.io/badge/iPadOS-8.0-178DF6) ![MacOS 10.10](https://img.shields.io/badge/MacOS-10.10-178DF6) [![Build & Test](https://github.com/diniska/swiftui-wrapping-stack/actions/workflows/test.yml/badge.svg)](https://github.com/diniska/swiftui-wrapping-stack/actions/workflows/test.yml) +![Swift 5.3](https://img.shields.io/badge/Swift-5.3-FA5B2C) ![Xcode 12.5](https://img.shields.io/badge/Xcode-12.5-44B3F6) ![iOS 9.0](https://img.shields.io/badge/iOS-9.0-178DF6) ![iPadOS 9.0](https://img.shields.io/badge/iPadOS-9.0-178DF6) ![MacOS 10.10](https://img.shields.io/badge/MacOS-10.10-178DF6) [![Build & Test](https://github.com/diniska/swiftui-wrapping-stack/actions/workflows/test.yml/badge.svg)](https://github.com/diniska/swiftui-wrapping-stack/actions/workflows/test.yml) A SwiftUI Views for wrapping HStack elements into multiple lines. -`WrappingHStack` - provides `HStack` that supports line wrapping +## List of supported views + +* `WrappingHStack` - provides `HStack` that supports line wrapping + +## How to use +### Step 1 +Add a dependency using Swift Package Manager to your project: [https://github.com/diniska/swiftui-wrapping-stack](https://github.com/diniska/swiftui-wrapping-stack) + +### Step 2 +Import the dependency + +```swift +import WrappingStack +``` + +### Step 3 +Replace `HStack` with `WrappingHStack` in your view structure. It is compatible with `ForEach`. + +```swift +struct MyView: View { + + let elements = ["Cat 🐱", "Dog 🐶", "Sun 🌞", "Moon 🌕", "Tree 🌳"] + + var body: some View { + WrappingHStack(id: \.self) { // use the same id is in the `ForEach` below + ForEach(elements, id: \.self) { element in + Text(element) + .padding() + .background(Color.gray) + .cornerRadius(6) + } + } + .frame(width: 300) // limiting the width for demo purpose. This line is not needed in real code + } + +} +``` + +The result of the code above: + +![WrappingHStack for macOS](./Docs/Resources/wrapping-hstack-macos.png) + + +## Customization + +Customize appearance using the next parameters. All the default SwiftUI modifiers can be applied as well. + +### `WrappingHStack` parameters + +Parameter name | Description +---------------|-------------- +`alignment` | horizontal and vertical alignment. `.center` is used by default. Vertical alignment is applied to every row +`horizontalSpacing` | horizontal spacing between elements +`verticalSpacing` | vertical spacing between the lines + +## Performance considerations + +The code written in a way to cache the elements representing views sizes, it doesn't re-calculate the size for different views with the same id. + +* huge numbers of elements are not recommended, although the same applies to `HStack` where `LazyHStack` is a better alternative for the long rows. If you have a large number of elements - double-check the memory and performance on a real device +* it is pretty good in terms of CPU consumption as every element calculates its size only once. diff --git a/Sources/WrappingStack/WrappingHStack.swift b/Sources/WrappingStack/WrappingHStack.swift index 6c5416b..a3690a5 100644 --- a/Sources/WrappingStack/WrappingHStack.swift +++ b/Sources/WrappingStack/WrappingHStack.swift @@ -32,13 +32,21 @@ public struct WrappingHStack, alignment: Alignment = .center, horizontalSpacing: CGFloat = 0, verticalSpacing: CGFloat = 0, @ViewBuilder content create: () -> ForEach - ){ + ) { let forEach = create() data = forEach.data content = forEach.content @@ -111,8 +119,24 @@ public struct WrappingHStack ForEach) { - self.init(id: \.id, content: create) + /// Creates a new WrappingHStack + /// + /// - Parameters: + /// - alignment: horizontal and vertical alignment. Vertical alignment is applied to every row + /// - horizontalSpacing: horizontal spacing between elements + /// - verticalSpacing: vertical spacing between the lines + /// - create: a method that creates an array of elements + public init( + alignment: Alignment = .center, + horizontalSpacing: CGFloat = 0, + verticalSpacing: CGFloat = 0, + @ViewBuilder content create: () -> ForEach + ) { + self.init(id: \.id, + alignment: alignment, + horizontalSpacing: horizontalSpacing, + verticalSpacing: verticalSpacing, + content: create) } } @@ -121,15 +145,21 @@ extension WrappingHStack where ID == Data.Element.ID, Data.Element: Identifiable @available(iOS 14, macOS 11, *) struct WrappingHStack_Previews: PreviewProvider { static var previews: some View { - WrappingHStack(id: \.self, alignment: .topLeading) { - ForEach(["Hello1", "world1", "Hello2", "world2", "Hello3", "world3", "Hello4", "world4 ", "Hello1"], id: \.self) { item in - Text(item) + WrappingHStack( + id: \.self, + horizontalSpacing: 8, + verticalSpacing: 8 + ) { + ForEach(["Cat 🐱", "Dog 🐶", "Sun 🌞", "Moon 🌕", "Tree 🌳"], id: \.self) { element in + Text(element) .padding() - .background(Color(.systemGray)) - .cornerRadius(3) + .background(Color.gray.opacity(0.1)) + .cornerRadius(6) } } + .padding() .frame(width: 300) + .background(Color.white) } }