Отображение таймера в метке пользовательской ячейки UIcollectionview - PullRequest
0 голосов
/ 20 мая 2018

Я создал пользовательский класс ячеек представления коллекции с собственным файлом xib.Я хочу отобразить таймер обратного отсчета.

У меня есть рабочая функция, которая может отображать таймер:

class Mainview: UIviewcontroller{
     // get the timer value that I want to countdown, e.g 15 mins
     // then I will pass the the value to the cell class
}


class TimerCell : UIcollectionviewcell{

  func timerRunning() {

            timeRemaining = timeRemaining - 1

            if (timeRemaining > 0)
            {
                let minutesLeft = Int(self.timeRemaining) / 60 % 60
                let secondsLeft = Int(self.timeRemaining) % 60
                self.timerLabel.text = "Refreshes in \(minutesLeft):\(secondsLeft)"
             }

 func runtime(){
                timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(timerRunning), userInfo: nil, repeats: true)
                RunLoop.current.add(timer, forMode: RunLoopMode.commonModes)
            }
}

Но поскольку теперь я реализую эту функцию в классе ячеек, таймер не будет работать.Я что-то здесь не так делаю?Спасибо, ребята.

1 Ответ

0 голосов
/ 21 мая 2018

Пара вещей:

  1. Я бы не советовал использовать вызовы вашего обработчика таймера для

    timeRemaining = timeRemaining - 1
    

    Я бы вместо этого сохранил nextRefreshTime ирассчитайте время, прошедшее между текущим временем и nextRefreshTime, чтобы отобразить оставшееся время.

  2. Обратите внимание, что когда вы вызываете scheduledTimer, это добавляет его в цикл выполнения для вас, поэтомувам не нужно также добавлять его в цикл выполнения самостоятельно.

    Или, если вы хотите использовать .commonModes в цикле выполнения, чтобы позволить таймеру метки продолжать работу во время прокрутки, просто создайте Timerс помощью init, а затем добавьте его в цикл выполнения.

    Но нет смысла снова использовать scheduleTimer и add в цикле выполнения.

  3. Я бы вышел из дела подсчета минут и секунд самостоятельно.Вы можете использовать DateComponentsFormatter для отображения оставшегося времени.

  4. Вы говорили о запуске таймера в viewDidLoad.Но UICollectionViewCell не имеет такого метода (и если вы создадите одно из этих имен, он не будет его использовать).Вместо этого вызовите некоторый метод для настройки ячейки в cellForItemAt.

  5. Объекты модели, например, когда будет происходить следующее обновление данных, не принадлежат ячейке.Ячейки - это временные объекты UIKit, которые будут входить и выходить из памяти при прокрутке, поэтому не следует отслеживать это.Для этого у вас должна быть своя собственная модель, и контроллер представления отвечает за облегчение передачи этой информации в ячейку при представлении ячейки:

    • первоначальная настройка ячейки с помощью nextRefreshTime
    • отправка некоторых уведомлений при изменении nextRefreshTime.

    Затем ячейка:

    • добавит себя в качестве наблюдателя к этому уведомлению, обновляя свои nextRefreshTime;
    • всякий раз, когда это уведомление отменяет любоепредыдущий таймер обновления метки, если он есть, и запуск нового при необходимости;
    • Этот обработчик обновления метки должен обновить метку

Таким образом:

class ViewController: UIViewController {

    @IBOutlet weak var collectionView: UICollectionView!

    static let resetTimerNotification = Notification.Name("someuniqueidentifier")

    // this handles the data refreshes (or whatever), say every two minutes, or whatever

    private var nextRefreshTime: Date? {
        didSet {
            NotificationCenter.default.post(name: ViewController.resetTimerNotification, object: nextRefreshTime)
            refreshDataTimer?.invalidate()
            if let when = nextRefreshTime {
                refreshDataTimer = Timer.scheduledTimer(withTimeInterval: when.timeIntervalSince(Date()), repeats: false) { [weak self] _ in
                    print("timer fired")
                    self?.resetTimer() // presumably, defer this until after the data refresh is done
                }
            }
        }
    }

    private weak var refreshDataTimer: Timer?

    override func viewDidLoad() {
        super.viewDidLoad()

        collectionView?.register(UINib(nibName: "TimeRemainingCell", bundle: nil), forCellWithReuseIdentifier: "TimeRemaining")

        resetTimer()
    }

    @IBAction func didTapResetButton(_ sender: Any) {
        resetTimer()
    }

    private func resetTimer() {
        nextRefreshTime = Date().addingTimeInterval(120) // or however you want to do this
    }
}

extension ViewController: UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 1
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

        // lets imagine that cell 0 is the TimeRemainingCell

        if indexPath.item == 0 {
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "TimeRemaining", for: indexPath) as! TimeRemainingCell

            cell.configure(for: nextRefreshTime)

            return cell
        }

        ... configure other types of cells
    }
}

и

class TimeRemainingCell: UICollectionViewCell {
    // MARK: - Properties

    @IBOutlet weak var timeRemainingLabel: UILabel!

    private var nextRefreshTime: Date? {
        didSet {
            labelUpdateTimer?.invalidate()

            if nextRefreshTime != nil {
                let timer = Timer(fire: nextRefreshTime!, interval: 0, repeats: false) { [weak self] timer in
                    // note, if cell is deallocated for any reason, let's stop the timer

                    guard let strongSelf = self else {
                        timer.invalidate()
                        return
                    }

                    strongSelf.updateLabel()
                }
                RunLoop.current.add(timer, forMode: .commonModes)
                labelUpdateTimer = timer
            }
        }
    }

    private weak var labelUpdateTimer: Timer?

    /// Formatter for time remaining
    ///
    /// Note, this gets us out of manually calculating minutes and seconds remaining

    private static let formatter: DateComponentsFormatter = {
        let _formatter = DateComponentsFormatter()
        _formatter.unitsStyle = .positional
        _formatter.allowedUnits = [.minute, .second]
        _formatter.zeroFormattingBehavior = .pad
        return _formatter
    }()


    // MARK: - init/deinit methods

    override init(frame: CGRect) {
        super.init(frame: frame)

        addNotificationObserver()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        addNotificationObserver()
    }

    deinit {
        labelUpdateTimer?.invalidate()
    }

    // MARK: - Configuration methods

    /// Add notification observer

    private func addNotificationObserver() {
        NotificationCenter.default.addObserver(forName: ViewController.resetTimerNotification, object: nil, queue: .main) { [weak self] notification in
            self?.nextRefreshTime = notification.object as? Date
            self?.updateLabel()
        }
    }

    /// Configure the cell for your model object.
    ///
    /// Called by collectionView(_:cellForItemAt:).
    ///
    /// Also starts the refresh timer.
    ///
    /// - Parameter object: Your model object

    func configure(for nextRefreshTime: Date?) {
        self.nextRefreshTime = nextRefreshTime
    }

    // MARK: - Label updating

    private func updateLabel() {
        let now = Date()

        if let when = nextRefreshTime {
            timeRemainingLabel.text = TimeRemainingCell.formatter.string(from: now, to: when)
        } else {
            timeRemainingLabel.text = "No time remaining"
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...