Xcode - cellForRowAtIndexpath - initialising cell - xcode

What's the difference between this:
var cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "cell")
And this:
var cell: UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as UITableViewCell
They both seem t work fine for me.
PS: I know this seems to be an amateur question but i'm beginner in Xcode, so no reason to be a smug.

When you write :
var cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "cell")
You are initializing a new cell using its constructor.
And when you write :
var cell = self.tableView.dequeueReusableCellWithIdentifier("cell")
You are dequeuing a cell, so you are assuming your cell with the identifier cell has been already registered in the tableView.
Typically, if the cell was designed in Interface Builder and set as a prototype cell or if you have registered your cell for reuse using the method self.tableView.registerClass(MyCell.classForCoder(), forCellReuseIdentifier: "cell") you won't need to use the constructor because it is already initialized in the tableView.
But if your cell is designed programmatically such as creating UILabel, UIImage or whatever components, you will have to use the constructor instead, and then use the dequeue method.
So, if you have to use the constructor (because you're initializing everything by code) your code will look like this :
override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
var cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
if cell == nil {
cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "cell")
}
cell.cellLabel.text = "Hello world"
cell.cellImage.image = UIImage(named: "funny_cat.jpg")
return cell
}
But if your cell was registered for reuse or if it is a prototype cell you will just have to use
override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
var cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
cell.cellLabel.text = "Hello world"
cell.cellImage.image = UIImage(named: "funny_cat.jpg")
return cell
}
I think the best place to look how tableview work, you should look the official documentation here : Table View Programming Guide for iOS

Related

Dynamic Type does not work with tableView.dequeueReusableCell(withIdentifier:)

I'm experiencing a quite strange behaviour for my UITableViewCells with style .subtitle regarding Dynamic Type. When using
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "DesignElementCell", for: indexPath) //Dynamic Type does not work properly in this case
cell.textLabel?.text = "Test"
cell.detailTextLabel?.text = "Test"
return cell
}
the output looks like
As you can see the header text is scaled fine, while cell labels are not. When using
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "DesignElementCell")
cell.textLabel?.text = "Test"
cell.detailTextLabel?.text = "Test"
return cell
}
Everything works fine:
The strangest thing in the first case is, that when I change dynamic type size in settings and head back to my app, suddenly everything seems to work as long as I'm not popping my ViewController. Does anyone experienced something similar? Have a nice day :)
First, check if you registered cell, and set delegate&dataSource of tableView in ViewDidLoad()
tableView.delegate = self
tableView.dataSource = self
tableView.register(Cell.self, forCellReuseIdentifier:"CellIdentifier")
Then, define rowHeight & estimatedRowHeight of tableView in ViewDidLoad()
tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = 150

How to store a hidden string with a UITableViewCell?

I want to attach information (a string) to each UITableViewCell when populating TableView with UITableViewCells. I don't want it to be visible- I just need to access this string when the row/cell is selected. What is the best way to do this?
Your best choice is probably to subclass UITableViewCell and add a string property. This can then be assigned either as a constant, or when setting up your cells in cellForRowAtIndexPath:.
class CustomCell: UITableViewCell {
var customString = "Default"
}
--------
func cellForRow(atIndexPath indexPath: IndexPath) -> UITableViewCell {
guard let cell: CustomCell = tableView.dequeueReusableCell(withReuseIdentifier: "identifier", indexPath: indexPath) as? CustomCell else {
return UITableViewCell()
}
cell.customString = "String"
return cell
}
func didSelectRow(atIndexPath indexPath: IndexPath) {
guard let cell: CustomCell = tableView.cellForRow(atIndexPath: indexPath) as? CustomCell else {
return
}
let stringFromCell = cell.customString
}
The above has not been compiled, so may not work word for word, but you get the principle.
Add a property selected to your model.
In cellForRow show/hide the string depending on the state of selected.
In didSelectRow toggle selected in the model and reload the row.

How can I add a UITapGestureRecognizer to a UILabel inside a table view cell? Swift2

I am using a custom table view cell. This cell has a label with outlet called chatNameLabel. Adding a UILongPressGestureRecognizer to this label never fires the associated event.
I'm guessing that the problem is that the UILabel is in a TableView and that the table/cell view is intercepting the taps. Can I do something about this?
All I want to do is perform some custom action when a UILabel is long pressed!
I believe this was answered in Objective-C however I am not familiar with the language at all and new to swift.
Here's the code I'm using:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if let sessions = self.chatSessions where sessions.indices.contains(indexPath.row) {
let session = sessions[indexPath.row]
if session.sessionId == nil {
//DO A THING
}
// existing session id (existing chat)
else {
let cell = tableView.dequeueReusableCellWithIdentifier("ChatListCell", forIndexPath: indexPath) as! ChatListTableViewCell
cell.tag = indexPath.row
if(session.unreadChats) {
cell.indicatorImageView.tintColor = AppStyles.sharedInstance.indicatorActive
}
else{
cell.indicatorImageView.hidden = true
}
//Want to do gesture on this label below cell.chatNameLabel
cell.chatNameLabel.text = session.chatName
... Some more code not needed for question below this
Your class needs implement UIGestureRecognizerDelegate , then the below code should work.
myLabel.userInteractionEnabled = true
let tap: UILongPressGestureRecognizer = UILongPressGestureRecognizer(
target: self, action: #selector(tappedTheLabel))
tap.minimumPressDuration = 0.5
tap.delaysTouchesBegan = true
myLabel.addGestureRecognizer(tap)
tap.delegate = self
}
func tappedTheLabel(sender: UITapGestureRecognizer)
{
print("label hit \(sender)")
}

getting the index path of a cell inside UITableViewCell in swift

could anyone tell me how to get the index of a cell inside its class which is uitableViewCell more specifically , inside an action function of UISwitch .
I did the following..
var cell = sender.superview?.superview as UITableViewCell
var table: UITableView = cell.superview as UITableView
let indexPath = table.indexPathForCell(cell)
but then it crashes.
what is the solution ?
Try this:
Assuming you have a UISwitch *cellSwitch object in cell custom class
In cellForRowAtIndexPath:
cell.cellSwitch.tag = indexPath.row
In IBAction for this switch:
let indexPath = NSIndexPath(forRow: sender.tag, inSection: 0)
You don't want to know the index path of the cell inside of the cell. The index path is an implementation detail of the UITableViewController. The cell should be an independent object.
What you really want to do is to assign an action to run when your switch is changed.
class MySwitchCell: UITableViewCell {
#IBOutlet weak var switchCellLabel: UILabel!
#IBOutlet weak var mySwitch: UISwitch!
//Declare an action to be run
var action: ((sender: UISwitch) -> Void)?
//then run it
#IBAction func switchAction(sender: UISwitch) {
action?(sender: sender)
}
}
Then give the action something to do when you configure the cell.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("SwitchCell", forIndexPath: indexPath) as! MySwitchCell
cell.switchCellLabel.text = items[indexPath.row]
cell.mySwitch.on = NSUserDefaults.standardUserDefaults().boolForKey(items[indexPath.row])
cell.action = { [weak self] sender in
if let tableViewController = self {
NSUserDefaults.standardUserDefaults().setBool(sender.on, forKey: tableViewController.items[indexPath.row]) }
}
return cell
}
For example this one sets a bool in the NSUserDefaults based on the state of that switch.
You can checkout the whole sample project from https://github.com/regnerjr/SimpleCellSwitchAction

UITableView with 3 custom cells but 1 different

I have a TableView and I have created custom cells for that TableView with images, labels..etc, But I want one cell to be different than the others with It's own content ( cells will get their data from a backend).
So how to create 2 different types of cells in the same TableView, and how to arrange them, for example put 1 type of cell between 2 others ?
Edit: My UITableView Class:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let myCell: CellTableViewCell = tableView.dequeueReusableCellWithIdentifier("myCell", forIndexPath: indexPath) as! CellTableViewCell
let otherCell: OtherCellTableViewCell = tableView.dequeueReusableCellWithIdentifier("otherCell", forIndexPath: indexPath) as! OtherCellTableViewCell
// Configure the cell...
return myCell
}
If you're using a storyboard, just drag another UITableViewCell into the table view to be used as a prototype. Give it a unique and subclass (if necessary). In -tableView:cellForRowAtIndexPath: dequeue either the cell you're currently dequeueing, or this new one.
If you're not using a storyboard you'll need to either
-registerClass:forHeaderFooterViewReuseIdentifier: or
-registerNib:forHeaderFooterViewReuseIdentifier: to make your other cell style available to the table view.
Here's a modified version of your code that should work:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if condition {
let myCell: CellTableViewCell = tableView.dequeueReusableCellWithIdentifier("myCell", forIndexPath: indexPath) as! CellTableViewCell
// Configure the cell...
return myCell
}
else {
let otherCell: OtherCellTableViewCell = tableView.dequeueReusableCellWithIdentifier("otherCell", forIndexPath: indexPath) as! OtherCellTableViewCell
// Configure the cell...
return otherCell
}
}

Resources