UITableViewCell selection in editing mode. - uitableview

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

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().

Getting Crash in cellForRowAtIndexPath in tableView on Crashlytics

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

Creating a TableView Programmatically with multiple cell in swift

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() {
}
}

Changing UITableViewController to UITableView crashes app

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)

Custom UITableViewCell programmatically using Swift

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

Resources