You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift4.0中用SegmentControl实现双TableView遇可选值解包崩溃问题

Fixing "Unexpectedly found nil while unwrapping an Optional value" in Your Dual TableView + SegmentControl Setup

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:

  1. Open your Storyboard and select your DemoTableViewController.
  2. Go to the Connections Inspector (right-hand panel, icon looks like a circle with an arrow).
  3. Verify that tableOne, tableTwo, and segmentDemo all 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 (OneTableViewCell and TwoTableViewCell) have their UILabel outlets properly connected in the Storyboard prototype cells.
  • If using prototype cells, confirm the cell's class is set to OneTableViewCell/TwoTableViewCell in the Identity Inspector.

内容的提问来源于stack exchange,提问作者Audrey Rozario

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 10:01:57