Estou tentando implementar um scrubber discreto com marcações de texto no SwiftUI da seguinte forma. Meu problema é que não consigo determinar a altura do HStack
interior do ScrollView
apriori, então tentei usar onGeometryChange
o modificador, mas não funciona (ou seja, o texto de sobreposição é truncado). Uma maneira de consertar é usando GeometryReader
e atribuindo a altura de HStack
com base no proxy de geometria, mas quero saber se há outra maneira de sair sem usar GeometryReader
.
struct ScrollScrubber: View {
var config:ScrubberConfig
@State var viewSize:CGSize?
var body: some View {
let horizontalPadding = (viewSize?.width ?? 0)/2
ScrollView(.horizontal) {
HStack(spacing:config.spacing) {
let totalSteps = config.steps * config.count
ForEach(0...totalSteps, id: \.self) { index in
let remainder = index % config.steps
Divider()
.background( remainder == 0 ? Color.primary : Color.gray)
.frame(width: 0, height: remainder == 0 ? 20 : 10, alignment: .center)
.frame(maxHeight: 20, alignment: .bottom)
.overlay(alignment: .bottom) {
if remainder == 0 {
Text("\(index / config.steps)")
.font(.caption)
.fontWeight(.semibold)
.textScale(.secondary)
.fixedSize()
.offset(y:20)
}
}
}
}
.frame(height:viewSize?.height)
}
.scrollIndicators(.hidden)
.safeAreaPadding(.horizontal, horizontalPadding)
.onGeometryChange(for: CGSize.self) { proxy in
proxy.size
} action: { newValue in
viewSize = newValue
print("View Size \(newValue)")
}
}
}
struct ScrubberConfig:Equatable {
var count:Int
var steps:Int
var spacing:CGFloat
}
#Preview {
ScrollScrubber(config: .init(count: 100, steps: 5, spacing: 5.0))
.frame(height:60)
}
Se você quiser que a altura da visualização de rolagem ocupe todo o espaço disponível, então é totalmente apropriado usar
GeometryReader
aqui. Não há nada de errado com isso. Caso contrário, você teria que usar alguma outra visualização que preencha o espaço disponível (por exemplo,Color.clear
), e medir a geometria dessa visualização.Claramente, usar apenas um
GeometryReader
é mais conveniente.Neste caso em particular, se você quiser apenas impedir que o
Text
s seja recortado, você pode simplesmente desabilitar o recorte colocandoscrollClipDisabled()
na visualização de rolagem. Não há necessidade de fazer nada relacionado à geometria e você pode remover oframe
na visualização de rolagem.Note que o
Text
s ainda estará fora dos limites doScrollView
. Se você quiser que eles fiquem dentro dos limites, considere usar aVStack
para dispor as marcas de escala e o texto:Isso pressupõe que todo o texto tenha a mesma altura. Se você não puder supor isso, você pode encontrar as alturas máximas de todos os
Text
s usando uma chave de preferência. Então, defina a altura doHStack
para ser a altura máxima dos textos, mais a altura máxima dos divisores (ou seja, 20).