Таймер прерывается после прокрутки списка

У меня есть класс с таймером (обновление каждую миллисекунду).

class TimeCount {
    let currentTimePublisher = Timer.TimerPublisher(interval: 0.001, runLoop: .main, mode: .common)
    let cancellable: AnyCancellable?

    init() {
        self.cancellable = currentTimePublisher.connect() as? AnyCancellable
    }

    deinit {
        self.cancellable?.cancel()
    }
}

У меня также есть List() объектов TimerView

List() {
    ForEach(self.timers) { timer in
        TimerPlusView(timer: timer)
    }
}

И внутри каждого объекта у меня есть текст, который обновляет свое содержимое, слушая таймер.

Text("\(currentTime.timeIntervalSince(timer.time ?? Date()))")
    .font(.largeTitle)
    .fontWeight(.bold)
    .foregroundColor(.black)
    .opacity(0.5)
    .onReceive(timeCount.currentTimePublisher) { newCurrentTime in
        self.currentTime = newCurrentTime
    }

Дело в том, что после (не во время) прокрутки списка примерно на 100 пикселей таймер перестает работать, а метки перестают обновляться, и я понятия не имею, почему.

Поведение, которое я получаю: введите здесь описание изображения

UPD: вот ссылка на полный проект для ознакомления. https://www.dropbox.com/s/47zoizfqp6upz1e/TimerMinimal.zip?dl=0


person Alexey Primechaev    schedule 09.01.2020    source источник
comment
Из вашего кода я не понимаю, почему ваши таймеры перестают обновляться. Возможно, вы могли бы опубликовать ссылку на минимальный проект xcode, чтобы помочь другим воспроизвести его на своей машине. Может быть, это тоже ошибка симулятора - вы пробовали это и на устройстве?   -  person Andreas Oetjen    schedule 10.01.2020
comment
Да, я тоже. Н.П., вот ссылка на проект dropbox. com/s/47zoizfqp6upz1e/TimerMinimal.zip?dl=0 И да, я пробовал использовать и симулятор, и реальное устройство — везде одно и то же.   -  person Alexey Primechaev    schedule 10.01.2020
comment
Когда прокручивается представление прокрутки, вступает в действие другой цикл выполнения. Вы также должны добавить свой таймер в цикл отслеживания. stackoverflow.com/questions/7551411/   -  person EmilioPelaez    schedule 28.01.2020


Ответы (1)


Я думаю, что проблема в ошибке в Xcode. Или, по крайней мере, что-то очень недокументированное. Или просто загадка.

Проблема, по-видимому, возникает, когда представления повторно используются из табличного представления (например, старый материал удаления из очереди), что затем каким-то образом приводит к потере подписки, и события таймера больше не публикуются.

Тем не менее, я нашел что-то вроде обходного пути.

В TimerPlusView.swift переместите обработчик .onReceive в самый низ основного вида, например.

var body: some View {

    Button(action: {
        self.timer.title = "Tapped"
    }) {
        VStack(alignment: .leading) {
            Text(timer.title ?? "New Timer")
                .font(.largeTitle)
                .fontWeight(.bold)
                .foregroundColor(.black)
            Text("\(currentTime.timeIntervalSince(timer.time ?? Date()))")
                .font(.largeTitle)
                .fontWeight(.bold)
                .foregroundColor(.black)
                .opacity(0.5)
            // onReceive doesn't seem to work here:
            //.onReceive(timeCount.currentTimePublisher) { newCurrentTime in
            //  self.currentTime = newCurrentTime
            // }

        }
    }
    .buttonStyle(BorderlessButtonStyle())
    .contextMenu {
        Button(action: {
            self.context.delete(self.timer)
        }) {
            Text("Delete")
        }
    }
    // here, onReceive works:
    .onReceive(timeCount.currentTimePublisher) { newCurrentTime in
      self.currentTime = newCurrentTime
    }
}
person Andreas Oetjen    schedule 27.01.2020