TableView 仅在屏幕旋转后更新

Posted

技术标签:

【中文标题】TableView 仅在屏幕旋转后更新【英文标题】:TableView Only Updates after Screen Rotates 【发布时间】:2017-07-20 22:35:04 【问题描述】:

Image of the tableview

我有一个表格视图,每个单元格中都有一个集合视图,全部链接到一个数组。每个集合视图都有标签,所以当我从一开始就在数组中有东西时,所有表格视图单元格和集合视图单元格都会正确显示在应用程序中。但是,当我在应用程序本身的数组中添加一个元素时(我有第二个视图控制器可以执行此操作),它可以工作,但新的表格视图单元格仅在屏幕旋转后出现(真的很奇怪)。我尝试在第二个视图控制器中添加视图控制器的对象和表视图,在其中我将元素添加到数组中。然后在 ViewWillDisappear 的第二个视图控制器中,我通过该对象重新加载数据(),如下所示:

var vc : ViewController? = ViewController()

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

但这会导致 EXC_BAD_INSTRUCTION

然后我尝试在视图控制器的 prepareForSegue 中添加 self.listOfActs.reloadData() 和表视图,以便我可以看到它至少在某个时间点刷新数据,但即使这样也不起作用当我第二次点击添加场景时。

更新:新 MainViewController

这是带有表格视图的新的第一个视图控制器。我重命名了它并实现了添加到数组和重新加载的方法。如果我在 reloadData 上使用 if let ,它会起作用,但我会回到原点,它只在我旋转屏幕时更新。当我摆脱 if let 它实际上可以尝试更新表格视图时,它给了我一个 Fata 错误:在展开时意外发现一个 nil。

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: "")])
]

非常感谢任何帮助。谢谢大家。

【问题讨论】:

【参考方案1】:

所以如果我没记错的话,我相信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 刷新的方式。

【讨论】:

好主意,不幸的是,这会导致奇怪的行为,其中 reloadData 导致在展开时意外发现 nil。谢谢你的好主意,我希望这能奏效。如果您有想法,请,但感谢您抽出宝贵的时间。非常感谢 仅供参考/更正 viewDidLoad 在轮换期间不会再次被调用【参考方案2】:

好吧 - viewDidLoad 仅在控制器第一次加载他的视图时加载(不确定旋转)。

如果你真的需要 - 你可以在 viewWillAppear 中重新加载 tableView - 但我不会这样做。

代替

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 是什么? Shit 被定义为 tableView - 但是你必须在你的控制器实例上调用这个方法。

另一件事 - 将您的设置从节 == 1 行的项目数更改为一个具有行数 == 项目数的节。 :)

【讨论】:

您的解决方案肯定比我的解决方案更有效,但现在出现了一个新问题,即在“self.listOfActs.reloadData()”方法的行中出现 EXC_BAD_EXCEPTION 表示意外发现 nil 而展开。将此行注释掉并再次运行会导致新单元格仅在屏幕旋转后出现的相同行为。谢谢。 @JaronSchreiber 当您打开细节控制器以添加新动作时,为什么要尝试重新加载表格视图?如果您可以使用基于我的新解决方案更新您的代码 - ti 会有所帮助。 :) 另外,我强烈建议您将第一个控制器从 ViewController 重命名为例如 ActsListViewController 并且永远不要再这样命名类... 我更新了代码。我还添加了我正在使用的模型和表格视图单元格本身的类,以防这个问题源于那里我暂时使用 if let 它不会使应用程序崩溃,但如果我只是尝试重新加载数据,它给了我致命的错误。 另外,好提醒!我知道这就是内存泄漏的发生方式,不会再这样做了。 @JaronSchreiber 如果您需要使用通知通知表它应该重新加载,这是不正确的......在巨大的应用程序中,您将丢失通知。不过没关系! :)

以上是关于TableView 仅在屏幕旋转后更新的主要内容,如果未能解决你的问题,请参考以下文章

旋转屏幕时,带有嵌入式 tableview 的 Collectionview 被剪裁

Swift 2:仅在全屏视频上进行屏幕旋转

UICollectionView 不会在屏幕旋转时更新其布局

Android 设定横屏,禁止屏幕旋转,Activity重置 [更新视频播放器相关]

屏幕旋转后获取自动旋转 UIView 的尺寸?

当我旋转屏幕时,progressDialog 停止更新