Lukas
Lukas

Reputation: 29

Segue in a UICollectionView embedded in a UITableviewCell, Swift

I have made a UICollectionView inside a UITableViewCell and it works pretty Fine. My only Problem is, that I can't perform a Segue on didSelectItemAt Method to another ViewController.

I know I have to perform it from the TableViewController,I made a segue on the Storyboard and I tried multiple possibilities but for some reasons it doesn't work.

Here my TableviewController:

import UIKit
import RealmSwift
import SwiftyJSON

class HomeVTwoTableViewController: UITableViewController {

let realm = try! Realm()
let headers = ["1","2","3"]

override func viewDidLoad() {
    super.viewDidLoad()
}

override func viewWillAppear(_ animated: Bool) {    
    self.tableView.separatorStyle = .none
}


//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
        cell.update()
        return cell
    } else {
        return UITableViewCell()
    }
}
// This on of my tries to perform a segue
func liveCellSelected() {
    performSegue(withIdentifier: "showChat", sender: nil)
}
}

And here my TableViewCell with the embedded CollectionView:

import UIKit
import RealmSwift



class HomeVTwoTableViewCell: UITableViewCell{

var liveCommunities: Results<Community>?

@IBOutlet weak var collectionView: UICollectionView!

override func awakeFromNib() {
    super.awakeFromNib()
    collectionView.delegate = self
    collectionView.dataSource = self
}
}

extension HomeVTwoTableViewCell: 
UICollectionViewDataSource,UICollectionViewDelegate {

func numberOfSections(in collectionView: UICollectionView) -> Int
{
    return 1
}

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
    return(liveCommunities?.count)!
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
    guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionCellBig", for: indexPath) as? HomeVTwoCollectionViewCell else
    {
        fatalError("Cell has wrong type")
    }
    //removes the old image
    cell.imageView.image = UIImage(named: "No Image")
    cell.titleLbl.text = nil

    //set url and Picture
    url = (liveCommunities?[indexPath.row].pictureId)!
    let name : String = (liveCommunities?[indexPath.row].communityName)!
    let channelName : String = (liveCommunities?[indexPath.row].channelName)!

    cell.titleLbl.text = name
    cell.senderLbl.text = channelName
    cell.imageView.downloadedFrom(link :"someSecretUrl")

    return cell
}

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    selectedCommunity = (liveCommunities?[indexPath.row].communityId)!
    HomeVTwoTableViewController().liveCellSelected()
}
}

I found another question with a similar theme, but couldn't implement a delegate Protocol without creating problems with the already existing delegates.

Maybe it`s an obvious mistake but I can't see it. Thanks in advance.

Upvotes: 0

Views: 712

Answers (3)

Lukas
Lukas

Reputation: 29

Ok as it seems there are two Solutions to this Problem. One is via a Variable and one via a delegate. As I know the delegate one is more common.

Solution 1 with Variable:

Here my TableviewController:

import UIKit

class TableViewController: UITableViewController {

let headers = ["1","2","3"]

override func viewDidLoad() {
    super.viewDidLoad()
}

override func numberOfSections(in tableView: UITableView) -> Int {
    return headers.count
}

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

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cellBig", for: indexPath) as! HomeVTwoTableViewCell
  //Fill function, insert Navigation      
  cell.didSelectAction = {
            self.performSegue(withIdentifier: "testSegue", sender: nil)
        }
        return cell
}

And here my TableViewCell with the embedded CollectionView:

import UIKit

class HomeVTwoTableViewCell: UITableViewCell{

var liveCommunities: Results<Community>?
//Instantiate function
var didSelectAction: () -> Void = {}

@IBOutlet weak var collectionView: UICollectionView!

override func awakeFromNib() {
    super.awakeFromNib()
    collectionView.delegate = self
    collectionView.dataSource = self
}
}

extension HomeVTwoTableViewCell: 
UICollectionViewDataSource,UICollectionViewDelegate {

func numberOfSections(in collectionView: UICollectionView) -> Int
{
    return 1
}

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
    return(liveCommunities?.count)!
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
    guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionCellBig", for: indexPath) as? HomeVTwoCollectionViewCell else
    {
        fatalError("Cell has wrong type")
    }
    //removes the old text
    cell.titleLbl.text = nil
    cell.senderLbl.text = nil

    let name : String = (liveCommunities?[indexPath.row].communityName)!
    let channelName : String = (liveCommunities?[indexPath.row].channelName)!

    cell.titleLbl.text = name
    cell.senderLbl.text = channelName

    return cell
}

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    selectedCommunity = (liveCommunities?[indexPath.row].communityId)!
  // Invoque your action 
  didSelectAction()
}
}

Solution 2 with Delegate and Protocol:

Here my TableviewController:

import UIKit

class TableViewController: UITableViewController {

let headers = ["1","2","3"]

override func viewDidLoad() {
    super.viewDidLoad()
}

override func numberOfSections(in tableView: UITableView) -> Int {
    return headers.count
}

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

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cellBig", for: indexPath) as! HomeVTwoTableViewCell
  //Add delegate      
  cell.delegate = self
  return cell
}
 //Add Extension with Navigation
 extension HomeVTwoTableViewController: CellCollectionViewDelegate {
     func didSelect() {
         performSegue(withIdentifier: "showChat", sender: nil)
    }
}

And here my TableViewCell with the embedded CollectionView:

import UIKit
//Create a delegate protocol
protocol CellCollectionViewDelegate: class{
func didSelect()
}

class HomeVTwoTableViewCell: UITableViewCell{

var liveCommunities: Results<Community>?
//Add a delegate property
weak var delegate: CellCollectionViewDelegate?

@IBOutlet weak var collectionView: UICollectionView!

override func awakeFromNib() {
    super.awakeFromNib()
    collectionView.delegate = self
    collectionView.dataSource = self
}
}
//Adopt and implement the Delegate Protocol
extension HomeVTwoTableViewCell: 
UICollectionViewDataSource,UICollectionViewDelegate {

func numberOfSections(in collectionView: UICollectionView) -> Int
{
    return 1
}

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
    return(liveCommunities?.count)!
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
    guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionCellBig", for: indexPath) as? HomeVTwoCollectionViewCell else
    {
        fatalError("Cell has wrong type")
    }
    //removes the old text
    cell.titleLbl.text = nil
    cell.senderLbl.text = nil

    let name : String = (liveCommunities?[indexPath.row].communityName)!
    let channelName : String = (liveCommunities?[indexPath.row].channelName)!

    cell.titleLbl.text = name
    cell.senderLbl.text = channelName

    return cell
}

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    selectedCommunity = (liveCommunities?[indexPath.row].communityId)!
    //call delegate method
    delegate?.didSelect()
}
}

I tried to sum up the Solutions from

Abdoelrhman Mohamed and Alexkater and write it out in detail.

Tell me if something is wrong or left out.

Upvotes: 0

Abdoelrhman
Abdoelrhman

Reputation: 916

You're instantiating a new instacne of your home controller with this:

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    selectedCommunity = (liveCommunities?[indexPath.row].communityId)!
    HomeVTwoTableViewController().liveCellSelected()
}

what you should do is to make it via a delegate, or you move your collectionview delegate to the main controller

protocol CellCollectionViewDelegate: class{
  func didselect()
}

and you implement the delegate in cell and your home controller

Upvotes: 1

Alexkater
Alexkater

Reputation: 76

May be you can create a variable to handle this. You can do optional also.

class HomeVTwoTableViewCell: UITableViewCell{
var liveCommunities: Results<Community>?
@IBOutlet weak var collectionView: UICollectionView!
var didSelectAction: () -> Void // add your action here

Here you can call to this function

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    selectedCommunity = (liveCommunities?[indexPath.row].communityId)!
    HomeVTwoTableViewController().liveCellSelected()
    didSelectAction() // Invoque your action
}

//On the cell creator, add the navigation or logic when you want to tap the cell

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    if indexPath.section == 0 {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cellBig", for: indexPath) as! HomeVTwoTableViewCell
        cell.update()
        cell.didSelectAction = {
           // add here your navigation
        }
        return cell
    } else {
        return UITableViewCell()
    }
}

Upvotes: 0

Related Questions