如何在Swift中使用pull刷新?

时间:2014-06-29 11:47:42

标签: ios swift xcode uirefreshcontrol

我正在使用swift构建RSS阅读器,需要实现pull to reload功能。

以下是我尝试这样做的方法。

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

我得到了:

  

财产&#39; self.refresh&#39;没有在super.init调用

初始化

请帮助我了解手势识别器的行为。一个可用的示例代码将是一个很好的帮助。

感谢。

18 个答案:

答案 0 :(得分:506)

拉动刷新是在iOS中构建的。你可以像在

那样迅速地做到这一点
var refreshControl = UIRefreshControl()

override func viewDidLoad() {
   super.viewDidLoad()

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

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

在某些时候你可能会结束更新。

refreshControl.endRefreshing()

答案 1 :(得分:124)

故事板和快速解决方案......

1。)打开.storyboard文件,在故事板中选择一个TableViewController,然后选择&#34;启用&#34;表视图控制器 - 实用程序中的刷新功能。

enter image description here

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

self.refreshControl?.addTarget(self, action: "refresh:", forControlEvents: UIControlEvents.ValueChanged)
Swift 2.2中的

OR

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

答案 2 :(得分:63)

iOS 10 - Swift 3(也适用于iOS 11 - Swift 4)

我想提一下自iOS 10以来一直包含的 PRETTY COOL 功能,其中包括:

目前,UICollectionViewUITableViewUIScrollView的每一个都直接支持UIRefreshControl

这些视图中的每一个都有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仅在使用滚动视图时有用;这就是为什么这种方法应该更明显地设置刷新控制视图。

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

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

答案 3 :(得分:37)

Swift 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 refresh tableView
}

你可以停止刷新:

refreshControl.endRefreshing()

答案 4 :(得分:7)

快捷键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
    }

答案 5 :(得分:6)

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

答案 6 :(得分:5)

Anhil的回答给了我很多帮助。

然而,经过进一步的实验,我发现建议的解决方案有时会导致不那么漂亮UI glitch

相反,选择this approach *为我做了诀窍。

* Swift 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

答案 7 :(得分:2)

我构建了一个RSS Feed应用程序,其中我有一个 Pull To refresh 功能,该功能最初具有上面列出的一些问题。

但是为了向用户添加上面的答案,我无处不在寻找我的用例并找不到它。我正在从网上下载数据(RSSFeed),我想要下载我的tableView故事刷新。

上面提到的内容涵盖了正确的领域,但是遇到了人们遇到的一些问题,这就是我所做的,并且它起作用:

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

&#13;
&#13;
//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()
}
&#13;
&#13;
&#13;

希望这有助于任何与我相同情况的人

答案 8 :(得分:2)

错误告诉您的是refresh未初始化。请注意,您选择使refresh不是可选的,在Swift中,它意味着 在您调用super.init之前有(或者它被隐式调用,这似乎是你的情况)。要么refresh可选(可能是你想要的),要么以某种方式初始化它。

我建议再次阅读Swift介绍性文档,其中详细介绍了这一点。

最后一件事,不是答案的一部分,正如@Anil所指出的那样,在iOS中有一个内置的拉动刷新控件叫UIRefresControl,这可能是值得研究的东西。

答案 9 :(得分: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()
}

答案 10 :(得分:1)

我建议在每个班级中使用“拉到刷新”的扩展名。

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")

答案 11 :(得分:1)

我要使用

进行刷新

DGElasticPullToRefresh

https://github.com/gontovnik/DGElasticPullToRefresh

<强>安装

pod&#39; DGElasticPullToRefresh&#39;

import DGElasticPullToRefresh
  

并将此函数放入swift文件中并从

中调用此函数

覆盖func viewWillAppear(_ animated:Bool)

     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!)
 }

并且不要忘记删除引用,而视图将获得消失

删除pull以刷新将此代码放入

覆盖func viewDidDisappear(_ animated:Bool)

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

 }

它看起来像

enter image description here

快乐编码:)

答案 12 :(得分:0)

由于可定制性较低、代码重复以及下拉刷新控制带来的错误,我创建了一个库 PullToRefreshDSL,它使用 DSL 模式,就像 SnapKit

// You only have to add the callback, rest is taken care of
tableView.ptr.headerCallback = { [weak self] in // weakify self to avoid strong reference
    DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(2)) { // your network call
        self?.tableView.ptr.isLoadingHeader = false // setting false will hide the view
    }
}

你只需要在任何 UIScrollView 子类后添加魔法关键字 ptr,即 UITableView/UICollectionView

你不必下载库,你可以探索和修改源代码,我只是指出一个可能的 iOS 下拉刷新实现

答案 13 :(得分:0)

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

enter image description here

const AWS = require('aws-sdk');
const dynamodb = new AWS.DynamoDB({ region: 'ap-south-1', apiVersion: '2012-08-10' });

exports.handler = function(event, context, callback) {
    var userSessionToken = event.headers.sessionToken;
    var userEmailId = event.queryStringParameters.emailId;

    var tmp = event.methodArn.split(':');
    var apiGatewayArnTmp = tmp[5].split('/');
    var resource = '/';
    if (apiGatewayArnTmp[3]) {
        resource += apiGatewayArnTmp[3];
    }

    var authResponse = {};

    if (userSessionToken && userEmailId) {
        callback(null, validateUserSession(userSessionToken, userEmailId));
    }  else {
        callback("session token or email id is missing");
    }

const generatePolicy = (principalId, effect, resource) => {
    var authResponse = {};
    authResponse.principalId = principalId;
    if (effect && resource) {
        var policyDocument = {};
        policyDocument.Version = '2012-10-17'; // default version
        policyDocument.Statement = [];
        var statementOne = {};
        statementOne.Action = 'execute-api:Invoke'; // default action
        statementOne.Effect = effect;
        statementOne.Resource = resource;
        policyDocument.Statement[0] = statementOne;
        authResponse.policyDocument = policyDocument;
    }
    return authResponse;
}

const generateAllow = (principalId, resource) => {
    return generatePolicy(principalId, 'Allow', resource);
};

const generateDeny = (principalId, resource) => {
    return generatePolicy(principalId, "Deny", resource);
}

const validateUserSession = (userSessionToken, userEmailId) => {
    var params = {
        Key: {
            "username": {
                S: userEmailId
            },
            "sessionToken": {
                S: userSessionToken
            }
        },
        TableName: "mytable"
    };
    dynamodb.getItem(params, function(err, data) {
    if(err) {
        callback(null, generateDeny('user', event.methodArn));
    } else {
        let currentUnixTime = new Date().getTime();
        let sessionTime = data.Item.session_created_at.N;
        let isSessionExpired = (currentUnixTime - 900) < sessionTime;

        if(isSessionExpired){
            callback(null, generateDeny('user', event.methodArn));
        } else {
            callback(null, generateAllow('user', event.methodArn));
        }
    }
    });
}
};

答案 14 :(得分:0)

详细信息

  • Xcode版本10.2.1(10E1001),Swift 5

解决方案

import UIKit

public extension UIScrollView {

    private var refreshControlIsAnimatingTag: Int { return 999 }

    func addRefreshControll(actionTarget: Any?, action: Selector, replaceIfExist: Bool = false) {
        if !replaceIfExist && refreshControl != nil { return }
        let _refreshControl = UIRefreshControl()
        _refreshControl.addTarget(self, action: #selector(refreshControllValueChanged), for: .valueChanged)
        _refreshControl.addTarget(actionTarget, action: action, for: .valueChanged)
        refreshControl = _refreshControl
    }

    func showRefreshControllActivityIndicatorView(changeContentOffsetWithAnimation: Bool = true, completion: (() -> Void)? = nil) {
        guard   let refreshControl = refreshControl,
                !refreshControl.isRefreshing else { return }
        refreshControl.tag = refreshControlIsAnimatingTag
        let contentOffset = CGPoint(x: 0, y: -refreshControl.frame.height )
        setContentOffset(contentOffset, animated: changeContentOffsetWithAnimation)
        refreshControl.beginRefreshing()
        completion?()
    }

    func pullAndRefresh() {
        guard   let refreshControl = refreshControl,
                !refreshControl.isRefreshing,
                refreshControl.tag != refreshControlIsAnimatingTag else { return}
        showRefreshControllActivityIndicatorView { refreshControl.sendActions(for: .valueChanged) }
    }

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

    @objc func refreshControllValueChanged() { refreshControl?.tag = refreshControlIsAnimatingTag }
}

用法

// 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.addRefreshControll(actionTarget: self, action: #selector(refreshData))
        self.tableView = tableView
    }
}

extension ViewController {
    @objc func refreshData(_ refreshControl: UIRefreshControl) {
        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
    }
}

答案 15 :(得分: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方法,之后,后续刷新将正常工作。

答案 16 :(得分:0)

you can use this subclass of 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 - in interface builder change the class of your tableView to PullToRefreshTableView or create a PullToRefreshTableView programmatically

2 - implement the PullToRefreshTableViewDelegate in your view controller

3 - tableViewDidStartRefreshing(tableView: PullToRefreshTableView) will be called in your view controller when the table view starts refreshing

4 - call yourTableView.endRefreshing() to finish the refreshing

答案 17 :(得分:-1)

其他答案是正确但更多细节请查看此帖子Pull to Refresh

  

在Storyboard中启用刷新

当您使用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. 我们希望为调用的UIControlEvent调用此操作 的ValueChanged
  3. 不要忘记致电refreshControl.endRefreshing()
  4. 欲了解更多信息,请参阅链接,并将所有信用转到该帖子