Skip to content

Commit

Permalink
Documentation with the setup guide and example added
Browse files Browse the repository at this point in the history
  • Loading branch information
diniska committed May 25, 2021
2 parents 85b076d + 4917f28 commit ff31300
Show file tree
Hide file tree
Showing 4 changed files with 192 additions and 11 deletions.
91 changes: 91 additions & 0 deletions .swiftpm/xcode/xcshareddata/xcschemes/WrappingStack.xcscheme
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1250"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "WrappingStack"
BuildableName = "WrappingStack"
BlueprintName = "WrappingStack"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "WrappingStackTests"
BuildableName = "WrappingStackTests"
BlueprintName = "WrappingStackTests"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "WrappingStackTests"
BuildableName = "WrappingStackTests"
BlueprintName = "WrappingStackTests"
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "WrappingStack"
BuildableName = "WrappingStack"
BlueprintName = "WrappingStack"
ReferencedContainer = "container:">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
Binary file added Docs/Resources/wrapping-hstack-macos.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
66 changes: 63 additions & 3 deletions Readme.md
Original file line number Diff line number Diff line change
@@ -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.
46 changes: 38 additions & 8 deletions Sources/WrappingStack/WrappingHStack.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,21 @@ public struct WrappingHStack<Data: RandomAccessCollection, ID: Hashable, Content
return result
}

/// Creates a new WrappingHStack
///
/// - Parameters:
/// - id: a keypath of element identifier
/// - 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(
id: KeyPath<Data.Element, ID>,
alignment: Alignment = .center,
horizontalSpacing: CGFloat = 0,
verticalSpacing: CGFloat = 0,
@ViewBuilder content create: () -> ForEach<Data, ID, Content>
){
) {
let forEach = create()
data = forEach.data
content = forEach.content
Expand Down Expand Up @@ -111,8 +119,24 @@ public struct WrappingHStack<Data: RandomAccessCollection, ID: Hashable, Content

@available(iOS 14, macOS 11, *)
extension WrappingHStack where ID == Data.Element.ID, Data.Element: Identifiable {
public init(@ViewBuilder content create: () -> ForEach<Data, ID, Content>) {
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<Data, ID, Content>
) {
self.init(id: \.id,
alignment: alignment,
horizontalSpacing: horizontalSpacing,
verticalSpacing: verticalSpacing,
content: create)
}
}

Expand All @@ -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)
}
}

Expand Down

0 comments on commit ff31300

Please sign in to comment.