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()
}
}
}
}