How to add a custom cell to a dynamicly generated tableview? - swift

Nearly during a week i'm trying to figure out how i can append a static-/custom tableviewcell to a dynamically generated tableview. I'm populating the cells based on the data i'm getting from the database. Basically what i'm trying to accomplish is like the following picture from the app ClassDojo:
As you may know and see, you can add add as many groups as you want with the ClassDojo app, but the latest cell, in this case Voeg een nieuwe klas toe, will always stay at the bottom of the tableview. That's exactly what i'm trying to do.
What i tried to do till this moment is trying to calculate the latest cell in the tableview and trying to append my custom cell, but unfortunately i couldn't get my head around it.
I would really appreciate if someone can help me out with this.
Thanks in advance.
Please let me know if you guys need any code.
---------EDITED POST---------
I did accomplish to assign my custom cell thanks to #Slayter, but now i'm facing with the problem that my custom cell is immediately overwritten by my dynamically created cells (with Alamofire).
Any help would be appreciated.

ClassDojo iOS Engineer here! More than happy to share how we do this.
Like Slayter mentioned, we do
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myData.count + 1 // Add one for your custom cell
}
But in addition we also do the following:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = nil;
if self.indexIsForCustomCell(indexPath) {
cell = tableView.dequeueReusableCellWithIdentifier(CustomCell.reuseIdentifier)
// Additional configuration
} else {
cell = tableView.dequeueReusableCellWithIdentifier(RegularCell.reuseIdentifier)
// Additional configuration
}
return cell
}
Where CustomCell looks something like this (not exact):
class CustomCell: UITableViewCell {
static let reuseIdentifier = "CustomCell"
// More code
}
And regular cell looks like:
class RegularCell: UITableViewCell {
static let reuseIdentifier = "RegularCell"
// More code
}
The reason your dynamic cells are getting overwritten is because of the line
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as UITableViewCell
Notice that you are using the same cellIdentifier regardless of whether or not it is the custom cell or the regular cells. This means that most likely you are doing the following:
self.tableView.registerClass(RegularCell.class, forIdentifier: "new group")
self.tableView.registerClass(CustomCell.class, forIdentifier: "new group")
When you should be doing:
self.tableView.registerClass(RegularCell.class, forIdentifier: RegularCell.reuseIdentifier)
self.tableView.registerClass(CustomCell.class, forIdentifier: CustomCell.reuseIdentifier)
OR
self.tableView.registerClass(RegularCell.class, forIdentifier: "regularCellReuseIdentifier")
self.tableView.registerClass(CustomCell.class, forIdentifier: "customCellReuseIdentifier")
By using the same identifier key, you are telling the UITableView to treat both cells as the same type. So when it needs to reclaim memory for a new cell being drawn on screen, it's going to use RegularCell and CustomCell interchangeably.
Hope this helped and thanks for checking out our App!
================= EDIT =================
Realized that I forgot to add the indexIsForCustomCell method. Here it is:
func indexIsForCustomCell(indexPath : NSIndexPath) -> Bool {
if self.myData.count > 0 {
return indexPath.section == 0 && indexPath.row == (self.myData.count+1)
}
return indexPath.section == 0 && indexPath.row == 0
}

It's pretty simple. You just need to tell the tableView to expect one more cell than what is in your data source.
Example
In your numberOfRowsInSection method, you will have something like this:
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myData.count + 1 // Add one for your custom cell
}
Then in your cellForRowAtIndexPath method you just need to add some custom logic for that indexPath
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as UITableViewCell
if indexPath.row < myData.count {
// configure cell as normal
} else {
// Add your custom cell logic here
}
return cell
}

Related

Reusable UITableView for Varying Data Input - Swift/Xcode

I have a TableView that I want to reuse for different categories of data (essentially as plugins.. the tableView being a skeleton and being filled with whatever I want it to be). The TableView is filled with different categories of data (and related actions) depending on essentially what ViewController the user came from. I understand how to make it display the various data (just send it whatever array I want it to display), but I can't figure out how I could control the actions for the data at the specific index selected in didSelectRowAtIndexPath.
How could I do this? How could I create an array that has both Strings and executable actions associated with each indices? For example:
arrayOneNames = ["Tigris", "Leo", "Barko"]
arrayOneActions = [displayTheTigers, displayTheCats, displayTheDogs]
If "Leo" is selected in the tableView, then "displayTheCats" is executed. Again, I want each array to be a separate Class that I can use as a plugin, so that I can fill the tableView with whichever Class of data I want it to display and execute, depending on which ViewController the user came from previously. Please answer in Swift.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell=UITableViewCell() // cell you've created
cell.data = arrayOneNames[indexPath.row] // passing relevant data
cell.tag = indexPath.row // the tag you want to pass for particular data
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath)! as UITableViewCell // take selected cell
if(cell.tag == 0) { // call relevant function accordingly.
self.tigrisTouch()
} else if (cell.tag == 1) {
self.leoTouch()
} else {
self.barkoTouch()
}
}
private func tigrisTouch() {
}
private func leoTouch() {
}
private func barkoTouch() {
}

Swift: How to access `UITableViewCell` from other UI Function

I have a UITableView which have some section inside. In one of the section, there is a UIDatePicker. I try to implement the reset date to my datePicker but i get some litle problem. I need to access cell.accessoryType from my UIDatePickerAction . Below my snippet code :
#IBAction func datePickerClaimedAction(_ sender: UIDatePicker) {
self.dateClaimedBy = datePickerClaimed.date
self.labelDateClaimed.text = programProperties.showDateFormatter.string(from: datePickerClaimed.date)
self.buttonDeleteDateClaimed.isHidden = false
// some code to access `cell.accessory`
}
I read some question about this but its still not work. Any other suggest or answer will help for me. Thanks in Advance
I solved it.
First, i make a variable var indexRow: IndexPath?. And then. I cast the indexPath from
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { .. } function.
I need cell from section 2 with row 4 so i add case like:
if indexPath.section == 2 && indexPath.row == 4 {
self.indexRow = indexPath
}
In my DatePicker Action, I use this
let cell = tableView(tableView, cellForRowAt: indexRow!)
So I can get access to cell.accessoryType = .disclosureIndicator to specific cell. Thanks in Advance

Putting multiple type TableViewCells in the same Table View

I have a table view that is currently displaying a type of tableViewCell creating in a XIB File and I want to display a different type of XIB file in the table View at random Places. e.g. Like having Ad display Cells in a tableview at Random Post positions.
How can I do this. I currently have the following code for displaying one Cell:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cardioCell:CardioItemsTableViewCell! = tableView.dequeueReusableCellWithIdentifier("CardioItemsTableViewCell") as? CardioItemsTableViewCell
cardioCell.cellNameLabel.text = "Test String"
return cardioCell!
}
I have this image of the apple news App with one cell without an image. In my instance though, the cell is completely different and has a different layout to the other cells.
Any ideas would be much appreciated.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath.row % 2 == 0
let cardioCell:CardioItemsTableViewCell! = tableView.dequeueReusableCellWithIdentifier("CardioItemsTableViewCell") as? CardioItemsTableViewCell
cardioCell.cellNameLabel.text = "Cells with even number of row"
return cardioCell!
else if indexPath.row % 2 != 0
let otherTypeCell:OtherTypeTableViewCell! = tableView.dequeueReusableCellWithIdentifier("OtherTypeItemsTableViewCell") as? OtherTypeTableViewCell
otherTypeCell.cellNameLabel.text = "Cells with odd number of row"
return otherTypeCell!
}
You can not load cells randomly. You need to have a certain logic based on which you can load different types of tableViewCell.

How to change an UIImageView into a custom cell

I have an UIImageView in a custom cell (#IBOutlet var imageSquadra: UIImageView!) and in the TableViewController I initialize this imageView:
let squadra = DataManager.sharedInstance.arrayCori[indexPath.row]
cell.imageSquadra.image = squadra.sfondo1
Now I need to change the image touching up inside the cell with an other image, and return to the previous image touching up into an other cell.
Is it hard to do?
EDIT: or if was easier, would be fine apply a CAFilter like a shadow to the image.
On your tableview set multiple selection to NO:
tableView.allowsMultipleSelection = false
Then you want something like this on your TableViewController
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
// Store which indexPath has been selected in a global variable
tableView.reloadData()
}
func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
tableView.reloadData()
}
In your cellForRow method you want something like...
cell.useSelectedImage(indexPath == storedIndexPath)
And finally in your custom cell class you want to add a method that does similar to this
func useSelectedImage(bool:useSelected)
{
self.myImage = useSelected ? "selectedImage" : "unselectedImage"
}
My Swift is a bit rusty... but you should be able to get the general idea from what I've done here

UIImage not appearing in prototype cells in UITableView (Swift)

I have created a UITableView Containing 10 cells, each of which have a UIImageView attached to them using auto-layout
The table is populated in my BackTableViewController by the code:
TableArray = ["Home Page", "Membership Card", "Recent News", "Fitness Schedule", "Training Log", "Pool Lap Calculator", "Martial Arts Schedule", "Pool Schedule", "Comments", "Acknowledgements"]
The issue is that none of these images appear in the table until the cell is selected while run. I am out of ideas as to why this is... anyone?
Thanks
Edit: BackTableViewController
import UIKit
class BackTableViewController: UITableViewController {
var TableArray = [String]()
var ImageArray = [String]()
override func viewDidLoad() {
super.viewDidLoad()
TableArray = ["Home Page", "Membership Card", "Recent News", "Fitness Schedule", "Training Log", "Pool Lap Calculator", "Martial Arts Schedule", "Pool Schedule", "Comments", "Acknowledgements"]
ImageArray = ["home-50.png","credit_card-50.png","info-50.png","math-50.png","news-50.png","report_card-50.png","weightlift-50.png"]
// Do any additional setup after loading the view.
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return TableArray.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier(TableArray[indexPath.row], forIndexPath: indexPath) as! UITableViewCell
cell.textLabel?.text = TableArray[indexPath.row]
return cell
}
}
You only need one prototype cell with a reuse identifier. The clue is in the name "prototype". Think of them as blueprints all cells are based off.
In your view controller class (which should be a UITableViewDelegate if it is a UITableViewContoller) you specify the number of rows with this method:
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return yourArray.count
}
This is the method that takes care of displaying the right info in the right row of the table.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
//We create a cell from our prototype that we gave an identifier to in our storyborad.
let cell = tableView.dequeueReusableCellWithIdentifier("ItemCell") as! UITableViewCell
//Create a single instance of an item from our items array. we use the indexpath.row that was passed into this method as the index for the array.
let item = yourArray[indexPath.row]
//You can go into your array and get the right info out regarding this row by using the indexPath.row
cell.textLabel?.text = item
cell.imageView?.image = UIImage(named: item)
//Finally this method returns the correctly configured cell fro the tableview to display.
return cell
}
Hope this helps.
Try giving "Center x" constraint instead of leading space for the image view, and you should give it width and height constraint also.
You need to set your cell identifier using Interface Builder in the Attributes Inspector:
Then in your cellForRowAtIndexPath you need to pass the cell identifier as you set before, something like this:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// set the identifier cell, not TableArray[indexPath.row]
var cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! UITableViewCell
cell.textLabel?.text = TableArray[indexPath.row]
return cell
}
I hope this help you.