UIRefreshControl incorrect title offset during first run and sometimes title missing
Solution 1
This is definitely an iOS 7 bug, but I haven't figured out exactly what caused it. It appears to have something to do with the view hierarchy — adding my UITableViewController as a child view to a wrapper view controller appeared to fix it for me at first, although the bug is back since iOS 7 GM.
It looks like adding the following code to your UITableViewController after creating the refresh view fixes the positioning issue for good:
dispatch_async(dispatch_get_main_queue(), ^{ [self.refreshControl beginRefreshing]; [self.refreshControl endRefreshing]; });
Solution 2
calling endRefreshing
under viewWillAppear
did it for me:
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.refreshControl endRefreshing];
}
Under iOS7 with a custom UITableViewController
inside a UINavigationController
Solution 3
I had the same problem and for me it worked with layoutIfNeeded
after setting the attributedTitle:
- (void)setRefreshControlText:(NSString *)text
{
UIColor *fg = [UIColor colorWithWhite:0.4 alpha:1.0];
NSDictionary *attrsDictionary = @{NSForegroundColorAttributeName: fg};
self.refreshControl.attributedTitle = [[NSAttributedString alloc] initWithString:text attributes:attrsDictionary];
[self.refreshControl layoutIfNeeded];
}
Cédric suggested to use [self.refreshControl setNeedsLayout]
, but this does not force an immediate update of the view, so you must use layoutIfNeeded
.
Solution 4
UIRefreshControl seems to still be broken on IOS9.3 when you change the attributedTitle while the tableView is pulled down. What seems to work is to subclass UIRefreshControl and force update its layout once the (attributed) title is changed. The core fix is to trigger a change to the tableView contentOffset (causing some hidden magic in the _update method which layouts the spinner and text subviews) and additionally forcing the frame height to its expected value ensuring the background color fills up the pulled down region.
@implementation MEIRefreshControl
{
__weak UITableView* _tableView;
}
- (instancetype)initWithTableView:(UITableView*)tableView
{
self = [super initWithFrame:CGRectZero];
if (self)
{
_tableView = tableView;
}
return self;
}
@synthesize title = _title;
- (void)setTitle:(NSString *)title
{
if (!PWEqualObjects(_title, title))
{
_title = title;
self.attributedTitle = [[NSAttributedString alloc] initWithString:_title ? _title : @""];
[self forceUpdateLayout];
}
}
- (void)forceUpdateLayout
{
CGPoint contentOffset = _tableView.contentOffset;
_tableView.contentOffset = CGPointZero;
_tableView.contentOffset = contentOffset;
CGRect frame = self.frame;
frame.size.height = -contentOffset.y;
self.frame = frame;
}
@end
Solution 5
I finally found the holy grail on this, which looks working in all cases
note : UIRefreshControl
is added to a UITableViewController
(note, never add UIRefreshControl
just as subview to a normal UIVIewController's UITableView
) (best to add UITableViewController
as a child VC inside a UIViewController
if you must)
note : that this also fixes the problem, that the UIRefreshControl is not vissible at first refresh (link)
Add to you .h
@interface MyViewController ()
@property (nonatomic, assign) BOOL refreshControlFixApplied;
- (void)beginRefreshing;
- (void)beginRefreshingWithText:(NSString *)text;
- (void)endRefreshing;
- (void)endRefreshingWithText:(NSString *)text;
@end
Add to you .m
////////////////////////////////////////////////////////////////////////
#pragma mark - UIRefreshControl Fix ([email protected]) https://stackoverflow.com/questions/19121276/uirefreshcontrol-incorrect-title-offset-during-first-run-and-sometimes-title-mis/
////////////////////////////////////////////////////////////////////////
- (void)beginRefreshingWithText:(NSString *)text {
[self setRefreshControlText:text];
[self beginRefreshing];
}
- (void)endRefreshingWithText:(NSString *)text {
[self setRefreshControlText:text];
[self.refreshControl endRefreshing];
}
- (void)beginRefreshing {
if (self.refreshControl == nil) {
return;
}
if (!self.refreshControlFixApplied) {
dispatch_async(dispatch_get_main_queue(), ^{
if ([self.refreshControl.attributedTitle length] == 0) {
[self setRefreshControlText:@" "];
}
[self.refreshControl beginRefreshing];
dispatch_async(dispatch_get_main_queue(), ^{
[self.refreshControl endRefreshing];
dispatch_async(dispatch_get_main_queue(), ^{
// set the title before calling beginRefreshing
if ([self.refreshControl.attributedTitle length] == 0) {
[self setRefreshControlText:@" "];
}
if (self.tableView.contentOffset.y == 0) {
self.tableView.contentOffset = CGPointMake(0, -self.refreshControl.frame.size.height);
}
[self.refreshControl beginRefreshing];
self.refreshControlFixApplied = YES;
});
});
});
} else {
if (self.tableView.contentOffset.y == 0) {
self.tableView.contentOffset = CGPointMake(0, -self.refreshControl.frame.size.height);
}
[self.refreshControl beginRefreshing];
}
}
- (void)endRefreshing {
if (self.refreshControl == nil) {
return;
}
if (!self.refreshControlFixApplied) {
dispatch_async(dispatch_get_main_queue(), ^{
[self endRefreshing];
});
} else {
if (self.tableView.contentOffset.y < 0) {
self.tableView.contentOffset = CGPointMake(0, 0);
}
[self.refreshControl endRefreshing];
}
}
- (void)setRefreshControlText:(NSString *)text {
UIFont * font = [UIFont fontWithName:@"Helvetica-Light" size:10.0];
NSDictionary *attributes = @{NSFontAttributeName : font, NSForegroundColorAttributeName : [UIColor colorWithHex:0x00B92E]};
self.refreshControl.attributedTitle = [[NSAttributedString alloc] initWithString:text attributes:attributes];
}
Use only methods
- (void)beginRefreshing;
- (void)beginRefreshingWithText:(NSString *)text;
- (void)endRefreshing;
- (void)endRefreshingWithText:(NSString *)text;
Peter Lapisu
At https://min60.com we deliver custom iOS, Android mobile applications for our clients...
Updated on June 06, 2022Comments
-
Peter Lapisu about 2 years
The text is offset wrong by the first launch of UIRefreshControl... later sometimes the refresh text doesn't show up at all and just the spiny is visible
I don't think i had this issue with iOS6... might be related to iOS7
Is in a UITableViewController added as a child to a VC, which resides in a modal presented UINavigationController
- (void)viewDidLoad { [super viewDidLoad]; [self setRefreshControlText:@"Getting registration data"]; [self.refreshControl beginRefreshing]; } - (void)setRefreshControlText:(NSString *)text { UIFont * font = [UIFont fontWithName:@"Helvetica-Light" size:10.0]; NSDictionary *attributes = @{NSFontAttributeName:font, NSForegroundColorAttributeName : [UIColor blackColor]}; self.refreshControl.attributedTitle = [[NSAttributedString alloc] initWithString:text attributes:attributes]; }