Not reuse data with ReusableCell of CollectionView - swift

I have tableView:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellTime", for: indexPath) as! TimeViewCell
let showHour = self.infoWripper.sorted(by: { $0.hour < $1.hour })[indexPath.row]
cell.labelTime.text = showHour.showHour()
cell.dataForShow = showHour.showFullTime()
return cell
}
Cell of TableView contents UICollectionView with cells.
class TimeViewCell: UITableViewCell {
#IBOutlet weak var labelTime: UILabel!
var dataForShow = [String]()
#IBOutlet weak var collectionView: UICollectionView!
override func awakeFromNib() {
super.awakeFromNib()
collectionView.delegate = self
collectionView.dataSource = self
}
}
extension TimeViewCell: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return dataForShow.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionCell", for: indexPath) as! CustomCollectionCell
cell.labelCollection.text = dataForShow[indexPath.row]
//heightForCell = collectionView.contentSize.height
return cell
}
}
class CustomCollectionCell: UICollectionViewCell {
#IBOutlet weak var labelCollection: UILabel!
}
When I open VC with table - view is OK, but when I begin scrolling - I see, that data not correct (reuse), but I can't find my mistake.
before scroll:
after:

You need to reload the collection view in your cell.
Try this:
class TimeViewCell: UITableViewCell {
....
var dataForShow = [String]() {
didSet {
self.collectionView?.reloadData()
}
}

Related

How to retrieve data from firebase in a collectionView inside a tableViewCell?

I'm trying to retrieve data from firebase, but I need the data to show up in a collectionView that's inside a tableViewCell (which makes it a bit confusing).
I have three files. FeedTableViewCell, FeedCollectionViewCell, FeedViewController.
Normally I would just call the retrieve function in the FeedViewController/ViewDidLoad function. But since I need it in the collectionView its not working in this case. Where can I add the function so I can use it in my collectionView? Or how can I reformat the code?
Code below isn't working (Fatal error in FeedTaabelViewCell file when run). I've tried adding the retrieve function inside FeedTableViewCell, but there isn't a ViewDidLoad function so its not working. The best I can come up with is to leave it in the main FeedViewController file, but then how do I call it in FeedTableViewCell file?
class FeedTableViewCell: UITableViewCell {
#IBOutlet weak var collectionView: UICollectionView!
var refArtists: DatabaseReference!
var userList = [UserModel]()
override func awakeFromNib() {
super.awakeFromNib()
collectionView.delegate = self
collectionView.dataSource = self
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
extension FeedTableViewCell: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 2
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! FeedCollectionViewCell
let artist: UserModel
//ERROR: INDEX OUT OF RANGE
artist = userList[indexPath.row]
cell.nameLabel.text = artist.name
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let itemWidth = collectionView.bounds.width
let itemHeight = collectionView.bounds.height
return CGSize(width: itemWidth, height: itemHeight)
}
}
class FeedCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var passionLabel: UILabel!
#IBOutlet weak var distanceLabel: UILabel!
#IBOutlet weak var feedCellImage: UIImageView!
}
class FeedViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 4
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "tableCell", for: indexPath)
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
var bounds = UIScreen.main.bounds
return bounds.size.height - (self.tabBarController?.tabBar.frame.size.height)!
}
var refArtists: DatabaseReference!
override func viewDidLoad() {
super.viewDidLoad()
refArtists = Database.database().reference().child("users");
//observing the data changes
refArtists.observe(DataEventType.value, with: { [self] (snapshot) in
//if the reference have some values
if snapshot.childrenCount > 0 {
//clearing the list
userList.removeAll()
//iterating through all the values
for artists in snapshot.children.allObjects as! [DataSnapshot] {
//getting values
let artistObject = artists.value as? [String: AnyObject]
let userName = artistObject?["name"]
let userId = artistObject?["id"]
let userInterest = artistObject?["interest"]
//creating artist object with model and fetched values
let artist = UserModel(id: userId as! String?, name: userName as! String?, interest: userInterest as! String?)
//appending it to list
userList.append(artist)
}
}
})
}
class FeedViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 4
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "tableCell", for: indexPath)
cell.userList = userList
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
var bounds = UIScreen.main.bounds
return bounds.size.height - (self.tabBarController?.tabBar.frame.size.height)!
}

Why is my collection view empty when I should have a bunch of reusable cells?

I have the following code:
class FinalImageViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
var not: [UIImage?] = [#imageLiteral(resourceName: "silly5"), #imageLiteral(resourceName: "special16")]
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 3
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return not.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CVImageView", for: indexPath) as! CVCell
cell.cellImageView.image = not[indexPath.row]
return cell
}
#IBOutlet weak var collectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
print(ImagesOnClick)
collectionView.delegate = self
collectionView.dataSource = self
collectionView.register(CVCell.self, forCellWithReuseIdentifier: "CVImageView")
}
}
And my cell:
class CVCell: UICollectionViewCell {
#IBOutlet weak var cellImageView: UIImageView!
}
Then in my storyboard I have set the imageView tag to 3, the cell identifier to CVImageView, but when I run the project the collection view is just all white and no items are showing. By the way if you're wondering what the ImagesOnClick is, it is just an array of the users selected images.
You can find your class name in storyboard, it should look like this
class FinalImageViewController: UIViewController,UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return ImagesOnClick.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withIdentifier: "CVImageView", for: indexPath) as! CVImageCell //replace CVImageCell with your cell class name
cell.imageView.image = ImagesOnClick[indexPath.row] //replace imageView with your image view name with tag=3
return cell
}
#IBOutlet weak var collectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
print(ImagesOnClick)
collectionView.register(CVImageCell.self, forCellWithReuseIdentifier: "CVImageView") //replace CVImageCell with your cell class name
collectionView.delegate = self
collectionView.dataSource = self
}
}

Receiving an error of when trying to add text to a label in a collection view

I am trying to input the arrayOfValues[indexPath.item] as the text for my textLabel in the collection view, but receive an error when I run the program saying 'Fatal error: Index Out of Range'
How would I fix this so that the collectionView cells are populated with information from the arrayOfValues?
Here is the code.
import UIKit
class NetworkViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
#IBOutlet weak var firstCollectionView: UICollectionView!
#IBOutlet weak var secondCollectionView: UICollectionView!
let arrayOfOrganizations = ["My Network", "Find Connections", "ss"]
let arrayOfValues = [""]
override func viewDidLoad() {
super.viewDidLoad()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if (collectionView == secondCollectionView) {
return arrayOfOrganizations.count
}
return arrayOfValues.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let firstCell = firstCollectionView.dequeueReusableCell(withReuseIdentifier: "firstCell", for: indexPath) as! FirstCollectionViewCell
firstCell.textLabel.text = arrayOfValues[indexPath.item] //error on this line
if (collectionView == secondCollectionView) {
let secondCell = secondCollectionView.dequeueReusableCell(withReuseIdentifier: "secondCell", for: indexPath) as! SecondCollectionViewCell
secondCell.backgroundColor = .black
return secondCell
}
return firstCell
}
}
class FirstCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var textLabel: UILabel!
}
class SecondCollectionViewCell: UICollectionViewCell {
}
The problem you had was the the first two lines of your cellForItemAt function were getting executed no matter the collectionView. So, essentially, you need to make sure the block of code corresponding to the firstCollectionView gets executed only when collectionView == firstCollectionView, and same goes for the secondCollectionView. In short, you just need to change your function to this instead:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if (collectionView == secondCollectionView) {
let secondCell = secondCollectionView.dequeueReusableCell(withReuseIdentifier: "secondCell", for: indexPath) as! SecondCollectionViewCell
secondCell.backgroundColor = .black
return secondCell
} else {
let firstCell = firstCollectionView.dequeueReusableCell(withReuseIdentifier: "firstCell", for: indexPath) as! FirstCollectionViewCell
firstCell.textLabel.text = arrayOfValues[indexPath.item] //error on this line
return firstCell
}
}

Collection View inside table view delegate

I have a table view with collection view inside table rows.
structure is next:
MainViewController.swift:
class MainViewController: UIViewController {
#IBOutlet weak var customTable: UITableView!
func callSegue() {
performSegue(withIdentifier: "customSegue", sender: self)
}
override func viewDidLoad() {
customTable(UINib(nibName: "CustomTableCell", bundle: nil), forCellReuseIdentifier: "TipsTableCell")
}
}
extension MainViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomTableCell", for: indexPath) as! CustomTableCell
//Fill cell with my data
return cell
}
}
CustomTableCell.swift
class CustomTableCell.swift: UITableViewCell {
#IBOutlet var collectionView: UICollectionView!
override func awakeFromNib() {
super.awakeFromNib()
self.collectionView.dataSource = self
self.collectionView.delegate = self
self.collectionView.register(UINib.init(nibName: "CustomTableCell", bundle: nil), forCellWithReuseIdentifier: "CustomTableCell")
}
}
extension CustomTableCell: UICollectionViewDataSource, UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return dataArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CustomTableCell", for: indexPath) as! CustomTableCell
cell.label1.text = dataArray[indexPath.item]
return cell
and my CustomCollectionvCell.swift
class CustomCollectionvCell: UICollectionViewCell {
#IBOutlet weak var label1: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
I need something like this:
I need to call "callSegue" func in MainViewController when I tapped at cell where label1.text == "Something".
Use closures to solve that.
Add a closure in CustomTableCell and call it when the collectionViewCell is tapped in collectionView(_:didSelectItemAt:) method, i.e.
class CustomTableCell: UITableViewCell {
var handler: (()->())?
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
self.handler?()
}
}
In MainViewController, set the closure while dequeuing CustomTableCell in tableView(_:cellForRowAt:) method, i.e.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomTableCell", for: indexPath) as! CustomTableCell
cell.handler = {[weak self] in
self.callSegue() //here.....
}
return cell
}
Also cross-check if you have a segue with identifier customSegue in your storyboard.
In your case, you have to implement delegate from CustomTableCell.swift and use in MainViewController.swift
// MainViewController.swift:
class MainViewController: UIViewController {
#IBOutlet weak var customTable: UITableView!
func callSegue() {
performSegue(withIdentifier: "customSegue", sender: self)
}
override func viewDidLoad() {
customTable(UINib(nibName: "CustomTableCell", bundle: nil), forCellReuseIdentifier: "TipsTableCell")
}
}
extension MainViewController: UITableViewDataSource, collectionViewCellTapped {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomTableCell", for: indexPath) as! CustomTableCell
//Fill cell with my data
return cell
}
func cellTapped(_ text: String) {
if let text = dataArray[indexPath.item], text == "Something" {
callSegue()
}
}
}
// CustomTableCell.swift
protocol collectionViewCellTapped {
func cellTapped(_ text: String)
}
class CustomTableCell : UITableViewCell {
#IBOutlet var collectionView: UICollectionView!
var delegate: collectionViewCellTapped!
override func awakeFromNib() {
super.awakeFromNib()
self.collectionView.dataSource = self
self.collectionView.delegate = self
self.collectionView.register(UINib.init(nibName: "CustomTableCell", bundle: nil), forCellWithReuseIdentifier: "CustomTableCell")
}
}
extension CustomTableCell: UICollectionViewDataSource, UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return dataArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CustomTableCell", for: indexPath) as! CustomTableCell
cell.label1.text = dataArray[indexPath.item]
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if let delegate = self.delegate {
delegate.cellTapped(text)
}
}
}

How to use NavigationController by taping on CollectionViewCell which in TableViewCell

I want to pushViewController and show detail about that cell!
Swift5
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var myTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
myTableView.delegate = self
myTableView.dataSource = self
}
}
extension ViewController: UITableViewDelegate, UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return ViewController.typeOfDishes.count
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return ViewController.typeOfDishes[section]
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.myTableView.dequeueReusableCell(withIdentifier: "MyTableViewCell") as! MyTableViewCell
return cell
}
}
import UIKit
class MyTableViewCell: UITableViewCell {
#IBOutlet weak var myCollectionView: UICollectionView!
override func awakeFromNib() {
super.awakeFromNib()
myCollectionView.delegate = self
myCollectionView.dataSource = self
}
}
extension MyTableViewCell: UICollectionViewDelegate, UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return ViewController.dishesCountByArray[ViewController.indexOfTableView!]
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
////////////There I want to do PushViewController Action
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = self.myCollectionView.dequeueReusableCell(withReuseIdentifier: "MyCollectionViewCell", for: indexPath) as! MyCollectionViewCell
return cell
}
}
I want to normally pu​sh when user taps on collection view cell
Inside here Add a delegate
class MyTableViewCell: UITableViewCell {
weak var delegate:ViewController?
.....
}
Then set it here
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.myTableView.dequeueReusableCell(withIdentifier: "MyTableViewCell") as! MyTableViewCell
cell.delegate = self
return cell
}
Now you can push here
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "SecondID") as! SecondViewController
self.delegate?.navigationController?.push////
}