0

I have an array of elements that I am using to display buttons. I'm using WrappingHStack here — that's not important, just understand that it's like a for loop HStack that wraps onto a new line when no more elements can be inserted on a line.

The buttons can be variable width and height as the text changes.

Not all elements need to be on the screen, but no elements can be off screen, and there can be no scrolling.

So, if there are elements that will overflow vertically off the screen, sufficient elements need to be removed to prevent this.

I started by getting the available height, and then using a onChange modifier, determine whether the button container height is greater than the available height and if so, remove an element. I then use another onChange modifier to detect when an element has been removed and make the comparison again (as the removal of elements may not result in one fewer rows).

Xcode complains:

onChange(of: Int) action tried to update multiple times per frame.

I can't find any way to prevent this error. I have no idea how I can achieve my goal another way. Would appreciate any suggestions / a solution!

EDIT: Also, to clarify: I specifically want to remove random elements from the array until there is nothing that does not fit.

import SwiftUI
import WrappingHStack

struct test2: View {
    
    @State var availableHeight: CGFloat = 0.0
    @State var contentHeight: CGFloat = 0.0
    
    var buttonContent = ["alpha", "beta", "gamma"]
    
    var body: some View {
        ZStack {
            VStack(spacing: 0) {
                Spacer()
            }
            .frame(width: UIScreen.main.bounds.width)
            .background(
                GeometryReader { proxy in
                    Color.clear.opacity(0.5)
                        .onAppear {
                            availableHeight = proxy.size.height
                        }
                }
            )
            VStack(spacing: 0) {
                WrappingHStack(0..<buttonContent.count, id:\.self, alignment: .center) { i in
                    Button(action: {
                        //
                    }) {
                        VStack {
                            Text(buttonContent[i])
                        }
                        .background(
                            RoundedRectangle(cornerRadius: 6)
                                .fill(.yellow)
                        )
                    }
                }
                .background(
                    GeometryReader { proxy in
                        Color.clear
                            .onChange(of: proxy.size) { newSize in
                                if newSize.height > availableHeight {
                                    contentHeight = newSize.height
                                    if let index = buttonContent.indices.randomElement() {
                                        let value = buttonContent.remove(at: index)
                                    }
                                }
                            }
                            .onChange(of: buttonContent.count) { newSize in
                                if contentHeight > availableHeight {
                                    if let index = buttonContent.indices.randomElement() {
                                        let value = buttonContent.remove(at: index)
                                    }
                                }
                            }
                    }
                )
                Spacer()
            }
        }
    }
}
Joseph
  • 691
  • 4
  • 18
  • Does this answer your question? [Display SwiftUI view only if it has enough space](https://stackoverflow.com/questions/66179524/display-swiftui-view-only-if-it-has-enough-space) – timbre timbre Jun 13 '23 at 01:01
  • Not quite, as I specifically want to remove random elements from the array until there is nothing that doesn't fit. As far as I can tell that solution does not allow me to do that. Thanks, though! – Joseph Jun 13 '23 at 04:52
  • I think it's a matter of approach: the approach where you add elements added to the screen while they fit is much more compatible with swiftUI's declarative approach than building everything, and then start removing some elements at the same iteration... but good luck – timbre timbre Jun 13 '23 at 14:08
  • @timbretimbre True — I've been trying a different solution based off [Helperbug's](https://stackoverflow.com/a/66189269/16370901) comment in that thread, but I'm unable to calculate the size of my variably sized elements before they are added to the view – Joseph Jun 14 '23 at 07:28

0 Answers0