2

I want to display a UITableView inside a UIViewController. This View Controller contains a UISegmentedControl with two screens (FirstViewControllerand SecondViewController).

The first View Controller is the one that contains the UIViewTable (please don't mind the second).

When I execute the app in the simulator, everything works fine, but when I try to scroll the table view in the first ViewController, the cells disappear. The only way to make them reappear is to kill the app and reopen it again.

I'm new to iOS development (I come from Android), and I'm obviously missing something here.

I already tried adding a UIViewTable outside a container UIView and it works fine. So I'm guessing the problem has to do with the container or the segmented control...

Here's my implementation:

  • Storyboard

storyboard config UIViewController with UISegmentedControl and UIView (which will contain the two screens of the segmented control).

  • View Controller

    @IBOutlet weak var container: UIView!
    var sectionViews:[UIView]!
    
    override func viewDidLoad() {
         super.viewDidLoad()
    
         sectionViews = [UIView]()
         sectionViews.append(FirstViewController().view)
         sectionViews.append(SecondViewController().view)
         for v in sectionViews {
            container.addSubview(v)
         }
         container.bringSubviewToFront(sectionViews[0])
    }
    
    @IBAction func switchViewsAction(_ sender: UISegmentedControl) {
         self.container.bringSubviewToFront(self.sectionViews[sender.selectedSegmentIndex])
    }
    
  • First View Controller

The FirstViewController has a swift and a xib files, and has two files Cell.swift and Cell.xib for the table cell.

FirstViewController.swift

@IBOutlet weak var tableView: UITableView!
let cellID = "CellId"

override func viewDidLoad() {
    super.viewDidLoad()

    self.tableView.register(UINib(nibName: "Cell", bundle: nil), forCellReuseIdentifier: cellID)
    self.tableView.dataSource = self
    self.tableView.delegate = self
}

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

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = self.tableView.dequeueReusableCell(withIdentifier: cellID, for: indexPath) as! Cell
    cell.label.text = "\(indexPath.row)"
    return cell
}

FirstViewController.xib

enter image description here

Cell.xib

enter image description here

Any help will be appreciated! Thanks

Youb
  • 93
  • 1
  • 10

2 Answers2

2

One obvious problem is that you are saying container.addSubview(v) without giving v any frame or constraints. Since you use autolayout to position container, you ought to use autolayout to position v as well. You should set its top, bottom, leading, and trailing anchors to equal those of container with a constant of zero. (And set its translates... to false.) Do that for both cases of v in the loop.

However, there is much more serious problem, which is that the view controllers that you create by saying FirstViewController() and SecondViewController() are not retained. Therefore they vanish in a puff of smoke. They thus lose their functionality; for example, the table view no longer has any data source or delegate so it has no cells.

What you are doing is totally illegal. You cannot simply use a view controller to "dumpster-dive" as a way of grabbing its view and shove its view, willy-nilly, into the interface. You must make the view controller a child view controller of your parent view controller (Item in this case). There is an elaborate dance you must do in order to ensure that the child view controller has its proper place in the view controller hierarchy and receives in good order all the messages that a view controller must receive, and you are not doing the dance.

For examples of how to do the dance, see for instance my answers

and

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • 1
    Despite those mistakes in your approach, I would like to congratulate you on an extremely well-asked question. You exposed, quite brilliantly, everything that was necessary in order to understand thoroughly what you were doing. That's quite difficult to do. – matt Mar 04 '19 at 02:15
  • 1
    One further comment: I cannot recommend holding on to two view controllers and their views when the user is only ever going to see one view controller's view at a time. There is no need to reinvent this wheel. You would be better off using something like a tab bar controller or even better a page view controller which is made for this kind of thing. – matt Mar 04 '19 at 02:24
  • Thank you Matt. I'll try your recommendations! It's reassuring to know that the question has enough details, I've been worried that it was too long... – Youb Mar 05 '19 at 18:52
0
import UIKit

class TestViewController: UIViewController , UITableViewDataSource, UITableViewDelegate{

    @IBOutlet weak var segmentControlOutlet: UISegmentedControl!
    @IBOutlet weak var tableView: UITableView!
    var arrayName = ["Label1", "Label2", "Label3","Label4","Label5","Label6","Label7","Label8","Label9","Label10"]
    var arrayName2 =  ["Label1", "Label2", "Label3","Label4","Label5"]

    override func viewDidLoad() {
        super.viewDidLoad()

    }

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

    @IBAction func segmentControlAction(_ sender: UISegmentedControl) {
        self.tableView.reloadData()
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if segmentControlOutlet.selectedSegmentIndex == 0 {
            return arrayName.count

        }else{
            return arrayName2.count

        }
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "TestTableViewCell", for: indexPath) as! TestTableViewCell
        if segmentControlOutlet.selectedSegmentIndex == 0 {
            cell.textLabel?.text = arrayName[indexPath.row]

        }else{
             cell.textLabel?.text = arrayName2[indexPath.row]
        }
        return cell
    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 50
    }
}

And this code is for UITableViewCell Class:-

import UIKit

class TestTableViewCell: UITableViewCell {
    @IBOutlet weak var labelName: UILabel!

    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)

        // Configure the view for the selected state
    }

}

my table view

Andrew
  • 26,706
  • 9
  • 85
  • 101
Deviyani Swami
  • 749
  • 8
  • 17
  • Thank you for your answer, but it does not use different view controllers for the two sections of the segmented control, only two lists. – Youb Mar 05 '19 at 18:53
  • On Segment Action you can do like this for move to next viewController: if segmentControlOutlet.selectedSegmentIndex == 0 { let vc = self.storyboard?.instantiateViewController(withIdentifier: "AViewController") as! AViewController self.navigationController?.pushViewController(vc, animated: true) }else{ let vc = self.storyboard?.instantiateViewController(withIdentifier: "BViewController") as! BViewController self.navigationController?.pushViewController(vc, animated: true) } – Deviyani Swami Mar 06 '19 at 10:09