dkk / wrappinghstack Goto Github PK
View Code? Open in Web Editor NEWA SwiftUI HStack with the ability to wrap contained elements
License: MIT License
A SwiftUI HStack with the ability to wrap contained elements
License: MIT License
Describe the bug
On iOS 16, app meets a severe hang when using a wrappingHastack inside of a navigation link if you hit back button on the navigation bar.
To Reproduce
Steps to reproduce the behavior:
struct ContentView: View {
var body: some View {
NavigationView {
VStack {
NavigationLink("Hit me ") {
WrappingView()
}
}
}
}
}
import WrappingHStack
struct WrappingView: View {
@State private var array: [String] = []
var body: some View {
VStack {
WrappingHStack(array,
id: \.self,
alignment: .center,
spacing: .constant(10)) { topic in
Text(topic)
}
}
.onAppear {
array = ["test",
"test3",
"test4",
"test5",
"test6",
"test7"]
}
}
}
Expected behavior
Go back the previous screen and not freeze the app
Screenshots
If applicable, add screenshots to help explain your problem.
Context:
Additional context
Freeze happens only if you use this lib inside of a Navigation link
My case here:
Each item comes different width (different text body)
My expectation is:
auto wrap and start a new line if no spacing
My item is kind of
Button(action: {
if !item.expressImgUrlStr.isEmpty {
// See https://stackoverflow.com/a/63482455/7430141
downloadForm()
}
}, label: {
OrderActionButton.download
})
Using a WrappingHStack the iOS app on iOS 16 beta 3 will crash.
To reproduce you can open a view with a WrappingHStack inside.
If a View is created based on a condition and when the condition is false, the "ghost" View is took into account when creating spacing. For example:
import SwiftUI
import WrappingHStack
struct Test: View {
let condition: Bool
var body: some View {
WrappingHStack {
view("view 1")
if condition {
view("view 2")
}
view("view 3")
view("view 4")
view("view 5")
view("view 6")
}
.border(Color.red.opacity(0.5))
.padding(32)
}
private func view(_ text: String) -> some View {
Text(text)
.padding(8)
.border(Color.black)
}
}
struct Test_Previews: PreviewProvider {
static var previews: some View {
Test(condition: false)
}
}
The result is the following (note the double spacing between view 1 and view 3):
There's a workaround in this case, that is applying spacing as trailing padding on each view and setting WrappingHStack.Spacing to .constant(0)
, but it would be nice to have it working naturally, especially since this way the trailing padding adds to the view width. Could it be possible to fix this? Thanks.
Describe the bug
The initial layout is correct, but changes to the available width do not cause the layout to update.
To Reproduce
Steps to reproduce the behavior:
WrappingHStack
containing a few dozen items.Expected behavior
When the available width changes, the layout should be recalculated correctly.
Screenshots
Correct initial layout in portrait
Incorrect layout after rotating to landscape
Incorrect layout after subsequent rotation back to portrait
Context:
Additional context
After extensive debugging, I have determined that the issue lies with LineManager
. Notice that it will only calculate the firstElementOfEachLine
once with the initial width, but over time new width values are passed to InternalWrappingHStack
by the GeometryReader
. This will cause the InternalWrappingHStack
to redraw, but it's still using stale firstItemOfEachLine
values.
In my testing the InternalWrappingHStack
is only redrawn when the width changes, which matches the desired lifetime of the cached firstItemOfEachLine
value. I believe the fix would be to remove line manager and allow InternalWrappingHStack
to own its logic once again.
Simplified Example
Paste this into ContentView.swift within a fresh project after adding WrappingHStack as an SPM dependency:
import SwiftUI
import WrappingHStack
struct ContentView: View {
@State var itemIndexes: [Int] = Array(1...30)
var body: some View {
WrappingHStack(self.itemIndexes, id: \.self, lineSpacing: 8) { index in
Text("Item: \(index)")
.padding(3)
.background(Rectangle().stroke())
}
.background(.gray.opacity(0.2))
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Describe the bug
For versions after 2.2.2 the CocoaPods support is missing
To Reproduce
Enter the current version 2.2.6 into the pod file and call "pod install".
Expected behavior
Cocoa pods should not run into a fault:
CocoaPods could not find compatible versions for pod "WrappingHStack": In Podfile: WrappingHStack (= 2.2.6)
Context:
Describe the bug
When using ForEach
inside a WrappingHStack
, the wrapping doesn't work correctly. It works fine only with the custom init.
// Works
WrappingHStack(recipients) { recipient in
RecipientChip(recipient: recipient)
}
// Does not work
WrappingHStack {
ForEach(recipients, id: \.self) { recipient in
RecipientChip(recipient: recipient)
}
}
Expected behavior
The behavior should be the same for the two ways. This is especially useful to add multiple items to the stack.
Context:
Describe the bug
WrappingHStack treats the Group
view as one view instead of its children.
To Reproduce
Steps to reproduce the behavior:
.padding(...)
is used because of:
// Code to reproduce bug
WrappingHStack {
Group {
Text("\(Image(systemName: "bag")) Limit 1")
.padding(.top, 5)
.border(.red)
Text("\(Image(systemName: "gearshape.2")) Automatically used")
.padding(.top, 5)
.border(.red)
Text("\(Image(systemName: "exclamationmark.circle")) Sold out")
.foregroundColor(Color.red)
.padding(.top, 5)
.border(.red)
}
.font(.system(size: 14, weight: .semibold))
.foregroundColor(.secondary)
.lineLimit(1)
.border(.blue)
}
Spacer()
// Workaround (not bugged)
WrappingHStack(alignment: .trailing) {
Text("\(Image(systemName: "bag")) Limit 1")
.padding(.top, 5)
.border(.red)
Text("\(Image(systemName: "gearshape.2")) Automatically used")
.padding(.top, 5)
.border(.red)
Text("\(Image(systemName: "exclamationmark.circle")) Sold out")
.foregroundColor(Color.red)
.padding(.top, 5)
.border(.red)
}
.font(.system(size: 14, weight: .semibold))
.foregroundColor(.secondary)
.lineLimit(1)
.border(.blue)
Expected behavior
WrappingHStack would treat individual Text
views as separate elements and provide spacing between them.
Screenshots
Screenshot of code above:
Context:
Additional context
I understand that is a very quirky and specific edge case, but I thought I should report it just in case. I really wish Apple provided a native view for this. Coming from a web background, it seems really odd that iOS doesn't have something as comprehensive as CSS's flexbox
.
Thank you for making this project! This package is such a lifesaver.
A bottleneck seems to be the size calculation done with the hosting controller.
Describe the bug
I am sure this is an issue on my end, but just checking if there is an easy fix. Recently I upgraded xcode (Version 14.2 (14C18)) and
WrappingH Stack works as expected until I press a button with an async operation in the app. I get the following error. This crashes the app.
Prior to upgrading OS and xcode there were no issues.
Unsupported use of UIKit view-customization API off the main thread. -setBackgroundColor: sent to <SwiftUI.TextEditorTextView: 0x1438ede00; baseClass = UITextView; frame = (0 0; 292 92); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x2834464c0>; backgroundColor = <UIDynamicSystemColor: 0x282dcfbc0; name = systemBackgroundColor>; layer = <CALayer: 0x28399ae00>; contentOffset: {0, 0}; contentSize: {292, 38}; adjustedContentInset: {0, 0, 0, 0}>
Screenshots
If applicable, add screenshots to help explain your problem.
Context:
Describe the bug
Wrapping HStack does not work when I use it with buttons with a custom button style. Also, the buttons are supposed to add a check mark next to them when clicked, increasing their width - will that be an issue for the wrapping hstack?
Screenshots
This is what happens:
Here's where I create the wrapping h stack:
Context:
the WrappingHStack
fires an error when I add array of Views inside like this:
enum SomeEnum: String, CaseIterable {
case tag1
case tag2
case tag3
case tag4
}
WrappingHStack { // <---- Initializer 'init(alignment:spacing:lineSpacing:content:)' requires that '[Text]' conform to 'View'
SomeEnum.allCases.map { tag in
Text(tag.rawValue)
}
}
I tried to wrap those views in a Group
but it didn't work either
When using large dynamic text styles, items are not properly spaced and are left in the positions they used for smaller text sizes. I tried using .multilineTextAlignment(.leading)
and .fixedSize()
but either work as intended.
Here is what is looks like with regular sized text:
And this is what happens with larger sized text:
Here is the code:
HStack {
Image(systemName: "archivebox")
WrappingHStack(post._embedded.wp_term[0], id: \.self, alignment: .leading) { term in
NavigationLink(destination: TermPostsListView(termInfo: term)) {
if post._embedded.wp_term[0].last?.id == term.id {
Text(htmlToString(htmlStr: term.name)!)
} else {
Text("\(htmlToString(htmlStr: term.name)!),")
}
}
}
}
.font(.subheadline)
NOTE: I did try without the NavigationLink and the if statements and it still occurred
Describe the bug
A clear and concise description of what the bug is.
WrappingHStack crashes in iOS 16 for iPhone but not iPad. When a Navigation Link to a View using WrappingHStack is clicked, this happens on all iOS 16 iPhone versions tried
Hi Daniel!
First of all: Thanks for your awesome work! :)
I noticed that using WrappingHStack in longer lists is quite CPU intensive (not sure if #4 already addresses my point - if so, feel free to close this issue).
Here is an example:
var body: some View {
ScrollView {
ForEach(1..<300) { index in
WrappingHStack {
Text("This")
Text("is")
Text("a")
Text("test")
Text("!1!!")
}
}
}
}
I checked what is causing the high CPU usage and found that the InternalWrappingHStack.init method is being called hundreds of times. Launching the app without further interaction already results in 597 calls. I made a small video that shows how a very small interaction skyrockets the number of calls:
I have no clue what's going on. I believe that GeometryReader in WrappingHStack.swift triggers these calls. However, as the video shows, the width does not change at all. Is there a possibility to react only to relevant changes to reduce the number of InternalWrappingHStack.init calls significantly?
(I had some success by moving the GeometryReader out of WrappingHStack.swift into the view that uses WrappingHStack and passing the width to WrappingHStack, but my knowledge of SwiftUI is too limited to understand what's going on with the height in WrappingHStack.swift)
Again, thanks a lot! :)
When drawing many items, for example with something like:
WrappingHStack(1...50) {
Text("Item: \($0)")
}
it takes way too long to draw.
Could the vertical spacing for the VStack be exposed or is there a better suggested way to tweak the space between lines?
Is there an option to allow trailing alignment?
Describe the bug
When a view nested inside of WrappingHStack tries to access an environment object on its initial render, it cannot find it and throws a fatal error. (see example below)
If the nested view waits until after the first render to access the environment object, it works.
In either case for the code below, replacing WrappingHStack with HStack works properly.
To Reproduce
Steps to reproduce the behavior:
Expected behavior
Replacing WrappingHStack
with HStack
gives the expected behavior of being able to access the environment object on initial render instead of crashing.
Context:
Additional context
Add any other context about the problem here.
Simplified Example
(adapted from https://www.hackingwithswift.com/quick-start/swiftui/how-to-use-environmentobject-to-share-data-between-views)
class GameSettings: ObservableObject {
@Published var score = 0
}
struct ScoreView: View {
@EnvironmentObject var settings: GameSettings
@State var ready = true // Set this to true and the code crashes, set it to false to skip the initial render, and it works.
var body: some View {
Text("Score: \(ready ? settings.score : -1)")
.onAppear {
ready = true
}
}
}
struct WrappingHStackBugDemo: View {
@StateObject var settings = GameSettings()
var body: some View {
VStack {
Button("Increase Score") {
settings.score += 1
}
// Replacing this with a regular HStack works in all cases, including initial render
WrappingHStack {
ScoreView()
}
}
.frame(height: 200)
.environmentObject(settings)
}
}
struct WrappingHStackBugPreview: PreviewProvider {
static var previews: some View {
WrappingHStackBugDemo()
}
}
Import failure of CGFloat in LineManager class, causing fastlane build error while compiling WrappingHStack.swift
Create a new Xcode project.
Add the following code to a file in the project:
import Foundation
enum Stage {
case child
case boyhood
case adult
case old
var ageBar: CGFloat {
switch self {
case .child:
return 0.2
case .boyhood:
return 0.4
case .adult:
return 0.8
case .old:
return 1.0
}
}
}
The project builds and runs without any errors.
The project fails to build with the following error:
Cannot find type 'CGFloat' in scope
Code that causing an issue with respect to WrappingHStack. reference
import Foundation
/// This class is in charge of calculating which items fit on which lines.
/// It should be reused whenever possible.
class LineManager {
private var contentManager: ContentManager!
private var spacing: WrappingHStack.Spacing!
private var width: CGFloat!
}
{
"package": "WrappingHStack",
"repositoryURL": "https://github.com/dkk/WrappingHStack.git",
"state": {
"branch": null,
"revision": "3cc84e786aa996da32f476d89dab4e02d30ce691",
"version": "2.1.3"
}
}
It seems that the import of the Foundation module is not sufficient to use the CGFloat type.
To fix this issue, you can import the CoreGraphics module, which defines the CGFloat type, by adding the following line to the top of the file:
import CoreGraphics
After importing the CoreGraphics module, the CGFloat type should be available and the project should build and run without any errors.
Not sure if it's a bug ore a feature request but
adding new line inside a loop would be great to have.
This is not working currently.
WrappingHStack(1...20, id:\.self) {
if $0 == 3 {
NewLine()
}
Text("Item: \($0)")
.padding(3)
.background(Rectangle().stroke())
}.frame(minWidth: 250)
Otherwise working really great.
Sometimes, forced line breaks caused by NewLine() do not cause a line break.
Describe the bug
When try to run the library 2.2.4 on MacOS an error is presented on following line:
@inline(__always) static func isVisible(view: AnyView) -> Bool { ... return NSHostingController(rootView: anyView).sizeThatFits(in: CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude)).width > 0 ... }
Swift Package Manager in Xcode 11.3.1, cannot resolve the dependency graph due to tag issues.
It makes no difference if I specify an explicit version or a range. I've verified that this works with a number of other repositories, so it does seem to be related to SwiftyDropbox.
I have a use case where I need to render WrappingHStacks with hundreds of lines.
When I enter a few lines it works beautifully, but as I cross a certain threshold of content inside the WrappingHStack the screen takes longer and longer to display, into seconds and even minutes.
Describe the bug
The layout breaks in the new iOS version 16.4.
On the first render, the layout is rendered vertically instead of horizontally, and goes back to horizontal on interaction.
To Reproduce
Steps to reproduce the behavior:
Make any component with WrappingHStack on iOS 16.4 (bug only in this version)
Expected behavior
It should always be horizontal
Screenshots
If applicable, add screenshots to help explain your problem.
I found that using WrappingHStack in List and reverse the order of the content dynamically will make my content padding disappear. Back to normal when I switch back to HStack. 😢
Describe the bug
I have my WrappingHStack on a view which animates in by sliding. Everything slides, except for the WrappingHStack contents, which just instantly appear in place when the view begins to animate. However, when the view animates away/out, everything slides, including the WrappingHStack
Expected behavior
The WrappingHStack should slide in along with all other content
Context:
Hi,
removing the padding()
of the WrappingHStack
results in an error:
SwiftUI/UpdateCoalescingTableView.swift:365: Fatal error: List update took more than 1 layout cycle to converge
so this works:
WrappingHStack {
Text("a")
}.padding()
while this doesnt:
WrappingHStack {
Text("a")
}
any ideas why this happens?
same error after reducing to padding(1)
Describe the bug
I receive this rare crash from production build:
Fatal Exception: NSInvalidArgumentException
Application tried to present modally a view controller <TtGC7SwiftUI29PresentationHostingControllerVS_7AnyView: 0x128056c00> that is already being presented by <TtGC7SwiftUI19UIHostingControllerGVS_15ModifiedContentVS_7AnyViewVS_12RootModifier_: 0x12801c600>.
Stack trace is unclear:
Fatal Exception: NSInvalidArgumentException
0 CoreFoundation 0x9e88 __exceptionPreprocess
1 libobjc.A.dylib 0x178d8 objc_exception_throw
2 UIKitCore 0x327898 -[UIViewController _presentViewController:withAnimationController:completion:]
3 UIKitCore 0x3267ec __63-[UIViewController _presentViewController:animated:completion:]_block_invoke
4 UIKitCore 0x304984 -[UIViewController _performCoordinatedPresentOrDismiss:animated:]
5 UIKitCore 0xd2af0 -[UIViewController _presentViewController:animated:completion:]
6 UIKitCore 0xd292c -[UIViewController presentViewController:animated:completion:]
7 SwiftUI 0xd6d77c OUTLINED_FUNCTION_28
8 SwiftUI 0x54ee0 OUTLINED_FUNCTION_764
9 UIKitCore 0x27d660 -[UIPresentationController transitionDidFinish:]
10 UIKitCore 0x763ce8 __56-[UIPresentationController runTransitionForCurrentState]_block_invoke.110
11 UIKitCore 0x2b6fdc -[_UIViewControllerTransitionContext completeTransition:]
12 UIKitCore 0x1034334 __UIVIEW_IS_EXECUTING_ANIMATION_COMPLETION_BLOCK__
13 UIKitCore 0xd1808 -[UIViewAnimationBlockDelegate _didEndBlockAnimation:finished:context:]
14 UIKitCore 0xd0764 -[UIViewAnimationState sendDelegateAnimationDidStop:finished:]
15 UIKitCore 0xcfe84 -[UIViewAnimationState animationDidStop:finished:]
16 UIKitCore 0xcff98 -[UIViewAnimationState animationDidStop:finished:]
17 QuartzCore 0x12fec CA::Layer::run_animation_callbacks(void*)
18 libdispatch.dylib 0x3fdc _dispatch_client_callout
19 libdispatch.dylib 0x127f4 _dispatch_main_queue_drain
20 libdispatch.dylib 0x12444 _dispatch_main_queue_callback_4CF
21 CoreFoundation 0x9a6f8 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__
22 CoreFoundation 0x7c058 __CFRunLoopRun
23 CoreFoundation 0x80ed4 CFRunLoopRunSpecific
24 GraphicsServices 0x1368 GSEventRunModal
25 UIKitCore 0x3a23d0 -[UIApplication _run]
26 UIKitCore 0x3a2034 UIApplicationMain
27 SwiftUI 0x1d1014 OUTLINED_FUNCTION_895
28 SwiftUI 0x13216c block_copy_helper.1
29 SwiftUI 0x11b4bc OUTLINED_FUNCTION_901
But my app doesn't use UIHostingController anywhere, it only appears inside WrappingHStack.
Context:
Describe the bug
When scrolling through items that contain a WrappingHStack instance in the component, our app freezes but never crashes. This is due to the main thread getting clogged up when trying to create a WrappingHStack for our component.
To Reproduce
Use a WrappingHStack instance in component, adding that component into a scroll view.
Try to scroll through items on iOS 16 device.
Expected behavior
Should be able scroll through feed without freezing.
Line in InternalWrappingHStack that appears to be clogging thread:
In the below give, the tags in each card is contained in a WrappingHStack instance:
Context:
Additional context
Appears to be related to issues #27 & #28.
Describe the bug
Sometimes app hangs when WrappingHStack is used inside a NavigationView > ScrollView. Removing WrappingHStack fixes the problem.
Example
var body: some View {
NavigationView {
ScrollView {
WrappingHStack(viewModel.responsibilities, alignment: .center, spacing: .constant(10), lineSpacing: 10) { responsibility in
Text(responsibility.name)
}
}
.navigationTitle("Directory")
.navigationBarTitleDisplayMode(.large)
}
}
Context:
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.