I am making a custom UIViewController that I want to show a message when a UITableViewCell is tapped. I create a UITableView and set the property
tableView.allowsSelectionDuringEditing = true
Even though that property has been set to true, tableView(:didSelectRowAtIndexPath) is not being called to display the message. Why is this and how do I fix it?
Here is my UITableViewController:
class GameListViewController: UIViewController, UITableViewDataSource, GameListViewDelegate, UITableViewDelegate
{
private var _games: [GameObject] = []
var tableView: UITableView {return (view as GameListView).tableView}
var gameListView: GameListView {return (view as GameListView) }
override func loadView()
{
view = GameListView(frame: UIScreen.mainScreen().bounds)
gameListView.delegate = self
}
override func viewDidLoad()
{
super.viewDidLoad()
tableView.dataSource = self
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return _games.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
var index: Int = indexPath.row
let currentGame = _games[index]
var cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: nil)
cell.textLabel?.lineBreakMode = NSLineBreakMode.ByCharWrapping
cell.textLabel?.numberOfLines = 2
if currentGame.isFinished == true
{
cell.imageView?.image = UIImage(named: "Finished.png")
cell.textLabel?.text = "Winner: Player\(currentGame.playerMakingMove)\nMissiles Launched: \(currentGame.missileCount)"
}
else
{
cell.imageView?.image = UIImage(named: "Resume.png")
cell.textLabel?.text = "Turn: Player\(currentGame.playerMakingMove)\nMissiles Launched: \(currentGame.missileCount)"
}
return cell
}
/*Handles deleting the cells*/
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath)
{
if (editingStyle == UITableViewCellEditingStyle.Delete)
{
var index: Int = indexPath.row
_games.removeAtIndex(index)
tableView.deleteRowsAtIndexPaths(NSArray(array: [indexPath]), withRowAnimation: UITableViewRowAnimation.Left)
}
tableView.reloadData()
}
/* Performs when a cell is touched */
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
{
var index: Int = indexPath.row
tableView.selectRowAtIndexPath(indexPath, animated: true, scrollPosition: UITableViewScrollPosition.None)
println("inside tableView: didSelectRowAtIndexPath")
var message = UIAlertView(title: "Row selected", message: "You have selected a row", delegate: nil, cancelButtonTitle: "Click ME!", otherButtonTitles: "no other buttons")
message.show()
}
func makeNewGame()
{
addGames(1)
tableView.reloadData()
}
func addGames(gameNumber: Int)
{
var p1 = Player()
var p2 = Player()
for var i = 0; i < gameNumber; i++
{
var randBool = Bool( round(drand48())) // True/False
var randPlayer:Int = Int( round( (drand48() + 1)) ) // 1 or 2
var randMissleCount:Int = Int( arc4random_uniform(5000) + 1 ) //1 - 5001
_games.append(GameObject(isFinished: randBool, playerMakingMove: randPlayer, missileCount: randMissleCount, player1: p1, player2: p2))
}
}
}
Here is my UIView that contains the UITableView:
protocol GameListViewDelegate: class
{
func makeNewGame()
}
class GameListView: UIView, PictureButtonDelegate
{
var newGameButton: PictureButton!
var tableView: UITableView!
weak var delegate: GameListViewDelegate? = nil
override init(frame: CGRect)
{
super.init(frame: frame)
newGameButton = PictureButton(frame: CGRect(), fileName: "NewGame.png")
newGameButton.backgroundColor = UIColor.grayColor()
newGameButton.delegate = self
newGameButton.setTranslatesAutoresizingMaskIntoConstraints(false)
tableView = UITableView()
tableView.allowsSelectionDuringEditing = true
tableView.setTranslatesAutoresizingMaskIntoConstraints(false)
self.addSubview(newGameButton)
self.addSubview(tableView)
}
override func layoutSubviews()
{
let views: [String : UIView] = ["button": newGameButton, "tableView": tableView]
addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-[button]-|", options: .allZeros, metrics: nil, views: views))
addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-[tableView]-|", options: .allZeros, metrics: nil, views: views))
addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-(>=15,<=25)-[button]-[tableView]-|", options: .allZeros, metrics: nil, views: views))
}
func buttonPushed()
{
delegate?.makeNewGame()
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
You have to set a delegate for the UITableView and implement func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath).
You only set the dataSource, but never the delegate (only a GameListViewDelegate for GameListView).
Change your viewDidLoad function in GameListViewController to:
override func viewDidLoad()
{
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
}
Related
How to update the tableview after adding the object .
I am doing in the mvvm ,
So
in cartviewmodel:-
var datasourceModel: DataSourceModel
var insertedArray: Model?
var finalArray: Array<Model>? = []
func add() {
datasourceModel.dataListArray?.append(insertedArray!)
print(datasourceModel.dataListArray)
self.datasourceModel.dataListArray = datasourceModel.dataListArray
}
In tableview cell i have used a button for adding.
So in viewcontroller i have done the action on the add button
AND thus it will add and display the other viewcontroller.
my hotelviewcontroller as:-
the code for add button action...
cell.cartaddCell = {[weak self] in
if let i = self?.tableView.indexPath(for: $0) {
let cartDataSource:DataSourceModel = DataSourceModel(array: nil)
let cartViewModel:ViewModel = ViewModel(withdatasource: cartDataSource)
let cartmodel:Model = Model(withoffermodel:self!.offerViewModel.datafordisplay(atindex: indexPath))
cartViewModel.insertedArray = cartmodel
print(cartViewModel.insertedArray)
cartViewModel.add()
let cartViewController:ViewController = ViewController(nibName: "ViewController", bundle: nil, withViewModel: cartViewModel)
self?.navigationController?.pushViewController(cartViewController, animated: true)
// self?.present(cartViewController, animated: true, completion: nil)
// print(cartViewModel.insertedArray )
print(cartmodel.offerprice)
print(cartmodel.offerdetailAddName)
print(cartmodel)
print(i)
// self?.chartViewModel.delete(atIndex: i)
}
}
now in my cartviewmodel as:-
class ViewModel: NSObject {
var datasourceModel:DataSourceModel
var insertedArray:Model?
var finalArray:Array<Model>? = []
init(withdatasource newDatasourceModel: DataSourceModel) {
datasourceModel = newDatasourceModel
print(datasourceModel.dataListArray)
}
func datafordisplay(atindex indexPath: IndexPath) -> Model{
return datasourceModel.dataListArray![indexPath.row]
}
func numberOfRowsInSection(section:Int) -> Int {
return datasourceModel.dataListArray!.count
}
func delete(atIndex indexPath: IndexPath) {
datasourceModel.dataListArray!.remove(at: indexPath.row)
}
func add() {
datasourceModel.dataListArray?.append(insertedArray!)
print(datasourceModel.dataListArray)
self.datasourceModel.dataListArray = datasourceModel.dataListArray
}
func insert(){
add()
datasourceModel.dataListArray!.insert(insertedArray?.offerdetailAddName, at: indexPath.row)
}
}
my cartviewcontroller:-
class ViewController: UIViewController ,UITableViewDataSource,UITabBarDelegate{
#IBOutlet private weak var tableView: UITableView!
#IBOutlet weak var orderbutton: UIButton!
#IBOutlet weak var displayresult: UIView!
private var chartViewModel :ViewModel!
init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?, withViewModel viewModel:ViewModel) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
chartViewModel = viewModel
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.backgroundColor = UIColor(red: 0.93, green: 0.86, blue: 1, alpha:1.0)
tableView.dataSource = self
displayresult.isHidden = false
self.tableView .reloadData()
self.tableView.tableFooterView = displayresult
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return chartViewModel.numberOfRowsInSection(section: section)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let identifier = "cell"
var cell: ChartCell! = tableView.dequeueReusableCell(withIdentifier: identifier) as? ChartCell
if cell == nil {
tableView.register(UINib(nibName: "ChartCell", bundle: nil), forCellReuseIdentifier: identifier)
cell = tableView.dequeueReusableCell(withIdentifier: identifier) as? ChartCell
}
cell.setEventData(charts: chartViewModel.datafordisplay(atindex: indexPath))
print(chartViewModel.insertedArray)
return cell
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
chartViewModel.delete(atIndex: indexPath)
tableView.deleteRows(at: [indexPath], with: .automatic)
self.tableView.reloadData()
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Actually i have 2 screens.
1.hotelscreen
2.cartscreen
So while clicking the hoteltableviewcell a have a button to add cart .
So while adding the hotel model will added in the cart screen.
for that i got the value already.
But my question is:-
when i clicked on the cartscreen the added object is not displayed.
So how to show the updated data.
Seems like all you need is tableView.reloadData().
I got weird crash on Crashlytics, It's showing crash in the line as commented in code.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath.row >= collectionArrayApiObject.collectionsArray.count {
return UITableViewCell(frame: CGRectZero)
}
var tableViewCell:CollectionsTableViewCell? = tableView.dequeueReusableCellWithIdentifier("collectionsCell") as? CollectionsTableViewCell
if (tableViewCell == nil) {
tableViewCell = CollectionsTableViewCell(style: .Default, reuseIdentifier: "collectionsCell")
}
// Got crash in below line
let collectionObject = collectionArrayApiObject.collectionsArray[indexPath.row]
tableViewCell!.setCollection(collectionObject)
return tableViewCell!
}
This is the stack trace that I got..
But not getting why this even happened? I have already put check that
index.row >= collectionArrayApiObject.collectionsArray.count
so it should not be index out of range case. Can any one give me just one case in which this can happen?
NOTE: This happened with only one user till now but still why this even happened?
Whole Table View Controller:
import UIKit
class CollectionsViewController: CUIBaseViewController, CollectionArrayApiObjectDelegate, UITableViewDataSource, UITableViewDelegate, UIViewControllerPreviewingDelegate {
var collectionsTableView : UITableView?
var collectionArrayApiObject : CollectionArrayApiObject = CollectionArrayApiObject()
var headerSearchButton : UIBarButtonItem?
var cityId: String?
required init?(coder aDecoder: NSCoder) {
fatalError("NSCoding not supported")
}
override init(nibName nibNameOrNil: String!, bundle nibBundleOrNil: NSBundle!) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
convenience init(CityId: String? = nil) {
self.init(nibName: nil, bundle: nil)
self.cityId = CityId
self.view.backgroundColor = Colors.white()
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.setUpHeaderView()
self.createTableView()
self.getData()
self.listenToNotifications()
self.automaticallyAdjustsScrollViewInsets = false
if #available(iOS 9.0, *) {
registerForPreviewingWithDelegate(self, sourceView: collectionsTableView!)
}
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.setNavigationBarHidden(false, animated: animated)
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
}
func listenToNotifications() {
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(CollectionsViewController.cityChanged), name: Constants.NOTIFIICATION_LOCATION_CHANGED_CITY, object: nil)
}
func cityChanged() {
self.getData()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
collectionsTableView?.frame = CGRectMake(0, Constants.NAV_BAR_HEIGHT, self.view.width, self.view.height - Constants.NAV_BAR_HEIGHT)
}
//MARK: view creation functions
func setUpHeaderView() {
self.navigationItem.title = "Collections"
let negativeSpacer = UIBarButtonItem.init(barButtonSystemItem: UIBarButtonSystemItem.FixedSpace, target: nil, action: nil)
negativeSpacer.width = 0;
headerSearchButton = UIBarButtonItem.init(title: "n", style: .Plain, target: self, action: #selector(CollectionsViewController.headerSearchButtonTapped))
headerSearchButton?.width = 40
headerSearchButton?.setTitleTextAttributes([NSFontAttributeName: Fonts.iconFont(18), NSForegroundColorAttributeName: Colors.gray()], forState: .Normal)
self.navigationItem.rightBarButtonItems = [negativeSpacer, headerSearchButton!];
}
func createTableView() {
collectionsTableView = UITableView(frame: CGRectZero, style: .Plain)
collectionsTableView?.separatorStyle = .None
collectionsTableView?.backgroundColor = Colors.white()
collectionsTableView?.dataSource = self
collectionsTableView?.delegate = self
self.view.addSubview(collectionsTableView!)
}
//MARK : tableview delegate methods
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1;
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return collectionArrayApiObject.collectionsArray.count
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return CollectionsTableViewCell.getHeight()
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath.row >= collectionArrayApiObject.collectionsArray.count {
return UITableViewCell(frame: CGRectZero)
}
var tableViewCell:CollectionsTableViewCell? = tableView.dequeueReusableCellWithIdentifier("collectionsCell") as? CollectionsTableViewCell
if (tableViewCell == nil) {
tableViewCell = CollectionsTableViewCell(style: .Default, reuseIdentifier: "collectionsCell")
}
let collectionObject = collectionArrayApiObject.collectionsArray[indexPath.row]
tableViewCell!.setCollection(collectionObject)
return tableViewCell!
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
Commons.trackEvent(Commons.createGAcategory([Constants.COLLECTIONS_PAGE, "collection"]), action: "click", label: nil, value: 1)
let exploreFeedForCollection = ExploreFeedViewController.init(collection: collectionArrayApiObject.collectionsArray[indexPath.row])
//self.presentViewController(CUINavigationController.init(rootViewController: exploreFeedForCollection), animated: true, completion: nil)
self.navigationController?.pushViewController(exploreFeedForCollection, animated: true)
}
func scrollViewDidScroll(scrollView: UIScrollView) {
let contentOffset = scrollView.contentOffset.y
if (collectionsTableView != nil) {
for cell in collectionsTableView!.visibleCells {
if (cell.isKindOfClass(CollectionsTableViewCell.self)) {
let cellOffset = cell.y - contentOffset;
//image parallax
let parallaxCut: CGFloat = 0.5
let percent = (cellOffset + cell.height)/(collectionsTableView!.height + cell.height);
let extraHeight = cell.height * (CollectionsTableViewCell.parallaxRatio-1.0) * parallaxCut;
let collectionCell = cell as! CollectionsTableViewCell;
collectionCell.bgImageView.y = -extraHeight*percent;
}
}
}
}
//MARK : get cities data functions
func getData() {
collectionArrayApiObject = CollectionArrayApiObject()
collectionArrayApiObject.fetchCollections(Delegate: self, CityId: self.cityId)
if collectionArrayApiObject.collectionsArray.count == 0 {
self.showLoader("Hmm, things are getting interesting")
}
}
func collectionsFetchedSuccessfully() {
self.hideNothingHereViewAndLoader()
self.collectionsTableView?.reloadData()
self.scrollViewDidScroll(self.collectionsTableView!)
}
func collectionsFetchingFailed(errorType: ErrorType) {
self.showNothingHereView(errorType, icon: nil, showTryAgain: true)
}
override func didTapReloadButton() {
self.getData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//MARK: UIViewControllerPreviewingDelegate
func previewingContext(previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? {
guard let indexPath = collectionsTableView?.indexPathForRowAtPoint(location) else{
return nil
}
// Lifting cell up on 3D touch before peeking
if #available(iOS 9.0, *) {
let cellRect = collectionsTableView?.rectForRowAtIndexPath(indexPath)
let sourceRect = previewingContext.sourceView.convertRect(cellRect!, fromView: collectionsTableView)
previewingContext.sourceRect = sourceRect
}
return ExploreFeedViewController.init(collection: collectionArrayApiObject.collectionsArray[(indexPath as NSIndexPath).row])
}
func previewingContext(previewingContext: UIViewControllerPreviewing, commitViewController viewControllerToCommit: UIViewController) {
self.navigationController?.pushViewController(viewControllerToCommit, animated: true)
}
//MARK : header button functions
func headerSearchButtonTapped() {
let searchVC = SearchViewController(CityId: cityId)
self.navigationController?.pushViewController(searchVC, animated: true)
}
deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
}
Some observations:
First
Instead of using this if statement:
if indexPath.row >= collectionArrayApiObject.collectionsArray.count {
return UITableViewCell(frame: CGRectZero)
}
You should use the numberOfRowsInSection to do that, like this:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return collectionArrayApiObject.collectionsArray.count
}
Second
In your cellForRowAtindexPath you just have to dequeue the cell and than set it as you need, like this:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//swift 3.0
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CollectionsTableViewCell
//swift 2.2
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! CollectionsTableViewCell
let collectionObject = collectionArrayApiObject.collectionsArray[indexPath.row]
cell.setCollection(collectionObject)
return cell
}
There isn't need to test if you use dequeueReusableCellWithIdentifier:forIndexPath:) which according to the docs always returns a valid cell
1- if you are using like this
let cell = tableChatHome.dequeueReusableCell(withIdentifier: "tableChatHomeCell1", for: indexPath) as! tableChatHomeCell1
2- change to like this work fine for me
let cell = tableView.dequeueReusableCell(withIdentifier: "tableChatHomeCell1") as! tableChatHomeCell1
As I am creating a TableView programmatically with multiple cell and in each cell having UICollectionView, UITableView, UITableView.... but I am not able to find the error and every time when I run the program it Shows
" Command failed due to signal: Segmentation fault: 11".
Has any one created this type of UI using coding.
New in Swift so forgive for errors.
// ViewController.swift
// Json Parsing in Swift
//
import UIKit
import Alamofire
import SwiftyJSON
class ViewController: UITableViewController {
var dataArray = Array<JSON>()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
Alamofire.request(.GET, "http://104.131.162.14:3033/api/ios/detail").validate().responseJSON { response in
switch response.result {
case .Success:
if let value = response.result.value {
let json = JSON(value)
print("JSON: \(json)")
var trafficJson = json["traffic_partners"]
trafficJson["type"] = "Traffic"
self.dataArray.append(trafficJson)
var newsJson = json["news"]
newsJson["type"] = "News"
self.dataArray.append(newsJson)
var categoryJson = json["category"]
categoryJson["type"] = "Category"
self.dataArray.append(categoryJson)
var topFreeApps = json["top_free_apps"]
topFreeApps["type"] = "TopApps"
self.dataArray.append(topFreeApps)
var topSites = json["top_sites"]
topSites["type"] = "TopSites"
self.dataArray.append(topSites)
var trendingVideos = json["tranding_video"]
trendingVideos["type"] = "TrendingVideos"
self.dataArray.append(trendingVideos)
var sports = json["sports"]
sports["type"] = "Sports"
self.dataArray.append(sports)
var jokes = json["jokes"]
jokes["type"] = "jokes"
self.dataArray.append(jokes)
print(self.dataArray[0]["detail"][0].object)
print(self.dataArray[2]["detail"].object)
self.tableView.reloadData()
}
case .Failure(let error):
print(error)
}
}
tableView.registerClass(MyCell.self, forCellReuseIdentifier: "cellId")
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataArray.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let myCell = tableView.dequeueReusableCellWithIdentifier("cellId", forIndexPath: indexPath) as! MyCell
myCell.nameLabel.text = dataArray[indexPath.row]["type"].string
if (dataArray[indexPath.row]["type"].string == "News") {
myCell.newsArray = dataArray[indexPath.row]["detail"].arrayObject
}
myCell.myTableViewController = self
return myCell
}
}
class MyCell: UITableViewCell {
var newsArray :NSMutableArray=[]
var myTableViewController: ViewController?
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupViews()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
let newsTableView: UITableView = {
let newsTV = UITableView(frame:UIScreen.mainScreen().bounds, style: UITableViewStyle.Plain)
newsTV.registerClass(NewsTableViewCell.self, forCellReuseIdentifier: "NewsTableViewCell")
return newsTV
}()
func tableView(newsTableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return newsArray.count
}
func tableView(newsTableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let myCell = newsTableView.dequeueReusableCellWithIdentifier("cellId", forIndexPath: indexPath) as! NewsTableViewCell
myCell.nameLabel.text = newsArray[indexPath.row]["title"].string
myCell.myTableViewController = myTableViewController
return myCell
}
let nameLabel: UILabel = {
let label = UILabel()
label.text = "Sample Item"
label.translatesAutoresizingMaskIntoConstraints = false
label.font = UIFont.boldSystemFontOfSize(14)
return label
}()
func setupViews() {
addSubview(newsTableView)
addSubview(nameLabel)
addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[v0]|", options: NSLayoutFormatOptions(), metrics: nil, views: ["v0": nameLabel]))
addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[v0]|", options: NSLayoutFormatOptions(), metrics: nil, views: ["v0": newsTableView]))
}
func handleAction() {
}
}
I have just changed from a UITableViewController to a UITableView, and everything is working fine, with the exception of the search bar, which when tapping on a key on the keyboard will crash the app with the error:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'unable to dequeue a cell with identifier rideCell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard'
I haven't changed anything in terms of the code. Someone suggested changing:
let cell = tableView.dequeueReusableCellWithIdentifier("rideCell", forIndexPath: indexPath) as! RideCell
to:
let cell = tableView.dequeueReusableCellWithIdentifier("rideCell") as! RideCell
but that didn't help at all.
Any suggestions?
EDIT:
Here is my code as requested:
class InitalViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var tableView: UITableView!
#IBOutlet weak var searchBar: UISearchBar!
var rideName = ""
var parkPassed: Park!
var searchResults = NSArray()
var loadingIndicator = UIActivityIndicatorView()
var backgroundLabel = LabelWithInsets()
var refreshSpinner = UIRefreshControl()
func handleRefresh(refreshControl: UIRefreshControl) {
DataManager.sharedInstance.loadRides(parkPassed.name!.stringByReplacingOccurrencesOfString(" ", withString: ""))
refreshControl.endRefreshing()
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
backgroundLabel.textAlignment = NSTextAlignment.Center
backgroundLabel.textColor = UIColorFromRGB(0x424242)
backgroundLabel.numberOfLines = 0
refreshSpinner.addTarget(self, action: "handleRefresh:", forControlEvents: UIControlEvents.ValueChanged)
tableView.addSubview(refreshSpinner)
//refreshControl = refreshSpinner
self.tableView.tableHeaderView = searchBar
self.tableView.contentOffset = CGPointMake(0, CGRectGetHeight(searchBar.frame))
self.navigationController?.navigationBar.barTintColor = UIColorFromRGB(0x0096FF)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "updateTableView", name: "onRidesLoadedNotification", object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "displayError", name: "onRidesLoadFailedNotification", object: nil)
}
override func viewWillAppear(animated: Bool) {
if DataManager.sharedInstance.rideArray.count == 0 {
tableView.separatorStyle = UITableViewCellSeparatorStyle.None
loadingIndicator.frame = CGRectMake((view.bounds.width / 2) - 25, (view.bounds.height / 2) - 50, 50, 50)
loadingIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.Gray
view.addSubview(loadingIndicator)
loadingIndicator.startAnimating()
backgroundLabel.text = "Loading rides..."
backgroundLabel.frame = CGRectMake(0, (view.bounds.height / 2) - 80, view.bounds.width, 30)
view.addSubview(backgroundLabel)
}
DataManager.sharedInstance.loadRides(parkPassed.name!.stringByReplacingOccurrencesOfString(" ", withString: ""))
UIApplication.sharedApplication().setStatusBarStyle(UIStatusBarStyle.LightContent, animated: false)
if NSUserDefaults.standardUserDefaults().arrayForKey("favourites") != nil {
let tempFavourites: NSArray = NSUserDefaults.standardUserDefaults().arrayForKey("favourites")!
favouritesArray = tempFavourites.mutableCopy() as! NSMutableArray
}
}
#IBAction func dismiss(sender: AnyObject) {
self.dismissViewControllerAnimated(true, completion: nil)
}
func updateTableView() {
tableView.reloadData()
tableView.separatorStyle = UITableViewCellSeparatorStyle.SingleLine
loadingIndicator.removeFromSuperview()
backgroundLabel.removeFromSuperview()
}
func displayError() {
loadingIndicator.removeFromSuperview()
backgroundLabel.removeFromSuperview()
backgroundLabel.text = "Failed to load rides. Please check your internet connection."
backgroundLabel.frame = CGRectMake(0, (view.bounds.height / 2) - 80, view.bounds.width, 60)
view.addSubview(backgroundLabel)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewDidAppear(animated: Bool) {
println(DataManager.sharedInstance.rideArray.count)
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("rideCell", forIndexPath: indexPath) as! RideCell
var ride: Ride
if tableView == self.searchDisplayController?.searchResultsTableView {
ride = DataManager.sharedInstance.getRideByName(searchResults[indexPath.row].name)!
} else {
ride = DataManager.sharedInstance.rideAtLocation(indexPath.row)!
}
cell.rideNameLabel.text = ride.name
var dateSinceUpdate = NSDate().timeIntervalSinceDate(ride.updated!)
var secondsSinceUpdate = Int(dateSinceUpdate)
var timeSinceUpdate = printSecondsConvert(secondsSinceUpdate)
cell.updatedLabel.text = timeSinceUpdate
if ride.waitTime == "Closed" {
cell.waitTimeLabel.text = ride.waitTime!
cell.timeBackgroundView.backgroundColor = getColorFromNumber(80)
cell.waitTimeLabel.font = UIFont(name: "Avenir", size: 13)
} else {
cell.waitTimeLabel.text = "\(ride.waitTime!)m"
cell.timeBackgroundView.backgroundColor = getColorFromNumber(ride.waitTime!.toInt()!)
cell.waitTimeLabel.font = UIFont(name: "Avenir", size: 17)
}
AsyncImageLoader.sharedLoader().cancelLoadingURL(cell.rideImageView.imageURL)
cell.rideImageView.image = UIImage(named: "Unloaded")
cell.rideImageView.imageURL = NSURL(string: ride.rideImageSmall!)
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableView == self.searchDisplayController?.searchResultsTableView {
return searchResults.count
} else {
return DataManager.sharedInstance.rideArray.count
}
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let currentCell = tableView.cellForRowAtIndexPath(indexPath) as! RideCell!
rideName = currentCell.rideNameLabel.text!
performSegueWithIdentifier("showDetail", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "showDetail") {
var viewController = segue.destinationViewController as! DetailViewController
viewController.currentRide = DataManager.sharedInstance.getRideByName(rideName)
viewController.parkPassed = parkPassed
}
}
func filterContentForSearchText(searchText: NSString) {
let resultPredicate = NSPredicate(format: "name contains[cd] %#", searchText) //Use either contains or beginswith
searchResults = (DataManager.sharedInstance.rideArray as NSArray).filteredArrayUsingPredicate(resultPredicate)
}
func searchDisplayController(controller: UISearchDisplayController!, shouldReloadTableForSearchString searchString: String!) -> Bool {
self.filterContentForSearchText(searchString)
return true
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 71
}
func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [AnyObject]? {
var cell = tableView.cellForRowAtIndexPath(indexPath) as! RideCell
let favourite = UITableViewRowAction(style: .Normal, title: " ") { action, index in
if favouritesArray.containsObject(cell.rideNameLabel.text!) {
favouritesArray.removeObject(cell.rideNameLabel.text!)
} else {
favouritesArray.addObject(cell.rideNameLabel.text!)
}
NSUserDefaults.standardUserDefaults().setObject(favouritesArray, forKey: "favourites")
tableView.setEditing(false, animated: true)
}
if favouritesArray.containsObject(cell.rideNameLabel.text!) {
favourite.backgroundColor = UIColor(patternImage: UIImage(named: "Unfavourite")!)
} else {
favourite.backgroundColor = UIColor(patternImage: UIImage(named: "Favourite")!)
}
return [favourite]
}
func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return true
}
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
}
}
Call next code in viewDidLoad method of view controller that contains your table view:
If you created RideCell in xib:
tableView.registerNib(UINib(nibName: "RideCell", bundle: nil),
forCellReuseIdentifier: "rideCell")
If you created RideCell in code:
tableView.registerClass(RideCell.self, forCellReuseIdentifier: "rideCell")
In your viewDidLoad you have to register the custom cell to your UITableView using this function:
func registerNib(_ nib: UINib,forCellReuseIdentifier identifier: String)
Hey all I am trying to create a custom UITableViewCell, but I see nothing on the simulator. Can you help me please.
I can see the label only if I var labUserName = UILabel(frame: CGRectMake(0.0, 0.0, 130, 30));
but it overlaps the cell. I don't understand, Auto Layout should know the preferred size/minimum size of each cell?
Thanks
import Foundation
import UIKit
class TableCellMessages: UITableViewCell {
var imgUser = UIImageView();
var labUserName = UILabel();
var labMessage = UILabel();
var labTime = UILabel();
override init(style: UITableViewCellStyle, reuseIdentifier: String) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
imgUser.layer.cornerRadius = imgUser.frame.size.width / 2;
imgUser.clipsToBounds = true;
contentView.addSubview(imgUser)
contentView.addSubview(labUserName)
contentView.addSubview(labMessage)
contentView.addSubview(labTime)
//Set layout
var viewsDict = Dictionary <String, UIView>()
viewsDict["image"] = imgUser;
viewsDict["username"] = labUserName;
viewsDict["message"] = labMessage;
viewsDict["time"] = labTime;
//Image
//contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-[image(100)]-'", options: nil, metrics: nil, views: viewsDict));
//contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[image(100)]-|", options: nil, metrics: nil, views: viewsDict));
contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[username]-[message]-|", options: nil, metrics: nil, views: viewsDict));
contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-[username]-|", options: nil, metrics: nil, views: viewsDict));
contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-[message]-|", options: nil, metrics: nil, views: viewsDict));
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Let's make a few assumptions:
You have an iOS8 project with a Storyboard that contains a single UITableViewController. Its tableView has a unique prototype UITableViewCell with custom style and identifier: "cell".
The UITableViewController will be linked to Class TableViewController, the cell will be linked to Class CustomTableViewCell.
You will then be able to set the following code (updated for Swift 2):
CustomTableViewCell.swift:
import UIKit
class CustomTableViewCell: UITableViewCell {
let imgUser = UIImageView()
let labUserName = UILabel()
let labMessage = UILabel()
let labTime = UILabel()
override func awakeFromNib() {
super.awakeFromNib()
imgUser.backgroundColor = UIColor.blueColor()
imgUser.translatesAutoresizingMaskIntoConstraints = false
labUserName.translatesAutoresizingMaskIntoConstraints = false
labMessage.translatesAutoresizingMaskIntoConstraints = false
labTime.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(imgUser)
contentView.addSubview(labUserName)
contentView.addSubview(labMessage)
contentView.addSubview(labTime)
let viewsDict = [
"image": imgUser,
"username": labUserName,
"message": labMessage,
"labTime": labTime,
]
contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[image(10)]", options: [], metrics: nil, views: viewsDict))
contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:[labTime]-|", options: [], metrics: nil, views: viewsDict))
contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[username]-[message]-|", options: [], metrics: nil, views: viewsDict))
contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-[username]-[image(10)]-|", options: [], metrics: nil, views: viewsDict))
contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-[message]-[labTime]-|", options: [], metrics: nil, views: viewsDict))
}
}
TableViewController.swift:
import UIKit
class TableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
//Auto-set the UITableViewCells height (requires iOS8+)
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 44
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 100
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! CustomTableViewCell
cell.labUserName.text = "Name"
cell.labMessage.text = "Message \(indexPath.row)"
cell.labTime.text = NSDateFormatter.localizedStringFromDate(NSDate(), dateStyle: .ShortStyle, timeStyle: .ShortStyle)
return cell
}
}
You will expect a display like this (iPhone landscape):
This is the update for swift 3 of the answer Imanou Petit.
CustomTableViewCell.swift:
import Foundation
import UIKit
class CustomTableViewCell: UITableViewCell {
let imgUser = UIImageView()
let labUerName = UILabel()
let labMessage = UILabel()
let labTime = UILabel()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
imgUser.backgroundColor = UIColor.blue
imgUser.translatesAutoresizingMaskIntoConstraints = false
labUerName.translatesAutoresizingMaskIntoConstraints = false
labMessage.translatesAutoresizingMaskIntoConstraints = false
labTime.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(imgUser)
contentView.addSubview(labUerName)
contentView.addSubview(labMessage)
contentView.addSubview(labTime)
let viewsDict = [
"image" : imgUser,
"username" : labUerName,
"message" : labMessage,
"labTime" : labTime,
] as [String : Any]
contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-[image(10)]", options: [], metrics: nil, views: viewsDict))
contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:[labTime]-|", options: [], metrics: nil, views: viewsDict))
contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-[username]-[message]-|", options: [], metrics: nil, views: viewsDict))
contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-[username]-[image(10)]-|", options: [], metrics: nil, views: viewsDict))
contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-[message]-[labTime]-|", options: [], metrics: nil, views: viewsDict))
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Settigns.swift:
import Foundation
import UIKit
class Settings: UIViewController, UITableViewDelegate, UITableViewDataSource {
private var myTableView: UITableView!
private let sections: NSArray = ["fruit", "vegitable"] //Profile network audio Codecs
private let fruit: NSArray = ["apple", "orange", "banana", "strawberry", "lemon"]
private let vegitable: NSArray = ["carrots", "avocado", "potato", "onion"]
override func viewDidLoad() {
super.viewDidLoad()
// get width and height of View
let barHeight: CGFloat = UIApplication.shared.statusBarFrame.size.height
let navigationBarHeight: CGFloat = self.navigationController!.navigationBar.frame.size.height
let displayWidth: CGFloat = self.view.frame.width
let displayHeight: CGFloat = self.view.frame.height
myTableView = UITableView(frame: CGRect(x: 0, y: barHeight+navigationBarHeight, width: displayWidth, height: displayHeight - (barHeight+navigationBarHeight)))
myTableView.register(CustomTableViewCell.self, forCellReuseIdentifier: "cell") // register cell name
myTableView.dataSource = self
myTableView.delegate = self
//Auto-set the UITableViewCells height (requires iOS8+)
myTableView.rowHeight = UITableViewAutomaticDimension
myTableView.estimatedRowHeight = 44
self.view.addSubview(myTableView)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// return the number of sections
func numberOfSections(in tableView: UITableView) -> Int{
return sections.count
}
// return the title of sections
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sections[section] as? String
}
// called when the cell is selected.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("Num: \(indexPath.row)")
if indexPath.section == 0 {
print("Value: \(fruit[indexPath.row])")
} else if indexPath.section == 1 {
print("Value: \(vegitable[indexPath.row])")
}
}
// return the number of cells each section.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return fruit.count
} else if section == 1 {
return vegitable.count
} else {
return 0
}
}
// return cells
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CustomTableViewCell
if indexPath.section == 0 {
cell.labUerName.text = "\(fruit[indexPath.row])"
cell.labMessage.text = "Message \(indexPath.row)"
cell.labTime.text = DateFormatter.localizedString(from: NSDate() as Date, dateStyle: .short, timeStyle: .short)
} else if indexPath.section == 1 {
cell.labUerName.text = "\(vegitable[indexPath.row])"
cell.labMessage.text = "Message \(indexPath.row)"
cell.labTime.text = DateFormatter.localizedString(from: NSDate() as Date, dateStyle: .short, timeStyle: .short)
}
return cell
}
}
In Swift 5.
The custom UITableViewCell:
import UIKit
class CourseCell: UITableViewCell {
let courseName = UILabel()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
// Set any attributes of your UI components here.
courseName.translatesAutoresizingMaskIntoConstraints = false
courseName.font = UIFont.systemFont(ofSize: 20)
// Add the UI components
contentView.addSubview(courseName)
NSLayoutConstraint.activate([
courseName.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20),
courseName.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -20),
courseName.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20),
courseName.heightAnchor.constraint(equalToConstant: 50)
])
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
The UITableViewController:
import UIKit
class CourseTableViewController: UITableViewController {
private var data: [Int] = [1]
override func viewDidLoad() {
super.viewDidLoad()
// You must register the cell with a reuse identifier
tableView.register(CourseCell.self, forCellReuseIdentifier: "courseCell")
// Change the row height if you want
tableView.rowHeight = 150
// This will remove any empty cells that are below your data filled cells
tableView.tableFooterView = UIView()
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "courseCell", for: indexPath) as! CourseCell
cell.courseName.text = "Course name"
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
}