如何在Swift中使用pull刷新?


247

我正在使用swift构建RSS阅读器,并且需要实现pull来重新加载功能。

这是我试图做到的方式。

class FirstViewController: UIViewController,
    UITableViewDelegate, UITableViewDataSource {

   @IBOutlet var refresh: UIScreenEdgePanGestureRecognizer
   @IBOutlet var newsCollect: UITableView

   var activityIndicator:UIActivityIndicatorView? = nil

   override func viewDidLoad() {
       super.viewDidLoad()
       self.newsCollect.scrollEnabled = true
      // Do any additional setup after loading the view, typically from a nib.

      if nCollect.news.count <= 2{
          self.collectNews()
       }
      else{
          self.removeActivityIndicator()
       }
      view.addGestureRecognizer(refresh)
   }



@IBAction func reload(sender: UIScreenEdgePanGestureRecognizer) {
    nCollect.news = News[]()
    return newsCollect.reloadData()
}

我正进入(状态 :

super.init调用未初始化属性“ self.refresh”

请帮助我了解手势识别器的行为。一个有效的示例代码将有很大的帮助。

谢谢。


您想拉动表格视图中的内容,例如techrepublic.com/blog/software-engineer/…–
Anil Varghese

是的,我只需要此功能,但不了解ObjC。要迅速实施。
xrage

Answers:


590

拉动刷新是iOS内置的。您可以像

var refreshControl = UIRefreshControl()

override func viewDidLoad() {
   super.viewDidLoad()

   refreshControl.attributedTitle = NSAttributedString(string: "Pull to refresh")
   refreshControl.addTarget(self, action: #selector(self.refresh(_:)), for: .valueChanged)
   tableView.addSubview(refreshControl) // not required when using UITableViewController
}

@objc func refresh(_ sender: AnyObject) {
   // Code to refresh table view  
}

在某些时候,您可能会刷新。

refreshControl.endRefreshing()

15
谢谢!我用这个 只需进行一些调整即可延迟加载。我会这样做:“ lazy var refreshControl = UIRefreshControl()”我发现避免强迫展开变量是一种很好的做法,因为这感觉像在破坏语言安全性。
nmdias 2014年

10
简要说明一下,您甚至不需要将refreshControl添加到表视图中。这是隐式处理的。
肯德里克·莱德

1
@KendrickLedet,即使您不使用UITableViewController?
Van Du Tran 2015年

3
我将如何在表格视图的顶部和底部使用此解决方案?
AustinT 2015年

3
Swift 4更新:refreshControl.addTarget(自我,行动​​:“ refresh:”,用于:.valueChanged)
akseli

148

情节提要和快速解决方案...

1.)打开您的.storyboard文件,在情节提要中选择一个TableViewController,然后在实用工具中“启​​用” Table View Controller-刷新功能。

在此处输入图片说明

2.)打开关联的UITableViewController-Class并将以下行添加到viewDidLoad-Method中。

self.refreshControl?.addTarget(self, action: "refresh:", forControlEvents: UIControlEvents.ValueChanged)

为Swift 5.0编辑:

self.refreshControl?.addTarget(self, action: #selector(refresh), for: UIControl.Event.valueChanged)

在Swift 2.2中:

self.refreshControl?.addTarget(self, action: #selector(TestTableViewController.refresh(_:)), forControlEvents: UIControlEvents.ValueChanged)

3.)在viewDidLoad-Method上方添加以下方法

func refresh(sender:AnyObject)
{
    // Updating your data here...

    self.tableView.reloadData()
    self.refreshControl?.endRefreshing()
}

7
此外,您还可以通过将故事板中的“刷新控件”拖动到您的ViewController中,来使用故事板添加刷新动作
uiureo 2015年

如果我的数据加载是异步的,我应该put self.tableView.reloadData()self.refreshControl?.endRefreshing()在回调?
钱琛

究竟!并将其reloadData()放入主队列以立即刷新UI: dispatch_async(dispatch_get_main_queue(),{ self.tableView.reloadData() });
空白

1
这种方式优先于公认的答案,有那么没有布局的bug(至少我使用SWIFT为2+)的原因
瑞恩·沃尔顿

1
这个答案应该是票数最多的,
才是

75

我想提到自iOS 10起就包含的PRETTY COOL功能,它是:

目前,和中的每个都直接支持UIRefreshControlUICollectionViewUITableViewUIScrollView

这些视图中的每个视图均具有refreshControl实例属性,这意味着不再需要将其添加为滚动视图中的子视图,您所要做的就是:

@IBOutlet weak var collectionView: UICollectionView!

override func viewDidLoad() {
    super.viewDidLoad()

    let refreshControl = UIRefreshControl()
    refreshControl.addTarget(self, action: #selector(doSomething), for: .valueChanged)

    // this is the replacement of implementing: "collectionView.addSubview(refreshControl)"
    collectionView.refreshControl = refreshControl
}

func doSomething(refreshControl: UIRefreshControl) {
    print("Hello World!")

    // somewhere in your code you might need to call:
    refreshControl.endRefreshing()
}

就个人而言,我发现将其作为滚动视图的属性而不是将其添加为子视图更为自然,尤其是因为唯一适合作为UIRefreshControl的超级视图的视图是滚动视图,即使用UIRefreshControl的功能仅在使用滚动视图时很有用;这就是为什么这种方法在设置刷新控件视图时应该更加明显。

但是,您仍然可以选择使用addSubview基于iOS版本的:

if #available(iOS 10.0, *) {
  collectionView.refreshControl = refreshControl
} else {
  collectionView.addSubview(refreshControl)
}

嘿,我在doSomething函数中怎么称呼?如果我调用reload.Data,它不想再次加载!

@JavierV。在doSomething函数中添加一个断点,如果可以实现,则必须检查代码。
艾哈迈德F

50

斯威夫特4

var refreshControl: UIRefreshControl!

override func viewDidLoad() {
    super.viewDidLoad()

    refreshControl = UIRefreshControl()
    refreshControl.attributedTitle = NSAttributedString(string: "Pull to refresh")
    refreshControl.addTarget(self, action: #selector(refresh), for: .valueChanged)
    tableView.addSubview(refreshControl) 
}

@objc func refresh(_ sender: Any) {
    //  your code to reload tableView
}

并且您可以停止刷新:

refreshControl.endRefreshing()

别忘了tableView.reloadData()在您的示例中提及!
Konstantinos Natsios

您好我通过这种方式创建我refreshcontrol: var refControl: UIRefreshControl{ let rfControl = UIRefreshControl() rfControl.attributedTitle = NSAttributedString(string: "") rfControl.tintColor = UIColor(red:0.16, green:0.68, blue:0.9, alpha:1) rfControl.addTarget(self, action: #selector(getNewMessageList), for: .valueChanged) return rfControl }。然后调用endRefreshing(),它不起作用(refreshcontrol仍显示在那里),但是按照您的方式,它起作用了,请让我知道为什么?
李李

@lee因为rfControl是本地的,所以无法从外部访问。尝试将rfControl初始化为VC的全局变量:var rfControl = UIRefreshControl(),然后使用rfControl.endRefreshing()停止
Gilad Brunfman

一个问题:.valueChanged事件在哪里触发?我不明白这种联系。
juniorgarcia

触发UIRefreshControl时,如addTarget:refreshControl.addTarget(self,action:#selector(self.refresh),for:UIControlEvents.valueChanged)所
看到的

9

Swift中使用它,

如果您想在WebView中进行刷新,

因此,请尝试以下代码:

override func viewDidLoad() {
    super.viewDidLoad()
    addPullToRefreshToWebView()
}

func addPullToRefreshToWebView(){
    var refreshController:UIRefreshControl = UIRefreshControl()

    refreshController.bounds = CGRectMake(0, 50, refreshController.bounds.size.width, refreshController.bounds.size.height) // Change position of refresh view
    refreshController.addTarget(self, action: Selector("refreshWebView:"), forControlEvents: UIControlEvents.ValueChanged)
    refreshController.attributedTitle = NSAttributedString(string: "Pull down to refresh...")
    YourWebView.scrollView.addSubview(refreshController)

}

func refreshWebView(refresh:UIRefreshControl){
    YourWebView.reload()
    refresh.endRefreshing()
}

谢谢你,先生!解决了我在学习时遇到的菜鸟问题。
阿德里安·史密斯

5

Anhil的回答对我很有帮助。

但是,在进一步试验之后,我注意到建议的解决方案有时会导致不太好的UI故障

相反,采用这种方法 *对我有用。

*迅捷2.1

//Create an instance of a UITableViewController. This will host your UITableView.
private let tableViewController = UITableViewController()

//Add tableViewController as a childViewController and set its tableView property to your UITableView.
self.addChildViewController(self.tableViewController)
self.tableViewController.tableView = self.tableView
self.refreshControl.addTarget(self, action: "refreshData:", forControlEvents: .ValueChanged)
self.tableViewController.refreshControl = self.refreshControl

2

错误告诉您的refresh是未初始化。请注意,你选择做refresh不是可有可无的,这在斯威夫特意味着它拥有你打电话之前必须有一个值super.init(或者它隐式调用,这似乎是你的情况)。将其设为refresh可选(可能是您想要的)或以某种方式对其进行初始化。

我建议再次阅读Swift入门文档,其中涵盖了很长的篇幅。

@Anil指出的最后一件事,不是答案的一部分,它有一个内置的提要来刷新iOS中的控件,称为UIRefresControl,这可能值得研究。


2

我构建了一个RSS feed应用程序,其中具有“刷新到”功能,该功能最初存在上面列出的一些问题。

但是要向上述用户添加答案,我到处都是用例,但找不到。我正在从Web(RSSFeed)下载数据,我想下拉故事的tableView进行刷新。

上面提到的内容涵盖了正确的领域,但是由于人们遇到了一些问题,这就是我所做的,并且可以有效地解决问题:

我采用@Blankarsch的方法,转到main.storyboard并选择表视图以使用刷新,然后未提及的是创建IBOutlet和IBAction以有效使用刷新

//Created from main.storyboard cntrl+drag refresh from left scene to assistant editor
@IBOutlet weak var refreshButton: UIRefreshControl

override func viewDidLoad() {
  ...... 
  ......
  //Include your code
  ......
  ......
  //Is the function called below, make sure to put this in your viewDidLoad 
  //method or not data will be visible when running the app
  getFeedData()
}

//Function the gets my data/parse my data from the web (if you havnt already put this in a similar function)
//remembering it returns nothing, hence return type is "-> Void"
func getFeedData() -> Void{
  .....
  .....
}

//From main.storyboard cntrl+drag to assistant editor and this time create an action instead of outlet and 
//make sure arguments are set to none and note sender
@IBAction func refresh() {
  //getting our data by calling the function which gets our data/parse our data
  getFeedData()

  //note: refreshControl doesnt need to be declared it is already initailized. Got to love xcode
  refreshControl?.endRefreshing()
}

希望这可以帮助和我一样的人


2
func pullToRefresh(){

    let refresh = UIRefreshControl()
    refresh.addTarget(self, action: #selector(handleTopRefresh(_:)), for: .valueChanged )
    refresh.tintColor = UIColor.appBlack
    self.tblAddressBook.addSubview(refresh)

}
@objc func handleTopRefresh(_ sender:UIRefreshControl){
    self.callAddressBookListApi(isLoaderRequired: false)
    sender.endRefreshing()
}

2

细节

  • Xcode版本10.3(10G8),Swift 5

特征

  • 以编程方式使“拉动刷新”的能力
  • 防止多次“拉动刷新”事件
  • 当切换视图控制器时(例如,使用TabController时),可以继续为活动指示器设置动画

import UIKit

class RefreshControl: UIRefreshControl {

    private weak var actionTarget: AnyObject?
    private var actionSelector: Selector?
    override init() { super.init() }

    convenience init(actionTarget: AnyObject?, actionSelector: Selector) {
        self.init()
        self.actionTarget = actionTarget
        self.actionSelector = actionSelector
        addTarget()
    }

    private func addTarget() {
        guard let actionTarget = actionTarget, let actionSelector = actionSelector else { return }
        addTarget(actionTarget, action: actionSelector, for: .valueChanged)
    }

    required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }

    func endRefreshing(deadline: DispatchTime? = nil) {
        guard let deadline = deadline else { endRefreshing(); return }
        DispatchQueue.global(qos: .default).asyncAfter(deadline: deadline) { [weak self] in
            DispatchQueue.main.async { self?.endRefreshing() }
        }
    }

    func refreshActivityIndicatorView() {
        guard let selector = actionSelector else { return }
        let _isRefreshing = isRefreshing
        removeTarget(actionTarget, action: selector, for: .valueChanged)
        endRefreshing()
        if _isRefreshing { beginRefreshing() }
        addTarget()
    }

    func generateRefreshEvent() {
        beginRefreshing()
        sendActions(for: .valueChanged)
    }
}

public extension UIScrollView {

    private var _refreshControl: RefreshControl? { return refreshControl as? RefreshControl }

    func addRefreshControll(actionTarget: AnyObject?, action: Selector, replaceIfExist: Bool = false) {
        if !replaceIfExist && refreshControl != nil { return }
        refreshControl = RefreshControl(actionTarget: actionTarget, actionSelector: action)
    }

    func scrollToTopAndShowRunningRefreshControl(changeContentOffsetWithAnimation: Bool = false) {
        _refreshControl?.refreshActivityIndicatorView()
        guard   let refreshControl = refreshControl,
                contentOffset.y != -refreshControl.frame.height else { return }
        setContentOffset(CGPoint(x: 0, y: -refreshControl.frame.height), animated: changeContentOffsetWithAnimation)
    }

    private var canStartRefreshing: Bool {
        guard let refreshControl = refreshControl, !refreshControl.isRefreshing else { return false }
        return true
    }

    func startRefreshing() {
        guard canStartRefreshing else { return }
        _refreshControl?.generateRefreshEvent()
    }

    func pullAndRefresh() {
        guard canStartRefreshing else { return }
        scrollToTopAndShowRunningRefreshControl(changeContentOffsetWithAnimation: true)
        _refreshControl?.generateRefreshEvent()
    }

    func endRefreshing(deadline: DispatchTime? = nil) { _refreshControl?.endRefreshing(deadline: deadline) }
}

用法

// Add refresh control to UICollectionView / UITableView / UIScrollView
private func setupTableView() {
    let tableView = UITableView()
    // ...
    tableView.addRefreshControll(actionTarget: self, action: #selector(refreshData))
}

@objc func refreshData(_ refreshControl: UIRefreshControl) {
    tableView?.endRefreshing(deadline: .now() + .seconds(3))
}

// Stop refreshing in UICollectionView / UITableView / UIScrollView
tableView.endRefreshing()

// Simulate pull to refresh in UICollectionView / UITableView / UIScrollView
tableView.pullAndRefresh()

完整样本

不要忘记在此处添加解决方案代码

import UIKit

class ViewController: UIViewController {

    private weak var tableView: UITableView?

    override func viewDidLoad() {
        super.viewDidLoad()
        setupTableView()
    }

    private func setupTableView() {
        let tableView = UITableView()
        view.addSubview(tableView)
        tableView.translatesAutoresizingMaskIntoConstraints = false
        tableView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
        tableView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
        tableView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
        tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
        tableView.dataSource = self
        tableView.delegate = self
        tableView.addRefreshControll(actionTarget: self, action: #selector(refreshData))
        self.tableView = tableView
    }
}

extension ViewController {
    @objc func refreshData(_ refreshControl: UIRefreshControl) {
        print("refreshing")
        tableView?.endRefreshing(deadline: .now() + .seconds(3))
    }
}

extension ViewController: UITableViewDataSource {
    func numberOfSections(in tableView: UITableView) -> Int { return 1 }
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 20 }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = UITableViewCell()
        cell.textLabel?.text = "\(indexPath)"
        return cell
    }
}

extension ViewController: UITableViewDelegate {
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.pullAndRefresh()
    }
}

2

迅捷5

private var pullControl = UIRefreshControl()

pullControl.attributedTitle = NSAttributedString(string: "Pull to refresh")
        pullControl.addTarget(self, action: #selector(refreshListData(_:)), for: .valueChanged)
        if #available(iOS 10.0, *) {
            tableView.refreshControl = pullControl
        } else {
            tableView.addSubview(pullControl)
        }
// Actions
@objc private func refreshListData(_ sender: Any) {
        self.pullControl.endRefreshing() // You can stop after API Call
        // Call API
    }

1

我建议对pull to Refresh进行扩展,以在每个类中使用。

1)制作一个空的swift文件:File-New-File-Swift File。

2)添加以下内容

    //  AppExtensions.swift

    import Foundation
    import UIKit    

    var tableRefreshControl:UIRefreshControl = UIRefreshControl()    

    //MARK:- VIEWCONTROLLER EXTENSION METHODS
    public extension UIViewController
    {
        func makePullToRefreshToTableView(tableName: UITableView,triggerToMethodName: String){

            tableRefreshControl.attributedTitle = NSAttributedString(string: "TEST: Pull to refresh")
            tableRefreshControl.backgroundColor = UIColor.whiteColor()
            tableRefreshControl.addTarget(self, action: Selector(triggerToMethodName), forControlEvents: UIControlEvents.ValueChanged)
            tableName.addSubview(tableRefreshControl)
        }
        func makePullToRefreshEndRefreshing (tableName: String)
        {
            tableRefreshControl.endRefreshing()
//additional codes

        }
    }    

3)在您的View Controller中,将这些方法称为:

  override func viewWillAppear(animated: Bool) {

self.makePullToRefreshToTableView(bidderListTable, triggerToMethodName: "pullToRefreshBidderTable")
}

4)在某些时候您想结束刷新:

  func pullToRefreshBidderTable() {
self.makePullToRefreshEndRefreshing("bidderListTable")    
//Code What to do here.
}
OR    
self.makePullToRefreshEndRefreshing("bidderListTable")

1
您的代码带有项目名称和版权声明,可能对您不利:)。无论你的代码贴在这里应该给大家使用的是免费的
阿尼尔Varghese表示

谢谢阿尼尔!我确实忘记删除那些。
AG

1

为了刷新我正在使用的拉力

DGElasticPullToRefresh

https://github.com/gontovnik/DGElasticPullToRefresh

安装

pod'DGElasticPullToRefresh'

import DGElasticPullToRefresh

并将此函数放入您的swift文件中,然后从您的

覆盖func viewWillAppear(_动画:布尔)

     func Refresher() {
      let loadingView = DGElasticPullToRefreshLoadingViewCircle()
      loadingView.tintColor = UIColor(red: 255.0/255.0, green: 255.0/255.0, blue: 255.0/255.0, alpha: 1.0)
      self.table.dg_addPullToRefreshWithActionHandler({ [weak self] () -> Void in

          //Completion block you can perfrom your code here.

           print("Stack Overflow")

           self?.table.dg_stopLoading()
           }, loadingView: loadingView)
      self.table.dg_setPullToRefreshFillColor(UIColor(red: 255.0/255.0, green: 57.0/255.0, blue: 66.0/255.0, alpha: 1))
      self.table.dg_setPullToRefreshBackgroundColor(self.table.backgroundColor!)
 }

并且不要忘记删除参考,而视图将消失

删除拉刷新以将此代码放入您的

覆盖func viewDidDisappear(_动画:布尔)

override func viewDidDisappear(_ animated: Bool) {
      table.dg_removePullToRefresh()

 }

它看起来像

在此处输入图片说明

快乐的编码:)


1

您可以通过使用几行代码来实现。那为什么要陷在第三方库或UI中呢?拉动刷新是iOS内置的。您可以像

在此处输入图片说明

var pullControl = UIRefreshControl()

override func viewDidLoad() {
   super.viewDidLoad()

   pullControl.attributedTitle = NSAttributedString(string: "Pull to refresh")
   pullControl.addTarget(self, action: #selector(pulledRefreshControl(_:)), for: UIControl.Event.valueChanged)
   tableView.addSubview(pullControl) // not required when using UITableViewController
}

@objc func pulledRefreshControl(sender:AnyObject) {
   // Code to refresh table view  
}

0

您可以使用tableView的此子类:

import UIKit

protocol PullToRefreshTableViewDelegate : class {
    func tableViewDidStartRefreshing(tableView: PullToRefreshTableView)
}

class PullToRefreshTableView: UITableView {

    @IBOutlet weak var pullToRefreshDelegate: AnyObject?
    private var refreshControl: UIRefreshControl!
    private var isFirstLoad = true

    override func willMoveToSuperview(newSuperview: UIView?) {
        super.willMoveToSuperview(newSuperview)

        if (isFirstLoad) {
            addRefreshControl()
            isFirstLoad = false
        }
    }

    private func addRefreshControl() {
        refreshControl = UIRefreshControl()
        refreshControl.attributedTitle = NSAttributedString(string: "Pull to refresh")
        refreshControl.addTarget(self, action: "refresh", forControlEvents: .ValueChanged)
        self.addSubview(refreshControl)
    }

    @objc private func refresh() {
       (pullToRefreshDelegate as? PullToRefreshTableViewDelegate)?.tableViewDidStartRefreshing(self)
    }

    func endRefreshing() {
        refreshControl.endRefreshing()
    }

}

1-在界面构建器中,将tableView的类更改为PullToRefreshTableView或以PullToRefreshTableView编程方式创建

2- PullToRefreshTableViewDelegate在您的视图控制器中实现

3- tableViewDidStartRefreshing(tableView: PullToRefreshTableView)当表视图开始刷新时,将在您的视图控制器中调用

4-致电yourTableView.endRefreshing()以完成刷新


0

这就是我使用Xcode 7.2使其工作的方式,我认为这是一个主要错误。我在我的UITableViewController内部使用它viewWillAppear

refreshControl = UIRefreshControl()
refreshControl!.addTarget(self, action: "configureMessages", forControlEvents: .ValueChanged)
refreshControl!.beginRefreshing()

configureMessages()

func configureMessages() {
    // configuring messages logic here

    self.refreshControl!.endRefreshing()
}

如您所见,我实际上必须configureMessage()在设置完之后调用该方法,UIRefreshControl然后再进行后续刷新就可以了。


-1

其他答案是正确的,但更多详细信息请查看此帖子以刷新

在情节提要中启用刷新

使用UITableViewController时,解决方案非常简单:首先,在情节提要中选择表视图控制器,打开属性检查器,然后启用刷新:

UITableViewController开箱即用,带有对UIRefreshControl的引用。您只需要连接一些东西即可在用户按下时启动并完成刷新。

覆盖viewDidLoad()

在对viewDidLoad()的覆盖中,添加一个目标来处理刷新,如下所示:

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
        
    self.refreshControl?.addTarget(self, action: "handleRefresh:", forControlEvents: UIControlEvents.ValueChanged)
}
  1. 由于我已经指定“ handleRefresh:”(注意冒号!)作为操作参数,因此我需要在此UITableViewController类中使用相同的名称定义一个函数。此外,该函数应采用一个参数
  2. 我们希望为名为ValueChanged的UIControlEvent调用此操作
  3. 别忘了打电话 refreshControl.endRefreshing()

有关更多信息,请转到提及链接,所有功劳都归于该帖子


尽管从理论上讲这可以回答问题,但最好在此处包括答案的基本部分,并提供链接以供参考。
Tunaki
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.