SwiftUI синхронизирует горизонтальные и вертикальные прокрутки с динамическим содержимымIOS

Программируем под IOS
Ответить Пред. темаСлед. тема
Anonymous
 SwiftUI синхронизирует горизонтальные и вертикальные прокрутки с динамическим содержимым

Сообщение Anonymous »

Я создаю представление SwiftUI с двумя синхронизированными областями прокрутки:
  • Горизонтальный элемент ScrollView, отображающий список разделов.
  • Вертикальный элемент ScrollView, отображающий содержимое, соответствующее этим разделам.
Проблема:
Реализация работает, когда каждый раздел имеет одинаковое количество элементов. Однако если разделы содержат разное количество элементов, синхронизация прерывается, и вертикальный элемент ScrollView часто прокручивается к неправильному разделу. Вот пример моего кода:

Код: Выделить всё

struct ContentView: View {
// Sample data
private let sections = (1...10).map { sectionIndex in
SectionData(
name: "Section \(sectionIndex)",
items: (1...(Int.random(in: 80...150))).map { "Item \($0)" }
)
}

@State private var selectedSection: String? = nil
@State private var currentVisibleSection: String? = nil

var body: some View {
VStack(spacing: 0) {
// Horizontal Selector
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 10) {
ForEach(sections) { section in
Button(action: {
selectedSection = section.name
}) {
Text(section.name)
.font(.headline)
.padding(.horizontal, 10)
.padding(.vertical, 5)
.background(
RoundedRectangle(cornerRadius: 10)
.fill(currentVisibleSection == section.name ? Color.blue : Color.gray.opacity(0.2))
)
.foregroundColor(currentVisibleSection == section.name ? .white : .primary)
}
}
}
.padding()
}
.background(Color(UIColor.systemGroupedBackground))

// Vertical Scrollable Content
ScrollViewReader { proxy in
ScrollView(.vertical, showsIndicators: false) {
LazyVStack(spacing: 20) {
ForEach(sections) { section in
VStack(alignment: .leading, spacing: 10) {
// Section Header
SectionHeader(name: section.name)
.id(section.name) // Each section has a unique ID

// Section Content
LazyVGrid(columns: Array(repeating: GridItem(.flexible()), count: 3), spacing: 10) {
ForEach(section.items, id: \.self) { item in
Text(item)
.frame(height: 100)
.frame(maxWidth: .infinity)
.background(Color.blue.opacity(0.2))
.cornerRadius(8)
}
}
}
.background(
GeometryReader { geo in
Color.clear.preference(
key: VisibleSectionPreferenceKey.self,
value: [section.name: calculateVisibleHeight(geo)]
)
}
)
}
}
.onPreferenceChange(VisibleSectionPreferenceKey.self) { visibleSections in
updateLargestVisibleSection(visibleSections)
}
.onChange(of: selectedSection) { sectionName in
guard let sectionName else { return }
withAnimation {
proxy.scrollTo(sectionName, anchor: .top)
}
}
}
}
}
}

// Update the largest visible section
private func updateLargestVisibleSection(_ visibleSections: [String: CGFloat]) {
if let largestVisibleSection = visibleSections.max(by: { $0.value < $1.value })?.key {
currentVisibleSection = largestVisibleSection
}
}

// Calculate the visible height of a section
private func calculateVisibleHeight(_ geometry: GeometryProxy) ->  CGFloat {
let frame = geometry.frame(in: .global)
let screenHeight = UIScreen.main.bounds.height
return max(0, min(frame.maxY, screenHeight) - max(frame.minY, 0))
}
}

// PreferenceKey to track visible sections
private struct VisibleSectionPreferenceKey: PreferenceKey {
static var defaultValue: [String: CGFloat] = [:]

static func reduce(value: inout [String: CGFloat], nextValue: () -> [String: CGFloat]) {
value.merge(nextValue(), uniquingKeysWith: max)
}
}

// Supporting Views and Models
struct SectionHeader: View {
let name: String

var body: some View {
Text(name)
.font(.headline)
.padding()
.frame(maxWidth: .infinity, alignment: .leading)
.background(Color.gray.opacity(0.2))
}
}

struct SectionData: Identifiable {
var id: String { name }
let name: String
let items: [String]
}

  • LazyVStack: хорошо работает для повышения производительности, но синхронизация прерывается, когда разделы содержат разное количество элементов.
  • VStack: исправления проблемы с синхронизацией, но приводит к снижению производительности при работе с большими наборами данных, поскольку весь контент быстро загружается в память.
  • Кроме того, взаимодействие с ленивым подпредставления (например, LazyVGrid) внутри VStack вызывает скачки прокрутки, что нарушает работу пользователя.
  • onPreferenceChange: используется специальный PreferenceKey для отслеживания видимых разделов. , но этот подход становится ненадежным из-за ленивой загрузки разделов и динамического подсчета элементов.


Подробнее здесь: https://stackoverflow.com/questions/792 ... ic-content
Реклама
Ответить Пред. темаСлед. тема

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение

Вернуться в «IOS»