SwiftUI останавливает обновления во время прокрутки списка - PullRequest
1 голос
/ 02 апреля 2020

Учитывая список в SwiftUI, после начала панорамирования обновление представлений в списке, кажется, приостанавливается до тех пор, пока прокрутка не будет остановлена. Есть ли способ предотвратить это?

Рассмотрим следующий код:

class Model: ObservableObject, Identifiable {
    @Published var offset: CGFloat = 0

    let id = UUID()
    private var timer: Timer!

    init() {
        timer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true, block: { _ in
            self.update()
        })
    }

    func update() {
        offset = CGFloat.random(in: 0...300)
    }
}

struct ContentView: View {
    @ObservedObject var model1 = Model()
    @ObservedObject var model2 = Model()
    @ObservedObject var model3 = Model()
    @ObservedObject var model4 = Model()

    var body: some View {
        List {
            ForEach([model1, model2, model3, model4]) {
                Rectangle()
                    .foregroundColor(.red)
                    .frame(width: $0.offset, height: 30, alignment: .center)
                    .animation(.default)
            }
        }
    }
}

Это приведет к такому поведению:

Video of result

Ответы [ 2 ]

1 голос
/ 02 апреля 2020

Вы могли бы использовать GCD, как в ответе Аспери, но это не объясняет, почему ваш код не работал.

Проблема в том, что, хотя представление прокрутки отслеживает ваше касание, оно запускает прогон l oop в режиме .tracking. Но поскольку вы создали Timer с использованием scheduledTimer(withTimeInterval:repeats:block:), Timer настроен на работу только в режиме .default.

Вы можете добавить таймер для всех общих режимов запуска l oop (включая .tracking), например:

RunLoop.main.add(timer, forMode: .common)

Но я бы, вероятно, вместо этого использовал бы издателя Combine, например:

class Model: ObservableObject, Identifiable {
    @Published var offset: CGFloat = 0

    let id = UUID()

    private var tickets: [AnyCancellable] = []

    init() {
        Timer.publish(every: 0.5, on: RunLoop.main, in: .common)
            .autoconnect()
            .map { _ in CGFloat.random(in: 0...300) }
            .sink { [weak self] in self?.offset = $0 }
            .store(in: &tickets)
    }
}
1 голос
/ 02 апреля 2020

Это связано с природой Timer и RunLoop. Вместо этого используйте GCD, как в подходе ниже

demo

init() {
    var runner: (() -> ())?
    runner = {
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in
            if let self = self {
                self.update()
                runner?()
            }
        }
    }
    runner?()
}
...