ForEach in Widget: How to fit only the number of items that can be fully visible?

Hello,

I have this widget:

And as you can see down at the bottom, next item is very barely visible. This is because on different devices (combined with varied text sizes + number of lines) the vertical space differs quite a lot.

In the past I had the limit set to 4 items in the bottom part but that often left empty space at the bottom or even overflowed still on iPhone SE.

What is the proper way to tell ForEach (or any other component) to "layout as many items as can fully fit, but no more".

The only solution I could think of is to use ViewThatFits and try first rendering 6 items, then 5 and so on to find the count that fully fits. But that seems maybe too complicated?

Thanks.

Post not yet marked as solved Up vote post of nemecek_f Down vote post of nemecek_f
892 views
  • Also interested in this… viewthatfits is the only trick I’ve in mind

Add a Comment

Replies

Ended up with the ViewThatFits solution that tries to fit as many rows as possible.

I have been stuck on this exact problem for a while now, tried almost everything you can think of:

  • hard code max values… but it's just silly because widget sizes varies PER device (large on iPhone SE is not the same as iPhone 15 Pro Max for example) etc…
  • read the geometry and try to hide overflowing items… kind of worked, but brought in so many more problems I gave up…
  • LazyVStack … same behavior of a VStack it will render overflowing content and you can see the last items clipped like your screenshot…

the only thing that works is to use ViewThatFits like you said, but how did you do it? @nemecek_f

In my case I have something like this, but I fear this may be memory intensive (or could be) and kill my widget, since it's very limited in memory in the widget process: `public var body: some View { ViewThatFits(in: .vertical) { ForEach(0..<maxSlots, id: .self) { cutOut in bodyThatFits(slots: maxSlots - cutOut) } } }

func bodyThatFits(slots: Int) -> some View { // … the actual content of my widget // rendered with only up to the slots passed `

so in my case, it will try to render the widget with the max slots possible, or try and hide 1, or hide 2, etc… until it basically hides all of them…

I have set max slots per widget family.

I think my solution is the same as you have. I have a function that returns View for the bottom part (the game names + days until release) and I call it three times in the ViewThatFits and pass max number of items to render. I think the range is from three to five and that seems to work in most cases 🤷🏻‍♂️

Wish there was better solution though. Something like ForEachThatFits :D