Open robdeans opened 3 years ago
Hi @robdeans , thanks for your feedback. Pager
is designed so that every page has the same size. pageSize
uses preferredItemSize
and itemAspectRatio
if the former isn't set. If none is set, the pageSize
is set to be the same as Pager
's. This means that every page has the same space available to fill. How it is used is your to te client. The reason for this is the item size needs to be the same in order to calculate the right translation.
SwiftUI
doesn't offer a bounds
property as UIKit
and therefore the size needs to be explicit here. The solution comes by using preferredItemSize
and setting the width of the longest text (plus the icon)
Thank you @fermoya for the explanation! If I understand correctly, Pager
sets equal widths in order to handle the transitions effectively. This is by design to accommodate SwiftUI, which does not provide the width/bounds of Views because of its declarative nature.
I agree that hardcoding the length of the longest Text/Icon component will get the closest result, and although it may leave a bit of empty space the UI is still very clean (plus SwiftUIPager saved hours, probably days of development 🙌 ).
Hypothetically, if there were to be a way to incorporate dynamic widths for each Page based on the content, do you think this would be feasible by extracting the width of each Page
, and including the value in the logic for pageSize
?
I experimented briefly with the following code and was able to extract the width for each View. Perhaps this value could be mapped to another variable (naturalSize
? Naming...) which could be used to determine pageSize
should the modifier be selected?
An obstacle I see is that the view would have to be rendered before any sizes are set, and this might not be the best way to pass data given existing structure. But would be glad to hear your thoughts on any potential approach 🙂
struct DemoView: View {
var textSize: CGSize = .zero
var body: some View {
Text("Some long text")
.background(
GeometryReader { proxy in
Color.clear
.preference(
key: SizePreferenceKey.self,
value: proxy.size
)
}
)
.onPreferenceChange(SizePreferenceKey.self) { preferences in
self.textSize = preferences // This would have to handle mutations, as in the SwiftUIPager extensions
}
}
}
struct SizePreferenceKey: PreferenceKey {
typealias Value = CGSize
static var defaultValue: Value = .zero
static func reduce(value: inout Value, nextValue: () -> Value) {
value = nextValue()
}
}
Hi @robdeans , thanks for the suggestions. Originally, I used this approach to calculate Pager
size but that has trouble when inside a ScrollView
.
I think a pager component should have all elements have the same size. Think of a book: what's the point of having different page sizes?
Jumping in. To your last question @fermoya, framed as such, then yes it wouldn’t make sense for each page to be inconsistently sized. That said, I’m looking at Pager perhaps less conceptually and more pragmatically. That is, its underlying qualities offer potential for more varied use cases. For example, what brought me to this is a need to create a UI similar to the camera mode picker where you horizontally scroll (snap) between different options. SwiftUIPager almost facilitates the same UI, except either there’s too much whitespace between items to accommodate the longest entry or there’s truncation to avoid excess whitespace. I guess a perfectly reasonable position for you to take is that the library is not intended for this particular use case. I’m hopeful there is a way though. If I’m not putting the Pager inside a ScrollView, will @robdeans’s approach be the workaround?
@fermoya this is actually big deal for a lot of use cases other than the basic pager. I've been attempting to accomplish it for some time now but the ScrollView trouble issue you mentioned is really annoying and its the same with the regular tabview.
Did you figure anything out with your approach so at least i'm heading in the right direction. Anything would be helpful!
Is your feature request related to a problem? Please describe. I am implementing a horizontally-scrolling Pager that fills the width of the screen, where unselected Pages are visible on the sides, and where each Page contains Text of varying lengths.
To do this I use the
preferredItemSize
with the width calculated by(geometryProxy.size.width / 3)
. This handles a majority of the cases, however when the Text length exceeds a certain amount (or the screen size is small), the display becomes truncated.Describe the solution you'd like Is there a way in which each Page's width is dynamic (as in, determined by the length of the String provided)?
For example, if the Strings provided are
["All", "Cat", "Lizard", "Dog"]
, whenCat
is selected,All
andLizard
would be visible on the sides (possibly trailing off the screen). WhenLizard
is selected, I would expect its width to appear larger than the other 3-letter animals, but still maintain the balance where the adjacent space is filled withCat
on the left andDog
on the right.In an extreme case, I would expect
Hippopotamus
to take up nearly the whole screen, but never exceeding the width of the Pager. It's possible that another person's criteria is that the text continues off the screen, but in my case I think size limitations could be handled by modifiers in the.content { }
closure (font shrinking, line breaks, etc).Describe alternatives you've considered From what I can tell the size of the Page is determined by the
pageSize
variable, which takes into accountpreferredItemSize
anditemAspectRatio
. ChangingitemAspectRatio
(values ranging from 0.1 to 10) did not produce the desired results.preferredItemSize
came the closest, but only by setting the constant from 3 togeometryProxy.size.width / 2
, which solves truncation but adds a lot of empty space.Additional context Perhaps there's something I'm missing to allow Pages to expand according to their own content width. If so let me know, as well as if you think this is worth working towards, why or why not. Thank you!