1

我在我的tableView(在MasterViewController中)實現了searchBar和searchDisplayController。 我有一個CustomCell,它適用於顯示我的tableView的單元格,但它不適用於我的搜索tableView的單元格,我不明白爲什麼。SearchBar,自定義單元格和setCell方法:致命錯誤:意外地發現零,同時展開一個可選值

這裏是我的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 

如果有人知道發生了什麼,這將是非常有益的。

非常感謝。

編輯:它不會崩潰,但我有一個空搜索的tableView如果我改變setCell方法到這一點:

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

三個參數pgrmName,noteInt和徽標名稱不是零,但pgrmLabel,noteLabel和logoView是。我猜(因爲這些是可選值),我不會以正確的方式分配值。

+0

我終於做到了另一種方式。 – 2014-10-23 14:18:00

+0

如果你想幫助別人,可能應該分享解決方案,否則這只是一個沒有答案的問題(垃圾郵件),應該刪除。 – 2014-10-26 20:51:39

回答

1

我終於做了另一種方式:我從mainstoryboard的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(這也是我創建的)將繼承它。

BaseTableViewController:

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 
} 
} 

MasterViewController:

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 
} 

} 

ResultsTableViewController

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 
} 
} 
+0

謝謝你的回答。它以許多其他解決方案沒有解決的方式解決了我的問題。我不知道爲什麼它不能正常工作,但是將我的UITableViewCell分解成它自己的NIB是有用的。 – djbp 2014-12-11 12:59:25

相關問題