UIRefreshControl 在 UITabBarController 中切换选项卡后卡住

Posted

技术标签:

【中文标题】UIRefreshControl 在 UITabBarController 中切换选项卡后卡住【英文标题】:UIRefreshControl Stuck After Switching Tabs in UITabBarController 【发布时间】:2014-08-12 00:48:07 【问题描述】:

我有一个 UITableViewController 作为 UINavigationController 中的根视图控制器,它又是 UITabBarController 中的视图控制器之一。全部连接在 Storyboard 中。

我也在 Storyboard 中为我的表格配置了 UIRefreshControl,通常它在拉动时看起来应该是这样的:

但是,如果我在其他选项卡之间切换一次或两次,它看起来像这样:

它没有旋转或其他任何东西,只是卡在“满”状态,并且一直保持这种状态,直到我完全拉动并触发刷新。

任何想法或建议表示赞赏。

【问题讨论】:

可能是ios本身的bug? 解决方案太多,不起作用。 【参考方案1】:

只需添加以下代码,它在所有条件下都能完美运行。

override func viewWillAppear(_ animated: Bool) 
    super.viewWillAppear(animated)
    guard refreshControl.isRefreshing else  return 
    refreshControl.endRefreshing()
    refreshControl.beginRefreshing()
    tableView?.contentOffset = CGPoint(x: 0, y: -(refreshControl.bounds.height))

【讨论】:

这是唯一对我有用的解决方案,重要的部分是设置 contentOffset。【参考方案2】:

详情

Xcode 版本 10.3 (10G8),Swift 5

问题

在选项卡之间切换会冻结活动指示器 refreshControl.beginRefreshing(); refreshControl.endRefreshing() - 将再发送一个请求/拉动刷新事件

解决方案

refreshControl.removeTarget(actionTarget, action: selector, for: .valueChanged)
refreshControl.endRefreshing()
refreshControl.beginRefreshing()
refreshControl.addTarget(actionTarget, action: actionSelector, for: .valueChanged)

完整的解决方案和示例

How to use pull to refresh in Swift?

【讨论】:

【参考方案3】:

你可以试试

refreshControl.beginRefreshing()
refreshControl.endRefreshing()

在 viewWillAppear 中,这应该可以工作。

【讨论】:

【参考方案4】:

我可以通过在 viewWillDisappear 中放置以下代码来解决这个问题

    DispatchQueue.main.async 
        self.refreshControl.beginRefreshing()
        self.refreshControl.endRefreshing()
    

如果将上述代码放在 viewWillAppear 中,则微调器将在几秒钟内设置动画并关闭,在任何一种情况下,问题都会得到解决,但是,从用户的角度来看,微调器不应该是可见的,所以它是最好把它放在 viewWillDisappear 中。

【讨论】:

【参考方案5】:

当刷新控件旋转时,如果您呈现其他控制器并在“结束刷新”之前返回;刷新动画会卡住。

以上答案都不适合我,我不想设置 bool 并在视图控制器的整个生命周期中遵循它。

我的解决方案是将以下代码添加到 viewWillAppear 的末尾。

在对象中;

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

在斯威夫特中;

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

逻辑是;每当它捕捉到刷新控件处于动画状态时,它会在不知道它是否卡住的情况下停止它并重新运行动画。

希望对你有帮助。

【讨论】:

【参考方案6】:

Swift 中最好的修复:

override func viewWillAppear(_ animated: Bool) 
  super.viewWillAppear(animated)

  //fixes bug with refreshControl freezing while switching tabs
  if tableView.contentOffset.y < 0 
    tableView.contentOffset = .zero
  

【讨论】:

【参考方案7】:

在多次遇到同样的问题后,我决定为它创建一个库SwiftPullToRefresh。

如前所述,refreshControl 需要在viewDidDisappear(_:) 上停止并在viewWillAppear(_:) 上启动。

当以编程方式启动refreshControl 动画时,tableView.contentOffset 需要设置为适当的值。

tableView.setContentOffset(CGPoint(x: 0, y: -(refreshControl?.bounds.height ?? 60) - topOffset), animated: true)

var topOffset: CGFloat 
    if let navigationBar = navigationController?.navigationBar, navigationBar.isTranslucent == true 
        return navigationBar.bounds.height + UIApplication.shared.statusBarFrame.height
     else 
        return 0
    

【讨论】:

【参考方案8】:

question 中的答案解决了我的问题

基本上在 viewWillAppear 上再次停止和启动微调器

【讨论】:

【参考方案9】:

在导航发生之前我无法调用 endRefreshing 来工作,所以我从另一端解决了问题,在 viewWillAppear 上我检查 tableView contentOffset 是否为负数,将 tableView 恢复为 0。

 - (void)viewWillAppear:(BOOL)animated

    [super viewWillAppear:animated];

    if (self.tableView.contentOffset.y < 0)
    
        [self.tableView setContentOffset:CGPointZero animated:YES];
    

【讨论】:

【参考方案10】:

希望我能最终帮助解决这个问题:

使用带有 Pull-To-Refresh 控件的 UITableView 存在问题。 这可以通过添加 UITableViewController 轻松解决。

UITableViewController *tableController = [[UITableViewController alloc] init];
    tableController.automaticallyAdjustsScrollViewInsets = NO;
    [tableController willMoveToParentViewController:self];
    [self addChildViewController:tableController];
    tableController.tableView = self.containerTableView;
    tableController.refreshControl = self.refreshControl;

这样,您只需将 UITableView 包装在控制器中,[self.refreshControl endRefreshing] 将在 viewWillAppearviewWillDisappear 中完全正常工作。在某些情况下,您可能需要同时调用它。

希望这会有所帮助! :)

【讨论】:

【参考方案11】:

我想要一些点,喜欢 Albert 的解决方案,但不是他的风格。

- (void)viewWillAppear:(BOOL)animated 
    [super viewWillAppear:animated];
    _table.contentOffset = CGPointMake(_table.contentOffset.x, _table.contentOffset.y + 1.0f);


- (void)viewWillDisappear:(BOOL)animated 
    [super viewWillDisappear:animated];
    _table.contentOffset = CGPointMake(_table.contentOffset.x, _table.contentOffset.y - 1.0f);

【讨论】:

【参考方案12】:

只需在viewWillDisappear: 中调用endRefreshing 即可为我解决所有问题。

- (void)viewWillDisappear:(BOOL)animated 
    [super viewWillDisappear:animated];
    [self.refreshControl endRefreshing];        

viewWillAppear:中做同样的事情导致拉表时刷新控件旋转异常。

【讨论】:

视图将消失当我们将应用程序置于后台时不会被调用,在这种情况下加载程序会卡住【参考方案13】:

总结并提供完整的解决方案。

问题:

如果 UIRefreshControl 在屏幕转换期间动画,它将保持可见但不会动画。视频https://vid.me/Bkb7

另外,如果拉动刷新动画开始但未完成,然后执行转换,UIRefreshControl 将被隐藏但卡住。视频https://vid.me/Bkb7

解决方案:

在 viewWillAppear 上启动 UIRefreshControl 动画并在 viewDidDisappear 上结束它。保存刷新过程的状态,以了解何时显示 UIRefreshControl。

Bellow 是一个完整的解决方案,它还可以处理正确的动画和简单的拉动刷新动画。

添加到 UITableView 或子类

初始化:

 /// Refresh indicator
var refreshControl = UIRefreshControl()

/// Refresh state
var isRefreshing = false

/// Refresh controll update funcion. Set to enable pull to refresh
var refreshControllUpdateFunction: (() -> ())?

/// Prepeares UIRefreshControll. Call from init
func initRefreshControl()
    addSubview(refreshControl)

    refreshControl.addTarget(self, action: "refreshControllTarget", forControlEvents: .ValueChanged)
    refreshControl.beginRefreshing()

    isRefreshing = true

Superview 事件处理程序:

/// Call on viewWillApper
func superviewWillApper()
    if isRefreshing && !refreshControl.refreshing
        startRefreshAnimation()
    


/// Call on viewDidDisapper
func superviewDidDisappear()
    endRefreshAnimation(false, dataFetched: !isRefreshing)

UIRefreshControl 动画处理程序:

/// Presents animating UIRefreshControll
func startRefreshAnimation()
    refreshControl.beginRefreshing()
    contentOffset = CGPointMake(0, -refreshControl.bounds.size.height)

    isRefreshing = true


/// Hides UIRefreshControll and saves state of refresh
func endRefreshAnimation(wasEmpty: Bool, dataFetched: Bool)
    refreshControl.endRefreshing()

    isRefreshing = !dataFetched

    if !wasEmpty
        setContentOffset(CGPointZero, animated: true)
    else
        setContentOffset(CGPointZero, animated: false)
    

添加

table.isRefreshing = true

到刷新调用的开始。

结果视频https://vid.me/LyuI

【讨论】:

【参考方案14】:

我知道这已经非常晚了,但我认为迟到总比没有好。

不幸的是,给定的答案都不适合我。唯一有效的是 viewWillAppear 中这段糟糕的代码:

self.refreshControl = nil;
UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
[refreshControl addTarget:self action:@selector(refreshFunction) forControlEvents:UIControlEventValueChanged];
self.refreshControl = refreshControl;

基本上,每次要出现视图时,我都会重新创建 UIRefreshControl。它可以工作,尽管同时给出以下警告:

尝试在刷新控制不空闲时更改它是 强烈劝阻,可能无法正常工作。

我真的很惊讶这仍然是一个问题(现在仍然在 iOS 9 中看到)。希望这可能对其他人有所帮助。

【讨论】:

很好的答案!我在 iOS 9 中仍然看到这个问题。这需要 Apple 在他们的下一个版本中解决。 对我来说也一样。我现在在我的应用程序中看到它。 iOS 10.2 (14C89)。 仍然是 iOS 12.2 唯一合适的解决方案。对于那些考虑在 viewDidAppear 或其他方法上简单地调用“endRefreshing”的人,可以相当快地更改选项卡/视图,这就是这些解决方案不起作用的原因。【参考方案15】:

我有一个 collectionView,我确实将它设置为 contentInset。

当按下一些ViewControllers,然后按home键,然后返回并弹回,UIRefreshControl总是在顶部。

最后我用这段代码暂时避开它。 (丑陋但停止它总是显示在顶部)

- (void)viewWillDisappear:(BOOL)animated

    [super viewWillDisappear:animated];

    [self.refreshControl endRefreshing];

    CGPoint currentContentOffset = self.collectionView.contentOffset;

    [self.collectionView setContentOffset:CGPointMake(currentContentOffset.x,
                                                      currentContentOffset.y - 1.0f)
                                 animated:YES];


- (void)viewWillAppear:(BOOL)animated

    [super viewWillAppear:animated];

    CGPoint currentContentOffset = self.collectionView.contentOffset;

    [self.collectionView setContentOffset:CGPointMake(currentContentOffset.x,
                                                      currentContentOffset.y + 1.0f)
                                 animated:YES];

【讨论】:

【参考方案16】:

以下解决方案可能会对您有所帮助。

CGFloat yTableViewOffset;

- (void)viewDidLoad 

      yTableViewOffset = kNilOptions;

- (void)viewWillAppear:(BOOL)animated

   if(yTableViewOffset != kNilOptions) 

     CGPoint offset = CGPointMake(tableView.contentOffset.x, yTableViewOffset);
     [refreshControl beginRefreshing];
     tableView.contentOffset = offset;

  

- (void)viewWillDisappear:(BOOL)animated

   if(refreshControl.isRefreshing) 
     yTableViewOffset = tableView.contentOffset.y;
     [refreshControl endRefreshing];
  

【讨论】:

【参考方案17】:

user2009606 几乎是正确的,我通过调用修复它

[refreshControl endRefreshing];

开启

viewWillAppear:

refreshControl 现在可以在切换选项卡后正常运行,与其状态无关。

【讨论】:

【参考方案18】:

我刚刚看到了同样的事情。我最终做的是 viewWillDisappear 上的 endRefreshing ,这至少消除了这个问题。 但是,回到选项卡时,我希望它在我重新启动刷新过程并再次调用 beginRefreshing 时再次开始旋转,但事实并非如此。在我向下钻取导航然后返回的情况下确实如此。

【讨论】:

以上是关于UIRefreshControl 在 UITabBarController 中切换选项卡后卡住的主要内容,如果未能解决你的问题,请参考以下文章

在uitableview中使用UIRefreshControl

UIRefreshControl 属性标题多行

UIRefreshControl 自动启动

UIRefreshControl:刷新时 UITableView '卡住'

如何在没有 UITableViewController 的情况下结束 UIRefreshControl

自定义 UIRefreshControl 动画