TableView 中的 CollectionView 显示 false Data swift

Posted

技术标签:

【中文标题】TableView 中的 CollectionView 显示 false Data swift【英文标题】:CollectionView in TableView displays false Data swift 【发布时间】:2017-06-26 10:19:16 【问题描述】:

我正在尝试将 CollectionViewTableView 结合起来,所以除了一个我自己无法解决的问题之外,一切正常。

我必须在 CollectionViews 中加载一些数据,这些数据与 CollectionView 所在的 TableViewCell 的标题一起排序。出于某种原因,每次我启动应用程序时,前三个TableViewCells 都是相同的。如果我垂直滚动一点,它们会更改为正确的数据。 但也有可能在使用它时有时会显示与TableViewCell 另一个TableViewCell 相同的数据,如果我稍微滚动一下,问题就会再次解决。

我认为问题出在 reusableCells 但我自己找不到错误。我尝试在重用之前插入colletionView.reloadData() 并将单元格设置为nil,遗憾的是这不起作用。

我的TableViewController

import UIKit
import RealmSwift
import Alamofire
import SwiftyJSON

let myGroupLive = DispatchGroup()
let myGroupCommunity = DispatchGroup()
var channelTitle=""

class HomeVTwoTableViewController: UITableViewController 


var headers = ["LIVE","Channel1", "Channel2", "Channel3", "Channel4", "Channel5", "Channel6"]

override func viewDidLoad() 
    super.viewDidLoad()

override func viewWillAppear(_ animated: Bool) 
     self.navigationController?.navigationBar.isTranslucent = false
   DataController().fetchDataLive(mode: "get")
   DataController().fetchDataCommunity(mode: "get")


//MARK: Custom Tableview Headers
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? 
    return headers[section]


//MARK: DataSource Methods
override func numberOfSections(in tableView: UITableView) -> Int 
    return headers.count


override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
    return 1


//Choosing the responsible PrototypCell for the Sections
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
    if indexPath.section == 0 
        let cell = tableView.dequeueReusableCell(withIdentifier: "cellBig", for: indexPath) as! HomeVTwoTableViewCell
        print("TableViewreloadMain")
        cell.collectionView.reloadData()
        return cell
    
    else if indexPath.section >= 1 
        // getting header Titel for reuse in cell
        channelTitle = self.tableView(tableView, titleForHeaderInSection: indexPath.section)!
        let cell = tableView.dequeueReusableCell(withIdentifier: "cellSmall", for: indexPath) as! HomeVTwoTableViewCellSmall
        // anti Duplicate protection
        cell.collectionView.reloadData()

        return cell
    

    else 
        channelTitle = self.tableView(tableView, titleForHeaderInSection: indexPath.section)!
        let cell = tableView.dequeueReusableCell(withIdentifier: "cellSmall", for: indexPath) as! HomeVTwoTableViewCellSmall
        // anti Duplicate protection
        cell.collectionView.reloadData()

        return cell
    



我的TableViewCell 带有`CollectionView

import UIKit
import RealmSwift

var communities: Results<Community>?

class HomeVTwoTableViewCellSmall: UITableViewCell

//serves as a translator from ChannelName to the ChannelId
var channelOverview: [String:String] = ["Channel1": "399", "Channel2": "401", "Channel3": "360", "Channel4": "322", "Channel5": "385", "Channel6": "4"]
//Initiaize the CellChannel Container
var cellChannel: Results<Community>!

//Initialize the translated ChannelId
var channelId: String = ""

@IBOutlet weak var collectionView: UICollectionView!

 

 extension HomeVTwoTableViewCellSmall: UICollectionViewDataSource,UICollectionViewDelegate 

//MARK: Datasource Methods
func numberOfSections(in collectionView: UICollectionView) -> Int

    return 1


func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int

    return (cellChannel.count)


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

    guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionCellSmall", for: indexPath) as? HomeVTwoCollectionViewCellSmall else
    
        fatalError("Cell has wrong type")
    
    //removes the old image and Titel
    cell.imageView.image = nil
    cell.titleLbl.text = nil
    //inserting the channel specific data
    let url : String = (cellChannel[indexPath.row].pictureId)
    let name :String = (cellChannel[indexPath.row].communityName)
    cell.titleLbl.text = name
    cell.imageView.downloadedFrom(link :"link")
    return cell



//MARK: Delegate Methods

override func layoutSubviews() 
    myGroupCommunity.notify(queue: DispatchQueue.main, execute: 

        let realm = try! Realm()
        //Getting the ChannelId from Dictionary
        self.channelId = self.channelOverview[channelTitle]!

        //load data from Realm into variables
        self.cellChannel = realm.objects(Community.self).filter("channelId = \(String(describing: self.channelId)) ")

        self.collectionView.dataSource = self
        self.collectionView.delegate   = self
        print("collectionView layout Subviews")
        self.collectionView.reloadData()
    )



func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) 
    selectedCommunity = (cellChannel[indexPath.row].communityId)
    let home = HomeViewController()
    home.showCommunityDetail()


提前致谢。

【问题讨论】:

【参考方案1】:

tl;dr 在您的单元格上创建 channelTitle 变量,而不是全局变量。另外,在prepareForReuse上清除它以及您的其他单元格变量


我在这里可能弄错了,但是您是否在创建单元格后将channelTitle 设置在单元格上?正如我所看到的,在您的 viewController 中,您根据标题创建单元格,并且对于每个单元格,您将 TableViewControllerchannelTitle 设置为给定部分的标题。

如果是这种情况,那么在您调用 reloadData() 之前,TableViewCell 实际上并没有收到任何关于它应该加载什么的信息。

一般来说,我还建议在您的HomeVTwoTableViewCellSmall 中实现prepareForReuse,因为它可以让您有机会清理任何过时的数据。您可能希望在该方法中设置cellChannelchannelId 为空字符串或nil,因此当重复使用单元格时,旧数据会保留。


另外,我刚刚重读了您拥有的单元格代码,看起来您正在layoutSubviews 中进行一些关键的初始单元格设置。该方法可能会被调用很多次,但您实际上只需要调用一次(对于它的大部分功能)。试试这个:

用单元格上的重用标识符覆盖初始化 在该初始化中,添加self.collectionView.dataSource = selfself.collectionView.delegate = selfchannelTitle 上添加didSet 在视图控制器中设置channelTitle

所以代码看起来像:

var channelTitle: String = "" 
    didSet 
        self.channelId = self.channelOverview[channelTitle]!
        self.cellChannel = realm.objects(Community.self).filter("channelId = \(String(describing: self.channelId)) ")
        self.collectionView.reloadData()
    

这样,您只会在单元格使用新频道更新时重新加载数据,而不是单元格视图的每个布局。


抱歉……又多了一个。我不知道您的channelTitle 实际上是如何通过的。正如我所看到的,您将 channelTitle 用作全局变量而不是本地变量。不要那样做!在实现上面的代码之前从当前位置删除channelTitle。您会看到一些错误,因为您在 ViewController 中设置它并在单元格中访问它。您想要的是在 ViewController 的单元格上设置 channelTitle(如上所述)。这也解释了为什么您在所有三个单元格中看到相同的数据。基本上,您只设置了一个 channelTitle,并且所有三个单元格都在寻找该全局值来获取它们的数据。

希望能有所帮助!

(另外,您应该能够在 cellForRowAtIndexPath 方法中删除您的 else if 块,因为它后面的 else 块包含相同的代码。您也可以删除您的 viewDidLoad,因为它不是做任何事情,通常你应该看看你是否可以摆脱任何!,因为它们不安全。改用?guardif let

【讨论】:

非常感谢您的帮助。我实施了您的更改,效果很好(没有重复),也感谢纠正我的不良编码习惯(我还在学习)。但现在的问题是,只有在我垂直滚动一点时才会加载数据。您认为我在某处缺少 reloadData() 吗?或者是什么导致了这个加载问题。 听起来问题可能是您第一次加载单元格时没有任何可用数据。您调用fetchDataLivefetchDataCommunity 之后,您可能会根据标题加载带有单元格的tableView 之后不久。但是,如果该 tableView 正在立即加载,那么很可能没有可用于填充这些单元格的数据(当您滚动时,数据可能可用)。看看有没有办法让 ViewController 知道这些 fetch 方法何时完成,然后在那里的桌子上调用reloadData 你说得对,我的 DataController().fetchDataLive(mode: "get")DataController().fetchDataCommunity(mode: "get") 有问题。现在我将获取更改为仅更新(而不是删除和重写),它现在可以正常工作了。非常感谢。

以上是关于TableView 中的 CollectionView 显示 false Data swift的主要内容,如果未能解决你的问题,请参考以下文章

使用 UICollectionViewDiffableDataSource 时如何重新加载 UICollectionView 中的部分数据?

频繁重新加载collectionView时崩溃

获取 UITableViewCell 索引以响应位于 tableview 中的 tableview 中的按钮单击的正确方法?

TableView 未从另一个 tableview 中的 didselectRowAtindexPath 加载

滚动视图中的 tableview,tableview didselect 没有按预期工作

在我点击 tableView 之前,不会显示 tableView 中的数据