Как перезагрузить представления в UIViewController по требованию?

#ios #swift

#iOS #swift

Вопрос:

У меня есть UIViewController с некоторыми метками и UICollectionView. UICollectionView представляет собой самодельный календарь, загруженный из xib. Когда я нажимаю на одну из ячеек представления коллекции (скажем, выбираю день в календаре), я соответствующим образом меняю свою базовую модель, и мне нужно обновить метки. Но при нажатии ничего не обновляется.

У меня есть метод, который обновляет метки и вызывает его в viewDidLoad(). После первой загрузки все метки становятся нулевыми, и объявление меток как ‘var’, а не как ‘weak var’ не помогает. При вызове некоторых функций в ViewController self.view, таких как layoutSubviews(), ярлыки, похоже, существуют, вызываются viewDidLoad() (и updateLabels()), но на экране ничего не меняется. Я попытался создать (подкласс) UIView с метками, используя xib и программно, разместил метки непосредственно в ViewController, поместив весь код в одно место — тот же результат. setNeedsDisplay(), методы жизненного цикла, удаление-добавление в superview — ничего из этого не сработало для меня. Я делаю что-то не так или упускаю что-то важное?

     class ChallengeViewController: UIViewController {

        var challenge = Challenge(title: Challenge.sampleTitle, startDate: Challenge.sampleStartDate, durationInDays: 40, checkIns: [Bool](repeating: true, count: 40), cheatDaysPlanned: 2, isActive: true, roundNumber: 1, pointsScored: 0)

    @IBOutlet weak var titleLabel: UILabel!

    @IBOutlet weak var calendarView: CalendarView!

    @IBOutlet weak var statisticsView: ChallengeStatisticsView!
    //contains labels that should be updated

    override func viewDidLoad() {
            super.viewDidLoad()
            calendarView.challenge = challenge
            calendarView.updateMonthAndYearLabels(for: challenge)
            statisticsView.updateLabels()
            titleLabel.text = challenge.title
        }
}

class CalendarView: UIView {

var challenge: Challenge? 

//other code here

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {

            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "calendarCell", for: indexPath) as! CalendarCollectionViewCell

            collectionView.performBatchUpdates({
                if let challenge = challenge {
                cell.update(challenge: challenge)
                    let viewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ChallengeScreenViewController") as! ChallengeViewController
                    viewController.view.layoutSubviews()
                    viewController.challenge = challenge
                    viewController.updateLabels()
    collectionView.reloadItems(at: [indexPath])
            }
        }, completion: nil)
    }
}
  

Ответ №1:

Вы создаете совершенно новый контроллер представления в своем cell.update , и он никогда не отображается на экране, поэтому вы не видите никаких обновлений. Не уверен, что ваш update метод на самом деле делает в любом случае, но похоже, что просто вызов self.updateLabels() или self.statisticsView.updateLabels() должен сделать трюк. Не уверен, что, поскольку у вас, похоже, есть оба.

Комментарии:

1. Большое вам спасибо! Это очень помогло.

Ответ №2:

Почему вы перезагружаете свой VC?

Когда вы нажимаете на CollectionViewCell, создайте протокол делегирования, чтобы сообщить VC перезагрузить метки с помощью datamodel.

Ответ №3:

  1. Используйте cellForItem(at:) метод для получения выбранной ячейки
  2. Чтобы обновить представление в текущем контроллере представления, просто вызовите метод update . Не создавайте новый контроллер представления.
 func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    let cell = collectionView.cellForItem(at: indexPath) as! CalendarCollectionViewCell

    if let challenge = challenge {
        cell.update(challenge: challenge)
        collectionView.reloadItems(at: [indexPath])
        self.updateLabels()
    }
}
  

Ответ №4:

Я не знал, что UIStoryboard(name:, bundle:).instantiateViewController(withIdentifier:) это создает совершенно новый vc, это была главная ошибка. Итак, я решил использовать Центр уведомлений, чтобы заставить контроллер реагировать на изменения в модели. Теперь все работает.

  class CalendarView: UIView {

    var challenge: Challenge? 

    //other code here

        func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {

                let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "calendarCell", for: indexPath) as! CalendarCollectionViewCell

                collectionView.performBatchUpdates({
                    if let challenge = challenge {
                    cell.update(challenge: challenge)

    // assigning a new value to calendarView's own variable, posting the notification
                         self.challenge = challenge
                    let name = Notification.Name("CellTapped")
                    NotificationCenter.default.post(name: name, object: nil)

        collectionView.reloadItems(at: [indexPath])
                }
            }, completion: nil)
        }
    }

class ChallengeViewController: UIViewController {

            var challenge = Challenge(title: Challenge.sampleTitle, startDate: Challenge.sampleStartDate, durationInDays: 40, checkIns: [Bool](repeating: true, count: 40), cheatDaysPlanned: 2, isActive: true, roundNumber: 1, pointsScored: 0)

        @IBOutlet weak var titleLabel: UILabel!

        @IBOutlet weak var calendarView: CalendarView!

        @IBOutlet weak var statisticsView: ChallengeStatisticsView!

// taking the changed variable from calendarView when the notification is posted
     @objc func updateAfterTap() {
        if let calendarViewChallenge = calendarView.challenge {
        self.challenge = calendarViewChallenge
        statisticsView.updateLabels()
        }
    }
        override func viewDidLoad() {
                super.viewDidLoad()
                calendarView.challenge = challenge
                calendarView.updateMonthAndYearLabels(for: challenge)
                statisticsView.updateLabels()
                titleLabel.text = challenge.title
NotificationCenter.default.addObserver(self, selector: #selector(self.updateAfterTap), name: Notification.Name("CellTapped"), object: nil)
            }
    }