SwiftUI List не прокручивается вверх с динамической высотой строки

Я использую Listс динамической высотой строки. Чтобы установить высоту для каждой строки, я использую anchorPreference, onPreferenceChange, а Statevar. Если я прокручу вниз 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
        }
    }
}

0 ответов

Другие вопросы по тегам