ios 如何在 Swift 中使用 pull 刷新?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24475792/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 01:10:11  来源:igfitidea点击:

How to use pull to refresh in Swift?

iosswiftxcodeuirefreshcontrol

提问by xrage

I am building an RSS reader using swift and need to implement pull to reload functionality.

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

Here is how i am trying to do it.

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

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

I am getting :

我正进入(状态 :

Property 'self.refresh' not initialized at super.init call

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

Please help me to understand the behaviour of Gesture recognisers. A working sample code will be a great help.

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

Thanks.

谢谢。

回答by Anil Varghese

Pull to refreshis built in iOS. You could do this in swift like

拉动刷新内置于 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  
}

At some point you could end refreshing.

在某些时候,您可以结束刷新。

refreshControl.endRefreshing()

回答by Blank

A Solution with storyboard and swift...

具有故事板和快速的解决方案...

1.) Open your .storyboard file, select a TableViewController in your storyboard and "Enable" the Table View Controller - Refreshing feature in the Utilities.

1.) 打开您的 .storyboard 文件,在您的故事板中选择一个 TableViewController 并在实用程序中“启用”表视图控制器 - 刷新功能。

enter image description here

在此处输入图片说明

2.) Open the associated UITableViewController-Class and add the following line into the viewDidLoad-Method.

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

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

Edited for Swift 5.0 :

为 Swift 5.0 编辑:

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

ORin Swift 2.2:

或者在 Swift 2.2 中:

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

3.) Add the following Method above the viewDidLoad-Method

3.) 在 vi​​ewDidLoad-Method 上方添加以下方法

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

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

回答by Ahmad F

I would like to mention a PRETTY COOLfeature that has been included since iOS 10, which is:

我想提一下自 iOS 10 以来已经包含的一个非常酷的功能,它是:

For now, UIRefreshControlis directly supported in each of UICollectionView, UITableViewand UIScrollView!

目前,UIRefreshControl直接支持在UICollectionView,UITableViewUIScrollView!

Each one of these views have refreshControlinstance property, which means that there is no longer a need to add it as a subview in your scroll view, all you have to do is:

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

Personally, I find it more natural to treat it as a property for scroll view more than add it as a subview, especially because the only appropriate view to be as a superview for a UIRefreshControl is a scrollview, i.e the functionality of using UIRefreshControl is only useful when working with a scroll view; That's why this approach should be more obvious to setup the refresh control view.

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

However, you still have the option of using the addSubviewbased on the iOS version:

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

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

回答by Gilad Brunfman

Swift 4

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

And you could stop refreshing with:

你可以停止刷新:

refreshControl.endRefreshing()

回答by Zaid Pathan

In Swiftuse this,

Swift 中使用这个,

If you wants to have pull to refresh in WebView,

如果你想在 WebView 中拉动刷新,

So try this code:

所以试试这个代码:

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

回答by Leverin

Anhil's answer helped me a lot.

Anhil 的回答对我帮助很大。

However, after experimenting further I noticed that the solution suggested sometimes causes a not-so-pretty UI glitch.

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

Instead, going for this approach* did the trick for me.

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

*Swift 2.1

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

回答by hiren

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

回答by Vasily Bodnarchuk

Details

细节

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

Features

特征

  • Ability to make "pull to refresh" programmatically
  • Protection from multi- "pull to refresh" events
  • Ability to continue animating of the activity indicator when view controller switched (e.g. in case of TabController)
  • 能够以编程方式进行“拉动刷新”
  • 保护免受多重“拉动刷新”事件的影响
  • 能够在视图控制器切换时继续为活动指示器设置动画(例如在 TabController 的情况下)

Solution

解决方案

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

Usage

用法

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

Full Sample

完整样本

Do not forget to add the solution code here

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

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

回答by Dom Bryan

I built a RSS feed app in which I have a Pull To refreshfeature that originally had some of the problems listed above.

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

But to add to the users answers above, I was looking everywhere for my use case and could not find it. I was downloading data from the web (RSSFeed) and I wanted to pull down on my tableView of stories to refresh.

但是要添加到上面的用户答案中,我到处寻找我的用例但找不到它。我正在从网络 (RSSFeed) 下载数据,我想下拉我的故事表视图以刷新。

What is mentioned above cover the right areas but with some of the problems people are having, here is what I did and it works a treat:

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

I took @Blankarsch 's approach and went to my main.storyboard and select the table view to use refresh, then what wasn't mentioned is creating IBOutlet and IBAction to use the refresh efficiently

我采用@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()
}

Hope this helps anyone in same situation as me

希望这可以帮助与我处于相同情况的任何人

回答by JustSid

What the error is telling you, is that refreshisn't initialized. Note that you chose to make refreshnot optional, which in Swift means that it hasto have a value before you call super.init(or it's implicitly called, which seems to be your case). Either make refreshoptional (probably what you want) or initialize it in some way.

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

I would suggest reading the Swift introductory documentation again, which covers this in great length.

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

One last thing, not part of the answer, as pointed out by @Anil, there is a built in pull to refresh control in iOS called UIRefresControl, which might be something worth looking into.

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