Should I dequeue cell instead allocate it, if cell is UITableViewCell? - ios

What is a better way:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell()
return cell
}
or
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(CellIdentifier) //cell has empty prototype in storyboard where setted this cellIdentifier
return cell
}
I know dequeueReusableCellWithIdentifier will reuse cells. But it would be better for performance in this case if we use simple UITableViewCell?

You should never use the first method. Performance wise the dequeuing method is much much better.
If you want a cell with a different style or different labels then create that in the storyboard or create a subclass.
But always use the dequeuing method.

Both are absolutely fine. It depends on you what type of setting you want. As far as dequeueReusableCell is consider it just save the memory. It allocates only some number of cell which can easily be shown into you device screen. For example if your screen can view 7 cells it will allocate 8 cells and then try to reuse these cell for different number of objects with respect to their indexPath.

Related

Reusing dynamic TableViewCells without having to maintain of each cell's state in Controller

I have a UITableView that uses a cell that has 3 expandable and collapsable subviews in them. I would prefer to maintain the state of these views in my UITableViewCell class itself (states as in collapsed or expanded)
Since they are reusable cells, currently, if I expand view 1 in cell A, and then scroll down to cell B, it's view 1 will be expanded. I don't want this. I want it collapsed. But, if I scroll back up to cell A, I want it to still be expanded.
Other than storing all of these states in an array or dictionary
var expandedViewOneCells: [Int] = []
var expandedViewTwoCells: [Int] = []
etc.
I would prefer to have the cells essentially of act individually and maintain their own state... But how would I do this when cells are reused? Keep in mind, I will always only have at most 3 of these kinds of cells, so can I set something like only reuse after 3 cells.
Would it be wise to keep an array of the cells I load, and then on cellForRowAt load the cell from that array based on the index and return it?
In your func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell function try not to deque a cell but create a new instance of your cell
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = MyCustomCell()
return cell
}
If you are loading your cell from a xib file you need a way to create your custom cell from that nib. Add the following method to your CustomCell class
static func loadFromNib() -> RequestTableViewCell {
let nib = UINib(nibName: "\(MyCustomCell.self)", bundle: Bundle.main)
let cell = nib.instantiate(withOwner: self, options: nil)[0] as! MyCustomCell
return cell
}
Then in your func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell this will create a new cell for every row and not reuse a cell when scrolling
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = MyCustomCell.loadFromNib()
return cell
}
A solution like this may not be optimal if your table view has a lot of rows but for a SMALL amount of rows this should be okay
I see 2 solutions to your problem:
Use 3 View Controllers. They should never get destroyed, and add / remove the corresponding VC's view on top of the .contentView of the cell as it appears or goes off-screen. See the solution here http://khanlou.com/2015/04/view-controllers-in-cells/ The Custom Cell itself is just a view, shouldn't really be concerned with the state, but if we move that logic to a View Controller - we should be fine, an we are not violating MVC. Plus, the View Controller can keep track of the height of the view, based on the state, and heightForRow(at:) can ask it for that
I'd use a Stack View as this is a perfect scenario for it. I'd probably represent the Cell itself as another stack view. Not sure exactly what the views look like and how they change, but it may end up as simple as hiding / unhiding the second view from the Stack View that represent a "cell".

Two TableViews that have same prototype cell, constraints behave differently

I created two ViewControllers and two TableViews. Then i added prototype cell to one TableView, set it up according to my needs, copied it to the other TableView, changed its class and identifier and linked it up in ViewController that is datasource and delegate for each one.
The problem is, FEEDING one is behaving good, having constraints as expected, and the WALKING one is not, but i have no idea why since they have all same properties in each one's:
ViewControllers:
FEEDING
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = myFeedingTableView.dequeueReusableCellWithIdentifier("feedingcell", forIndexPath: indexPath) as! FeedingCell
cell.time.text = self.vremena[indexPath.row]
return cell
}
WALKING
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = myWalkingTableView.dequeueReusableCellWithIdentifier("walkingcell", forIndexPath: indexPath) as! WalkingCell
cell.time.text = self.vremena[indexPath.row]
return cell
}
CustomCell files
each one is connected to its class
FeedingCell is class of feeding prototype cell
WalkingCell is class of feeding prototype cell
Constraints
and the constraints are same, as you can see on the picture.
Here is the image providing different results and constraints:
image
Solved by changing rowHeight settings in TableView. Thanks #SilentLupin

UITableView with transparent cells overlap old cells when reloading data

I have a UITableView with a transparent background color and cells which also have a transparent background color. When I reload my tableView with:
dataSource = some new data
tableView.reloadData()
I can see the new cells overlap the old ones.
I did try to use use
tableView.beginUpdates()
// remove all rows here
change data source
// insert new rows here
tableView.endUpdates()
but it did not work. I tried as well tableView.reloadRowsAtIndexPath(...) but still no luck.
And finally I set all my cells and my table view to clear graphic context when redrawn but it did not manage to fix this issue.
Any help would be greatly appreciated.
My cell creation function:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("suggestioncell")
cell.backgroundColor = UIColor.whiteColor().alpha(0.1)
cell.textLabel?.text = (suggestions![indexPath.row] as! SVPlacemark).formattedAddress
cell.clearsContextBeforeDrawing = true
cell.contentView.clearsContextBeforeDrawing = true
return cell
}
Try overriding prepareForReuse in you UITableViewCell subclass, and reset content there.
Here's what the documentation says about that:
Prepares a reusable cell for reuse by the table view's delegate.
If a UITableViewCell object is reusable—that is, it has a reuse identifier—this method is invoked just before the object is returned from the UITableView method dequeueReusableCellWithIdentifier:. For performance reasons, you should only reset attributes of the cell that are not related to content, for example, alpha, editing, and selection state. The table view's delegate in tableView:cellForRowAtIndexPath: should always reset all content when reusing a cell. If the cell object does not have an associated reuse identifier, this method is not called. If you override this method, you must be sure to invoke the superclass implementation.
Custom UITableViewCell class:
class customCell: UITableViewCell {
override func prepareForReuse() {
self.textLabel?.text = nil
}
}
In your cellForRowAtIndexPath method:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("suggestionCell", forIndexPath: indexPath) as! customCell
cell.backgroundColor = UIColor.whiteColor().alpha(0.1)
cell.textLabel?.text = (suggestions![indexPath.row] as! SVPlacemark).formattedAddress
return cell
}
And, of course in your XIB/Storyboard, set the cell class to CustomCell, and set its reuse identifier.

Add custom UIView with different height into UITableViewcell one after another top to bottom

Hi I am newbie of swift ios, how can I add custom UIView(s) with different height (depends on condition) into UITableViewCell one by one, and one below another. Just like the reminder app in iphone but each cell with different/same customViews - depends on condition. What's the best approach or practice for this. Thanks gurus.
To use custom views into UITableViewCell you have to add a xib file and add to a class extended of UITableViewCell.
To specify your custom view and UITablaView you must do it here:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell.
Example:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("yourCellIdentifier") as! CustomView
...
return cell
}
To set auto height and more info you can follow this tutorial:
dynamic table view cell height ios 8 swift

UITableView strange behaviour when using more than 1 Prototype Cell

So I have a UITableView which I plan to use 2 prototype cells inside of it. Let's call them Cell A and Cell B. Cell A has it's own layout and Cell B has it's own layout.
Here's the thing, in a typical UITableView implementation with only 1 prototype cell, after setting up all the cell and it's properties, cellForRowAtIndexPath takes care of populating all the rows based on the (x) number of items from numberOfRowsInSection.
Here is where I am having a problem. I've implemented both my prototype cells in my UITableView and when I run it, I notice cellForRowAtIndexPath is only being called twice, even though I have a value in (x) number of items which is greater than 2. Doesn't matter what I set it to, it only gets called twice. I already have the necessary if statements to pick a cell prototype based on the cell index etc...so that's not the issue. The issue is cellForRowAtIndexPath just gets called twice instead of looping thru all the items.
Why is this and how can I fix it?
This is my code for the DataSource methods:
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 8
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
Scripts.log("Data Count = \(indexPath.row)")
if indexPath.row == 0{
var cell: ContactDetailImageCell = tableView.dequeueReusableCellWithIdentifier(NAME_OF_CUSTOM_IMAGE_CELL) as ContactDetailImageCell
cell.cardPhoto.image = aContact.profilePicture
cell.fullName.text = aContact.getDisplayName()
cell.workplace.text = aContact.workplace
return cell
}
else{
var cell: ContactDetailPhoneNumCell = tableView.dequeueReusableCellWithIdentifier(NAME_OF_CUSTOM_PHONE_CELL) as ContactDetailPhoneNumCell
return cell
}
return UITableViewCell()
}
What height are your cells? cellForRowAtIndexPath will only get called if the table thinks it needs to display the cell. Hence the whole reuse mechanism. So if you have 8 cells and it thinks 2 fill the screen it will not ask for any more until you scroll up/down.
What are you returning for heightForRowAtIndexPath.

Resources