Collection View inside table view delegate - swift

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

Related

How to Navigate to new View Controller from collection view cell? In Swift

I have two View controllers(VC-1, VC-2).
View controller 1 is having a table view and further that table view is consists of 4 collection views. I want to navigate to new VC(VC-2). But unable to get "self.storyboard.instantiateViewController" in didSelect method of collection View.
So now how I can navigate to VC-2
You should use delegate. you can create a protocol and define a method for routing and implement it into VC1
You can use the below sample code.
This code is using closure syntax
class FirstVC: UIViewController, UITableViewDataSource, UITableViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TableCell_Identifier") as! TableCell
cell.configureCell(data: ["Your", "Data"])
cell.didSelectRow = { data in
// Goto second view controller
let vc = self.storyboard?.instantiateViewController(withIdentifier: "SecondVC")
self.navigationController?.pushViewController(vc!, animated: true)
}
return cell
}
}
class TableCell: UITableViewCell, UICollectionViewDataSource, UICollectionViewDelegate {
#IBOutlet weak var colView: UICollectionView!
var didSelectRow: ((_ data: String) -> Void)? = nil // Closure
var arrData = [String]()
func configureCell(data: [String]) {
// Setup CollectionView data
self.arrData = data
self.colView.reloadData()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.arrData.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CELL_IDENTIFIER", for: indexPath)
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let data = self.arrData[indexPath.row]
didSelectRow?(data)
}
}

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)!
}

TableViewCell as Delegate not receiving anything

I don't understand why my delegate doesn't 'receive' anything. Later down the road, I want to hand over a UIImage from the ViewController into a CollectionViewCell inside a TableViewCell by using the delegate. The ViewController contains a tableView and I created a custom cell with a Nib (Xib) file. Each cell contains a collectionView. The tableview's cell class is my delegate, but when the ViewController 'sends' (is that the correct term?) anything to the delegate, nothing happens. Here is the code for my viewController:
protocol ViewControllerDelegate {
func delegateMethod(with string: String)
}
class ViewController: UIViewController {
var viewControllerdelegate: ViewControllerDelegate?
#IBOutlet weak var searchBar: UISearchBar!
#IBOutlet var suggestionTableView: UITableView!
var image = UIImage()
override func viewDidLoad() {
super.viewDidLoad() suggestionTableView.delegate = self
suggestionTableView.dataSource = self
suggestionTableView.register(UINib(nibName: "SuggestionTableViewCell", bundle: nil), forCellReuseIdentifier: "ID")
searchBar.delegate = self
}
}
extension ViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ID", for: indexPath) as! SuggestionTableViewCell
cell.currentImage = image
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
}
extension ViewController: UISearchBarDelegate {
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
print(searchText)
viewControllerdelegate?.delegateMethod(with: "Henlo")
}
}
And here is my TableViewCell class:
class SuggestionTableViewCell: UITableViewCell {
#IBOutlet weak var collectionView: UICollectionView!
var currentCollectionIndex = IndexPath()
var currentCell = UICollectionViewCell()
var currentImage = UIImage()
let controller = ViewController()
override func awakeFromNib() {
super.awakeFromNib()
controller.viewControllerdelegate = self
collectionView.delegate = self
collectionView.dataSource = self
collectionView.register(UINib(nibName: "CollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "CollectionCell")
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
extension SuggestionTableViewCell: ViewControllerDelegate {
func delegateMethod(with string: String) {
print(string)
}
}
extension SuggestionTableViewCell: UICollectionViewDataSource, UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 3
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionCell", for: indexPath) as! CollectionViewCell
cell.userImage.image = currentImage
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
}
Why is my delegate not working? I am so sorry for the code mess, but stackoverflow just doesn't let me organize it properly this time...
In the TableViewCell class you create an instance of ViewController class
let controller = ViewController()
this would be a different instance than the one that created the TableViewCell hence the delegate won't be called as you are setting the delegate of the wrong instance.
Also why do you need a delegate for this? I think creating a function in the TableViewCell class should do.

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

Not reuse data with ReusableCell of CollectionView

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()
}
}