Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UITableViewController refreshControl is glitchy when UITableViewController frame.height is small

I have a problem that stems from the fact that UITableViewController refreshControl is glitchy when the frame of the UITableViewController is below a certain height.

As it stands, I have a UIViewController, and in it I have a ContainerView that embeds a UITableViewController. I want the height to be 50% of the screen.

When I use the refreshControl, I get this kind of behavior: The tableView jumps down at the very end when scrolling down. You'll notice it towards the end of this video when I decide to scroll down slowly.

This problem does not occur when the ContainerView frame is above a certain value. So, when the height is 75% of the screen, everything works perfectly and the refreshControl is smooth. When it is 50%, then that bug happens.

Two different things I have tried:

  1. self.tableView.frame = CGRectMake(0, numOfPixelsToDropTableBy, self.tableView.frame.size.width, self.tableView.frame.size.height) is one thing that I tried. The problem with this is if you want to give the tableView rounded corners via the ContainerView and the fact that your ContainerView still takes up more space and this makes constraints for other elements awkward.

  2. I went to the Storyboard and I basically had the top of the ContainerView where I wanted. Then, I had the bottom extend beyond the bottom of the screen to give the ContainerView a large enough height... but the user would never know. Except, they would know because now the tableView extends beyond the screen and I can't see the last few rows of my tableView.

Ultimately... I don't want to use a 3rd-party library, but I want a perfectly functioning refreshControl. How can I fix this?

like image 496
David Avatar asked Sep 15 '15 21:09

David


2 Answers

1.I've created next architecture
enter image description here
2.Added some constraints
enter image description here
3.In TableViewController I've added next code

import UIKit

class TableViewController: UITableViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        self.refreshControl = UIRefreshControl(frame: CGRectZero)
        self.refreshControl!.addTarget(self, action: "refresh:", forControlEvents: .ValueChanged)
    }

    func refresh(sender:UIRefreshControl)
    {
        self.refreshControl?.endRefreshing()
    }
}
  1. And uploaded example to github

IMPORTANT NOTE I've used Xcode 7 and Swift 2.

like image 191
Alexey Bondarchuk Avatar answered Oct 14 '22 19:10

Alexey Bondarchuk


I managed to recreate your issue exactly by accident and managed to fix it, but at the cost of having no margins at all.

The jumping seems to happen if you use margin based constraints or any kind of margin for your container view. If you remove the margin relative part of the constraints, the jumping disappears.

Very strange, but seems to be the issue. As soon as I add any margin relative constraint for the container, the issue returns. Removing it and the display goes back to smooth scrolling.

This would seem to be a bug and I think you will need to raise a bug report with Apple.

Update:

Looking again, the issue seems to appear as soon as the container view is not the full width of the screen. Adding any sort of margin to the container view (via layout relative to margin or by setting a non zero offset on a constraint) results in the jumpy behavior.

Update:

Something would appear to be fundamentally broken with UITableView scrolling inside a container view which has any kind of margin. If you override the scrolling delegate, the content offset/bounds of the scroll view are being changed at the moment the refresh is about to trigger. Here is some debug showing the issue

Start pulling down:

Scroll bounds = {{0, -127.33333333333333}, {374, 423}}
Scroll pos = [0.000000,-127.333333]
Scroll bounds = {{0, -127.66666666666667}, {374, 423}}
Scroll pos = [0.000000,-127.666667]
Scroll bounds = {{0, -128.33333333333334}, {374, 423}}
Scroll pos = [0.000000,-128.333333]

Ok before here ------->

Activity spinner becomes fully populated. Jump in scroll position upwards.

Scroll bounds = {{0, -104}, {374, 423}}
Scroll pos = [0.000000,-104.000000]

Scroll position corrects itself

Scroll bounds = {{0, -128.33333333333334}, {374, 423}}
Scroll pos = [0.000000,-128.333333]
Scroll position jumps the other direction by the same amount
Scroll bounds = {{0, -151.33333333333334}, {374, 423}}
Scroll pos = [0.000000,-151.333333]

Value changed target action fires. Bounds seem to reset (think 44 is height of refresh control

Scroll bounds = {{0, -44}, {374, 423}}
Scroll pos = [0.000000,-44.000000]
Corrects back
Scroll bounds = {{0, -151.33333333333334}, {374, 423}}
Scroll pos = [0.000000,-151.333333]
Fully corrects to the right scroll position by jumping back.

Ok after here ------>

Scroll bounds = {{0, -128.66666666666666}, {374, 423}}
Scroll pos = [0.000000,-128.666667]
Scroll bounds = {{0, -129}, {374, 423}}
Scroll pos = [0.000000,-129.000000]
Scroll bounds = {{0, -129.33333333333334}, {374, 423}}
Scroll pos = [0.000000,-129.333333]
Scroll bounds = {{0, -129.66666666666666}, {374, 423}}
Scroll pos = [0.000000,-129.666667]
Scroll bounds = {{0, -130}, {374, 423}}

Conclusion

There seems to be no easy way I can find to work around this. I tried creating my own table view controller and the jumping goes away but is replaced by a different effect: that being that when you scroll down the top cell disappears, then reappears. I imagine it relates to the same internal issue, just being expressed differently.

Unfortunatley looks like you might have to put up with the effect or go for no margin. I would raise a bug report with Apple.

Only alternative option would be to create the margins in your UITableViewCells. You could make the cell content view have a clear background and introduce a left and right margin to your cells using an internal container view for your cell content. I think that may be you best chance.

And Finally...

Not to be defeated, you can apply a scaling transform to the navigation controller for the table view to create a margin doing the following in your table view controller:

override func viewWillAppear(animated: Bool) {
  super.viewWillAppear(animated)

  // Add a scaling transform to the whole embedded controller view.      
  self.navigationController!.view.transform=CGAffineTransformMakeScale(0.9, 0.9);
}

This makes the view for the embedded controller appear 90% smaller so it has a margin around the border. Change the scale to to change the border size.

Not ideal, but works perfectly with no jump scrolling and has a border. It also leaves you totally free to use rounded corners etc as the whole content is scaled.

like image 37
Rory McKinnel Avatar answered Oct 14 '22 20:10

Rory McKinnel