0

I would like to show table view with cell where will be one label and tableview below this label. I have such layout right now:

enter image description here

view above my tables view will be hidden in some conditions so as a result I will have table view on whole screen. So... I found this video where developer managed to solve my task. I did everything similarly to his video but I didn't manage to show table view inside table view cell. Here is my steps:

  1. Added all views to general view.
  2. Attached tags to my table views: 100 for main table view and 90 for inside table view.
  3. Created cell classes and attached them to both cells.
  4. Added extension at main cell like in video.
  5. Handled table view tag in main view controller.

As I see the problem is with inside table view which can't be shown. Below you can see classes for cells. Main cell:

class GeneralCell : UITableViewCell {
    @IBOutlet weak var answerOptions: UITableView!
    @IBOutlet weak var questionText: UILabel!
}

extension GeneralCell{
    func setTableViewDataSourceDelegate <D:UITableViewDelegate & UITableViewDataSource> (_ dataSourceDelegate: D, forRow row: Int)
    {
        answerOptions.delegate = dataSourceDelegate
        answerOptions.dataSource = dataSourceDelegate
        
        answerOptions.reloadData()
    }
}

inside cell:

class AnswersCell: UITableViewCell {
    @IBOutlet weak var answerOption: UILabel!
}

and here is my view controller:

class PollsController: UIViewController, UITableViewDataSource,UITableViewDelegate {

    @IBOutlet weak var questionTableView: UITableView!
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        
        questionTableView.delegate = self
        questionTableView.dataSource = self
        
    }
    

    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
         return 1
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        print(tableView.tag)
        if tableView.tag == 100 {
            let cell: GeneralCell = tableView.dequeueReusableCell(withIdentifier: "GeneralCell") as! GeneralCell
            cell.answerOptions.reloadData()
            return cell
        }else{
            let cell: AnswersCell = tableView.dequeueReusableCell(withIdentifier: "AnswersCell") as! AnswersCell
            return cell
        }
    }
    
    func numberOfSections(in tableView: UITableView) -> Int {
        if tableView.tag == 100 {
            return 1
        }else{
            return 6
        }
    }
}

Then I did some test for getting to know where the problem is. I added print() into data loader function:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        print(tableView.tag)
        if tableView.tag == 100 {
            print("1")
            let cell: GeneralCell = tableView.dequeueReusableCell(withIdentifier: "GeneralCell") as! GeneralCell
            cell.answerOptions.reloadData()
            return cell
        }else{
            print("2")
            
            let cell: AnswersCell = tableView.dequeueReusableCell(withIdentifier: "AnswersCell") as! AnswersCell
            return cell
        }
    }

and in the output I saw only one tag:

100

and as I see inside table view can't be loaded but I can't understand why. Maybe someone of you will find problem or mistake?

Andrew
  • 1,947
  • 2
  • 23
  • 61
  • 1
    why u need tableview inside tableview cell? – aiwiguna Jul 29 '20 at 07:25
  • @aiwiguna, because I will put here some poll, and inner tableview will represent answer options, why are you asking? – Andrew Jul 29 '20 at 07:27
  • 1
    because that approach is not recommended it will create a new problem with scrolling behavior later, I think that kind of UI can be achieved with only 1 tableview, just use section for the question and cell for answer – aiwiguna Jul 29 '20 at 07:32
  • @aiwiguna, I agree with your opinion but I can't imagine how to reach my target with one tableview :( what if I will have more than one question in general tableview with their answers? If if have one question and some answers I agree that one table view will solve my problem, but what if I will have more that 1 question? – Andrew Jul 29 '20 at 07:34
  • setTableViewDataSourceDelegate is not called in your current code – Nikunj Kumbhani Jul 29 '20 at 07:36
  • @NikunjKumbhani, where I have to set it? it will solve my problem? – Andrew Jul 29 '20 at 07:37
  • @Andrew Please check my answer – Nikunj Kumbhani Jul 29 '20 at 08:24

2 Answers2

2

I suggest you use only one tableview, here some example

struct Answer {
    let answerText: String
}

struct Question {
    let questionText: String
    let answers: [Answer]
}

class ViewController: UIViewController {
    
    @IBOutlet weak var tableView: UITableView!
    var questions: [Question] = []
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        for i in 1...10 {
            let question = Question(questionText: "Question \(i)", answers: [
                Answer(answerText: "Answer 1 Question \(i)"),
                Answer(answerText: "Answer 2 Question \(i)"),
                Answer(answerText: "Answer 3 Question \(i)"),
                Answer(answerText: "Answer 4 Question \(i)")
            ])
            questions.append(question)
        }
        tableView.dataSource = self
        tableView.delegate = self
        tableView.sectionHeaderHeight = UITableView.automaticDimension;
        tableView.estimatedSectionHeaderHeight = 44
    }
    
    
}

extension ViewController: UITableViewDataSource, UITableViewDelegate {
    func numberOfSections(in tableView: UITableView) -> Int {
        return questions.count
    }
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return questions[section].answers.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "AnswerCell", for: indexPath) as! AnswerCell
        
        cell.answerLabel.text = questions[indexPath.section].answers[indexPath.row].answerText
        return cell
    }
    
    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        let cell = tableView.dequeueReusableCell(withIdentifier: "QuestionCell") as! QuestionCell
        
        cell.questionLabel.text = questions[section].questionText
        return cell
    }
}

enter image description here

aiwiguna
  • 2,852
  • 2
  • 15
  • 26
  • so we will use two cells in your example? I don't understand how I have to attach this cells in storyboard – Andrew Jul 29 '20 at 08:14
  • you can change the cell with what you have right now without the tableview, it only 1 label in each cell – aiwiguna Jul 29 '20 at 08:23
  • in general I will need some cells for table view row, but I can't understand how it can be assigned to storyboard, of where it was presented – Andrew Jul 29 '20 at 08:24
  • @Andrew You need to use two prototype cells in tableView One for header Question and Other for answer – Nikunj Kumbhani Jul 29 '20 at 08:26
  • @Andrew check this git https://github.com/aiwiguna/ExampleTableView/tree/feature/section-header – aiwiguna Jul 29 '20 at 08:29
  • @aiwiguna, when I set two prototype cells I got this error - `Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key questionText.'` but I have this key in my cell class and I also added two prototype cells to the table view, maybe I did some mistake again? – Andrew Jul 29 '20 at 08:45
  • check you iboutlet connection in the storyboard https://stackoverflow.com/questions/24523086/xcode-interface-builder-correct-way-to-delete-rename-miswired-iboutlets-ib/24523200 – aiwiguna Jul 29 '20 at 08:47
  • @yes, you are right )) I have to outlets in storyboard :) thank you for your help, your solution is much better than mine with nested tableviews :) – Andrew Jul 29 '20 at 08:50
1

Try this

class GeneralCell : UITableViewCell {
    @IBOutlet weak var answerOptions: UITableView!
    @IBOutlet weak var questionText: UILabel!
}



func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        print(tableView.tag)
        if tableView == questionTableView{
            let cell: GeneralCell = tableView.dequeueReusableCell(withIdentifier: "GeneralCell") as! GeneralCell
            cell.answerOptions.delegate = self
            cell.answerOptions.dataSource = self
            cell.answerOptions.reloadData()
            return cell
        }else{
            let cell: AnswersCell = tableView.dequeueReusableCell(withIdentifier: "AnswersCell") as! AnswersCell
            return cell
        }
    }
Nikunj Kumbhani
  • 3,758
  • 2
  • 26
  • 51