SearchBar、自定义单元格和 setCell 方法:致命错误:在展开可选值时意外发现 nil
Posted
技术标签:
【中文标题】SearchBar、自定义单元格和 setCell 方法:致命错误:在展开可选值时意外发现 nil【英文标题】:SearchBar, Custom Cell and setCell method: fatal error: unexpectedly found nil while unwrapping an Optional value 【发布时间】:2014-10-11 10:25:05 【问题描述】:我在我的 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
另外,这是 Program 类:
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 方法更改为此,我有一个空的搜索 tableView:
self.pgrmLabel?.text=pgrmName
self.noteLabel?.text=String(noteInt)
self.logoView?.image=UIImage(named: logoName)
pgrmName、noteInt 和 logoName 三个参数不是 nil,但 pgrmLabel、noteLabel 和 logoView 是。我猜(因为这些是可选值)我没有以正确的方式分配值。
【问题讨论】:
我终于用另一种方式做到了。 如果你想帮助别人,可能应该分享解决方案,否则这只是一个没有答案的问题(垃圾邮件),应该被删除。 【参考方案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
主视图控制器:
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
【讨论】:
感谢您的回答。它以许多其他解决方案没有的方式解决了我的问题。我不知道为什么它不起作用,但是将我的 UITableViewCell 拆分为它自己的 NIB 就可以了。以上是关于SearchBar、自定义单元格和 setCell 方法:致命错误:在展开可选值时意外发现 nil的主要内容,如果未能解决你的问题,请参考以下文章
UITableView 错误的图像加载了自定义单元格和 UIView
Swift - 从自定义单元格和单元格数据传递到另一个控制器
iPhone-如何将 Storyboard 与自定义 UITableView 单元格和 CellWithIdentifier 一起使用