UIRefreshControl senza UITableViewController


308

Solo curioso, poiché non sembra immediatamente possibile, ma c'è un modo subdolo per sfruttare la nuova UIRefreshControlclasse iOS 6 senza utilizzare una UITableViewControllersottoclasse?

Uso spesso a UIViewControllercon una UITableViewsottoview e mi conformo UITableViewDataSourcee UITableViewDelegatepiuttosto che usare un UITableViewControllervero e proprio.


6
@DaveDeLong: No, Dave, cosa dovrebbe fare? Più avanti in questa pagina dici che la sua soluzione non è supportata, quindi qual è la soluzione giusta?
matt

3
@ Matt si dovrebbe usare un UITableViewControllere inviare un bug che richiede API per utilizzare un UIRefreshControlcon una UITableViewdirettamente.
Dave DeLong,

31
UITableViewController ha avuto (e continua ad avere) troppi bug e problemi oscuri e di nicchia con gerarchie di viste non banali ... che tutti magicamente scompaiono quando si passa all'uso di un VC standard con una sottoview TableView. "Usa UITVC" è un cattivo inizio per qualsiasi soluzione, IMHO.
Adam,

@Adam Questi bug emergono quando si utilizza 'UITableViewController' come controller di visualizzazione figlio (dando così accesso alla visualizzazione della personalizzazione senza spostarsi nella gerarchia di tableViewController)? Non ho mai riscontrato problemi durante l'utilizzo in questo modo.
Memmons

Risposte:


388

In un sospetto, e basato sull'ispirazione di DrummerB, ho provato semplicemente ad aggiungere UIRefreshControlun'istanza come sottoview alla mia UITableView. E magicamente funziona!

UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
[refreshControl addTarget:self action:@selector(handleRefresh:) forControlEvents:UIControlEventValueChanged];
[self.myTableView addSubview:refreshControl];

Questo aggiunge una vista UIRefreshControlsopra la tabella e funziona come previsto senza dover usare un UITableViewController:)


EDIT: Questo sopra funziona ancora, ma come alcuni hanno sottolineato, c'è una leggera "balbuzie" quando si aggiunge UIRefreshControl in questo modo. Una soluzione a ciò è creare un'istanza di UITableViewController e quindi impostare UIRefreshControl e UITableView su quello, ovvero:

UITableViewController *tableViewController = [[UITableViewController alloc] init];
tableViewController.tableView = self.myTableView;

self.refreshControl = [[UIRefreshControl alloc] init];
[self.refreshControl addTarget:self action:@selector(getConnections) forControlEvents:UIControlEventValueChanged];
tableViewController.refreshControl = self.refreshControl;

48
Cordiali saluti, questo è un comportamento non supportato e potrebbe cambiare in futuro. L'unica garanzia che Apple fornisce è che il suo utilizzo secondo l'API fornito (in questo caso -[UITableViewController setRefreshControl:]) continuerà a funzionare.
Dave DeLong,

18
Con questa implementazione, sembra che proprio prima di innescare handleRefresh:ci sia un cambiamento inatteso nel valore della vista di scorrimento contentInsetsper una frazione di secondo. Qualcun altro lo sperimenta o ha una soluzione per questo? (sì, so che questo non è supportato in primo luogo!)
Tim

6
Dovresti semplicemente usare una vista contenitore per questo. Basta impostare la classe del controller di visualizzazione sulla sottoclasse personalizzata di UITableViewControllere "lo farai nel modo giusto".
Eugene,

3
In iOS7 (sconosciuto per iOS6) la versione modificata funziona correttamente tranne quando chiudi e riapri l'app. L'animazione non viene riprodotta fino alla seconda volta che si aggiorna. La prima volta che si aggiorna dopo aver riaperto l'app, è solo un cerchio completo invece del cerchio incrementale in base a quanto lo si sposta verso il basso. C'è una soluzione?
david2391,

30
Per evitare che il "slutter", come accennato in precedenza, pur escludendo l'UITableViewController, basta aggiungere il controllo di aggiornamento sotto la vista tabella in questo modo: [_tableView insertSubview:_refreshControl atIndex:0];. Testato con iOS 7 e 8;)
ptitvinou,

95

Per eliminare la balbuzie causata dalla risposta accettata, puoi assegnare il tuo UITableViewa UITableViewController.

_tableViewController = [[UITableViewController alloc]initWithStyle:UITableViewStylePlain];
[self addChildViewController:_tableViewController];

_tableViewController.refreshControl = [UIRefreshControl new];
[_tableViewController.refreshControl addTarget:self action:@selector(loadStream) forControlEvents:UIControlEventValueChanged];

_theTableView = _tableViewController.tableView;

MODIFICARE:

Un modo per aggiungere un UIRefreshControlno UITableViewControllercon nessun balbuzie e conservare la bella animazione dopo aver aggiornato i dati sulla vista tabella.

UIRefreshControl *refreshControl = [UIRefreshControl new];
[refreshControl addTarget:self action:@selector(handleRefresh:) forControlEvents:UIControlEventValueChanged];
[self.theTableView addSubview:refreshControl];
[self.theTableView sendSubviewToBack:refreshControl];

Successivamente durante la gestione dei dati aggiornati ...

- (void)handleRefresh:(UIRefreshControl *)refreshControl {
    [self.theTableView reloadData];
    [self.theTableView layoutIfNeeded];
    [refreshControl endRefreshing];
}

7
Non è nemmeno necessario creare un nuovo UITableView. Dì solo initWithStyle :istingTableView.style - e successivamente fai newTableViewController.tableView =istingTableView esistente e assegna refreshControl. Riduce a tre righe.
Trenskow,

Non dimenticare di chiamare [_tablewViewController didMoveToParentViewController:self];dopo aver aggiunto la sottoview.
qix,

4
self.tableView = _tableViewController.tableView;dovrebbe essere_tableViewController.tableView = self.tableView
Ali,

@Linus perché è necessario? Ho aggiunto _tableViewController.view come subView nel mio metodo viewDidLoad viewCidoller personalizzato e questo sembrava fare il trucco. Non avevo nemmeno bisogno di impostare _tableViewController.tableView
taber

Il motivo per cui non riesco a utilizzare un UITableViewController e sto usando un UIViewController con una vista tabella all'interno. stackoverflow.com/questions/18900428/...
lostintranslation

21

Quello che proveresti è usare la vista container all'interno di ViewController che stai utilizzando. è possibile definire una sottoclasse UITableViewController pulita con vista tabella dedicata e posizionarla nel ViewController.


2
Precisamente, il modo più pulito sembra aggiungere il controller di visualizzazione figlio di tipo UITableViewController.
Zdenek,

È quindi possibile afferrare UITableViewController utilizzando self.childViewControllers.firstObject.
cbh2000,

^ potresti anche prendere in considerazione l'utilizzo prepareForSegueper ottenere un riferimento all'oggetto UITableViewController in una vista contenitore poiché questo viene immediatamente attivato come evento successivo all'istanza dallo storyboard.
crarho,

18

Bene UIRefreshControl è una sottoclasse UIView, quindi puoi usarla da sola. Non sono sicuro di come si renda da solo. Il rendering potrebbe semplicemente dipendere dal frame, ma potrebbe anche fare affidamento su UIScrollView o UITableViewController.

Ad ogni modo, sarà più un trucco che una soluzione elegante. Ti consiglio di consultare uno dei cloni di terze parti disponibili o di scrivere il tuo.

ODRefreshControl

inserisci qui la descrizione dell'immagine

SlimeRefresh

inserisci qui la descrizione dell'immagine


1
Grazie per la tua risposta, ma non esattamente quello che stavo cercando. La prima frase del tuo post, tuttavia, mi ha ispirato a provare quella che alla fine è stata la soluzione!
Keller,

7
ODRefreshControl è fantastico - grazie per il suggerimento! Molto semplice, e fa il lavoro. E ovviamente molto più flessibile di quello di Apple. Non ho mai capito perché qualcuno avrebbe usato UITableViewController, IMO la peggiore classe dell'intera API. Non fa nulla (accanto a), ma rende le tue opinioni inflessibili.
n13,

In realtà dipende dal tuo progetto quello che usi lì dentro .. usare una uitableview nel mio caso stava aumentando la complessità del progetto e ora sono passato a uitableviewcontroller che ha diviso il mio codice in due segmenti Sono contento di avere 2 file più piccoli che possono essere usato per il debug invece di un enorme file ..
kush,

@ n13 Ciao, un buon motivo per usare UITableViewController è riuscire a progettare con celle statiche. Purtroppo non disponibile in un tableView residente in un UIViewController.
Jean Le Moignan,

@JeanLeMoignan Gli strumenti e le librerie iOS stanno cambiando rapidamente, quindi un mio commento di 3 anni non è più valido. Sono passato alla creazione di tutte le interfacce utente in codice con SnapKit e onestamente è enormemente più efficiente rispetto a fare clic su 10 mila volte nel generatore di interfacce. Anche cambiare un UITableViewController in un UIViewController è facile e richiede solo un secondo, mentre in IB è un grosso problema.
13

7

Prova a ritardare la chiamata al -endRefreshmetodo refreshControl di una frazione di secondo dopo che tableView ha ricaricato il suo contenuto, utilizzando NSObject -performSelector:withObject:afterDelay:o GCD dispatch_after.

Ho creato una categoria su UIRefreshControl per questo:

@implementation UIRefreshControl (Delay)

- (void)endRefreshingAfterDelay:(NSTimeInterval)delay {
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC));
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        [self endRefreshing];
    });
}

@end

L'ho provato e questo funziona anche con le visualizzazioni della raccolta. Ho notato che è sufficiente un ritardo di soli 0,01 secondi:

// My data refresh process here while the refresh control 'isRefreshing'
[self.tableView reloadData];
[self.refreshControl endRefreshingAfterDelay:.01];

4
I ritardi e le attese sono davvero di cattivo stile.
orkoden,

2
@orkoden sono d'accordo. Questa è una soluzione alternativa per un uso già non supportato di UIRefreshControl.
Boliva,

Puoi chiamare un metodo con un ritardo molto più facile con. [self performSelector:@selector(endRefreshing) withObject:nil afterDelay:0.01]
orkoden,

2
L'effetto finale è esattamente lo stesso e non rende l'hacking più / meno elegante. Se utilizzare -performSelector:withObject:afterDelay:o GCD per questo dipende da una questione di gusti personali.
Boliva,

7

IOS 10 Swift 3.0

è semplice

import UIKit

class ViewControllerA: UIViewController, UITableViewDataSource, UITableViewDelegate {

    @IBOutlet weak var myTableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()

        myTableView.delegate = self
        myTableView.dataSource = self

        if #available(iOS 10.0, *) {
            let refreshControl = UIRefreshControl()
            let title = NSLocalizedString("PullToRefresh", comment: "Pull to refresh")
            refreshControl.attributedTitle = NSAttributedString(string: title)
            refreshControl.addTarget(self,
                                     action: #selector(refreshOptions(sender:)),
                                     for: .valueChanged)
            myTableView.refreshControl = refreshControl
        }
    }

    @objc private func refreshOptions(sender: UIRefreshControl) {
        // Perform actions to refresh the content
        // ...
        // and then dismiss the control
        sender.endRefreshing()
    }

    // MARK: - Table view data source

    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 12
    }


    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)

        cell.textLabel?.text = "Cell \(String(indexPath.row))"
        return cell
    }

}

inserisci qui la descrizione dell'immagine

Se vuoi saperne di più su iOS 10 UIRefreshControl leggi qui .


3

L'aggiunta del controllo di aggiornamento come visualizzazione secondaria crea uno spazio vuoto sopra le intestazioni di sezione.

Invece, ho incorporato un UITableViewController nel mio UIViewController, quindi ho cambiato la mia tableViewproprietà in modo che punti verso quello incorporato e viola! Modifiche minime al codice. :-)

passi:

  1. Crea un nuovo UITableViewController nello Storyboard e incorporalo nel tuo UIViewController originale
  2. Sostituire @IBOutlet weak var tableView: UITableView!con quello del UITableViewController appena incorporato, come mostrato di seguito

class MyViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    weak var tableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()

        let tableViewController = self.childViewControllers.first as! UITableViewController
        tableView = tableViewController.tableView
        tableView.dataSource = self
        tableView.delegate = self

        // Now we can (properly) add the refresh control
        let refreshControl = UIRefreshControl()
        refreshControl.addTarget(self, action: "handleRefresh:", forControlEvents: .ValueChanged)
        tableViewController.refreshControl = refreshControl
    }

    ...
}

2

Per Swift 2.2.

Per prima cosa crea UIRefreshControl ().

var refreshControl : UIRefreshControl!

Nel tuo metodo viewDidLoad () aggiungi:

refreshControl = UIRefreshControl()
    refreshControl.attributedTitle = NSAttributedString(string: "Refreshing..")
    refreshControl.addTarget(self, action: #selector(YourUIViewController.refresh(_:)), forControlEvents: UIControlEvents.ValueChanged)
    self.tableView.addSubview(refreshControl)

E fai la funzione di aggiornamento

func refresh(refreshControl: UIRefreshControl) {

    // do something ...

    // reload tableView
    self.tableView.reloadData()

    // End refreshing
    refreshControl.endRefreshing()
}

0

Ecco un'altra soluzione leggermente diversa.

Ho dovuto usarlo a causa di alcuni problemi di gerarchia della vista che avevo: stavo creando alcune funzionalità che richiedevano il passaggio di viste in luoghi diversi nella gerarchia della vista, che si interrompeva quando si utilizzava la vista tabella di UITableViewController b / c la vista tabella è la vista radice di UITableViewController ( self.view) e non solo una vista normale, ha creato gerarchie di controller / viste incoerenti e ha causato un arresto anomalo.

Fondamentalmente creare la propria sottoclasse di UITableViewController e sovrascrivere loadView per assegnare self.view una vista diversa e sovrascrivere la proprietà tableView per restituire una vista tabella separata.

per esempio:

@interface MyTableVC : UITableViewController
@end

@interface MyTableVC ()
@property (nonatomic, strong) UITableView *separateTableView;
@end

@implementation MyTableVC

- (void)loadView {
    self.view = [[UIView alloc] initWithFrame:CGRectZero];
}

- (UITableView *)tableView {
    return self.separateTableView;
}

- (void)setTableView:(UITableView *)tableView {
    self.separateTableView = tableView;
}

@end

Se combinato con la soluzione di Keller, questo sarà più robusto nel senso che tableView è ora una vista normale, non una vista radice di VC, ed è più robusto contro il cambiamento delle gerarchie di viste. Esempio di utilizzo in questo modo:

MyTableVC *tableViewController = [[MyTableVC alloc] init];
tableViewController.tableView = self.myTableView;

self.refreshControl = [[UIRefreshControl alloc] init];
[self.refreshControl addTarget:self action:@selector(getConnections) forControlEvents:UIControlEventValueChanged];
tableViewController.refreshControl = self.refreshControl;

C'è un altro possibile utilizzo per questo:

Poiché la sottoclasse in questo modo separa self.view da self.tableView, è ora possibile utilizzare questo UITableViewController come più di un normale controller e aggiungere altre subview a self.view senza le stranezze dell'aggiunta di subview a UITableView, quindi si potrebbe prendere in considerazione l'idea di creare il proprio visualizza i controller direttamente una sottoclasse di UITableViewController invece di avere figli UITableViewController.

Alcune cose a cui prestare attenzione:

Poiché stiamo sovrascrivendo la proprietà tableView senza chiamare super, potrebbero esserci alcune cose a cui fare attenzione e che dovremmo gestire dove necessario. Ad esempio, l'impostazione del tableview nel mio esempio precedente non aggiungerà il tableview a self.view e non imposterà il frame che potresti voler fare. Inoltre, in questa implementazione non viene fornito tableView predefinito quando viene istanziata la classe, che è anche qualcosa che potresti considerare di aggiungere. Non lo includo qui perché caso per caso, e questa soluzione si adatta perfettamente alla soluzione di Keller.


1
La domanda specificava "senza usare una sottoclasse UITableViewController" e questa risposta è per una sottoclasse UITableViewController.
Brian,

0

Prova questo,

Le soluzioni di cui sopra vanno bene, ma tableView.refreshControl è disponibile solo per UITableViewController, fino a iOS 9.x ed è disponibile in UITableView da iOS 10.x in poi.

Scritto in Swift 3 -

let refreshControl = UIRefreshControl()
refreshControl.addTarget(self, action: #selector(FeedViewController.loadNewData), for: UIControlEvents.valueChanged)
// Fix for the RefreshControl Not appearing in background
tableView?.addSubview(refreshControl)
tableView.sendSubview(toBack: refreshControl)

La migliore soluzione finora
Karthik KM

-1

Il primo suggerimento di Keller provoca uno strano bug in iOS 7 in cui l'inserzione della tabella viene aumentata dopo la ricomparsa del controller di visualizzazione. Passare alla seconda risposta, usando uitableviewcontroller, ha risolto le cose per me.


-6

Si scopre che è possibile utilizzare quanto segue quando si utilizza un UIViewController con una sottoview UITableView e conforme a UITableViewDataSource e UITableViewDelegate:

self.refreshControl = [[UIRefreshControl alloc]init];
[self.refreshControl addTarget:self action:@selector(refresh:) forControlEvents:UIControlEventValueChanged];

9
Disaccordo. self.refreshControlè una proprietà per a UITableViewController, non a UIViewController.
Rickster,

NON FUNZIONANTE, referhControl non è definito per uiviewcontroller
OMGPOP,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.