Je construis un lecteur RSS à l'aide de Swift et je dois implémenter une fonctionnalité d'extraction pour recharger.
Voici comment j'essaye de le faire.
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()
}
Je reçois :
Propriété 'self.refresh' non initialisée à l'appel super.init
S'il vous plaît, aidez-moi à comprendre le comportement des reconnaisseurs de geste. Un exemple de code de travail sera d'une grande aide.
Merci.
Tirez pour actualiser est construit dans iOS. Vous pouvez le faire dans Swift comme
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
}
À un moment donné, vous pourriez finir par rafraîchir.
refreshControl.endRefreshing()
Une solution avec storyboard et Swift ...
1.) Ouvrez votre fichier .storyboard, sélectionnez un TableViewController dans votre storyboard et "activez" la fonctionnalité Contrôleur de vue de table - Actualisation dans les utilitaires.
2.) Ouvrez la classe UITableViewController associée et ajoutez la ligne suivante à la méthode viewDidLoad.
self.refreshControl?.addTarget(self, action: "refresh:", forControlEvents: UIControlEvents.ValueChanged)
OUdans Swift 2.2:
self.refreshControl?.addTarget(self, action: #selector(TestTableViewController.refresh(_:)), forControlEvents: UIControlEvents.ValueChanged)
3.) Ajoutez la méthode suivante au-dessus de la méthode viewDidLoad
func refresh(sender:AnyObject)
{
// Updating your data here...
self.tableView.reloadData()
self.refreshControl?.endRefreshing()
}
J'aimerais mentionner une fonctionnalité PRETTY COOL incluse depuis iOS 10, à savoir:
Pour l'instant, UIRefreshControl est directement pris en charge dans chacune des variables UICollectionView
, UITableView
et UIScrollView
!
Chacune de ces vues possède la propriété refreshControl instance, ce qui signifie que il n'est plus nécessaire de l'ajouter en tant que sous-vue dans votre vue de défilement}, il vous suffit de:
@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()
}
Personnellement, je trouve plus naturel de le traiter comme une propriété pour une vue défilée plutôt que de l'ajouter comme une sous-vue, en particulier parce que la seule vue appropriée pour servir de vue d'ensemble pour un UIRefreshControl est un scrollview, c'est-à-dire que la fonctionnalité d'utilisation de UIRefreshControl est uniquement utile lorsque vous travaillez avec une vue de défilement; C'est pourquoi cette approche devrait être plus évidente pour configurer la vue de contrôle d'actualisation.
Cependant, vous avez toujours la possibilité d'utiliser la variable addSubview
basée sur la version iOS:
if #available(iOS 10.0, *) {
collectionView.refreshControl = refreshControl
} else {
collectionView.addSubview(refreshControl)
}
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
}
Et vous pouvez arrêter de vous rafraîchir avec:
refreshControl.endRefreshing()
Dans Swift utilisez ceci,
Si vous voulez avoir tirer pour actualiser dans WebView,
Alors essayez ce 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()
}
La réponse d'Anhil m'a beaucoup aidé.
Cependant, après des expériences plus poussées, j’ai remarqué que la solution suggérée entraînait parfois un/pas si joli UI glitch .
Au lieu de cela, aller pour cette approche * a fait le tour pour moi.
//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
Ce que l'erreur vous dit, c'est que refresh
n'est pas initialisé. Notez que vous avez choisi de rendre refresh
non facultatif, ce qui signifie dans Swift que doit avoir une valeur avant d'appeler super.init
(ou de manière implicite, ce qui semble être votre cas). Rendez refresh
optionnel (probablement ce que vous voulez) ou initialisez-le d’une manière ou d’une autre.
Je suggérerais de relire à nouveau la documentation d’introduction de Swift, qui couvre la question en détail.
Une dernière chose, qui ne fait pas partie de la réponse, comme l'a souligné @Anil, est un tirage intégré pour actualiser le contrôle dans iOS appelé UIRefresControl
, qui pourrait être intéressant à examiner.
J'ai construit une application de flux RSS dans laquelle je dispose d'un Pull To refresh qui présentait à l'origine certains des problèmes énumérés ci-dessus.
Mais pour ajouter aux réponses des utilisateurs ci-dessus, je cherchais partout mon cas d'utilisation et je ne le trouvais pas. Je téléchargeais des données sur le Web (RSSFeed) et je voulais afficher sur ma table les histoires à actualiser.
Ce qui est mentionné ci-dessus couvre les bons domaines, mais avec certains des problèmes que rencontrent les gens, voici ce que j'ai fait et cela fonctionne très bien:
J'ai suivi l'approche de @Blankarsch et suis allé sur mon tableau principal.storyboard et sélectionnez l'affichage sous forme de tableau pour utiliser l'actualisation.
//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()
}
J'espère que cela aide quelqu'un dans la même situation que moi
Je suggère de faire une extension de pull To Refresh à utiliser dans chaque classe.
1) Créez un fichier Swift vide: Fichier - Nouveau - Fichier - Fichier Swift.
2) Ajouter ce qui suit
// 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) Dans votre contrôleur View, appelez ces méthodes comme suit:
override func viewWillAppear(animated: Bool) {
self.makePullToRefreshToTableView(bidderListTable, triggerToMethodName: "pullToRefreshBidderTable")
}
4) À un moment donné, vous vouliez mettre fin au rafraîchissement:
func pullToRefreshBidderTable() {
self.makePullToRefreshEndRefreshing("bidderListTable")
//Code What to do here.
}
OR
self.makePullToRefreshEndRefreshing("bidderListTable")
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()
N'oubliez pas de ajoutez le code de la solution ici
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()
}
}
vous pouvez utiliser cette sous-classe de 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 - dans le générateur d’interface, changez la classe de votre tableView en PullToRefreshTableView
ou créez un PullToRefreshTableView
par programme
2 - implémentez la PullToRefreshTableViewDelegate
dans votre contrôleur de vue
3 - tableViewDidStartRefreshing(tableView: PullToRefreshTableView)
sera appelé dans votre contrôleur de vue lorsque la vue de la table commence à se rafraîchir
4 - appelez yourTableView.endRefreshing()
pour terminer le rafraîchissement
Pour l'actualisation, j'utilise
DGElasticPullToRefresh
https://github.com/gontovnik/DGElasticPullToRefresh
Installation
pod 'DGElasticPullToRefresh'
import DGElasticPullToRefresh
et mettez cette fonction dans votre fichier Swift et appelez cette fonction depuis votre
override func viewWillAppear (_ animé: 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!)
}
Et n'oubliez pas de supprimer la référence pendant que la vue disparaîtra
pour retirer tirez pour actualiser mettez ce code dans votre
override func viewDidDisappear (_ animé: Bool)
override func viewDidDisappear(_ animated: Bool) {
table.dg_removePullToRefresh()
}
Et ça va ressembler
Bonne codage :)
C’est ainsi que j’ai fait fonctionner cela avec Xcode 7.2, ce qui, à mon avis, est un bug majeur. Je l'utilise dans ma UITableViewController
dans ma viewWillAppear
refreshControl = UIRefreshControl()
refreshControl!.addTarget(self, action: "configureMessages", forControlEvents: .ValueChanged)
refreshControl!.beginRefreshing()
configureMessages()
func configureMessages() {
// configuring messages logic here
self.refreshControl!.endRefreshing()
}
Comme vous pouvez le constater, je dois littéralement appeler la méthode configureMessage()
après avoir configuré mon UIRefreshControl
, après quoi les actualisations suivantes fonctionneront correctement.
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()
}