Swift Custom Tableview Cell with UITableViewCellStyle - ios

I am trying to make a custom table view cell.
If I do this:
class TableViewCell: UITableViewCell {
#IBOutlet weak var cellBackgroundImage : UIImageView!
}
And:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! TableViewCell
cell.cellBackgroundImage.backgroundColor = UIColor.white
cell.cellBackgroundImage.layer.masksToBounds = false
cell.cellBackgroundImage.layer.cornerRadius = 5
let event = self.fetchedResultsController.object(at: indexPath)
self.configureCell(cell, withEvent: event)
return cell
}
I obtain a white rounded cell background. Easy. And I can use the original cell.textLabel.text.
Perfect.
But, if I want to do something more complex:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as? TableViewCell
if (cell == nil) {
cell = UITableViewCell(style: UITableViewCellStyle.subtitle, reuseIdentifier: "Cell") as? TableViewCell
cell?.cellBackgroundImage.backgroundColor = UIColor.white
cell?.cellBackgroundImage.layer.masksToBounds = false
cell?.cellBackgroundImage.layer.cornerRadius = 5
self.configureCell(cell!, withObject: object)
}
And at the same time to use the original table view properties as UITableViewCellStyle.subtitle and cell.accessoryView, the app crashes or shows the wrong output.
THIS MEANS THAT I MUST USE A FULL CUSTOM CELL WITH MORE OUTLETS TO REPLACE THE ORIGINAL ELEMENTS AS UITableViewCellStyle.subtitle and cell.accessoryView ???
I will express it in a different way:
Can I use a custom tableview cell only for one purpose (like the rounded background) and use the original elements such as the subtitle style and the accesory view?
In afirmative case, how?

Related

How to center a spinner with cell content view after removing a cell accessory?

I have a tableview in my storyboard where the prototype cell has a disclosure indicator by default.
When I populate my table I want to remove the indicator only from the last cell AND center a spinner on it.
I'm doing it like this:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CharacterCell", for: indexPath) as! CharacterCell
if indexPath.row == charactersViewModel.charactersCount - 1 {
cell.accessoryType = .none
cell.accessoryView = .none
// Spinner
let spinner = UIActivityIndicatorView(style: .large)
spinner.color = .white
spinner.center = cell.contentView.center
cell.contentView.addSubview(spinner)
spinner.startAnimating()
}
return cell
}
The problem is that the spinner is offcenter, a little bit to the left, just like if the accessory is still there, but hidden.
I feel maybe I'm missing the lifecycle of a table cell, maybe it's getting the center value of the content view when the accessory is still there, so when it's removed it is offcenter?
I tried on willDisplay as well but the same thing happens.
Any tips on this?
As #Paulw11 mentioned, I used a second subclass and created another cell prototype in my tableview.
Then when the last position at the table is reached, we can use the second prototype on cellForRowAt.
Here how it is:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row >= charactersViewModel.charactersCount - 1 {
reloadRows(indexPath: indexPath)
let cell = tableView.dequeueReusableCell(withIdentifier: "LoadingCharacterCell", for: indexPath) as! LoadingCharacterCell
cell.startSpinner()
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "CharacterCell", for: indexPath) as! CharacterCell
cell.configureCell(charactersViewModel: charactersViewModel, cell: cell, index: indexPath.row)
return cell
}
}
private func reloadRows(indexPath: IndexPath) {
var indexPathList = [IndexPath]()
indexPathList.append(indexPath)
charactersTableView.reloadRows(at: indexPathList, with: .automatic)
}
And with the reloadRows function, the last cell is updated and removed when the table receives more data.

replacing a cell on the fly in a static UITableView

in an App I am writing, i have a form built using storyboard around a UITableView with static cells in a UITableViewController. The table has 6 rows that collects different bits of information. Row 0,3,4,5 have textfield/textview/labels to collect/display some information, row 1 has a UICollectionView with a dozen or so icons. row 2 has a height of 50 but is empty for now.
this setup works well but i am trying to add some functionality and i am stumped.
depending on what I select in the UICollectionView (in row 1), i would like to load one of a number of different cells in row 2
for example, if i click on the 2nd icon (blood pressure), the cell to load in row 2 will be used to input the systolic and diastolic values. if I click on the 3rd icon (temperature), the cell to load in row 2 will be used to input the temperature, etc...
i've designed the cells in separate class files of UITableViewCell and the corresponding xib files. i know how to register them using
tableView.register(UINib(nibName: "myBPTableCell", bundle: nil), forCellReuseIdentifier: "myBPTableCell")
just not sure where to put the logic to update the table on the fly.
if eventTypeCollectionIndex == 4 {
let cell = tableView.dequeueReusableCell(withIdentifier: "myBPTableCell", for: indexPath) as! myBPdTableCell
return cell
}
i was thinking in
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
but since this function has to return a cell, i am not sure how to return the pre-existing cells for anything other than row 2
thanks
Sami
You would want to define different cases in the cell for row at index method. And then when the user taps a button, reload the cell that you would want to change.
You would either use reload rows or self.tableView.reloadData()
So for example:
var didTap = false
func buttonTapped() {
didTap = true
// Reload table
}
And then either use a different nib for the cell:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if didTap {
// show new appearance
let cell = tableView.dequeueReusableCell(withIdentifier: "myBPTableCell", for: indexPath) as! myBPdTableCell
return cell
} else {
// show original
let cell2 = tableView.dequeueReusableCell(withIdentifier: "myBPTableCell2", for: indexPath) as! myBPdTableCell2
return cell2
}
}
Or you could update the properties of the same nib class:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "myBPTableCell", for: indexPath) as! myBPdTableCell
if didTap {
// show new appearance
cell.backgroundColor = .white
} else {
// show original
cell.backgroundColor = .black
}
return cell
}
Edit based on comment:
You can also just do the following, where you have one cell for row 2 and then another cell for the other rows:
if indexPath.row == 2 {
let cell = tableView.dequeueReusableCell(withIdentifier: "myBPTableCell", for: indexPath) as! myBPdTableCell
if didTap {
cell.backgroundColor = .white
} else {
cell.backgroundColor = .black
}
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "myOtherCell", for: indexPath) as! myOtherCell
return cell
}

ios swift making table list keep getting error about nill

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellReuseIdentifier") as! CustomTableViewCell
let text = data[indexPath.row]
cell.label.text = text
return cell
}
above on is the code that I saw to follow.
my code is as below
I don't know why it getting nil value on
tableView.dequeueReusableCell(withIdentifier: "locCell")
my storyboard is as below
I added identifier like below(you can see it on bottom-right section of pic
You need to register the cell for reuse.
tableView.register(LocationTableCell.self, forCellReuseIdentifier: "locCell")
Or enter your reuse identifier in the storyboard by selecting your cell and then entering the reuse identifier in the properties to the right.
Simply because tableView.dequeueReusableCell(withIdentifier: "cell") by default is nil.
It is the same case for any optional when trying to print it out, example:
let optionalString: String? = ""
print(optionalString)
leads to get:
So, by declaring a constant as:
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")
since dequeueReusableCell(withIdentifier:) returns an optional UITableViewCell instance, the type of cell would be UITableViewCell? (optional UITableViewCell), that's why you are seeing this error.
How to get rid of it?
Assuming that you have set the cell right identifier for your cell:
Well, in case of having your custom cell, you could cast it as:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as? MyCustomCell else {
// something goes wrong
return UITableViewCell()
}
print(cell) // it would be fine for now
// ...
return cell
}
And if you don't have a custom cell, all you have to do is to remove the as? MyCustomCell down casting.
Replace
let cell = tableView.dequeueReusableCell(withIdentifier: "locCell")
With this code:
let cell = tableView.dequeueReusableCell(withIdentifier: "locCell", for: indexPath)
func tableView (_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "cellReuseIdentifier", for: indexPath) as! CustomTableViewCell
cell.label.text = data[indexPath.row].name
return cell
}
NOTE: In your storyboard set tableView Delegate, DataSource and set cell ID cellReuseIdentifier.

Accessing the components in a custom cell?

I have a table using custom cells, within a custom cell i have a picker and a textfield.
I have a custom class for the cell that has outlets for these elements.
In the parent VC of the table I want to be able to reference the contensts of the text field and the value of the picker.
How do i gain access to these values when they are in a custom cell rather than just on the main viewcontroller view?
Code for cell:
class NewExerciseTableViewCell: UITableViewCell {
static let reuseIdentifier = "Cell"
#IBOutlet weak var setNumber: UILabel!
#IBOutlet weak var repsPicker: UIPickerView!
#IBOutlet weak var userExerciseWeight: UITextField!
}
I tried to access it by creating let setCell = NewExerciseTableViewCell() and then trying to access its contents components via its properties, but thats not the way to do it!
Appreciate any assistance here on how I can pull out the values in this cell!
edit: here is my callForRowAt
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as? NewExerciseTableViewCell else {
fatalError("Unexpected Index Path")
}
cell.backgroundColor = UIColor.customBackgroundGraphite()
cell.textLabel?.textColor = UIColor.white
return cell
}
You need to create an object of UITableViewCell with the type of your custom type. Let say you are making a table view then you can add the cells in cellForRowAt. See below code for reference
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! NewExerciseTableViewCell
cell.setNumber.text = // Some Value
}
In the above method the cell is now of type NewExerciseTableViewCell and you can now access any public property of that class.
If you want to get the values of particular cell then you will need to get the cell first. Check the below code for reference
let cell = yourTableView.cellForRowAtIndexPath(indexPath) as! NewExerciseTableViewCell
print(cell.setNumber.text)
This question contains multiple parts, and I'll try to address them one by one.
1- First of all if you want to set the values, properties, etc. you have to do that in tableView(_:cellForRowAt:) method.
(Set the cellIdentifer in your storyboard.)
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellIndetifier", for: indexPath) as! NewExerciseTableViewCell
cell.setNumber.text = "1"
...
return cell
}
2- You cannot access cell's subview just by creating a new one. That doesn't make sense. Use let cell = tableView.cellForRow(at: ...) and then you have a valid cell.setNumber
P.S. btw the setNumber is not a good practice for naming a label. Use set only for setter methods.

Swift : UITableViewCell separator for the first the cell only

I want to display comments without separator as the image below I tried using the separator but it didn't work I need separator only for the first cell.
super.viewDidLoad()
self.commentstableView.separatorColor = UIColor.clearColor()
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
//post's section == 0
if indexPath.section == 0 {
let cell = tableView.dequeueReusableCellWithIdentifier("postCID", forIndexPath: indexPath) as! postCell
self.commentstableView.separatorColor = UIColor.grayColor()
}
let cell = tableView.dequeueReusableCellWithIdentifier("commentCID", forIndexPath: indexPath) as! commentCell
// Configure the cell...
cell.textLabel?.text = comments[indexPath.row]
return cell
}
In StoryBoard, go to your postCell prototype and add a thin UIView and set its background color and height in the Attributes inspector.
But then...
Your logic is garbled and you're shadowing declarations of cell, so it will need to fit into the following structure:
(Edited)
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath.section == 0 {
let cell = tableView.dequeueReusableCellWithIdentifier("postCID", forIndexPath: indexPath) as! postCell
// Additional cell configuration
return cell
}
else {
let cell = tableView.dequeueReusableCellWithIdentifier("commentCID", forIndexPath: indexPath) as! commentCell
// Configure the cell...
cell.textLabel?.text = comments[indexPath.row]
return cell
}
}
You can add a grey view with 1 point height in your first custom cell, and in the rest of cells you don't need this view, and of curse you should hide the separator of the table view so you can show only your custom separator. I hope this helps.

Resources