Deselect row of a UITableViewController's UITableView when embedded in a PageViewController - ios

Normally when a UITableViewController that is embedded in a UINavigationController, is returned to from it's detail screen the previously selected row's highlight fades away.
I am trying to replicate this behaviour when a UITableViewController is loaded by a UIPageViewController, but have been unsuccessful so far.
To be clear, my hierarchy when in the detail screen is: UINavigationController>UIPageViewController>UITableViewController>DetailController
Putting the deselectRow:atIndexPath: table view method in the viewDidAppear of the UITableViewController as viewDidAppear is not called. Putting it in the same method, but of the UIPageViewController works, but there is a noticeable delay before the selection fades away.
Has anyone managed to get this to work properly?

Are you referring to UITableView or UITableViewController here
I am trying to replicate this behaviour when a
UITableViewController is loaded by a UINavigationController, but have been unsuccessful so far.
If its a UITableViewController, then it has this clearsSelectionOnViewWillAppear property which does it for you. And it defaults to true.

Not an ideal solution, but this works for me...
Table View Controller code changes:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.tableView.reloadData() //added this
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "appCell") as! AppTableViewCell
cell.app = apps[indexPath.row]
cell.delegate = self
cell.reset() //added this
return cell
}
Table View Cell code changes:
//added this method
func reset() {
shortcutTypesTableView.reloadData()
}
Basically, I'm reloading the whole top table view, and in tableView(_:cellForRowAt:) I'm telling each cell to reset, which force's each cell's table view to also reload.
My app doesn't have much overhead when loading this table view (at least yet) so I don't notice a performance difference. However, I would not call this solution "ideal" for performance.

Related

Some custom UITableViewCells are not rendering

I have a custom cell UITableViewCell, sometimes when the tableview loads, I am getting missing cells (cells where the content view is not rendering at all. I get this behavior fairly consistently when I use reloadRowsAtIndexPath (when a custom object the cell is using is updated for example). If I call reloadData on the tableview, I usually don't get this behavior.
Here is what it looks like when view debugging:
Here is the cell under that (which rendered fine):
My initialization of the cell in cellForRowAtIndexPath is the usual pattern:
Edit - entire cellForRowAtIndexPath:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let reuseIdentifier = "WorkOrderListCell"
let cell:WorkOrderListCell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier) as! WorkOrderListCell
If I scroll the tableview so that the cell which did not render is off screen, and then scroll back, the cell will render.
I have also ensured that I am on the main queue by wrapping my reloadRowsAtIndexPath in a main queue closure but that doesn't make a difference.
What am I missing?
Some times there's an issue with table view on first load. So I would suggest reloading tableView data twice. You can use this extension:
extension UITableView {
func reloadDataWithAutoSizingCellWorkAround() {
self.reloadData()
self.setNeedsLayout()
self.layoutIfNeeded()
self.reloadData()
}
}
Which is found in this issue https://github.com/smileyborg/TableViewCellWithAutoLayoutiOS8/issues/10
Or you can call directly:
self.reloadData()
self.setNeedsLayout()
self.layoutIfNeeded()
self.reloadData()
Issue was that I had a ambiguous constraint in the content view of the cell. It only got reported when I added setNeedsLayout in the delegate method to reload the row. Once I found that, it was easy to find using the a breakpoint and debugging the view. When I removed the distance constraint that was causing the issue, all the rendering issues went away. Thanks to #DionizB for putting me on a good path.

How to push to a different UITableView within the same ViewController and navigate back to first UITableView?

I have a Tab Bar Controller setup. When the user selects a tab, it takes them to a UIViewController. Currently I have a UITableView setup in this View Controller. What I would like to do is, upon selecting a cell from TableView1, I'd like to have a "push" effect where TableView2 comes in from the right side of the screen, and take over.
I've found the following question, similar to mine:
UITableView segue within the same ViewController
One of the suggestion is exactly what I want to achieve, however I could not get it to have that slide effect.
What I've done is added two UITableViews, then tableView1.hidden = true in viewDidLoad.
Then in code:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
var cell: UITableViewCell!
if tableView == tableView1
{
// Dequeue the cell to load data
cell = tableView.dequeueReusableCellWithIdentifier("ID1", forIndexPath: indexPath)
....
}
else if tableView == tableView2
{
// Dequeue the cell to load data
cell = tableView.dequeueReusableCellWithIdentifier("ID2", forIndexPath: indexPath)
....
}
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
{
if tableView == tableView1
{
tableView2.hidden = false
tableView1.hidden = true
playlistVideosTableView.reloadData()
// Deselects the row
tableView1.deselectRowAtIndexPath(indexPath, animated: true)
}
}
However, it doesn't have that "slide effect" and makes the tableview appears instant.
I could make another UIViewController with the second tableview but I don't want to make my Tab Bar disappear.
How can I achieve this, and how can I ensure the ensure gets back to the first UITableView?
Thanks
I would recommend to simply create another UIViewController that has a UITableView, so preferably a UITableViewController.
Then in tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) you call a segue to the new ViewController. This will give you the desired effect and is a cleaner solution as well.
You will not lose your TabBar either with this solution if you embed all ViewControllers in this tab into a NavigationController.
EDIT: sample code
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
performSegueWithIdentifier("editSpecialStage", sender: tableView.cellForRowAtIndexPath(indexPath))
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let indexPath = self.tableView.indexPathForCell(sender as! UITableViewCell)!
let secondViewController = segue.destinationViewController as! SecondViewController
}
And in the Storyboard you have a NavigationController as the first ViewController from the TabBar. Then the first ViewController and then the second, connected via push segue.
I could make another UIViewController with the second tableview but I don't want to make my Tab Bar disappear.
Why will your tab bar disappear? It wouldn't if you used a UINavigationController!
Just do what I say, alright?
First create that new view controller and connect the two VCs with a show segue
First VC -show-> Second VC
Now the whole picture would look like
Tab Bar VC -view controllers-> First VC -show-> Second VC
Now modify this to
Tab Bar VC -view controllers-> Navigation Controller -root vc-> First VC -show-> Second VC
Now the segue will slide the Second VC to the left and your tab bar won't disappear!

Dynamic UITableView is not calling the delegate

I need to add a table view in a specific are of my screen which is inside another UIView. When the user press a button the table view is created and displayed perfectly fine. However I need to know the user selection so I create it in the following way:
self.dynamicTableView = UITableView(frame: self.viewForTableView.bounds)
self.dynamicTableView?.dataSource = self
self.dynamicTableView?.delegate = self
self.messageZone.addSubview(self.dynamicTableView!)
self.dynamicTableView?.reloadData()
The data sources methods are being called but the delegate is not, so the following println is not working
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
println("Hello")
}
As additional information I'm using a custom table view cell
UPDATE: I just discover that if I implement shouldHighlightRowAtIndexPath sometimes if I do a long press it works, like if I keep the cell pressed for about four seconds, but not always, just some times. Can it be any view that is behind the table view what is causing this weird behaviour?

Static tableview cell stays highlighted when returning from different scene

I have a uiviewcontroller with a container on it, embedded inside of that is a uitableviewcontroller that has static cells and static content. When I tap the cell "Trip Info" it segues to the Trip Info View Controller. When coming back the static cell is highlighted.
I have seen many posts saying to add code to the didselectrow tableview method but I dont use one because my content is static. Any ideas?
Okay, just because you have static content doesn't mean you get to skip using the tableview delegate. It's not hard to set up.
tableView.deselectRowAtIndexPath(index)
There's not another way.
I ended up figuring it out on my own.
You need to make a swift file for that UITableViewController that is embedded into the UIViewController.
Then make sure your tableview is a delegate, and then call the
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
}
in that new file you created.

Why does my UITableView's formatting go completely awry when I return from a view controller I segued to?

I have a UITableView with a custom cell, which has a few labels in it that dynamically decide the height of the cell. When I tap on one cell and segue to a new view controller, upon returning all the formatting for the cells is completely messed up, and I can't figure out what is causing it.
Here is what the cells normally look like:
And I have some pretty basic constraints set on them. The top label is pinned to the top and left margins, and must always be >= 20 from the right. The other labels are aligned to the left of this first label, with vertical spacing set between all of them. The middle label has a right spacing constraint to the margin, and the bottom labels are aligned to the baseline of the first and have horizontal spacing between all of them.
When I segue back to this table view it looks like this however:
I can't figure out what is causing it to layout differently than when I left. If I scroll around it seems to "reset" them back to what they should be, but on initial load they're really messed up. I can attach the project if desired, but there's really not much outside of the Storyboard.
cellForRowAtIndexPath:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as CustomTableViewCell
let object = objects[indexPath.row]
cell.title1.text = object.name
cell.title2.text = object.color
cell.title3.text = object.roar
return cell
}
Sample project: http://cl.ly/040L2z0q0V2d
It appears that the table view cells aren't resizing based on the contents when returning from the segue. Using the sample project, I threw a reload data in the viewWillAppear and that seemed to fix the issue.
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.tableView.reloadData()
}
There are actually couple of issues with your project.
Data Loading and AutoLayout.
The first one is causing a strange behavior at the time of drawing the cells with data. When unwinding from the segue you'll see those additional cells on top of your table caused by ambiguous layout calculation.
Solution: Move the data into override func viewWillAppear(animated: Bool) { and perform a tableView.reloadData() (as correctly suggested by #rFessler).
On the other hand, Autolayout is a kind of fiery beast. Tamable. It's worth investigating the topic further. I wasn't able to make your layout work with autosizing cell height but I'll leave few references and the project for you.
References:
http://www.appcoda.com/self-sizing-cells/
http://captechconsulting.com/blog/tyler-tillage/ios-8-tutorial-series-auto-sizing-table-cells
Project:
http://cl.ly/3z3a2Z3a3U2K
I've had a similar problem myself. I downloaded your project and it seems I've solved it by removing and tweaking some constraints. This is how my constraints look now:
Also I've added this to viewDidLoad:
self.tableView.estimatedRowHeight = 120
self.tableView.rowHeight = UITableViewAutomaticDimension
I also added this to test delete:
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete
{
self.objects.removeAtIndex(indexPath.row)
self.tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
}
}
Now you can even rotate the device and remove rows and it's all working splendid!
However, there's still problem if you push this view on a Navigation Controller (Which is what my problem was about in the beginning). See my storyboard below to get some funky labels:
To solve this, it seems we actually have to do a hack! (Damn you apple, what is going on with this?!)
var firstAppearance=true
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
if firstAppearance
{
if let indexPaths = self.tableView.indexPathsForVisibleRows()
{
self.tableView.reloadRowsAtIndexPaths(indexPaths, withRowAnimation: UITableViewRowAnimation.None)
self.firstAppearance = false
}
}
}
At the moment, I think this is as good as it gets.
I played with this and find a simple solution, add this seems to fix the problem.
override func viewWillDisappear(animated:Bool) {
super.viewWillDisappear(animated)
self.tableView.estimatedRowHeight = 166.0
}
Since the the method tableView:estimatedHeightForRowAtIndexPath will be called every time you segue to a new MVC, and change the autolayout, you can just do
override func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
to reuse the autolayout

Resources