UIRefreshControl-当 UITableViewController 在 UINavigationController 内时,开始刷新无法工作

我已经在我的 UITableViewController (位于 UINavigationController 内部)中设置了一个 UIRefreshControl,它按预期工作(即下拉触发正确的事件)。但是,如果以编程方式调用刷新控件上的 beginRefreshing实例方法,例如:

[self.refreshControl beginRefreshing];

什么都没发生。它应该动画下来,并显示旋转器。在刷新之后调用 endRefreshing方法时,该方法正常工作。

我用这种行为创建了一个基本的原型项目,当我的 UITableViewController 被直接添加到应用程序代表的根视图控制器时,它能正常工作,例如:

self.viewController = tableViewController;
self.window.rootViewController = self.viewController;

但是如果我首先将 tableViewController添加到 UINavigationController,然后将导航控制器添加为 rootViewController,那么 beginRefreshing方法就不再起作用了。例如。

UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:tableViewController];
self.viewController = navController;
self.window.rootViewController = self.viewController;

我的感觉是,这与导航控制器中嵌套的视图层次结构不能很好地处理刷新控件有关——有什么建议吗?

谢谢

71174 次浏览

似乎如果您开始以编程方式刷新,您必须自己滚动表视图,比如说,通过更改内容偏移量

[self.tableView setContentOffset:CGPointMake(0, -self.refreshControl.frame.size.height) animated:YES];

我猜想这样做的原因是当用户位于表视图的中/底部时,滚动到刷新控件可能是不受欢迎的?

Swift 2.2 version by@muhasturk

self.tableView.setContentOffset(CGPoint(x: 0, y: -refreshControl.frame.size.height), animated: true)

简而言之,为了保持这个便携性,添加这个扩展

UIRefreshControl + ProgramaticallyBeginRefresh.swift

extension UIRefreshControl {
func programaticallyBeginRefreshing(in tableView: UITableView) {
beginRefreshing()
let offsetPoint = CGPoint.init(x: 0, y: -frame.size.height)
tableView.setContentOffset(offsetPoint, animated: true)
}
}

也请看这个问题

UIRefreshControl 在调用启动刷新时不显示刺状,contentOffset 为0

在我看来,这是一个 bug,因为它只在 tableView 的 contentOffset 属性为0时发生

我用下面的代码(UITableViewController 的方法)修复了这个问题:

- (void)beginRefreshingTableView {


[self.refreshControl beginRefreshing];


if (self.tableView.contentOffset.y == 0) {


[UIView animateWithDuration:0.25 delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^(void){


self.tableView.contentOffset = CGPointMake(0, -self.refreshControl.frame.size.height);


} completion:^(BOOL finished){


}];


}
}

除了@Dymitry Shevchenko 的解决方案。

我找到了解决这个问题的好办法。你可以创建 UIRefreshControl的扩展来覆盖方法:

// Adds code forgotten by Apple, that changes content offset of parent scroll view (table view).
- (void)beginRefreshing
{
[super beginRefreshing];


if ([self.superview isKindOfClass:[UIScrollView class]]) {
UIScrollView *view = (UIScrollView *)self.superview;
[view setContentOffset:CGPointMake(0, view.contentOffset.y - self.frame.size.height) animated:YES];
}
}

你可以通过在 身份调查员中设置自定义类来使用新类来刷新控件的 Interface Builder。

UITableViewController 在 iOS7之后具有 自动调整 ScrollViewInsets属性。表视图可能已经具有 contentOffset,通常是(0,-64)。

因此,在通过编程方式开始刷新之后,正确的方法是向现有 contentOffset 中添加 resh Control 的高度。

 [self.refreshControl beginRefreshing];
[self.tableView setContentOffset:CGPointMake(0, self.tableView.contentOffset.y-self.refreshControl.frame.size.height) animated:YES];

其他的答案对我都不管用。它们会导致旋转器显示和旋转,但刷新操作本身永远不会发生。这种方法是有效的:

id target = self;
SEL selector = @selector(example);
// Assuming at some point prior to triggering the refresh, you call the following line:
[self.refreshControl addTarget:target action:selector forControlEvents:UIControlEventValueChanged];


// This line makes the spinner start spinning
[self.refreshControl beginRefreshing];
// This line makes the spinner visible by pushing the table view/collection view down
[self.tableView setContentOffset:CGPointMake(0, -1.0f * self.refreshControl.frame.size.height) animated:YES];
// This line is what actually triggers the refresh action/selector
[self.refreshControl sendActionsForControlEvents:UIControlEventValueChanged];

注意,这个示例使用了一个表视图,但它也可以是一个集合视图。

已经提到的办法:

[self.refreshControl beginRefreshing];
[self.tableView setContentOffset:CGPointMake(0, self.tableView.contentOffset.y-self.refreshControl.frame.size.height) animated:YES];

会使旋转器可见。但它不会有生命。我改变的一件事就是这两种方法的顺序,所有的方法都奏效了:

[self.tableView setContentOffset:CGPointMake(0, self.tableView.contentOffset.y-self.refreshControl.frame.size.height) animated:YES];
[self.refreshControl beginRefreshing];

下面是一个使用上述策略的 Swift 扩展。

extension UIRefreshControl {
func beginRefreshingManually() {
if let scrollView = superview as? UIScrollView {
scrollView.setContentOffset(CGPoint(x: 0, y: scrollView.contentOffset.y - frame.height), animated: true)
}
beginRefreshing()
}
}

Fort Swift 2.2 +

    self.tableView.setContentOffset(CGPoint(x: 0, y: -refreshControl.frame.size.height), animated: true)

我使用相同的技术显示用户“数据是更新”的视觉标志。结果用户带来的应用程序从后台和饲料/列表将更新用户界面喜欢用户拉表来刷新自己。我的版本包含三件事

1)谁发送“唤醒”

- (void)applicationDidBecomeActive:(UIApplication *)application {
[[NSNotificationCenter defaultCenter] postNotificationName:kNotificationHaveToResetAllPages object:nil];
}

2) UIViewController 中的观察员

- (void)viewDidLoad {
[super viewDidLoad];


[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(forceUpdateData) name:kNotificationHaveToWakeUp:nil];
}

3)协议

#pragma mark - ForcedDataUpdateProtocol


- (void)forceUpdateData {
self.tableView.contentOffset = CGPointZero;


if (self.refreshControl) {
[self.refreshControl beginRefreshing];
[self.tableView setContentOffset:CGPointMake(0, -self.refreshControl.frame.size.height) animated:YES];
[self.refreshControl performSelector:@selector(endRefreshing) withObject:nil afterDelay:1];
}
}

result

对我来说很完美:

斯威夫特3:

self.tableView.setContentOffset(CGPoint(x: 0, y: -self.refreshControl!.frame.size.height - self.topLayoutGuide.length), animated: true)

如果你在 Swift 3.1中使用 Rxswift,可以使用以下方法:

func manualRefresh() {
if let refreshControl = self.tableView.refreshControl {
self.tableView.setContentOffset(CGPoint(x: 0, y: -refreshControl.height), animated: true)
self.tableView.refreshControl?.beginRefreshing()
self.tableView.refreshControl?.sendActions(for: .valueChanged)
}
}

这适用于 Swift 3.1,iOS 10。

这里是 斯威夫特3号及以后扩展,显示了旋转器以及动画它。

import UIKit
extension UIRefreshControl {


func beginRefreshingWithAnimation() {


DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) {


if let scrollView = self.superview as? UIScrollView {
scrollView.setContentOffset(CGPoint(x: 0, y: scrollView.contentOffset.y - self.frame.height), animated: true)
}
self.beginRefreshing()
}
}
}

为了 Swift 4/4.1

现有答案的组合为我做了工作:

refreshControl.beginRefreshing()
tableView.setContentOffset(CGPoint(x: 0, y: tableView.contentOffset.y - (refreshControl.frame.size.height)), animated: true)

希望这个能帮上忙!

对于 Swift 5,对于我来说,唯一缺少的就是打电话给 refreshControl.sendActions(.valueChanged)。我做了一个延期,让它更干净。

extension UIRefreshControl {


func beginRefreshingManually() {
if let scrollView = superview as? UIScrollView {
scrollView.setContentOffset(CGPoint(x: 0, y: scrollView.contentOffset.y - frame.height), animated: false)
}
beginRefreshing()
sendActions(for: .valueChanged)
}


}

在 Swift 5上测试

viewDidLoad()中使用这个

fileprivate func showRefreshLoader() {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) {
self.tableView.setContentOffset(CGPoint(x: 0, y: self.tableView.contentOffset.y - (self.refreshControl.frame.size.height)), animated: true)
self.refreshControl.beginRefreshing()
}
}