SwiftUI List не прокручивается вверх с динамической высотой строки
Я использую
List
с динамической высотой строки. Чтобы установить высоту для каждой строки, я использую
anchorPreference
,
onPreferenceChange
, а
State
var. Если я прокручу вниз
List
, все выглядит нормально, но когда я прокручиваю вверх, прокрутка работает с ошибками, я едва могу двигаться вверх (см. изображение ниже).
Я предполагаю, что эта проблема может возникнуть, когда я устанавливаю высоту строки (frame
высота строки).
Я максимально упростил свой код, чтобы выразить свою точку зрения без потери функциональности и дизайна, которые мне нужны, в основном логики внутри
InnerView
(anchorPreference
,
onPreferenceChange
).
ПРИМЕЧАНИЕ. Если я использую
ScrollView
и
ForEach
вместо
List
, прокрутка плавная и работает нормально. Я использую
List
потому что мне нужна навигационная часть.
Любые идеи, как установить высоту строки (Geometry Reader
с
height
чтобы соответствовать содержимому) и прекратить это странное поведение при прокрутке вверх?
Спасибо за вашу помощь!
В
ParentView
имеет
VStack
с
InnerView
(у него черная рамка) и
Text
элемент (текст Row #).
struct ParentView: View {
var body: some View {
List(1..<20) { idx in
VStack {
InnerView(idx: idx)
Text("Row #\(idx)")
}
.background(Color.yellow)
}
}
}
В
InnerView
имеет
Text
с участием
green
фон, расположенный где-то внутри
ZStack
(за счет использования
alignmentGuide
), делая
ZStack
Высота динамическая и разная для каждой строки. Внутри
onPreferenceChange
модификатора, я могу рассчитать окончательную высоту для
ZStack
и обновите соответствующий
State
переменная (finalH
).
struct InnerView: View {
let item = SomeStruct(text: "one")
let idx: Int
@State private var preferences: [SomeStruct: CGRect] = [:]
@State private var finalH: CGFloat = 0
var body: some View {
GeometryReader { proxy in
ZStack(alignment: .topLeading) {
Text("")
Text(item.text)
.padding()
.background(Rectangle().fill(Color.green))
.alignmentGuide(.leading, computeValue: { dimension in
-preferences[item, default: .zero].origin.x
})
.alignmentGuide(.top, computeValue: { dimension in
-preferences[item, default: .zero].origin.y
})
.anchorPreference(key: MyPreferenceKey<SomeStruct>.self, value: Anchor<CGRect>.Source.bounds, transform: { anchor in
[item: proxy[anchor].size]
})
}
.border(Color.black, width: 2)
.onPreferenceChange(MyPreferenceKey<SomeStruct>.self, perform: { value in
var newPreferences: [SomeStruct: CGRect] = [:]
let size = value[item, default: CGSize.zero]
let xy = CGFloat(40 * (idx % 2))
newPreferences[item] = CGRect(origin: CGPoint(x: xy, y: xy), size: size)
preferences = newPreferences
finalH = xy + size.height
})
}
.frame(height: finalH)
}
}
Вот поддержка
PreferenceKey
struct SomeStruct: Identifiable, Hashable {
let id = UUID()
var text: String
}
private struct MyPreferenceKey<ItemValue: Hashable>: PreferenceKey {
typealias Value = [ItemValue: CGSize]
static var defaultValue: Value { [:] }
static func reduce(value: inout Value, nextValue: () -> Value) {
value.merge(nextValue()) { (currentValue: CGSize, newValue: CGSize) -> CGSize in
newValue
}
}
}