Swiftui Scrollposition и ViewAge -Alingeding поведениеIOS

Программируем под IOS
Ответить
Anonymous
 Swiftui Scrollposition и ViewAge -Alingeding поведение

Сообщение Anonymous »

У меня есть дискретная реализация скруббера с использованием Scrollview в Swiftui, которая работает, но иногда в конечных точках не сбои. Например, прокрутка его до и дна показывает значение 87 вместо 100. Но если прокрутить вниз, нажав + кнопку, до тех пор, пока не достигнет конца, он покажет правильное значение 100, когда достигнет конца. Но кнопка Tapping - не прокручивает скруббер, чтобы прыгать до правильного значения, пока она не нажат два раза. Я понимаю, что это связано только с целевым поведением прокрутки .viewalened , но не понимаю, в чем именно проблема.

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

import SwiftUI

struct VerticalScrubber: View {
var config: ScrubberConfig
@Binding var value: CGFloat

@State private var scrollPosition: Int?

var body: some View {
GeometryReader { geometry in
let verticalPadding = geometry.size.height / 2 - 8

ZStack(alignment: .trailing) {
ScrollView(.vertical, showsIndicators: false) {
VStack(spacing: config.spacing) {
ForEach(0...(config.steps * config.count), id: \.self) { index in
horizontalTickMark(for: index)
.id(index)
}
}
.frame(width: 80)
.scrollTargetLayout()
.safeAreaPadding(.vertical, verticalPadding)
}
.scrollTargetBehavior(.viewAligned)
.scrollPosition(id: $scrollPosition, anchor: .top)

Capsule()
.frame(width: 32, height: 3)
.foregroundColor(.accentColor)
.shadow(color: .accentColor.opacity(0.3), radius: 3, x: 0, y: 1)
}
.frame(width: 100)
.onAppear {
DispatchQueue.main.async {
scrollPosition = Int(value * CGFloat(config.steps))
}
}
.onChange(of: value, { oldValue, newValue in
let newIndex = Int(newValue * CGFloat(config.steps))
print("New index \(newIndex)")
if scrollPosition != newIndex {
withAnimation {
scrollPosition = newIndex
print("\(scrollPosition)")
}
}
})
.onChange(of: scrollPosition, { oldIndex, newIndex in
guard let pos = newIndex else { return }
let newValue = CGFloat(pos) / CGFloat(config.steps)
if abs(value - newValue) > 0.001 {
value = newValue
}
})
}
}

private func horizontalTickMark(for index: Int) ->  some View {
let isMajorTick = index % config.steps == 0
let tickValue = index / config.steps

return HStack(spacing: 8) {
Rectangle()
.fill(isMajorTick ? Color.accentColor : Color.gray.opacity(0.5))
.frame(width: isMajorTick ? 24 : 12, height: isMajorTick ? 2 : 1)

if isMajorTick {
Text("\(tickValue * 5)")
.font(.system(size: 12, weight: .medium))
.foregroundColor(.primary)
.fixedSize()
}
}
.frame(maxWidth: .infinity, alignment: .trailing)
.padding(.trailing, 8)
}
}

#Preview("Vertical Scrubber") {
struct VerticalScrubberPreview: View {
@State private var value: CGFloat = 0
private let config = ScrubberConfig(count: 20, steps: 5, spacing: 8)

var body: some View {
VStack {
Text("Vertical Scrubber (0–100 in steps of 5)")
.font(.title2)
.padding()

HStack(spacing: 30) {
VerticalScrubber(config: config, value: $value)
.frame(width: 120, height: 300)
.background(Color(.systemBackground))
.border(Color.gray.opacity(0.3))

VStack {
Text("Current Value:")
.font(.headline)
Text("\(value * 5, specifier: "%.0f")")
.font(.system(size: 36, weight: .bold))
.padding()

HStack {
Button("−5") {
let newValue = max(0, value - 1)
if value != newValue {
value = newValue
UISelectionFeedbackGenerator().selectionChanged()
}

print("Value \(newValue), \(value)")
}
.disabled(value = CGFloat(config.count))
}
.buttonStyle(.bordered)
}
}

Spacer()
}
.padding()
}
}

return VerticalScrubberPreview()
}



Подробнее здесь: https://stackoverflow.com/questions/796 ... d-behavior
Ответить

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

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

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

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

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