UIRefreshControl - 当UITableViewController在UINavigationController中时,beginRefreshing无效

问题描述 投票:111回答:14

我在我的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;

我的感觉是这与导航控制器中的嵌套视图层次结构有关,不能与复习控件一起使用 - 任何建议?

谢谢

ios objective-c uitableview uinavigationcontroller uirefreshcontrol
14个回答
177
投票

似乎如果以编程方式开始刷新,则必须自己滚动表视图,例如,通过更改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)        
    }
}

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中进行刷新控制。


3
投票

Fort Swift 2.2+

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

3
投票

如果您使用Rxswift for swift 3.1,可以使用以下内容:

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。


1
投票

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

}

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

result


76
投票

UITableViewController在iOS 7之后具有automaticAdjustsScrollViewInsets属性。表视图可能已经有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];

30
投票

这是使用上述策略的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()
    }
}

13
投票

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

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

请注意,此示例使用表视图,但它也可以是集合视图。


11
投票

已经提到的方法:

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

7
投票

另见这个问题

UIRefreshControl not showing spiny when calling beginRefreshing and contentOffset is 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){

        }];

    }
}

7
投票

For Swift 4/4.1

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

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

希望这可以帮助!


4
投票

它对我来说很完美:

斯威夫特3:

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

4
投票

这是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()
      }
   }
}
© www.soinside.com 2019 - 2024. All rights reserved.