Singular UITableViewDiffableDataSource used by multiple tableviews - ios

How would I go about implementing multiple UITableViewControllers all using the same UITableViewDiffableDataSource object.
The constructor takes only one tableview object like so
dataSource = UITableViewDiffableDataSource<Int, UUID>(tableView: tableView) {
(tableView: UITableView, indexPath: IndexPath, itemIdentifier: UUID) -> UITableViewCell? in
// configure and return cell
}
Which makes me think it is not even possible to make a UITableViewDiffableData reusable

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".

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

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.

Expand UITableViewCell with multiple sections

I want to expand my UITableViewCells with a UITableView that has multiple sections. The way I'm doing it is as follows:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
selectedRowIndex = indexPath
habitTableView.beginUpdates()
habitTableView.endUpdates()
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if (selectedRowIndex != nil && indexPath == selectedRowIndex!){
return 147
}
return 90
}
However there are some strange behaviors, for example, if one cell expands it sort of "eats up" the next section header underneath it so the section header disappears. I am just wondering - is there any nuances with a UITableView that has multiple sections?
So, you can do one thing is to have two protocol type of UITableViewCell. One is for normal and another one is for expanded. Once you type on a cell, you just need to update the delegate to use expanded one instead normal one. When updating, you only need to call reloadRowsAtIndexPaths to prevent reload everything.
You only need to create one more cell prototype and have a boolean value for indicating the state. Then, add your logic to cellForRowAtIndexPath.

How are functions in table view controllers called?

I am a new to iOS development using Swift.
I am trying to understand how functions are called in a view controller that controls a table view.
In the examples I am looking at, the view controller runs three functions, all called 'table view', and each function does something unique such as returning how many rows are in a section, or using reusable cells.
But I just can't see when or how these functions are called.
Are they called when the user navigates to the view? If so, how? And how come these different functions all have the same name (i.e. func tableView ())?
Here is some sample code:
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dwarves.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier(simpleTableIdentifier)
as? UITableViewCell
if (cell == nil) {
cell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: simpleTableIdentifier)
}
cell!.textLabel.text = dwarves[indexPath.row]
cell!.textLabel.font = UIFont .boldSystemFontOfSize(15)
return cell!
}
I just can't see when or how these functions are called.
A UITableView calls these methods on its delegate or dataSource to get information about what it should display, and communicate when certain actions occur. For a UITableViewController, the controller itself is both the delegate and the data source. So you won't see these methods get called unless you set a breakpoint within them.
Are they called when the user navigates to the view? If so, how?
When the user navigates to the view, the default implementation of UITableViewController sets the table view's delegate and data source properties to self. The table view itself calls these methods lazily when it needs information to create, size, layout, and display table cells appropriately.
And how come these different functions all have the same name? I.e func tableView ()
They don't. In both Swift and Objective-C, argument names are part of the method name. For example, this method:
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 80
}
is named tableView(_:heightForRowAtIndexPath:).

Modify custom made cells from storyboard

I have an iOS app with a lot of static cells (for a preferences view), so it makes sense to put all of that in storyboard, but I would like to be able to add a checkmark to them based on if the preference is set or not.
I have my delegate method setup
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
}
I just can't figure out how to "grab" the cell from the interface builder using the indexpath so that I can decide programmatically whether or not I should add a checkmark. I have a feeling there is some sort of superclass/delegate method I can call, but I'm not sure what it is. Thanks.
When you use static cells, you need to put them in a UITableViewController because there's magic going in there. Under the hood, it implements those data source methods for you. But you can override them. The important thing is that you need to call the super version to let it do it's job. If the method returns a value you need to return that too.
So in your case:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = super.tableView(tableView, cellForRowAtIndexPath: indexPath)
let isChecked = true // put your logic to determine whether the cell should be checked here
cell.accessoryType = isChecked ? UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone
// ...
return cell
}

Resources