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

以下是我如何努力做到这一点。

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调用

请帮助我理解手势识别器的行为。一个工作样例代码将是一个很大的帮助。

谢谢。


这个错误告诉你,刷新没有初始化。注意,你选择使refresh不是可选的,这在Swift中意味着它必须在调用super之前有一个值。Init(或者隐式调用,这似乎是你的情况)。要么使刷新是可选的(可能是你想要的),要么以某种方式初始化它。

我建议你再读一遍Swift的介绍文档,里面详细地介绍了这一点。

最后,正如@Anil指出的,iOS中有一个内置的名为UIRefresControl的拉刷新控件,这可能是值得研究的东西。


拉动刷新是iOS内置的。你可以像这样快速完成

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

使用storyboard和Swift的解决方案:

Open your .storyboard file, select a TableViewController in your storyboard and "Enable" the Table View Controller: Refreshing feature in the Utilities. Open the associated UITableViewController class and add the following Swift 5 line into the viewDidLoad method. self.refreshControl?.addTarget(self, action: #selector(refresh), for: UIControl.Event.valueChanged) Add the following method above the viewDidLoad method func refresh(sender:AnyObject) { // Updating your data here... self.tableView.reloadData() self.refreshControl?.endRefreshing() }


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

但是为了补充上面用户的答案,我到处寻找我的用例,但没有找到它。我正在从网络(RSSFeed)下载数据,我想下拉我的故事tableView来刷新。

上面提到的内容涵盖了正确的领域,但人们遇到了一些问题,以下是我所做的,它很有效:

我采取了@Blankarsch的方法,去了我的主。然后选择表视图来使用刷新,然后没有提到的是创建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() }

希望这能帮助到和我处境相同的人


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

你可以使用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编程

在你的视图控制器中实现PullToRefreshTableViewDelegate

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

4 -调用yourTableView.endRefreshing()来完成刷新


我建议在每个类中使用pull to Refresh的扩展。

1)创建一个空的swift文件:文件-新建-文件- swift文件。

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)在你的视图控制器中调用这些方法:

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

这就是我如何使用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()
}

正如你所看到的,我必须在设置我的UIRefreshControl之后调用configureMessage()方法,随后的刷新将正常工作。


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

其他答案是正确的,但更多细节请查看这篇文章拉到刷新

在故事板中启用刷新

当你使用UITableViewController时,解决方案相当简单:首先,在你的故事板中选择表视图控制器,打开属性检查器,并启用刷新:

UITableViewController自带一个UIRefreshControl的引用。您只需要连接一些东西,以便在用户下拉时启动和完成刷新。

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

因为我指定了“handleRefresh:”(注意冒号!)作为 action参数,我需要在这里定义一个函数 UITableViewController类的同名。此外, 函数应该有一个参数 我们希望这个动作在UIControlEvent被调用时被调用 ValueChanged 别忘了调用refreshControl.endRefreshing()

要了解更多信息,请去提到链接,所有的功劳都归于那个帖子


斯威夫特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()

UIRefreshControl直接支持在每个UICollectionView, UITableView和UIScrollView(需要iOS 10+)!

这些视图中的每一个都有一个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
}

@objc 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)
}

对于拉刷新,我正在使用

DGElasticPullToRefresh

https://github.com/gontovnik/DGElasticPullToRefresh

安装

豆荚的DGElasticPullToRefresh

import DGElasticPullToRefresh

然后把这个函数放到你的swift文件中,然后从你的

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

不要忘记在视图消失时删除引用

要删除拉刷新,请将此代码放入您的

重写func viewDidDisappear(_ animated: Bool)

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

 }

看起来就像

快乐编码:)


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

细节

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

您可以通过使用几行代码来实现这一点。所以为什么你会困在第三方库或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  
}


斯威夫特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
    }

由于可定制性较差,代码重复和刷新控件带来的错误,我创建了一个库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