简体   繁体   中英

CollectionView in TableView displays false Data swift

I'm trying to combine a CollectionView with a TableView , so fare everything works except one problem, which I cant fix myself.

I have to load some data in the CollectionViews which are sorted with the header of the TableViewCell where the CollectionView is inside. For some reason, every time I start the app, the first three TableViewCells are identical. If I scroll a little bit vertically, they change to the right Data. But it can also happen that while using it sometimes displays the same Data as in on TableViewCell another TableViewCell , here again the problem is solved if I scroll a little.

I think the problem are the reusableCells but I cant find the mistake myself. I tried to insert a colletionView.reloadData() and to set the cells to nil before reusing, sadly this didn`t work.

My 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
    }
}
}
}

My TableViewCell with `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()
}
}

Thanks in advance.

tl;dr make channelTitle a variable on your cell and not a global variable. Also, clear it, and your other cell variables, on prepareForReuse


I may be mistaken here, but are you setting the channelTitle on the cells once you create them? As I see it, in your viewController you create cells based on your headers, and for each cell you set TableViewController 's channelTitle to be the title at the given section.

If this is the case, then the TableViewCell actually isn't receiving any information about what it should be loading before you call reloadData() .

In general, I would also recommend implementing prepareForReuse in your HomeVTwoTableViewCellSmall , since it will give you a chance to clean up any stale data. Likely you would want to do something like set cellChannel and channelId to empty strings or nil in that method, so when the cell is reused that old data is sticking around.


ALSO, I just reread the cell code you have, and it looks like you're doing some critical initial cell setup in layoutSubviews . That method is going to be potentially called a lot, but you really only need it to be called once (for the majority of what it does). Try this out:

  • override the init with reuse identifier on the cell
  • in that init, add self.collectionView.dataSource = self and self.collectionView.delegate = self
  • add a didSet on channelTitle
  • set channelTitle in the viewController

So the code would look like:

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

This way you're only reloading your data when the cell is updated with a new channel, rather than every layout of the cell's views.


Sorry... one more addition. I wasn't aware of how your channelTitle was actually being passed. As I see it, you're using channelTitle as a global variable rather than a local one. Don't do that! remove channelTitle from where it is currently before implementing the code above. You'll see some errors, because you're setting it in the ViewController and accessing it in the cell. What you want is to set the channelTitle on the cell from the ViewController (as I outlined above). That also explains why you were seeing the same data across all three cells. Basically you had set only ONE channelTitle and all three cells were looking to that global value to fetch their data.

Hope that helps a little!

(also, you should be able to remove your else if block in the cellForRowAtIndexPath method, since the else block that follows it covers the same code. You can also delete your viewDidLoad, since it isn't doing anything, and you should, as a rule, see if you can get rid of any ! 's because they're unsafe. Use ? or guard or if let instead)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM