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;
To eliminate the stutter that is caused by the accepted answer, you can assign your UITableView
to a UITableViewController
.
_tableViewController = [[UITableViewController alloc]initWithStyle:UITableViewStylePlain];
[self addChildViewController:_tableViewController];
_tableViewController.refreshControl = [UIRefreshControl new];
[_tableViewController.refreshControl addTarget:self action:@selector(loadStream) forControlEvents:UIControlEventValueChanged];
_theTableView = _tableViewController.tableView;
EDIT:
A way to add a UIRefreshControl
with no UITableViewController
with no stutter and retain the nice animation after refreshing data on the tableview.
UIRefreshControl *refreshControl = [UIRefreshControl new];
[refreshControl addTarget:self action:@selector(handleRefresh:) forControlEvents:UIControlEventValueChanged];
[self.theTableView addSubview:refreshControl];
[self.theTableView sendSubviewToBack:refreshControl];
Later when handling the refreshed data...
- (void)handleRefresh:(UIRefreshControl *)refreshControl {
[self.theTableView reloadData];
[self.theTableView layoutIfNeeded];
[refreshControl endRefreshing];
}
What you would try is use container view inside ViewController you are using. you can define clean UITableViewController subclass with dedicated tableview and place that in the ViewController.
Well UIRefreshControl is a UIView subclass, so you can use it on it's own. I'm not sure though how it renders itself. The rendering could simply depend on the frame, but it also could rely on a UIScrollView or the UITableViewController.
Either way, it's going to be more of a hack than an elegant solution. I recommend you look into one of the available 3rd party clones or write your own.
ODRefreshControl
SlimeRefresh
Try delaying the call to the refreshControl -endRefresh
method by a fraction of a second after the tableView reloads its contents, either by using NSObject's -performSelector:withObject:afterDelay:
or GCD's dispatch_after
.
I created a category on UIRefreshControl for this:
@implementation UIRefreshControl (Delay)
- (void)endRefreshingAfterDelay:(NSTimeInterval)delay {
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[self endRefreshing];
});
}
@end
I tested it and this also works on Collection Views. I've noticed that a delay as small as 0.01 seconds is enough:
// My data refresh process here while the refresh control 'isRefreshing'
[self.tableView reloadData];
[self.refreshControl endRefreshingAfterDelay:.01];
IOS 10 Swift 3.0
it's simple
import UIKit
class ViewControllerA: UIViewController, UITableViewDataSource, UITableViewDelegate {
@IBOutlet weak var myTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
myTableView.delegate = self
myTableView.dataSource = self
if #available(iOS 10.0, *) {
let refreshControl = UIRefreshControl()
let title = NSLocalizedString("PullToRefresh", comment: "Pull to refresh")
refreshControl.attributedTitle = NSAttributedString(string: title)
refreshControl.addTarget(self,
action: #selector(refreshOptions(sender:)),
for: .valueChanged)
myTableView.refreshControl = refreshControl
}
}
@objc private func refreshOptions(sender: UIRefreshControl) {
// Perform actions to refresh the content
// ...
// and then dismiss the control
sender.endRefreshing()
}
// MARK: - Table view data source
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 12
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)
cell.textLabel?.text = "Cell \(String(indexPath.row))"
return cell
}
}
If you want to learn about iOS 10 UIRefreshControl read here.
Adding the refresh control as a subview creates an empty space above section headers.
Instead, I embedded a UITableViewController into my UIViewController, then changed my tableView
property to point towards the embedded one, and viola! Minimal code changes. :-)
Steps:
@IBOutlet weak var tableView: UITableView!
with the one from the newly embedded UITableViewController, as shown belowclass MyViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
let tableViewController = self.childViewControllers.first as! UITableViewController
tableView = tableViewController.tableView
tableView.dataSource = self
tableView.delegate = self
// Now we can (properly) add the refresh control
let refreshControl = UIRefreshControl()
refreshControl.addTarget(self, action: "handleRefresh:", forControlEvents: .ValueChanged)
tableViewController.refreshControl = refreshControl
}
...
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With