UIRefreshControl首次运行时标题偏移不正确,有时标题会丢失。

29

界面刷新控件(UIRefreshControl)首次启动时,文字显示偏移错误...有时候刷新的文字根本不显示,只有圆圈在转。

我觉得在iOS6中没有这个问题...可能与iOS7有关。

该控件位于UITableViewController中,作为子视图控制器添加到一个模态展示的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];

}

输入图像描述

输入图像描述


这个问题是在模拟器上出现的还是在真实设备上出现的?我花了几个小时来解决这个问题,但当我在真实设备上尝试相同的代码时,它就顺利运行了。 - autremoi
你找到解决办法了吗?我也遇到同样的问题。 - Felix
没有一件事情对我来说百分之百地有效 :( - Peter Lapisu
@FelixGuerrero 看一下答案,终于找到了解决方案。 - Peter Lapisu
苹果公司会修复这个问题吗? - lostintranslation
8个回答

39
这明显是 iOS 7 的一个 bug,但我还没弄清具体是什么原因。似乎与视图层次结构有关——将我的 UITableViewController 添加为子视图到包装视图控制器似乎一开始就修复了它,尽管自从 iOS 7 GM 后这个 bug 又出现了。
在创建刷新视图后,添加以下代码到你的 UITableViewController 中,看起来可以永久解决位置问题:
dispatch_async(dispatch_get_main_queue(), ^{
    [self.refreshControl beginRefreshing];
    [self.refreshControl endRefreshing];
});

确实有所帮助,但在我的情况下,attributedTitle 仍然与 tableCells 在刷新过程的开头和结尾重叠。当我切换到 Xcode 5 后,这种情况开始发生。 - autremoi
3
即使采用了解决方法,这个问题在以后的iOS版本中仍然出现了。我注意到邮件应用程序在应用程序旋转器中没有标签,这让我相信这是一个内部已知的问题,他们还没有完全解决。 - Ben Jackson
终于找到了解决方法,详见帖子。 - Peter Lapisu
有趣的是,我在使用SSPullToRefresh时遇到了类似的问题,并尝试切换到UIRefreshControl来解决这个问题,但似乎这里存在着更大的混乱。 - Marius Soutier
2
呃,针对这种问题似乎更适合使用 [self.refreshControl setNeedsLayout];。开始/结束刷新是一种hack方法。 - onekiloparsec
显示剩余4条评论

4

我在viewWillAppear方法中调用endRefreshing方法解决了这个问题:

-(void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    [self.refreshControl endRefreshing];
}

在iOS7中,使用自定义的UITableViewControllerUINavigationController中:

3

我曾经遇到过同样的问题,对我来说,在设置了attributedTitle之后使用layoutIfNeeded可以解决。

- (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 建议使用 [self.refreshControl setNeedsLayout],但这并不会立即更新视图,所以您必须使用 layoutIfNeeded


2

在IOS9.3上,当您在下拉tableView时更改attributedTitle时,UIRefreshControl似乎仍然存在问题。看起来的解决方法是子类化UIRefreshControl并在更改(attributed)标题后强制更新其布局。

核心修复方法是触发对tableView contentOffset的更改(导致_update方法中的一些隐藏魔法布局旋转器和文本子视图),并且还将框架高度强制设置为其预期值,确保背景颜色填充下拉区域。

@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

2

我终于在这个问题上找到了圣杯,它在所有情况下都有效。

注意:将UIRefreshControl添加到UITableViewController中(请注意,不要将UIRefreshControl作为普通UIViewControllerUITableView的子视图添加)(最好将UITableViewController作为子VC添加到UIViewController中,如果必须)

注意:这也解决了一个问题,即第一次刷新时UIRefreshControl不可见的问题(链接

添加到你的.h文件中

@interface MyViewController ()

@property (nonatomic, assign) BOOL refreshControlFixApplied;

- (void)beginRefreshing;
- (void)beginRefreshingWithText:(NSString *)text;
- (void)endRefreshing;
- (void)endRefreshingWithText:(NSString *)text;

@end

添加到你的.m文件中。
////////////////////////////////////////////////////////////////////////
#pragma mark - UIRefreshControl Fix (peter@min60.com) https://dev59.com/S2Ik5IYBdhLWcg3wl_TE
////////////////////////////////////////////////////////////////////////

- (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];

}

只使用方法

- (void)beginRefreshing;
- (void)beginRefreshingWithText:(NSString *)text;
- (void)endRefreshing;
- (void)endRefreshingWithText:(NSString *)text;

1
对我来说,这是为了一个如此小的问题而编写的太多代码(我相信苹果很快就会解决)。 - meaning-matters
4
好的,仍未修复。 - Jasper

1
我曾经遇到过同样的问题,我通过在初始化刷新控件后直接设置带空格字符串的属性文本来解决它。
_refreshControl = [[UIRefreshControl alloc]init];
[_refreshControl setAttributedTitle:[[NSAttributedString alloc]initWithString:@" "]];

之后,将新的属性文本设置为刷新控件来进行刷新没有任何问题。

[[self refreshControl] setAttributedTitle:[[NSAttributedString alloc]initWithString:[NSString stringWithFormat:@"Последнее обновление: %@", [dateFormat stringFromDate:[_post dateUpdated]]]]];

更新

我注意到当我使用attrsDictionary时,问题会再次出现:

这段代码可以正常工作。

NSAttributedString* attributedString = [[NSAttributedString alloc]initWithString:string];
[[self refreshControl] setAttributedTitle: attributedString];

并且这使得下拉刷新控件的标题在视图加载后直接显示。
NSAttributedString* attributedString = [[NSAttributedString alloc]initWithString:string attributes:attrsDictionary];
[[self refreshControl] setAttributedTitle: attributedString];

我还没有找到解决方案。

更新

最终找到了解决方案,在刷新控件初始化后,设置带有属性attrsDictionary的属性字符串。

NSDictionary *attrsDictionary = [NSDictionary dictionaryWithObjects:
                                 [NSArray arrayWithObjects:[UIColor appDarkGray], [UIFont fontWithName:@"OpenSans-CondensedLight" size:14.0f], nil] forKeys:
                                 [NSArray arrayWithObjects:NSForegroundColorAttributeName, NSFontAttributeName, nil]];
[_refreshControl setAttributedTitle:[[NSAttributedString alloc]initWithString:@" " attributes:attrsDictionary]];

所以在此之后,设置新的刷新控件标题就没有问题了。

1
这是似乎可以解决所有问题的代码。其中许多涉及开始或结束刷新的其他控件都会干扰其他部分的控制。
//This chunk of code is needed to fix an iOS 7 bug with UIRefreshControls
static BOOL refreshLoadedOnce = NO;
if (!refreshLoadedOnce) {
  __weak typeof(self) weakself = self;
  [UIView animateWithDuration:0.25 delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^(void){
    self.tableView.contentOffset = CGPointMake(0, -weakself.refreshControl.frame.size.height);
  } completion:^(BOOL finished) {
    weakself.refreshControl.attributedTitle = self.refreshControl.attributedTitle;
    [weakself.refreshControl setNeedsUpdateConstraints];
    [weakself.refreshControl setNeedsLayout];
    refreshLoadedOnce = YES;
  }];
}
//End of bug fix

我将这段代码放在第一次调用控件的beginRefreshing方法之前。它只会被调用一次,所以如果多次调用也不会影响性能。但是我想它也可以在其他几个地方执行(例如ViewWillAppear、ViewDidAppear,甚至是ViewDidLoad)。 - Brett

0
对我来说解决方案是在viewDidAppear中设置文本,不需要在主队列上调用beginRefreshingendRefreshing
-(void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];

    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    [formatter setDateFormat:@"d MMM, HH:mm"];
    NSString *lastUpdated = [NSString stringWithFormat:NSLocalizedString(@"refresh_last_updated", nil),[formatter stringFromDate:[NSDate dateWithTimeIntervalSince1970:[[[DatabaseController sharedInstance] getCurrentSettings].lastTimeStamp doubleValue]]]];
    UIFont *font = [UIFont fontWithName:FONT_LATO_LIGHT size:12.0f];
    NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:lastUpdated attributes:@{NSFontAttributeName:font}];

    _refreshControl.attributedTitle = attrString;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接