Search code examples
iosswiftswift3tableviewcollectionview

How can I figure out which tableView Cell is selected when collectionview is scrolled horizontally


I have collectionView inside TableViewCell. TableView has 1 section and 4 rows.

I want to figure out which row of tableviewcell is selected when collectionView is Scrolled Horizontally.

I tryed to figure out by putting tableView row in "var nowRow" variable like below code. But It does not work.

How can I find out which row of tableviewcell is selected?

class Home2ViewController: UIViewController, UITableViewDataSource,  UITableViewDelegate, UICollectionViewDataSource, UICollectionViewDelegate {

    var posts = [Post]()
    var nowRow = Int()

    override func viewDidLoad() {
        // get posts elements here 
        //posts.append(element).......
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

            return posts.count // 4counts 

    }

    func numberOfSections(in tableView: UITableView) -> Int {

        return 1
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
       // let cell = DetailMutiTableViewCell()
        let cell = tableView.dequeueReusableCell(withIdentifier: "DetailMutiTableViewCell", for: indexPath) as! DetailMutiTableViewCell

        nowRow = indexPath.row

        cell.post = posts[nowRow]
        cell.collectionView1.delegate = self
        cell.collectionView1.dataSource = self

        cell.collectionView1.reloadData()
        return cell
    }



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

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return posts[nowRow].imageUrls.count
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MultiImagesCollectionViewCell", for: indexPath) as! MultiImagesCollectionViewCell

        let photoUrlString = posts[nowRow].imageUrls[indexPath.item] // ←error occured
            let photoUrl = URL(string: photoUrlString)
            cell.imageView1.sd_setImage(with: photoUrl)


        return cell
    }
}

editeditedit


Solution

  • As you have no doubt discovered, interacting with the collectionViews in your table rows does not trigger the tableView method tableView(_:didSelectRowAt:), and tableView.indexPathForSelectedRow is nil because no row has been selected. You need a way to map from the collectionView to the indexPath.row that contains it. This information is known at tableViewCell setup time.

    Add a dictionary to your Home2ViewController that maps a UICollectionView to a row Int:

    var collectionViewRow = [UICollectionView : Int]()
    

    Add the entries in tableView(_:cellForRowAt:) where you know the row and collectionView:

    collectionViewRow[cell.collectionView1] = indexPath.row
    

    Then, anytime you have a collectionView, you can look up its row:

    let row = collectionViewRow[collectionView]!
    

    Here's the whole code:

    class Home2ViewController: UIViewController, UITableViewDataSource,  UITableViewDelegate, UICollectionViewDataSource, UICollectionViewDelegate {
    
        var posts = [Post]()
        var collectionViewRow = [UICollectionView : Int]()
    
        override func viewDidLoad() {
            // get posts elements here
            //posts.append(element).......
        }
    
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    
            return posts.count // 4counts
        }
    
        func numberOfSections(in tableView: UITableView) -> Int {
            return 1
        }
    
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            // let cell = DetailMutiTableViewCell()
            let cell = tableView.dequeueReusableCell(withIdentifier: "DetailMutiTableViewCell", for: indexPath) as! DetailMutiTableViewCell
    
            collectionViewRow[cell.collectionView1] = indexPath.row
    
            cell.post = posts[indexPath.row]
            cell.collectionView1.delegate = self
            cell.collectionView1.dataSource = self
    
            cell.collectionView1.reloadData()
            return cell
        }
    
        func numberOfSections(in collectionView: UICollectionView) -> Int {
            return 1
        }
    
        func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
            let row = collectionViewRow[collectionView]!
            return posts[row].imageUrls.count
        }
    
        func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MultiImagesCollectionViewCell", for: indexPath) as! MultiImagesCollectionViewCell
    
            let row = collectionViewRow[collectionView]!
            let photoUrlString = posts[row].imageUrls[indexPath.item] // ←error occured
            let photoUrl = URL(string: photoUrlString)
            cell.imageView1.sd_setImage(with: photoUrl)
    
            return cell
        }
    }
    

    Notes:

    1. The force unwrap of the dictionary lookup won't fail because the collectionView will be in there. If you want to unwrap it with if let, you'll need to decide what to do when you don't get a row (which shouldn't happen).
    2. This method could fail if your table allows editing (user can rearrange order of rows or delete rows). If you don't support editing, then this will work fine. To support editing, you could change the dictionary to [UICollectionView : UITableViewCell]. Then, given a UICollectionView, you could look up its cell, and then call let indexPath = tableView.indexPath(for: cell) to get the indexPath and your row is then just indexPath.row.