UILabel 怎么可能总是 nil——在隐式展开 Optional 值时意外发现 nil

How could UILabel always be nil -- Unexpectedly found nil while implicitly unwrapping an Optional value

由于很多人遇到,我尝试构建tableView。我发现了许多类似的问题,但似乎答案无济于事。如果有人能帮助我,我将不胜感激。我遇到的问题:

Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value

This is a description Xcode gives me

这是我所做的:

(1) 我把storyboard里的Labels和它相关的class连起来了,应该是对的,不是空洞的。

(2) 我使用了 tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath),我尝试打印 cell 我得到了,所有单元格都不是 nil 并且属于 CollegeTableViewCell,这是正确。

(3)我把tableViewCell的identifier改成Cell匹配,我也把它的class改成了CollegeTableViewCell

我的程序在执行下面的代码时直接崩溃了。我只在我将标签设为可选时才工作。所以问题是我做错了什么,单元格中的标签总是零?

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! CollegeTableViewCell
    let college = colleges[indexPath.row]

    cell.collegeName.text = college.name // <-CRASH
    cell.collegeGeo.text = college.city + ", " + college.state
    return cell
}

以下是我的CollegeTableViewCellclass:

class CollegeTableViewCell: UITableViewCell {


@IBOutlet weak var collegeName: UILabel!
@IBOutlet weak var collegeGeo: UILabel!

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

}

编辑:与此问题相关的更多代码。

class CollegeChooseViewController: UIViewController {

@IBOutlet weak var searchBar: UISearchBar!
@IBOutlet weak var tableView: UITableView!

var colleges = [CollegeInfo]()
let searchController = UISearchController(searchResultsController: nil)
let collegeApiUrl = "https://api.collegeai.com/v1/api/autocomplete/colleges?api_key=b47484dd6e228ea2cc5e1bf6ca&query="

override func viewDidLoad() {
    super.viewDidLoad()
    tableView.delegate = self
    tableView.dataSource = self
    tableView.register(CollegeTableViewCell.self, forCellReuseIdentifier: "Cell")
    getColleges(contentInSearch: "MIT")
}

func getColleges(contentInSearch: String) {
    guard let url = URL(string: (collegeApiUrl + contentInSearch)) else { return }
    URLSession.shared.fetchData(for: url) {(result: Result<Initial, Error>) in
        switch result {
        case .success(let initial):
            self.colleges = initial.collegeList
            DispatchQueue.main.async {
                self.tableView.reloadData()
            }
        case .failure(let error):
            print("failed fetching college list from API: \(error)")
        }
    }
}

}

extension URLSession {
func fetchData<T: Decodable>(for url: URL, completion: @escaping (Result<T, Error>) -> Void) {
self.dataTask(with: url) { (data, response, error) in
  if let error = error {
    completion(.failure(error))
  }
  if let data = data {
    do {
      let object = try JSONDecoder().decode(T.self, from: data)
        completion(.success(object))
    } catch let decoderError {
      completion(.failure(decoderError))
    }
  }
}.resume()

} }

extension CollegeChooseViewController: UITableViewDataSource, UITableViewDelegate {

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! CollegeTableViewCell
    let college = colleges[indexPath.row]

    cell.collegeName.text = college.name // <-CRASH
    cell.collegeGeo.text = college.city + ", " + college.state
    return cell
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    print(colleges.count)
    return colleges.count
}

}

class CollegeTableViewCell: UITableViewCell {


@IBOutlet weak var collegeName: UILabel!
@IBOutlet weak var collegeGeo: UILabel!

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

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

}

您为 dequeueReusableCell 使用了错误的 bundle 名称。

而不是 cell 使用 CollegeTableViewCell

应该是:

let cell = tableView.dequeueReusableCell(withIdentifier: "CollegeTableViewCell", for: indexPath) as! CollegeTableViewCell

这是您的 tableview 编程方式的示例...如果我不知道您的数据来自何处,我使用了您的数组模拟...使您的控制器符合 UITableViewDelegate 和数据源:

class YourController: UIViewController, UITableViewDelegate, UITableViewDataSource 

现在设置 tableView 和约束

var name = ["Mike", "Jhon", "Carl", "Steve", "Elon", "Bill", "Bruce"] // simulation of your array
var city = ["Milano", "New Yor", "Paris", "Los Angeles", "Madrid", "Amsterdam", "Tokyo"] // simulation of your array
var state = ["Italia", "USA", "France", "USA", "Spain", "Holland", "Japan"] // simulation of your array

let tableView = UITableView()

override func viewDidLoad() {
    super.viewDidLoad()

    view.backgroundColor = .darkBlue
    
    tableView.backgroundColor = .white
    tableView.register(CollegeTableViewCell.self, forCellReuseIdentifier: "cellId") // register cell
    tableView.delegate = self
    tableView.dataSource = self
    tableView.translatesAutoresizingMaskIntoConstraints = false
    tableView.separatorColor = .lightGray
    
    view.addSubview(tableView)
    tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
    tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
    tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
    tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
    
    // this is my extension to configure navigation bar, you can configure it as you want
    configureNavigationBar(largeTitleColor: .red, backgoundColor: .black, tintColor: .red, title: "Sample", preferredLargeTitle: true)
}

之后设置您的 tableView 委托和数据源:

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

// Mark: - set number of rows with your array.count
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return name.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    
    let name = name[indexPath.row]
    let city = city[indexPath.row]
    let state = state[indexPath.row]
    
    let cell = tableView.dequeueReusableCell(withIdentifier: "cellId", for: indexPath) as! CollegeTableViewCell
    cell.collegeName.text = name
    cell.collegeGeo.text = "\(city), \(state)"
    
    return cell
}

这是你的手机的样子:

class CollegeTableViewCell: UITableViewCell {

let collegeName: UILabel = {
    let label = UILabel()
    label.textColor = .white
    label.font = .systemFont(ofSize: 16, weight: .semibold)
    label.backgroundColor = .clear
    label.translatesAutoresizingMaskIntoConstraints = false
    
    return label
}()

let collegeGeo: UILabel = {
    let label = UILabel()
    label.textColor = .white
    label.font = .systemFont(ofSize: 14, weight: .semibold)
    label.backgroundColor = .clear
    label.translatesAutoresizingMaskIntoConstraints = false
    
    return label
}()

override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
    super.init(style: style, reuseIdentifier: reuseIdentifier)
    
    contentView.backgroundColor = .ultraDark
    
    let stackView = UIStackView(arrangedSubviews: [collegeName, collegeGeo]) // use stack view for automatic table view dimension
    stackView.axis = .vertical
    stackView.distribution = .fillEqually
    stackView.spacing = 2
    stackView.translatesAutoresizingMaskIntoConstraints = false
    
    contentView.addSubview(stackView)
    stackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 20).isActive = true
    stackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -20).isActive = true
    stackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20).isActive = true
    stackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20).isActive = true
}

required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
 }
}

这是结果:

编辑 基于新信息完整代码和Json解码器:

struct CollegeInfo: Decodable {
let collegeList: [MyDataResults]
}

struct MyDataResults: Decodable {
let id: String
let name: String
let city: String
let state: String
}

class tableController: UIViewController, UITableViewDelegate, UITableViewDataSource {

var myData = [MyDataResults]() // simulation of your array
let urlString = "https://api.collegeai.com/v1/api/autocomplete/colleges?api_key=b47484dd6e228ea2cc5e1bf6ca&query="

let tableView = UITableView()

override func viewDidLoad() {
    super.viewDidLoad()

    view.backgroundColor = .darkBlue
    
    tableView.backgroundColor = .white
    tableView.register(CollegeTableViewCell.self, forCellReuseIdentifier: "cellId") // register cell
    tableView.delegate = self
    tableView.dataSource = self
    tableView.translatesAutoresizingMaskIntoConstraints = false
    tableView.separatorColor = .lightGray
    
    view.addSubview(tableView)
    tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
    tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
    tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
    tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
    
    // this is my extension to configure navigation bar, you can configure it as you want
    configureNavigationBar(largeTitleColor: .red, backgoundColor: .black, tintColor: .red, title: "Sample", preferredLargeTitle: true)
    
    fetchJson { [weak self] (res) in
        
        switch res {
        case .success(let dataResults):
            dataResults.forEach { (dataresult) in
                self?.myData.removeAll()
                
                DispatchQueue.main.asyncAfter(deadline: .now() + 0) {
                    self?.myData = dataresult.collegeList
                    self?.tableView.reloadData()
                }
            }
        case .failure(let err):
            print("Failed to fetch json", err)
        }
    }
}

fileprivate func fetchJson(completion: @escaping (Result<[CollegeInfo], Error >) -> ()) {
    
    guard let url = URL(string: urlString) else { return }
    URLSession.shared.dataTask(with: url) { data, resp, err in
        
        if let err = err {
            completion(.failure(err))
            return
        }
        
        do {
            guard let data = data else { return }
            let results = try JSONDecoder().decode(CollegeInfo.self, from: data)
            
            //succesful
            completion(.success([results]))
            
        } catch let jsonErr {
            completion(.failure(jsonErr))
        }
        
    }.resume()
}

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

// Mark: - set number of rows with your array.count
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return myData.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    
    let myResults = myData[indexPath.row]
    
    let cell = tableView.dequeueReusableCell(withIdentifier: "cellId", for: indexPath) as! CollegeTableViewCell
    cell.collegeName.text = myResults.name
    cell.collegeGeo.text = "\(myResults.city), \(myResults.state)"
    
    return cell
}

单元格:

class CollegeTableViewCell: UITableViewCell {

let collegeName: UILabel = {
    let label = UILabel()
    label.textColor = .white
    label.font = .systemFont(ofSize: 16, weight: .semibold)
    label.backgroundColor = .clear
    label.translatesAutoresizingMaskIntoConstraints = false
    
    return label
}()

let collegeGeo: UILabel = {
    let label = UILabel()
    label.textColor = .white
    label.font = .systemFont(ofSize: 14, weight: .semibold)
    label.backgroundColor = .clear
    label.translatesAutoresizingMaskIntoConstraints = false
    
    return label
}()

override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
    super.init(style: style, reuseIdentifier: reuseIdentifier)
    
    contentView.backgroundColor = .ultraDark
    
    let stackView = UIStackView(arrangedSubviews: [collegeName, collegeGeo]) // use stack view for automatic table view dimension
    stackView.axis = .vertical
    stackView.distribution = .fillEqually
    stackView.spacing = 2
    stackView.translatesAutoresizingMaskIntoConstraints = false
    
    contentView.addSubview(stackView)
    stackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 20).isActive = true
    stackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -20).isActive = true
    stackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20).isActive = true
    stackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20).isActive = true
}

required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
 }
}

结果: