TableView.reloadData() не обновляет входные данные пользователя из постоянного хранилища

#ios #swift

#iOS #swift

Вопрос:

Я новичок в разработке ios и изучаю основные данные и застрял в обновлении ячеек tableview с помощью reloaddata(). Вот код в моем GoalsVC:

 import UIKit

import CoreData

//make app delegate accessible for all view controllers as in has core data things

let appDelegate = UIApplication.shared.delegate as? AppDelegate

class GoalsVC: UIViewController {

    @IBOutlet weak var tableView: UITableView!
    
    var goals: [Goal] = []
    
    override func viewDidLoad() {
        super.viewDidLoad()
        //set tableview delegate and source so that it knows what its delegate its datasource is from otherwise it does not know where its getting information from
        tableView.delegate = self
        tableView.dataSource = self
        tableView.isHidden = false
    }
    
    //need to think about when should we fetch the data. viewdidlaod only called once when the view is originally load. after we presented goalvc, creategoalvc on the top and dismiss them, viewdidload is not called bcs were just presenting a view on top of it and dismissing it.
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        self.fetch { (complete) in
        if complete {
            if goals.count >= 1 {
                //showing tableview or hide them depending if data is available or not
                tableView.isHidden = false
            } else {
                tableView.isHidden = true
            }
        }
    }
        self.tableView.reloadData()
}
 

Я не знаю, почему self.tableView.reloadData() в конце не будет обновляться tableview.

Вот код, который я сделал для сохранения данных в FinishGoalVC, если это поможет получить более четкое представление:

 func save(completion: (_ finished: Bool) -> ()) {
            
    
    guard let managedContext = appDelegate?.persistentContainer.viewContext else {
                return
            }
            
    
    //accessing core data properties
            
    let goal = Goal(context: managedContext)
            
    goal.goalDescription = goalDescription
    goal.goalType = goalType.rawValue
    goal.goalCompletionValue = Int32(pointsTextField.text!)!
    goal.goalProgress = Int32(0)
            
    //using save in order to commit unsaved changes to the context parents store
            
    do {
        try managedContext.save()
        completion(true)
    } catch {
        debugPrint("Could not save (error.localizedDescription)")
        completion(false)
    }
}
 

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

1. Обработчик завершения в save бессмыслен, потому что весь код синхронен. И я думаю, что так и есть fetch . Метод с параметром вместо обработчика завершения решил бы проблему.

2. Использовать developer.apple.com/documentation/coredata/…

Ответ №1:

Обновите tableview после завершения выборки. if goals.count >= 1 использование !goals.isEmpty — хорошая практика.

 override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    self.fetch { complete in
        if complete {
            tableView.isHidden = goals.isEmpty
            if !goals.isEmpty {
                //showing tableview or hide them depending if data is available or not
                DispatchQueue.main.async {
                    self.tableView.reloadData()
                }
            }
        }
    }
}
 

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

1. Спасибо за ввод и полезный совет! 🙂

Ответ №2:

Просто отредактируйте свою функцию, как показано ниже;

 if goals.count >= 1 {
   //showing tableview or hide them depending if data is available or not
    tableView.isHidden = false
} else {
    tableView.isHidden = true
    self.tableView.reloadData()
}
 

Потому что ваша функция перезагрузки работает до получения данных из-за потоков. Вы должны запустить это после извлечения данных.

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

1. Спасибо за предложение! 🙂

Ответ №3:

Итак, я только что понял, почему он не показывает обновленный tableview. Я просто помещу это здесь на случай, если кто-то столкнется с подобными проблемами в будущем (?), лол.

Так что да, в любом случае,

  1. Я ввел точки останова для TableView.reloadData() и проверил отладчик и увидел, что у него есть данные, и он вызвал метод reloadData(), когда я закончил создавать цели. Итак, никаких проблем с reloadData()
  2. Однако самость.вызов выборки в функции viewWillAppear не был запущен после того, как я закончил создавать новые цели. Он запускался только при запуске приложения и получении уже созданных целей.

Прежде чем двигаться дальше, вот начальный код, который я использовал для расширения UIViewController под названием presentDetail:

расширение UIViewController {

    func presentDetail(_ viewControllerToPresent: UIViewController) {

    let transition = CATransition()

    transition.duration = 0.3

    transition.type = CATransitionType.push

    transition.subtype = CATransitionSubtype.fromRight

    self.view.window?.layer.add(transition, forKey: kCATransition)

    present(viewControllerToPresent, animated: false, completion: nil)        
}
 

и это расширение использовалось для кнопки addGoal, которая представляет следующий VC, createGoalVC:

 @IBAction func addGoalBtnWasPressed(_ sender: Any) {
    guard let createGoalVC = storyboard?.instantiateViewController(identifier: 
    "CreateGoalVC") else {
        return
    }
    presentDetail(createGoalVC)
}
 

}

У меня также было другое расширение для UIViewController, которое было:

 func presentSecondaryDetail(_ viewControllerToPresent: UIViewController) {

    let transition = CATransition()

    transition.duration = 0.3

    transition.type = CATransitionType.push

    transition.subtype = CATransitionSubtype.fromRight
    
    guard let presentedViewController = presentedViewController else {return}
    
    presentedViewController.dismiss(animated: false) {

    self.view.window?.layer.add(transition, forKey: kCATransition)

    self.present(viewControllerToPresent, animated: false, completion: 
      nil)

    }
}
 

эта функция использовалась для nextButton, которая представляет окончательный VC, finishGoalVC:

 @IBAction func nextBtnWasPressed(_ sender: Any) {

    if goalTextView.text != "" amp;amp; goalTextView.text != "What is your goal?" {
        guard let finishGoalVC = 

        storyboard?.instantiateViewController(identifier: "FinishGoalVC") as?

        FinishGoalVC else {
            return
        }
        finishGoalVC.initData(description: goalTextView.text!, type: goalType)
        
        presentingViewController?.presentSecondaryDetail(finishGoalVC)
    }
    
}
 

И поскольку мой первый VC (GoalsVC) не был представлен в полноэкранном режиме, createGoalsVC и finishGoalsVC не были должным образом отклонены, поскольку GoalsVC не охватывал два других контроллера представления. Если это имеет какой-то смысл. Следовательно, почему функция viewWillAppear вызывалась только один раз, а не после создания новых целей.

Таким образом, окончательный код для функции presentDetail расширения UIViewController был изменен на:

расширение UIViewController {

 func presentDetail(_ viewControllerToPresent: UIViewController) {

    let transition = CATransition()

    transition.duration = 0.3

    transition.type = CATransitionType.push
    
    transition.subtype = CATransitionSubtype.fromRight
    
    self.view.window?.layer.add(transition, forKey: kCATransition)
    
    let navigationController: UINavigationController = 

    UINavigationController(rootViewController: viewControllerToPresent)

    navigationController.modalPresentationStyle = .fullScreen

    present(navigationController, animated: false, completion: nil)
}
 

и для функции presentSecondaryDetail:

 func presentSecondaryDetail(_ viewControllerToPresent: UIViewController) {

    let transition = CATransition()
    transition.duration = 0.3

    transition.type = CATransitionType.push

    transition.subtype = CATransitionSubtype.fromRight
    
    guard let presentedViewController = presentedViewController else { return }
    
        presentedViewController.dismiss(animated: false) {

        self.view.window?.layer.add(transition, forKey: kCATransition)
        
        let navigationController: UINavigationController = 

        UINavigationController(rootViewController: viewControllerToPresent)

        navigationController.modalPresentationStyle = .fullScreen

        self.present(navigationController, animated: false, completion: nil)
    }
}
 

И это решило проблему! Tableview был обновлен с учетом новых целей пользователя 🙂