129

我在我的 UITableViewController 中设置了一个 UIRefreshControl(它位于 UINavigationController 中),它按预期工作(即下拉触发正确的事件)。但是,如果我以编程方式调用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;

我的感觉是这与导航控制器中的嵌套视图层次结构与刷新控件不兼容 - 有什么建议吗?

谢谢

4

15 回答 15

201

似乎如果您开始以编程方式刷新,您必须自己滚动表格视图,例如,通过更改 contentoffset

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

我猜这是因为当用户位于表格视图的中间/底部时滚动到刷新控件可能是不可取的?

@muhasturk 的 Swift 2.2 版本

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)        
    }
}
于 2013-02-06T00:43:02.417 回答
80

UITableViewController 在iOS 7 之后有automaticAdjustsScrollViewInsets属性。table view 可能已经有contentOffset,通常是(0, -64)。

因此,在编程开始刷新后显示 refreshControl 的正确方法是将 refreshControl 的高度添加到现有的 contentOffset。

 [self.refreshControl beginRefreshing];
 [self.tableView setContentOffset:CGPointMake(0, self.tableView.contentOffset.y-self.refreshControl.frame.size.height) animated:YES];
于 2014-03-18T05:34:21.363 回答
35

这是使用上述策略的 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()
    }
}
于 2016-02-17T21:50:34.703 回答
15

其他答案都不适合我。它们会导致微调器显示和旋转,但刷新操作本身永远不会发生。这有效:

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

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

于 2014-05-20T18:01:19.893 回答
13

已经提到的方法:

[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];
于 2015-02-13T12:50:10.857 回答
11

对于 Swift 4/4.1

现有答案的混合为我完成了这项工作:

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

希望这可以帮助!

于 2018-05-27T10:53:47.953 回答
8

另请参阅此问题

调用 beginRefreshing 并且 contentOffset 为 0 时 UIRefreshControl 不显示刺

对我来说它看起来像一个错误,因为它仅在 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){

        }];

    }
}
于 2013-04-27T10:06:40.867 回答
7

这是Swift 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()
      }
   }
}
于 2017-10-10T12:46:34.053 回答
5

对于 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)
    }

}
于 2019-04-14T10:46:44.690 回答
4

它对我来说很完美:

斯威夫特 3:

self.tableView.setContentOffset(CGPoint(x: 0, y: -self.refreshControl!.frame.size.height - self.topLayoutGuide.length), animated: true)
于 2017-02-16T15:30:33.740 回答
3

除了@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];
    }
}

您可以通过在Identity Inspector中设置自定义类来使用新类,以便在 Interface Builder 中进行刷新控制。

于 2014-03-11T12:21:00.640 回答
3

如果你在 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。

于 2017-05-05T06:27:27.900 回答
3

斯威夫特堡 2.2+

    self.tableView.setContentOffset(CGPoint(x: 0, y: -refreshControl.frame.size.height), animated: true)
于 2016-05-25T18:28:38.327 回答
2

在 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() 
    }
}
于 2020-03-26T16:53:57.947 回答
0

我使用相同的技术来显示用户“数据正在更新”视觉标志。结果用户从后台带来应用程序,并且提要/列表将使用 UI 进行更新,例如用户拉表以刷新自己。我的版本包含 3 件事

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

结果

于 2016-12-23T21:57:18.350 回答