如何在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功能,它是:

目前,和中的每个都直接支持UIRefreshControl。UICollectionViewUITableViewUIScrollView

这些视图中的每个视图均具有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.