没有UITableViewController的UIRefreshControl


308

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

我经常将A UIViewControllerUITableView子视图结合使用UITableViewDataSourceUITableViewDelegate而不是UITableViewController直接使用,而是遵循。


6
@DaveDeLong:不,Dave,他该怎么办?在此页面的后面,您说不支持他的解决方案,那么正确的解决方案是什么?
马特2012年

3
@马特他应该使用UITableViewController和文件中的错误请求API使用UIRefreshControlUITableView直接。
Dave DeLong 2012年

31
UITableViewController拥有(并且继续有)太多模糊不清的漏洞以及非平凡的视图层次结构的问题……当您切换到使用带有TableView子视图的标准VC时,所有这些魔术般消失了。恕我直言,“使用UITVC”对于任何解决方案都是不好的开端。
亚当

@Adam在将“ UITableViewController”用作子视图控制器时是否会出现这些错误(因此可以访问视图自定义,而不会影响tableViewControllers层次结构)?使用这种方式我从未遇到过问题。
记忆

Answers:


388

凭直觉,并基于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;

48
仅供参考,这是不受支持的行为,将来可能会更改。Apple做出的唯一保证是,根据提供的api(在这种情况下-[UITableViewController setRefreshControl:])使用它会继续起作用。
Dave DeLong 2012年

18
通过此实现,似乎在触发之前handleRefresh:,滚动视图的值会瞬间发生意外变化contentInsets。还有其他人遇到这个问题或有解决办法吗?(是的,我知道这首先是不受支持的!)
Tim

6
您确实应该为此简单地使用容器视图。只需将其视图控制器的类设置为的自定义子类,UITableViewController您将“做对了”。
尤金(Eugene)

3
在iOS7(iOS6未知)中,编辑后的版本可以正常工作,除非您关闭并重新打开该应用程序。动画直到第二次刷新才播放。重新打开应用程序后第一次刷新,它只是一个完整的圆圈,而不是根据您将其拉下的距离递增的圆圈。有解决办法吗?
david2391

30
为了避免具有上述同时仍然绕过的UITableViewController如提到的“slutter”,只需添加刷新控制像这样的表视图下面:[_tableView insertSubview:_refreshControl atIndex:0];。经过iOS 7和8测试;)
ptitvinou 2014年

95

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

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

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

_theTableView = _tableViewController.tableView;

编辑:

一种在表视图中刷新数据后添加UIRefreshControlUITableViewController包含任何内容的no并保留精美动画的方法。

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

7
您甚至不必创建新的UITableView。只需说initWithStyle:existingTableView.style-然后执行newTableViewController.tableView = existingTableView并分配refreshControl。归结为三行。
Trenskow

[_tablewViewController didMoveToParentViewController:self];添加子视图后,别忘了调用。
2013年

4
self.tableView = _tableViewController.tableView;应该是_tableViewController.tableView = self.tableView
阿里

@Linus为什么需要这个?我在自定义viewController的viewDidLoad方法中将_tableViewController.view添加为子视图,这似乎可以解决问题。我甚至没有需要设置_tableViewController.tableView
泰伯

我无法使用UITableViewController并使用带有表视图的UIViewController的原因。stackoverflow.com/questions/18900428/...
lostintranslation

21

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


2
准确地说,最干净的方法似乎是添加UITableViewController类型的子视图控制器。
Zdenek 2013年

然后,您可以使用抓取UITableViewController self.childViewControllers.firstObject
cbh2000

^您可能还想考虑prepareForSegue用于在容器视图中获取对UITableViewController对象的引用,因为这是从情节提要实例化时作为segue事件立即触发的。
crarho

18

UIRefreshControl是一个UIView子类,因此您可以自己使用它。我不确定它如何呈现自己。呈现可能仅取决于框架,但也可能取决于UIScrollView或UITableViewController。

无论哪种方式,它都将是一个hack,而不是一个优雅的解决方案。我建议您调查可用的第三方副本之一或编写自己的副本。

OD刷新控制

在此处输入图片说明

史莱姆刷新

在此处输入图片说明


1
感谢您的回复,但不完全是我想要的。但是,您帖子的第一句话确实激发了我尝试最终解决问题的方法!
凯勒2012年

7
ODRefreshControl很棒-感谢您的提示!非常简单,可以完成工作。当然,它比苹果的灵活得多。我从来不明白为什么有人会使用UITableViewController,这是IMO在整个API中最差的类。它什么也不做,但是使您的视图不灵活。
2013年

它实际上取决于您的项目,在那里您使用的是什么..就我而言,使用uitableview会增加项目的复杂性,现在我切换到uitableviewcontroller,将我的代码分为两部分,我很高兴我有2个较小的文件用于调试,而不是调试一个大文件..
库什,2013年

@ n13您好,使用UITableViewController的一个很好的理由是能够使用静态单元格进行设计。不幸的是,在UIViewController中的tableView中不可用。
Jean Le Moignan '16

@JeanLeMoignaniOS工具和库的变化很快,因此我3年的评论不再有效。我已经改用SnapKit在代码中创建所有用户界面,说实话,这比在界面生成器中单击1万次要高效得多。将UITableViewController更改为UIViewController也很容易,只需要一秒钟,而在IB中这是一个很大的痛苦。
n13

7

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

我为此在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];

4
延迟和等待真的是不好的作风。
orkoden 2014年

2
@orkoden我同意。对于已经不支持的,这是一种解决方法UIRefreshControl
boliva 2014年

您可以更轻松地调用具有延迟的方法。[self performSelector:@selector(endRefreshing) withObject:nil afterDelay:0.01]
orkoden 2014年

2
最终效果是完全相同的,并且不会使“ hack”更加优雅。是否使用-performSelector:withObject:afterDelay:此功能还是GCD取决于个人喜好。
boliva 2014年

7

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,请阅读此处


3

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

相反,我将一个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
    }

    ...
}

2

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

0

这是另一个解决方案,有些不同。

由于存在一些视图层次结构问题,我不得不使用它:我正在创建一些功能,要求将视图传递到视图层次结构的不同位置,当使用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的解决方案非常吻合。


1
该问题指定为“不使用UITableViewController子类”,并且此答案适用于UITableViewController子类。
布莱恩

0

尝试这个,

上面的解决方案很好,但是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)

迄今为止最好的解决方案
Karthik KM

-1

Keller的第一个建议在iOS 7中引起一个奇怪的错误,该错误会在视图控制器重新出现后增加表的插入量。使用uitableviewcontroller切换到第二个答案,为我解决了一些问题。


-6

事实证明,当将UIViewController与UITableView子视图一起使用并符合UITableViewDataSource和UITableViewDelegate时,可以使用以下代码:

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

9
不同意。 self.refreshControl是的财产UITableViewController,而不是的财产UIViewController
Rickster 2013年

不能正常使用,尚未为uiviewcontroller定义ReferhControl
OMGPOP
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.