Swift4.0中用SegmentControl实现双TableView遇可选值解包崩溃问题
Hey there, let's break down why you're hitting that fatal nil unwrap error in your Swift 4.0 project. After going through your code, there are a few critical issues that are almost certainly causing this crash—let's fix them step by step.
Common Causes & Solutions
1. Incorrect TableView Identification in DataSource Methods
Right now, you're using tableOne.isHidden to decide which data and cell type to use, but this approach is unreliable. The tableView parameter passed into numberOfRowsInSection and cellForRowAt tells you exactly which table is requesting data—you should use that instead. Relying on hidden state can lead to mismatches (like if both tables are hidden temporarily) that force a nil unwrap when the wrong cell type is dequeued.
Fix:
Replace your data source methods with these:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { if tableView == tableOne { return arrayOne.count } else if tableView == tableTwo { return arrayTwo.count } return 0 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { if tableView == tableOne { guard let cell = tableView.dequeueReusableCell(withIdentifier: "One") as? OneTableViewCell else { // Fallback to default cell if dequeue fails return UITableViewCell(style: .default, reuseIdentifier: "FallbackOne") } cell.lblOne.text = arrayOne[indexPath.row].one return cell } else { guard let tcell = tableView.dequeueReusableCell(withIdentifier: "Two") as? TwoTableViewCell else { return UITableViewCell(style: .default, reuseIdentifier: "FallbackTwo") } tcell.labeltwo.text = arrayTwo[indexPath.row].two return tcell } }
We swapped force unwrapping (as!) for guard let here—it prevents crashes if the cell can't be dequeued and gives you a safe fallback.
2. Missing Cell Registration
Since you're not using XIB cells, you need to register your custom cell classes with each table view. Skip this step, and dequeueReusableCell will return nil, triggering that fatal error when you try to unwrap it.
Fix:
Add these lines to your viewDidLoad method:
// Register custom cells for each table view tableOne.register(OneTableViewCell.self, forCellReuseIdentifier: "One") tableTwo.register(TwoTableViewCell.self, forCellReuseIdentifier: "Two")
If you're using Storyboard prototype cells instead of code-based cells, double-check that the reuse identifier in the Storyboard matches exactly what you're using in code ("One" and "Two")—even a tiny typo here will cause nil.
3. Unconnected IBOutlets
Another common culprit is unconnected IBOutlets. If tableOne, tableTwo, or segmentDemo aren't properly linked from your Storyboard to your ViewController code, they'll be nil when you try to access them (like setting delegate or isHidden).
Fix:
- Open your Storyboard and select your
DemoTableViewController. - Go to the Connections Inspector (right-hand panel, icon looks like a circle with an arrow).
- Verify that
tableOne,tableTwo, andsegmentDemoall have active connections to their respective UI elements. If any show a "!" or are empty, re-drag the connection from the outlet in your code to the UI element.
Modified Full ViewController Code
Here's your updated ViewController with all fixes applied:
import UIKit class DemoTableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { struct structOne { let cell : Int let one : String } struct structTwo { let cell : Int let two : String } var arrayOne = [structOne]() var arrayTwo = [structTwo]() @IBOutlet weak var tableOne: UITableView! @IBOutlet weak var segmentDemo: UISegmentedControl! @IBOutlet weak var tableTwo: UITableView! override func viewDidLoad() { super.viewDidLoad() arrayOne = [structOne(cell: 0, one: "One"), structOne(cell: 1, one: "One"), structOne(cell: 2, one: "One")] arrayTwo = [structTwo(cell: 0, two: "Two"), structTwo(cell: 1, two: "Two"), structTwo(cell: 2, two: "Two")] // Register custom cells tableOne.register(OneTableViewCell.self, forCellReuseIdentifier: "One") tableTwo.register(TwoTableViewCell.self, forCellReuseIdentifier: "Two") self.tableOne.delegate = self self.tableOne.dataSource = self self.tableTwo.delegate = self self.tableTwo.dataSource = self self.tableTwo.isHidden = true self.tableOne.isHidden = false self.tableTwo.reloadData() self.tableOne.reloadData() self.tableTwo.tableFooterView = UIView() self.tableOne.tableFooterView = UIView() } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } @IBAction func segmentAction(_ sender: Any) { switch self.segmentDemo.selectedSegmentIndex { case 0: self.tableTwo.isHidden = true self.tableOne.isHidden = false self.tableOne.reloadData() case 1: self.tableTwo.isHidden = false self.tableOne.isHidden = true self.tableTwo.reloadData() default: break } } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { if tableView == tableOne { return arrayOne.count } else if tableView == tableTwo { return arrayTwo.count } return 0 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { if tableView == tableOne { guard let cell = tableView.dequeueReusableCell(withIdentifier: "One") as? OneTableViewCell else { return UITableViewCell(style: .default, reuseIdentifier: "FallbackOne") } cell.lblOne.text = arrayOne[indexPath.row].one return cell } else { guard let tcell = tableView.dequeueReusableCell(withIdentifier: "Two") as? TwoTableViewCell else { return UITableViewCell(style: .default, reuseIdentifier: "FallbackTwo") } tcell.labeltwo.text = arrayTwo[indexPath.row].two return tcell } } }
Final Checks
- Make sure your custom cell classes (
OneTableViewCellandTwoTableViewCell) have theirUILabeloutlets properly connected in the Storyboard prototype cells. - If using prototype cells, confirm the cell's class is set to
OneTableViewCell/TwoTableViewCellin the Identity Inspector.
内容的提问来源于stack exchange,提问作者Audrey Rozario

