Apr 29, 2024 iOS

SwiftUI HStack with Wrap

SwiftUI
SwiftUI

Explore

Certainly! SwiftUI provides an HStack for arranging views horizontally, but if you need to handle overflowing elements by automatically positioning them on the next lines, you might want to explore a custom solution. Here are a few options:

  1. WrappingHStack by dkk:
    • Description: A SwiftUI HStack with the ability to wrap contained elements.
    • Usage:
WrappingHStack {
    // Add your views here
}
  1. Installation:
    • Swift Package Manager:
      1. In Xcode, open your project and navigate to File → Swift Packages → Add Package Dependency.
      2. Paste the repository URL: WrappingHStack and click Next.
      3. Select the desired version rule and click Finish.
    • Cocoapods: Add the following line to your Podfile:pod 'WrappingHStack'
  2. Known Issues:
    • Item sizes may be calculated incorrectly when WrappingHStack has modifiers that change the size of its elements.
    • Workaround needed when using WrappingHStack in a NavigationLink (iOS 16 only).
  3. GitHub Repository: WrappingHStack1.
  4. WrappingStack by diniska:
  5. MultilineHStack (Custom Implementation):
    • Description: A custom view that wraps children along multiple lines, similar to a collection view.
    • Usage:
struct ContentView: View {
    let texts = ["a", "lot", "of", "texts"]
    var body: some View {
        MultilineHStack(self.texts) { Text($0) }
    }
}

  1. Stacking Approach: You can use one VStack with multiple HStack elements, each containing the views for one row 3.

Certainly! Here are a couple more options for achieving a wrapping behavior similar to an HStack in SwiftUI:

  1. Custom Wrapping HStack (No External Dependencies):
    • You can create your own custom view that wraps elements in an HStack and automatically moves overflowing items to the next line. Here’s a basic example:
    • Adjust the itemsPerRow parameter as needed to control how many items appear in each row.
struct CustomWrappingHStack<Content: View>: View {
    let content: [Content]
    let itemsPerRow: Int

    init(itemsPerRow: Int, @ViewBuilder content: () -> [Content]) {
        self.itemsPerRow = itemsPerRow
        self.content = content()
    }

    var body: some View {
        VStack(alignment: .leading, spacing: 8) {
            ForEach(0..<content.count, id: \.self) { index in
                if index % itemsPerRow == 0 {
                    HStack(spacing: 8) {
                        ForEach(index..<min(index + itemsPerRow, content.count)) { innerIndex in
                            content[innerIndex]
                        }
                    }
                }
            }
        }
    }
}

// Usage:
CustomWrappingHStack(itemsPerRow: 3) {
    Text("Item 1")
    Text("Item 2")
    Text("Item 3")
    // Add more views here
}

  1. LazyVGrid (Available in iOS 14+):
    • While not exactly the same as an HStack, LazyVGrid allows you to create a grid of views with a specified number of columns. You can use it to achieve a similar wrapping effect.
    • Example:
LazyVGrid(columns: [GridItem(.adaptive(minimum: 100))], spacing: 8) {
    Text("Item 1")
    Text("Item 2")
    Text("Item 3")
    // Add more views here
}
  1. Adjust the minimum value in GridItem(.adaptive(minimum: 100)) to control the minimum width of each item.

Remember to adapt these solutions to your specific use case and adjust styling, spacing, and alignment as needed. SwiftUI provides flexibility, so feel free to experiment and find the approach that best suits your UI requirements! 🚀