uitableview - SearchBar、Custom Cell 和 setCell 方法 : fatal error: unexpectedly found nil while unwrapping an Optional value

标签 uitableview swift uisearchbar custom-cell uisearchbardisplaycontrol

我在我的 tableView 上(在 MasterViewController 中)实现了 searchBar 和 searchDisplayController。 我有一个 CustomCell,它可以很好地显示我的 tableView 的单元格,但它不适用于我的搜索 tableView 的单元格,我不明白为什么。

这是我的 CustomCell 类:

import UIKit

class CustomCell: UITableViewCell {

@IBOutlet weak var pgrmLabel: UILabel!
@IBOutlet weak var noteLabel: UILabel!
@IBOutlet weak var logoView: UIImageView!


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
}

func setCell(pgrmName:String, noteInt:Int, logoName:String){
    println("setCell")
    if let itemName=self.pgrmLabel{
        itemName.text=pgrmName
        println("pgrmName: \(pgrmName)")
    }
    if let itemNote=self.noteLabel{
        itemNote.text=String(noteInt)
    }
    if let itemLogo=self.logoView{
        itemLogo.image=UIImage(named: logoName)
    }
    else{
        println("setCell ELSE")
        self.pgrmLabel.text=pgrmName as String
        self.noteLabel.text=String(noteInt)
        self.logoView.image=UIImage(named: logoName as String)
    }   
}
}

以及 masterViewController 中的 cellForRowAtIndexPath 方法:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

var program:Program
var cell: CustomCell!=tableView.dequeueReusableCellWithIdentifier("Cell") as CustomCell!

    if(cell==nil){
        println("cell nil")
        tableView.registerClass(CustomCell.classForCoder(), forCellReuseIdentifier: "Cell")
        cell=CustomCell(style: UITableViewCellStyle.Default, reuseIdentifier: "Cell")
    }
    else{
    if tableView == searchDisplayController?.searchResultsTableView{
        println("SearchCell")
        program=filteredPrograms[indexPath.row]
    }
    else{
        println("MasterCell")
        program=arrayOfPrograms[indexPath.row]
    }
    cell.setCell(program.name as String, noteInt: program.note, logoName: program.imageName as String)
    }
    return cell
}

此外,这是程序类:

import Foundation

class Program{
    var name:String
    var note:Int
    var imageName:String
    var channel:String

init(name:String, note:Int, imageName:String, channel:String){
    self.name=name
    self.note=note
    self.imageName=imageName
    self.channel=channel
}
}

当我在 setCell 方法的 searchBar 中键入一个字母时,应用程序崩溃了。它打印:

cell nil
SearchCell
setCell
setCell ELSE
fatal error: unexpectedly found nil while unwrapping an Optional value

如果有人知道发生了什么,那将非常有帮助。

非常感谢。

编辑:它不会崩溃,但如果我将 setCell 方法更改为:

self.pgrmLabel?.text=pgrmName
self.noteLabel?.text=String(noteInt)
self.logoView?.image=UIImage(named: logoName)

pgrmName、noteInt 和 logoName 这三个参数不是 nil,但是 pgrmLabel、noteLabel 和 logoView 是。我猜(因为这些是可选值)我没有以正确的方式分配值。

最佳答案

我最终采用了另一种方式:我从主 Storyboard的 TableView 中删除了单元格,并创建了一个新文件(带有 xib 文件)来创建我的自定义单元格。

这是我的自定义类单元格的代码:

class TableCell: UITableViewCell {

@IBOutlet weak var pgrmLabel: UILabel!
@IBOutlet weak var noteLabel: UILabel!
@IBOutlet weak var logoView: UIImageView!

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
}

func setCell(cell: TableCell, pgrmName:String, noteInt:Int, logoName:String){
    if let itemName=self.pgrmLabel{
        itemName.text=pgrmName
    }
    if let itemNote=self.noteLabel{
        itemNote.text="\(noteInt)"
    }
    if let itemLogo=self.logoView{
        itemLogo.image=UIImage(named: logoName)
        if ((itemLogo.image) == nil){
            itemLogo.image=UIImage(named: "placeholder.jpg")
        }
    }
  }
}

我还创建了一个 BaseTableViewController 类,我的 MasterViewController(主 TableView )和我的 ResultsTableViewController(我也创建了)将从它继承。

基 TableView Controller :

class BaseTableViewController: UITableViewController {
// MARK: Types

struct Constants {
    struct Nib {
        static let name = "TableCell"
    }

    struct TableViewCell {
        static let identifier = "cellID"
    }
}

// MARK: View Life Cycle

override func viewDidLoad() {
    super.viewDidLoad()

    let nib = UINib(nibName: Constants.Nib.name, bundle: nil)

    // Required if our subclasses are to use: dequeueReusableCellWithIdentifier:forIndexPath:
    tableView.registerNib(nib, forCellReuseIdentifier: Constants.TableViewCell.identifier)
}


// MARK:

func configureCell(cell: TableCell, forProgram program: Program) {
    cell.setCell(cell, pgrmName: program.name, noteInt: program.note, logoName: program.imageName)
}

override func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    return 100
}

override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    return 100
}
}

主视图 Controller :

class MasterViewController: BaseTableViewController, UITableViewDataSource, UITableViewDelegate, UISearchBarDelegate, UISearchDisplayDelegate, UISearchResultsUpdating {

struct RestorationKeys {
    static let viewControllerTitle = "ViewControllerTitleKey"
    static let searchControllerIsActive = "SearchControllerIsActiveKey"
    static let searchBarText = "SearchBarTextKey"
    static let searchBarIsFirstResponder = "SearchBarIsFirstResponderKey"
}

//controllers
var detailViewController: DetailViewController? = nil
var searchController: UISearchController!
var resultsTableController: ResultsTableViewController!
var restoredState=SearchControllerRestorableState()

//array of programs to display in the tableview
var arrayOfPrograms=[Program]()

struct SearchControllerRestorableState {
    var wasActive=false
    var wasFirstResponder=false
}

override func awakeFromNib() {
    super.awakeFromNib()
    if UIDevice.currentDevice().userInterfaceIdiom == .Pad {
        self.clearsSelectionOnViewWillAppear = false
        self.preferredContentSize = CGSize(width: 320.0, height: 600.0)
    }
}

override func viewDidLoad() {
    super.viewDidLoad()
    //self.navigationItem.leftBarButtonItem = self.editButtonItem()
    if let split = self.splitViewController {
        let controllers = split.viewControllers
        self.detailViewController = controllers[controllers.count-1].topViewController as? DetailViewController
    }
    self.setUpPrograms()

    let tableViewController=navigationController?.viewControllers[0] as MasterViewController

    resultsTableController=ResultsTableViewController()
    resultsTableController.tableView.delegate=self

    searchController=UISearchController(searchResultsController: resultsTableController)
    searchController.searchResultsUpdater=self
    searchController.searchBar.sizeToFit()
    tableView.tableHeaderView=searchController.searchBar
    searchController.delegate=self
    searchController.dimsBackgroundDuringPresentation=false
    searchController.searchBar.delegate=self
    definesPresentationContext=true

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)

    // Restore the searchController's active state.
    if restoredState.wasActive {
        searchController.active = restoredState.wasActive
        restoredState.wasActive = false

        if restoredState.wasFirstResponder {
            searchController.searchBar.becomeFirstResponder()
            restoredState.wasFirstResponder = false
        }
    }
}

func searchBarSearchButtonClicked(searchBar: UISearchBar) {
    searchBar.resignFirstResponder()
}

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    var selectedProgram:Program

    if tableView == self.tableView {
        selectedProgram=arrayOfPrograms[indexPath.row]
    }
    else{
        selectedProgram=resultsTableController.filteredPrograms[indexPath.row]
    }

    let detailViewController=DetailViewController.forProgram(selectedProgram)
    navigationController!.pushViewController(detailViewController, animated: true)
    tableView.deselectRowAtIndexPath(indexPath, animated: true)
}

// MARK: - Table View

override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    return 1
}

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return arrayOfPrograms.count
}



func updateSearchResultsForSearchController(searchController: UISearchController) {
    let searchResults=arrayOfPrograms

    // Strip out all the leading and trailing spaces.
    let whitespaceCharacterSet = NSCharacterSet.whitespaceCharacterSet()
    let strippedString = searchController.searchBar.text.stringByTrimmingCharactersInSet(whitespaceCharacterSet)
    let searchItems = strippedString.componentsSeparatedByString(" ") as [String]

    // Build all the "AND" expressions for each value in the searchString.
    var andMatchPredicates = [NSPredicate]()

    for searchString in searchItems {
        // Each searchString creates an OR predicate for: name, yearIntroduced, introPrice.
        //
        // Example if searchItems contains "iphone 599 2007":
        //      name CONTAINS[c] "iphone"
        //      name CONTAINS[c] "599", yearIntroduced ==[c] 599, introPrice ==[c] 599
        //      name CONTAINS[c] "2007", yearIntroduced ==[c] 2007, introPrice ==[c] 2007
        //
        var searchItemsPredicate = [NSPredicate]()

        // Name field matching.
        var lhs = NSExpression(forKeyPath: "name")
        var rhs = NSExpression(forConstantValue: searchString)
        var finalPredicate = NSComparisonPredicate(leftExpression: lhs, rightExpression: rhs, modifier: .DirectPredicateModifier, type: .ContainsPredicateOperatorType, options: .CaseInsensitivePredicateOption)
        searchItemsPredicate.append(finalPredicate)

        // Channel field matching.
        lhs = NSExpression(forKeyPath: "channel")
        rhs = NSExpression(forConstantValue: searchString)
        finalPredicate = NSComparisonPredicate(leftExpression: lhs, rightExpression: rhs, modifier: .DirectPredicateModifier, type: .ContainsPredicateOperatorType, options: .CaseInsensitivePredicateOption)
        searchItemsPredicate.append(finalPredicate)

        // Add this OR predicate to our master AND predicate.
        let orMatchPredicates = NSCompoundPredicate.orPredicateWithSubpredicates(searchItemsPredicate)
        andMatchPredicates.append(orMatchPredicates)
    }

    // Match up the fields of the Product object.
    let finalCompoundPredicate = NSCompoundPredicate.andPredicateWithSubpredicates(andMatchPredicates)

    let filteredResults = searchResults.filter { finalCompoundPredicate.evaluateWithObject($0) }

    // Hand over the filtered results to our search results table.
    let resultsController = searchController.searchResultsController as ResultsTableViewController
    resultsController.filteredPrograms = filteredResults
    resultsController.tableView.reloadData()
}

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier(Constants.TableViewCell.identifier, forIndexPath: indexPath) as TableCell
    let program=arrayOfPrograms[indexPath.row]
    configureCell(cell, forProgram: program)
    return cell
}

override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
    // Return false if you do not want the specified item to be editable.
    return false 
}

override func encodeRestorableStateWithCoder(coder: NSCoder) {
    super.encodeRestorableStateWithCoder(coder)

    // Encode the title.
    coder.encodeObject(navigationItem.title!, forKey:RestorationKeys.viewControllerTitle)

    // Encode the search controller's active state.
    coder.encodeBool(searchController.active, forKey:RestorationKeys.searchControllerIsActive)

    // Encode the first responser status.
    coder.encodeBool(searchController.searchBar.isFirstResponder(), forKey:RestorationKeys.searchBarIsFirstResponder)

    // Encode the search bar text.
    coder.encodeObject(searchController.searchBar.text, forKey:RestorationKeys.searchBarText)
}

override func decodeRestorableStateWithCoder(coder: NSCoder) {
    super.decodeRestorableStateWithCoder(coder)

    // Restore the title.
    if let decodedTitle = coder.decodeObjectForKey(RestorationKeys.viewControllerTitle) as? String {
        title = decodedTitle
    }
    else {
        fatalError("A title did not exist. In your app, handle this gracefully.")
    }

    // Restore the active state:
    // We can't make the searchController active here since it's not part of the view
    // hierarchy yet, instead we do it in viewWillAppear.
    //
    restoredState.wasActive = coder.decodeBoolForKey(RestorationKeys.searchControllerIsActive)

    // Restore the first responder status:
    // Like above, we can't make the searchController first responder here since it's not part of the view
    // hierarchy yet, instead we do it in viewWillAppear.
    //
    restoredState.wasFirstResponder = coder.decodeBoolForKey(RestorationKeys.searchBarIsFirstResponder)

    // Restore the text in the search field.
    searchController.searchBar.text = coder.decodeObjectForKey(RestorationKeys.searchBarText) as String
}

}

结果 TableView Controller

class ResultsTableViewController: BaseTableViewController {

var filteredPrograms=[Program]()

override func viewDidLoad() {
    super.viewDidLoad()
    println("results table view")
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    println("results table view nb of raws in section")
    return filteredPrograms.count
}

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    //let cell=tableView.dequeueReusableCellWithIdentifier(Constants.TableViewCell.identifier) as TableCell!
    let cell = tableView.dequeueReusableCellWithIdentifier(Constants.TableViewCell.identifier, forIndexPath: indexPath) as TableCell
    let program = filteredPrograms[indexPath.row]
    configureCell(cell, forProgram: program)
    return cell
}
}

关于uitableview - SearchBar、Custom Cell 和 setCell 方法 : fatal error: unexpectedly found nil while unwrapping an Optional value,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26313631/

相关文章:

ios - 在 iOS 11 上,导航项中的搜索栏在导航弹出时折叠并卡在状态栏下

ios - 使用 NSLayoutConstraint constraintWithVisualFormat 在基本 UITableVewCell 中设置 ImageView

ios - 将事件传递给 UITableView 之外的 UITableViewCell

ios - 无法快速将图像加载到单元格

ios - 如何在相机拍摄后将图像保存到应用程序的内存中

ios - "performSegueWithIdentifier"给出 fatal error

ios - 我们可以在 Swift 中通过扩展添加静态存储变量吗?

iOS 11 自定义导航栏中的搜索栏

ios - 使用搜索栏在结果的 UITableView 上显示原始 UITableView

ios - 我想在 ios 的 tableview 上从电话中获取联系人列表?