UIRefreshControl的endRefreshing不够流畅

22
当我的数据加载完成并且表格视图已经重新加载时,我会在刷新控件上调用endRefreshing方法,它会从其加载状态“跳出”并消失 - 我该如何实现一个平滑的动画,在完成时将刷新控件滑开?
7个回答

32

鉴于唯一的答案解释不太精确,我会添加一个新的答案。而且这可能对评论来说太多了。

Halpo 的解决方案是正确的。但所提到的原因是错误的。

调用 -[NSObject (NSDelayedPerforming) performSelector:withObject:afterDelay:] 保证在下一次运行循环迭代中执行该调用。

所以这不起作用,因为有一个小延迟,但是您将其移动到下一个循环。

实际上,您可以将延迟减少到0.0,它仍然有效。此外,还可以进行更正确的(从使用旨在实现某些内容的对象的角度来看)实现。

以下是各种代码片段:

// SOLUTION I

[[self tableView] reloadData];
[[self refreshControl] performSelector:@selector(endRefreshing) withObject:nil afterDelay:0.0];

// SOLUTION II (semantically correct)

[[self tableView] reloadData];
[[NSOperationQueue currentQueue] addOperationWithBlock:^{
    [[self refreshControl] endRefreshing];
}];

// SOLUTION III (GCD)

dispatch_async(dispatch_get_main_queue(), ^{
    [[self refreshControl] endRefreshing];
}];

编辑

正如johann-fradj所指出的,您也可以使用GCD。我非常喜欢GCD,但我认为解决方案II最能描述正在发生的事情。此外,GCD不太符合Objective-C的风格,所以我个人更愿意坚持我的解决方案。


这应该是正确的答案。此外,您可以在主队列中简单地使用dispatch_async。 - Johann Fradj
1
有趣的是,解决方案1起作用了,但解决方案2却没有。在主队列上使用dispatch_async也不行。 - donkey
@JohannFradj 感谢您的建议,我已将其添加到我的答案中。但正如我在评论中指出的那样,我不会在这里使用它;-) - Julian F. Weinert
1
最大公约数(GCD)解决方案以前对我有用,但现在不行了。即使使用解决方案 II 也会导致动画卡顿。然而,在重新加载带有[refreshControl endRefreshing]完成块的表格视图周围提交CATransaction将导致非常平滑的动画。我承认这感觉像是一个hack,因为据我所知[tableView reloadData]是无法动画化的,但这是我特定情况下唯一可行的解决方案。 - Chris
很奇怪它停止工作了。交易的方法很有趣,我一定会尝试看看我是否喜欢它。“停止工作”是什么意思?你是在说一个特定的iOS更新吗? - Julian F. Weinert
#1 对我有用:self.refreshControl.perform(#selector(UIRefreshControl.endRefreshing), with: nil, afterDelay: 0) - Jordan H

27

已解决问题 - 在重新加载数据后,我给endRefresh添加了一个微小的延迟:

[self.tableView reloadData];
[self.refreshControl performSelector:@selector(endRefreshing) withObject:nil afterDelay:0.05];

当刷新完成时,必须显式调用“endRefreshing”。 - alexmorhun
我说“endRefreshing”必须在数据源加载完毕后调用。但是它应该在重新加载TableView或CollectionView之前调用,否则会产生奇怪的错误和卡顿效果。 - Anjan Biswas
1
在Swift中:self.refreshControl.perform(#selector(UIRefreshControl.endRefreshing), with: nil, afterDelay: 0) - Jordan H

5

Swift 4解决方案

    tableView.reloadData()
    if refreshControl?.isRefreshing == true {
        DispatchQueue.main.async {
            self.refreshControl?.endRefreshing()
        }
    }

其他解决方案(延迟,使用CATransaction等)更加复杂,没有任何明显的优势。


4

尝试将对endRefreshing()的调用包装在UIView动画块中:

我也遇到了这个问题。关于endRefreshing(),文档中表示:

UIView.animateWithDuration(0.5) {
    self.refreshControl.endRefreshing()
}

文档说明:
如果启用了动画效果,控件将使用动画隐藏。

0

原因之一可能是在调用endRefresh后,您刷新了表视图。例如,我的调用以前是这样的:

[self.refreshControl beginRefreshing];
[self.network getThings:^(id things, NSError *error) {
    [self.refreshControl endRefreshing];

    if (error) {
        ...
    } else {
        self.things = things;
        [self.tableView reloadData];
    }
}];

这是不好的,因为当 endRefresh 动画运行时,表格会自动更新。所以正确的代码应该像这样:

[self.refreshControl beginRefreshing];
[self.network getThings:^(id things, NSError *error) {
    if (error) {
        [self.refreshControl endRefreshing];
        ...
    } else {
        self.things = things;
        [self.tableView reloadData];
        [self.refreshControl endRefreshing];
    }
}];

1
只是为了在代码设计方面给出一点“教训”:将 endRefreshing 调用放在 else 关闭之后。这样可以使您的代码更加简洁易读。此外,如果您仅对开发过程中的错误原因感兴趣(不应该这样),您可以简单地反转 if 语句,并且可以使用更少的代码。 - Julian F. Weinert
1
你是对的,朱利安。复制的代码片段看起来像这样,因为“...”需要在endRefreshing动画之后运行 :) 在这个特定的例子中,你建议的方法会更好。 - gklka

0
我的解决方案是在调用tableView.reloadData()之前运行refreshControl?.endRefreshing()
refreshControl?.endRefreshing()
tableView.reloadData()

当调用endRefreshing()在调用reloadData()之后时,每当数据重新加载时,TableView都会跳动。

0
我发现在tableView完成重新加载的同时调用endRefreshing()会导致弹跳/闪烁故障。我尝试了其他答案中提到的使用OperationQueueperformSelector的选项,但它们没有为我提供其他应用程序中看到的控制或平滑度。 相反,我选择了这个针对Swift和我的特定情况更新的解决方案:
UIView.animate(withDuration: self.refreshControl.isRefreshing ? 0.5 : 0) {
    self.refreshControl.endRefreshing()
} completion: { finished in
    self.tableView.reloadData()
}

通过这种方式,我们可以确保在tableView重新加载之前结束refreshControl的刷新。这样,我们就可以确保两个操作的分离,而不会发生动画冲突。

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