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
How to use pull to refresh in Swift?
提问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 并在实用程序中“启用”表视图控制器 - 刷新功能。
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.) 在 viewDidLoad-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
, UITableView
and UIScrollView
!
目前,UIRefreshControl直接支持在UICollectionView
,UITableView
和UIScrollView
!
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 addSubview
based 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 refresh
isn't initialized. Note that you chose to make refresh
not 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 refresh
optional (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
,这可能值得研究。