【问题标题】:UIRefreshControl without UITableViewController没有 UITableViewController 的 UIRefreshControl
【发布时间】:2012-09-11 22:51:42
【问题描述】:

只是好奇,因为这似乎不可能立即实现,但是有没有一种偷偷摸摸的方法来利用新的 iOS 6 UIRefreshControl 类而不使用 UITableViewController 子类?

我经常使用UIViewControllerUITableView 子视图,并符合UITableViewDataSourceUITableViewDelegate,而不是直接使用UITableViewController

【问题讨论】:

  • @DaveDeLong :不,戴夫,他应该怎么做?稍后在此页面上您说他的解决方案不受支持,那么正确的解决方案是什么?
  • @matt 他应该使用UITableViewController 并提交一个错误请求API 以直接使用UIRefreshControlUITableView
  • UITableViewController 已经(并将继续存在)太多晦涩难懂的错误和非平凡视图层次结构的问题......当您切换到使用带有 TableView 子视图的标准 VC 时,所有这些都会神奇地消失.恕我直言,“使用 UITVC”对于任何解决方案来说都是一个糟糕的开始。
  • @Adam 当使用 'UITableViewController' 作为子视图控制器时,这些错误是否会浮出水面(从而可以访问视图自定义而无需在 tableViewControllers 层次结构中乱搞)?以这种方式使用它时我从未遇到过问题。

标签: objective-c ios uitableview ios6 uirefreshcontrol


【解决方案1】:

凭直觉,并根据 DrummerB 的灵感,我尝试简单地将UIRefreshControl 实例作为子视图添加到我的UITableView。它神奇地起作用了!

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

这会在您的表格视图上方添加一个 UIRefreshControl 并按预期工作,而无需使用 UITableViewController :)


编辑:上面仍然有效,但正如一些人指出的那样,以这种方式添加 UIRefreshControl 时会出现轻微的“口吃”。一个解决方案是实例化一个 UITableViewController,然后将你的 UIRefreshControl 和 UITableView 设置为那个,即:

UITableViewController *tableViewController = [[UITableViewController alloc] init];
tableViewController.tableView = self.myTableView;

self.refreshControl = [[UIRefreshControl alloc] init];
[self.refreshControl addTarget:self action:@selector(getConnections) forControlEvents:UIControlEventValueChanged];
tableViewController.refreshControl = self.refreshControl;

【讨论】:

  • 仅供参考,这是不受支持的行为,将来可能会改变。 Apple 做出的唯一保证是根据提供的 api(在本例中为 -[UITableViewController setRefreshControl:])使用它将继续运行。
  • 通过这个实现,似乎在触发handleRefresh: 之前,滚动视图的contentInsets 的值在一瞬间发生了意外变化。其他人遇到过这种情况或有解决方法吗? (是的,我知道这首先不受支持!)
  • 您真的应该为此简单地使用容器视图。只需将其视图控制器的类设置为您的 UITableViewController 的自定义子类,您就会“做对了”。
  • 在 iOS7(对于 iOS6 未知)中,编辑后的版本可以正常工作,除非您关闭并重新打开应用程序。动画直到您第二次刷新时才会播放。重新打开应用后第一次刷新,它只是一个完整的圆圈,而不是根据你拉下来的距离递增的圆圈。有解决办法吗?
  • 为了避免在绕过 UITableViewController 的同时出现上面提到的“slutter”,只需在表格视图下方添加刷新控件,如下所示:[_tableView insertSubview:_refreshControl atIndex:0];。已在 iOS 7 和 8 上测试过;)
【解决方案2】:

要消除由接受的答案引起的口吃,您可以将UITableView 分配给UITableViewController

_tableViewController = [[UITableViewController alloc]initWithStyle:UITableViewStylePlain];
[self addChildViewController:_tableViewController];

_tableViewController.refreshControl = [UIRefreshControl new];
[_tableViewController.refreshControl addTarget:self action:@selector(loadStream) forControlEvents:UIControlEventValueChanged];

_theTableView = _tableViewController.tableView;

编辑:

一种添加UIRefreshControl 且没有UITableViewController 且不会出现卡顿并在tableview 上刷新数据后保留漂亮动画的方法。

UIRefreshControl *refreshControl = [UIRefreshControl new];
[refreshControl addTarget:self action:@selector(handleRefresh:) forControlEvents:UIControlEventValueChanged];
[self.theTableView addSubview:refreshControl];
[self.theTableView sendSubviewToBack:refreshControl];

稍后在处理刷新的数据时...

- (void)handleRefresh:(UIRefreshControl *)refreshControl {
    [self.theTableView reloadData];
    [self.theTableView layoutIfNeeded];
    [refreshControl endRefreshing];
}

【讨论】:

  • 你甚至不必创建一个新的 UITableView。只需说 initWithStyle:existingTableView.style - 然后执行 newTableViewController.tableView = existingTableView 并分配 refreshControl。归结为三行。
  • 添加子视图后不要忘记调用[_tablewViewController didMoveToParentViewController:self];
  • self.tableView = _tableViewController.tableView; 应该是_tableViewController.tableView = self.tableView
  • @Linus 为什么这是必要的?我在自定义 viewController 的 viewDidLoad 方法中添加了 _tableViewController.view 作为子视图,这似乎可以解决问题。我什至不需要设置 _tableViewController.tableView
  • 我无法使用 UITableViewController 并且正在使用带有表格视图的 UIViewController 的原因。 stackoverflow.com/questions/18900428/…
【解决方案3】:

您将尝试在您正在使用的 ViewController 中使用容器视图。您可以使用专用的 tableview 定义干净的 UITableViewController 子类并将其放置在 ViewController 中。

【讨论】:

  • 确切地说,最干净的方法似乎是添加 UITableViewController 类型的子视图控制器。
  • 然后您可以使用self.childViewControllers.firstObject 获取 UITableViewController。
  • ^ 您可能还想考虑使用prepareForSegue 在容器视图中获取对 UITableViewController 对象的引用,因为这会作为故事板实例化时的 segue 事件立即触发。
【解决方案4】:

UIRefreshControl 是 UIView 的子类,所以你可以单独使用它。我不确定它是如何呈现自己的。渲染可以简单地依赖于框架,但它也可以依赖于 UIScrollView 或 UITableViewController。

无论哪种方式,它都更像是一种 hack,而不是一种优雅的解决方案。我建议您查看可用的第 3 方克隆之一或自己编写。

ODRefreshControl

SlimeRefresh

【讨论】:

  • 感谢您的回复,但不是我想要的。但是,您帖子的第一句话确实激发了我尝试最终成为解决方案的方法!
  • ODRefreshControl 很棒 - 感谢您的提示!非常简单,并且可以完成工作。当然比苹果的灵活得多。我从来不明白为什么有人会使用 UITableViewController,IMO 是整个 API 中最糟糕的类。它(几乎)什么都不做,但会让你的观点不灵活。
  • 这实际上取决于您的项目您在其中使用的内容.. 在我的情况下使用 uitableview 会增加项目的复杂性,现在我切换到 uitableviewcontroller 将我的代码分为两个部分很高兴我有 2 个可用于调试的小文件,而不是一个大文件..
  • @n13 你好,使用 UITableViewController 的一个很好的理由是能够使用静态单元格进行设计。不幸的是,在 UIViewController 中的 tableView 中不可用。
  • @JeanLeMoignaniOS 工具和库正在迅速变化,所以我 3 年前的评论不再有效。我已经转而使用 SnapKit 在代码中创建所有用户界面,老实说,它比在界面构建器中单击 10000 次要高效得多。将 UITableViewController 更改为 UIViewController 也很容易,只需要一秒钟,而在 IB 中这是一个很大的痛苦。
【解决方案5】:

尝试使用 NSObject 的 -performSelector:withObject:afterDelay: 或 GCD 的 dispatch_after 将 tableView 重新加载其内容后对 refreshControl -endRefresh 方法的调用延迟几分之一秒。

我为此在 UIRefreshControl 上创建了一个类别:

@implementation UIRefreshControl (Delay)

- (void)endRefreshingAfterDelay:(NSTimeInterval)delay {
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC));
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        [self endRefreshing];
    });
}

@end

我对其进行了测试,这也适用于集合视图。我注意到延迟小到 0.01 秒就够了:

// My data refresh process here while the refresh control 'isRefreshing'
[self.tableView reloadData];
[self.refreshControl endRefreshingAfterDelay:.01];

【讨论】:

  • 延迟和等待真的很糟糕。
  • @orkoden 我同意。这是针对已经不受支持的 UIRefreshControl 使用的解决方法。
  • 您可以更轻松地调用具有延迟的方法。 [self performSelector:@selector(endRefreshing) withObject:nil afterDelay:0.01]
  • 最终效果完全一样,不会让“hack”变得更优雅/更不优雅。是否使用-performSelector:withObject:afterDelay: 或 GCD 取决于个人喜好。
【解决方案6】:

IOS 10 Swift 3.0

很简单

import UIKit

class ViewControllerA: UIViewController, UITableViewDataSource, UITableViewDelegate {

    @IBOutlet weak var myTableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()

        myTableView.delegate = self
        myTableView.dataSource = self

        if #available(iOS 10.0, *) {
            let refreshControl = UIRefreshControl()
            let title = NSLocalizedString("PullToRefresh", comment: "Pull to refresh")
            refreshControl.attributedTitle = NSAttributedString(string: title)
            refreshControl.addTarget(self,
                                     action: #selector(refreshOptions(sender:)),
                                     for: .valueChanged)
            myTableView.refreshControl = refreshControl
        }
    }

    @objc private func refreshOptions(sender: UIRefreshControl) {
        // Perform actions to refresh the content
        // ...
        // and then dismiss the control
        sender.endRefreshing()
    }

    // MARK: - Table view data source

    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 12
    }


    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)

        cell.textLabel?.text = "Cell \(String(indexPath.row))"
        return cell
    }

}

如果你想了解 iOS 10 UIRefreshControl,请阅读here

【讨论】:

    【解决方案7】:

    将刷新控件添加为子视图会在部分标题上方创建一个空白区域。

    相反,我将一个 UITableViewController 嵌入到我的 UIViewController 中,然后将我的 tableView 属性更改为指向嵌入的那个,中提琴!最少的代码更改。 :-)

    步骤:

    1. 在 Storyboard 中创建一个新的 UITableViewController 并将其嵌入到您原来的 UIViewController 中
    2. @IBOutlet weak var tableView: UITableView!替换为新嵌入的UITableViewController中的那个,如下图

    class MyViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
        weak var tableView: UITableView!
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            let tableViewController = self.childViewControllers.first as! UITableViewController
            tableView = tableViewController.tableView
            tableView.dataSource = self
            tableView.delegate = self
    
            // Now we can (properly) add the refresh control
            let refreshControl = UIRefreshControl()
            refreshControl.addTarget(self, action: "handleRefresh:", forControlEvents: .ValueChanged)
            tableViewController.refreshControl = refreshControl
        }
    
        ...
    }
    

    【讨论】:

      【解决方案8】:

      适用于 Swift 2.2。

      首先制作 UIRefreshControl() 。

      var refreshControl : UIRefreshControl!
      

      在你的 viewDidLoad() 方法中添加:

      refreshControl = UIRefreshControl()
          refreshControl.attributedTitle = NSAttributedString(string: "Refreshing..")
          refreshControl.addTarget(self, action: #selector(YourUIViewController.refresh(_:)), forControlEvents: UIControlEvents.ValueChanged)
          self.tableView.addSubview(refreshControl)
      

      并制作刷新功能

      func refresh(refreshControl: UIRefreshControl) {
      
          // do something ...
      
          // reload tableView
          self.tableView.reloadData()
      
          // End refreshing
          refreshControl.endRefreshing()
      }
      

      【讨论】:

        【解决方案9】:

        这是另一个有点不同的解决方案。

        我不得不使用它,因为我遇到了一些视图层次结构问题:我正在创建一些功能,需要将视图传递到视图层次结构中的不同位置,当使用 UITableViewController 的 tableview b/c 时,tableView 是 UITableViewController 的根视图 (self.view) 而不仅仅是常规视图,它创建了不一致的控制器/视图层次结构并导致崩溃。

        基本上创建自己的 UITableViewController 子类并重写 loadView 以分配 self.view 不同的视图,并重写 tableView 属性以返回单独的 tableview。

        例如:

        @interface MyTableVC : UITableViewController
        @end
        
        @interface MyTableVC ()
        @property (nonatomic, strong) UITableView *separateTableView;
        @end
        
        @implementation MyTableVC
        
        - (void)loadView {
            self.view = [[UIView alloc] initWithFrame:CGRectZero];
        }
        
        - (UITableView *)tableView {
            return self.separateTableView;
        }
        
        - (void)setTableView:(UITableView *)tableView {
            self.separateTableView = tableView;
        }
        
        @end
        

        当与 Keller 的解决方案结合使用时,这将更加健壮,因为 tableView 现在是常规视图,而不是 VC 的根视图,并且对于更改视图层次结构更加健壮。以这种方式使用它的示例:

        MyTableVC *tableViewController = [[MyTableVC alloc] init];
        tableViewController.tableView = self.myTableView;
        
        self.refreshControl = [[UIRefreshControl alloc] init];
        [self.refreshControl addTarget:self action:@selector(getConnections) forControlEvents:UIControlEventValueChanged];
        tableViewController.refreshControl = self.refreshControl;
        

        这还有另一种可能的用途:

        由于这种子类化方式将 self.view 与 self.tableView 分开,现在可以将此 UITableViewController 用作更多的常规控制器,并将其他子视图添加到 self.view 而不会向 UITableView 添加子视图的奇怪之处,因此可以考虑让他们的视图控制器直接成为 UITableViewController 的子类,而不是拥有 UITableViewController 子类。

        注意事项:

        由于我们在不调用 super 的情况下覆盖 tableView 属性,因此可能需要注意一些事情,并在必要时进行处理。例如,在我上面的示例中设置 tableview 不会将 tableview 添加到 self.view 并且不会设置您可能想要做的框架。此外,在这个实现中,当类被实例化时,没有默认的 tableView 给你,这也是你可以考虑添加的。我不在这里包括它,因为这是个案,这个解决方案实际上很适合 Keller 的解决方案。

        【讨论】:

        • 问题指定“不使用 UITableViewController 子类”,这个答案是针对 UITableViewController 子类的。
        【解决方案10】:

        试试这个,

        上述解决方案都很好,但 tableView.refreshControl 仅适用于 UITableViewController,直到 iOS 9.x 并且从 iOS 10.x 开始进入 UITableView。

        用 Swift 3 编写 -

        let refreshControl = UIRefreshControl()
        refreshControl.addTarget(self, action: #selector(FeedViewController.loadNewData), for: UIControlEvents.valueChanged)
        // Fix for the RefreshControl Not appearing in background
        tableView?.addSubview(refreshControl)
        tableView.sendSubview(toBack: refreshControl)
        

        【讨论】:

        • 迄今为止最好的解决方案
        【解决方案11】:

        Keller 的第一个建议在 iOS 7 中导致了一个奇怪的错误,即在视图控制器重新出现后表格的插入增加了。使用 uitableviewcontroller 更改为第二个答案,为我解决了问题。

        【讨论】:

          【解决方案12】:

          事实证明,当您使用带有 UITableView 子视图的 UIViewController 并符合 UITableViewDataSource 和 UITableViewDelegate 时,您可以使用以下内容:

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

          【讨论】:

          • 不同意。 self.refreshControlUITableViewController 的属性,而不是 UIViewController
          • 不工作,没有为uiviewcontroller定义referhControl
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2014-02-15
          • 1970-01-01
          • 2018-04-11
          • 1970-01-01
          • 2016-05-06
          • 2013-01-21
          • 1970-01-01
          相关资源
          最近更新 更多