TableView обновляется только после поворота экрана

Изображение таблицы

У меня есть табличное представление с представлением коллекции в каждой ячейке, все они связаны с массивом. Каждое представление коллекции имеет теги, поэтому, когда у меня есть материал в массиве с самого начала, все ячейки представления таблицы и ячейки представления коллекции правильно отображаются в приложении. Но когда я добавляю элемент в массив в самом приложении (у меня есть второй контроллер представления со всем необходимым для этого), он работает, но новая ячейка табличного представления появляется только после поворота экрана (действительно странно). Я попытался добавить объект контроллера представления с табличным представлением во втором контроллере представления, где я добавляю элемент в массив. Затем во втором контроллере представления в ViewWillDisappear я перезагружаю данные() через этот объект следующим образом:

var vc : ViewController? = ViewController()

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    vc?.listOfActs.reloadData()
}

Но это приводит к EXC_BAD_INSTRUCTION

Затем я попытался добавить self.listOfActs.reloadData() в prepareForSegue в контроллере представления с табличным представлением, чтобы я мог видеть, что он, по крайней мере, обновляет данные в какой-то момент времени, но даже это не работает, когда я нажимаю при добавлении сцены второй раз.

ОБНОВЛЕНИЕ: новый MainViewController

Это новый контроллер первого представления с табличным представлением. Я переименовал его и реализовал метод добавления в массив и перезагрузки. Это работает, если я использую if let для reloadData, но затем я возвращаюсь к исходной точке, где он обновляется только при повороте экрана. Когда я избавляюсь от if let, чтобы он действительно мог попытаться обновить представление таблицы, он выдает мне ошибку Fata: неожиданно найден ноль при распаковке.

class MainViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

//The Table View

@IBOutlet var AddActButton: UIButton!

@IBOutlet weak var listOfActs: UITableView!

var sectionTapped : Int?
var indexitemTapped : Int?

override func viewDidLoad() {

    super.viewDidLoad()

    listOfActs.delegate = self

    listOfActs.dataSource = self

}

//Table View Functions

func numberOfSections(in tableView: UITableView) -> Int {

    return actsCollection.count
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

    return 1
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    let cell = tableView.dequeueReusableCell(withIdentifier: "actCell", for: indexPath) as! ActCell

    cell.setCollectionViewDataSourceDelegate(self, forSection: indexPath.section)

    return cell
}

//Add To Table View

func addObjects(appendage: Act) {
    actsCollection.append(appendage)

    if let shit = listOfActs {

        shit.reloadData()

    }
}

//Header Cell

func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {

    let cellHeader = tableView.dequeueReusableCell(withIdentifier: "headerCell") as! HeaderCell

    cellHeader.headerName.text = actsCollection[section].actName

    return cellHeader
}

func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
    return 40
    }

}

//Scene Collection in Act Cell

extension MainViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {

    return actsCollection[collectionView.tag].actScenes.count
}

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

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

    cell.sceneTitle.text = actsCollection[collectionView.tag].actScenes[indexPath.item].sceneTitle

    return cell
}

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

    sectionTapped = collectionView.tag
    indexitemTapped = indexPath.item

    performSegue(withIdentifier: "showDetail", sender: self)

}

//Segue Prepare

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "showDetail" {

        let detailsVC = segue.destination as! SceneDetailController

        detailsVC.textToAppearInSceneName = actsCollection[sectionTapped!].actScenes[indexitemTapped!].sceneTitle

    }
}

}

ОБНОВЛЕНИЕ: Новый второй контроллер представления, который добавляется к массиву.

class AddActController: UIViewController, UITextFieldDelegate {

@IBOutlet var sceneLiveName: UILabel!
@IBOutlet var sceneNameTextField: UITextField!

@IBOutlet var sceneDescriptionTextField: UITextField!

@IBOutlet var AddSceneButton: UIButton!

@IBOutlet var cardBounds: UIView!

var newName: String? = ""

@IBOutlet var cardShadow: UIView!

var shit = MainViewController()

override func viewDidLoad() {
    super.viewDidLoad()

    sceneNameTextField.delegate = self

    AddSceneButton.alpha = 0.0

    cardBounds.layer.cornerRadius = 20.0
    cardShadow.layer.shadowRadius = 25.0
    cardShadow.layer.shadowOpacity = 0.4


    // Do any additional setup after loading the view.
}

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
}

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    UIView.animate(withDuration: 0.2){
        self.AddSceneButton.alpha = 1.0
    }
}


@IBAction func exitButton(_ sender: UIButton) {
    dismiss(animated: true, completion: nil)
}

@IBAction func addSceneButton(_ sender: UIButton) {
    if newName == "" {
        sceneLiveName.text = "Enter Something"
        sceneNameTextField.text = ""
    }
    else {
        let appendAct: Act = Act(actName: newName!, actTheme: "Action", actScenes: [Scene(sceneTitle: "Add Act", sceneDescription: "")])
        shit.addObjects(appendage: appendAct)

        dismiss(animated: true, completion: nil)
    }
}

//MARK: textField

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    let text: NSString = (sceneNameTextField.text ?? "") as NSString
    let resultString = text.replacingCharacters(in: range, with: string)
    sceneLiveName.text = resultString
    newName = String(describing: (sceneLiveName.text)!.trimmingCharacters(in: .whitespacesAndNewlines))
    return true
}

func textFieldShouldReturn(_ textField: UITextField) -> Bool {
    sceneNameTextField.resignFirstResponder()
    return true
}

/*
// MARK: - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    // Get the new view controller using segue.destinationViewController.
    // Pass the selected object to the new view controller.
}
*/

}

Вот класс для uitableviewcell, который содержит собственное представление коллекции.

class ActCell: UITableViewCell {

@IBOutlet fileprivate weak var sceneCollection: UICollectionView!

}

extension ActCell {
    func setCollectionViewDataSourceDelegate<D: UICollectionViewDataSource & UICollectionViewDelegate>(_ dataSourceDelegate: D, forSection section: Int) {

    sceneCollection.delegate = dataSourceDelegate
    sceneCollection.dataSource = dataSourceDelegate
    sceneCollection.tag = section
    sceneCollection.reloadData()
    }

}

А вот и модель с пользовательскими данными, включая акты и сцены.

struct Scene {
var sceneTitle: String
var sceneDescription: String
//var characters: [Character]
//var location: Location
}

struct Act {
    var actName: String
    var actTheme: String
    var actScenes : [Scene] = []
}

var actsCollection : [Act] = [
    Act(actName: "dfdsfdsfdsf", actTheme: "Action", actScenes: [Scene(sceneTitle: "Example Act", sceneDescription: "")])
]

Любая помощь приветствуется. Спасибо тебе за все.


person Jaron Schreiber    schedule 20.07.2017    source источник


Ответы (2)


Поэтому, если я не ошибаюсь, я считаю, что метод viewDidLoad вызывается во время поворота экрана. Так что это объясняет, почему он обновляется во время так. Теперь, чтобы заставить его обновляться без поворота устройства, я бы добавил наблюдателя в notificationCenter, чтобы следить за любыми обновлениями в tableView, а затем вызвать #selector, чтобы выполнить reloadData(). Итак, вот пример этого. В методе viewDidLoad добавьте

NotificationCenter.default.addObserver(self, selector: #selector(refreshTable), name: NSNotification.Name(rawValue: "load"), object: nil)

Затем добавьте метод refreshTable()

func refreshTable() {
listOfActs.reloadData()
}

Это в основном то, как я справляюсь с обновлением tableView.

person CarpenterBlood    schedule 20.07.2017
comment
Хорошая идея, к сожалению, это приводит к странному поведению, когда reloadData приводит к неожиданному обнаружению нуля при распаковке. Спасибо за прекрасную идею, хотелось бы, чтобы это сработало. Если у вас есть идеи, пожалуйста, но спасибо, что нашли время. Хорошо оценен - person Jaron Schreiber; 21.07.2017
comment
Просто FYI / Correction viewDidLoad не вызывается снова во время вращения - person Robert J. Clegg; 25.02.2019

Хорошо - viewDidLoad загружается только в первый раз, когда контроллер загружает свое представление (не уверен насчет ротации).

Если очень нужно - можно перезалить tableView в viewWillAppear - но я бы этого делать не стал.

Вместо

actsCollection.append(appendAct)
dismiss(animated: true, completion: nil)

создайте метод на первом контроллере, например addObjectToList(appendAct), и в этом методе просто добавьте объект в массив списка и перезагрузите tableView после добавления.

Вы будете перезагружать tableView только тогда, когда вы действительно что-то добавите в свой список, а не каждый раз, когда появляется контроллер, вам также не нужен наблюдатель уведомлений.

ИЗМЕНИТЬ – ОБНОВИТЬ

Что это?

if newName == "" {
    sceneLiveName.text = "Enter Something"
    sceneNameTextField.text = ""
}
else {
    let appendAct: Act = Act(actName: newName!, actTheme: "Action", actScenes: [Scene(sceneTitle: "Add Act", sceneDescription: "")])
    shit.addObjects(appendage: appendAct)

    dismiss(animated: true, completion: nil)
}

Я имею в виду - что такое shit.AddObjects? Дерьмо определяется как tableView, но вы должны вызывать этот метод на экземпляре вашего контроллера.

Еще одна вещь - измените свою настройку с разделов == количество элементов с 1 строкой на один раздел с количеством строк == количество элементов. :)

person Jakub Kašpar    schedule 20.07.2017
comment
Ваше решение определенно более эффективно, чем мое, но теперь возникает новая проблема, когда в строке метода с self.listOfActs.reloadData() возникает ошибка EXC_BAD_EXCEPTION(), говорящая о том, что при распаковке неожиданно был обнаружен нуль. Комментирование этой строки и повторный запуск приводят к тому же поведению, когда новая ячейка появляется только после поворота экрана. Спасибо. - person Jaron Schreiber; 21.07.2017
comment
@JaronSchreiber И почему вы пытаетесь перезагрузить табличное представление, когда открываете контроллер деталей, чтобы добавить новый акт? Если бы вы могли обновить свой код новым решением, основанным на мне, это помогло бы. :) Кроме того, я настоятельно рекомендую вам переименовать первый контроллер с ViewController, например, на ActsListViewController и никогда больше не называть класс так... - person Jakub Kašpar; 21.07.2017
comment
Я обновил код. Я также добавил модель, которую я использую, и класс для самой ячейки табличного представления на случай, если эта проблема связана с этим. Я временно использую if let, чтобы приложение не вылетало, но если я просто попытаюсь перезагрузить data, это дает мне фатальную ошибку. - person Jaron Schreiber; 22.07.2017
comment
Кроме того, хорошее напоминание! Я понимаю, что так происходит утечка памяти, больше так делать не буду. - person Jaron Schreiber; 22.07.2017
comment
@JaronSchreiber Ответил как РЕДАКТИРОВАТЬ. Если возможно (если это открытый исходный код) - дайте ссылку на свой проект на github. - person Jakub Kašpar; 28.07.2017
comment
Все в порядке, я решил это. Мне нужно было использовать уведомление. Однако, спасибо - person Jaron Schreiber; 28.07.2017
comment
@JaronSchreiber Что неверно, если вам нужно использовать уведомление, чтобы сообщить таблице, что она должна перезагрузиться ... В огромном приложении вы потеряетесь в уведомлениях. Но ладно! :) - person Jakub Kašpar; 28.07.2017