UIRefreshControl() in iOS 11 Glitchy effect
For me self.extendedLayoutIncludesOpaqueBars = YES
fixed the issue.
UITableViewController refreshControl is glitchy when UITableViewController frame.height is small
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.
How to add refresh control to collection view large titles navigation bar in iOS 11?
As of iOS 10, UITableView
and UICollectionView
has refreshControl
property.
So, instead of:
tableView.addSubview(refreshControl)
you do:
tableView.refreshControl = refreshControl
and this should work for new big headers in iOS 11.
UIRefreshControl not showing in landscape when in a navigation controller with large titles
We fixed this by recreating the refresh control on every rotation. It's called in viewDidLoad() too.
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
if traitCollection.verticalSizeClass != previousTraitCollection?.verticalSizeClass {
tableView.refreshControl = UIRefreshControl() // !!!
tableView.refreshControl.addTarget(self, action: #selector(didPullToRefresh), for: .valueChanged)
}
}
@objc func didPullToRefresh() {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [weak self] in
tableView.refreshControl.endRefreshing()
}
// ...
}
iOS 11 scroll to top when using large titles doesn't work properly
Okay so I found why the problem occurs, but not how to fix it in that exact scenario.
If you're using large titles and a UITableViewController with the navigation bar translucency set to off the problem will occur.
When you turn translucent back on the problem goes away.
If you're using a TableView in a normal UIViewController the problem always occurs.
Edit
Turns out setting "extendedLayoutIncludesOpaqueBars = true" fixes the problem if you're using a translucent navigation bar!
Similar question: UIRefreshControl() in iOS 11 Glitchy effect
UIRefreshControl without UITableViewController
On a hunch, and based on DrummerB's inspiration, I tried simply adding a UIRefreshControl
instance as a subview to my UITableView
. And it magically just works!
UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
[refreshControl addTarget:self action:@selector(handleRefresh:) forControlEvents:UIControlEventValueChanged];
[self.myTableView addSubview:refreshControl];
This adds a UIRefreshControl
above your table view and works as expected without having to use a UITableViewController
:)
EDIT: This above still works but as a few have pointed out, there is a slight "stutter" when adding the UIRefreshControl in this manner. A solution to that is to instantiate a UITableViewController, and then setting your UIRefreshControl and UITableView to that, i.e.:
UITableViewController *tableViewController = [[UITableViewController alloc] init];
tableViewController.tableView = self.myTableView;
self.refreshControl = [[UIRefreshControl alloc] init];
[self.refreshControl addTarget:self action:@selector(getConnections) forControlEvents:UIControlEventValueChanged];
tableViewController.refreshControl = self.refreshControl;
Related Topics
How to Copy Skspritenode with Skphysicsbody
Swift: Hashable Struct with Dictionary Property
How to Migrate Core Data's Data to App Group's Data
Swift Alternative to Respondstoselector:
Is Dispatchsemaphore a Good Replacement for Nslock
Swift Extension Storage for Protocols
Make a Type Itself -- Not Its Instances -- Conform to a Protocol
Struggling with Notificationcenter/Combine in Swiftui/Avplayer
How to Prevent Eventstore Access Error on First Run
Realitykit - Load Another Scene from the Same Reality Composer Project
Changing Nav Bar Item Programmatically in Swift
How to Create a Nsmutabledictionary in Swift
Why It Is Called the Memberwise Initialiser
Use Different Googleservice-Info.Plist for Single Project in Xcode Using Swift4
"Use Default Container" Doesn't Show in Icloud Capabilities
Countforfetchrequest in Swift 2.0
Need Detailed Explanation for Memoize Implementation in Swift (Wwdc 14, Session 404)