web-dev-qa-db-fra.com

Comment utiliser pull pour rafraîchir dans Swift?

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.

185
xrage

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()
491
Anil Varghese

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.

enter image description here

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

iOS 10 - Swift 3 (également applicable pour iOS 11 - Swift 4)

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)
}
61
Ahmad F

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()
34
Gilad Brunfman

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()
}
6
Zaid Pathan

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.

* 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
5
Leverin

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.

2
JustSid

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

2
Dom Bryan

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")
1
A.G

Détails

  • Xcode version 10.3 (10G8), Swift 5

Caractéristiques

  • Possibilité de faire "tirer pour actualiser" par programme
  • Protection contre les événements multiples "tirer pour rafraîchir"
  • Possibilité de poursuivre l'animation de l'indicateur d'activité lors de la commutation du contrôleur de vue (par exemple dans le cas de 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()

Échantillon complet

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()
    }
}
1
Vasily Bodnarchuk

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

0
Chuy47

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

 enter image description here

Bonne codage :)

0

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.

0
jaytrixz

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