objective c - UIRefreshControl sem UITableViewController



objective-c ios (8)

Apenas curioso, pois não parece imediatamente possível, mas existe uma maneira sorrateira de aproveitar a nova classe UIRefreshControl iOS 6 sem usar uma subclasse UITableViewController ?

Costumo usar um UIViewController com uma subvisualização UITableView e conformar com UITableViewDataSource e UITableViewDelegate vez de usar um UITableViewController restrições.


IOS 10 Swift 3.0

é simples

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
    }

}

Se você quiser aprender sobre o iOS 10, use o UIRefreshControl here .


A primeira sugestão de Keller causa um bug estranho no iOS 7, onde a inserção da tabela é aumentada após o controlador de visualização reaparecer. Mudando para a segunda resposta, usando o uitableviewcontroller, consertei as coisas para mim.


Adicionar o controle de atualização como uma subvisualização cria um espaço vazio acima dos cabeçalhos de seção.

Em vez disso, eu inseri um UITableViewController em meu UIViewController, então mudei minha propriedade tableView para apontar para a incorporada e viola! Mudanças mínimas de código. :-)

Passos:

  1. Crie um novo UITableViewController no Storyboard e incorpore-o ao seu UIViewController original
  2. Substitua @IBOutlet weak var tableView: UITableView! com o do UITableViewController recém-incorporado, como mostrado abaixo
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
    }

    ...
}

Aqui está outra solução que é um pouco diferente.

Eu tive que usá-lo por causa de alguns problemas de hierarquia de visão que eu tinha: Eu estava criando algumas funcionalidades que exigiam passando visualizações para diferentes locais na hierarquia de exibição, que quebradas ao usar uma tableview do UITableViewController b / c tableView é a visão raiz do UITableViewController self.view) e não apenas uma visualização regular, ele criava hierarquias de controlador / exibição inconsistentes e causava uma falha.

Basicamente crie sua própria subclasse de UITableViewController e sobrescreva o loadView para atribuir self.view uma view diferente, e sobrescreva a tableView propriedade para retornar uma tableview separada.

por exemplo:

@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

Quando combinado com a solução de Keller, isso será mais robusto no sentido de que o tableView é agora uma visão regular, não uma visão raiz do VC, e será mais robusto contra as mudanças nas hierarquias de visão. Exemplo de uso desta maneira:

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;

Existe outro uso possível para isso:

Como a subclassificação desta forma separa o self.view do self.tableView, agora é possível usar esse UITableViewController como mais de um controlador regular, e adicionar outras subviews ao self.view sem as esquisitices de adicionar subviews ao UITableView, portanto, pode-se considerar ver controladores diretamente uma subclasse de UITableViewController em vez de ter filhos UITableViewController.

Algumas coisas a observar:

Como estamos sobrescrevendo a propriedade tableView sem chamar super, pode haver algumas coisas que devem ser observadas e devem ser tratadas quando necessário. Por exemplo, definir a tableview no exemplo acima não adicionará a tableview a self.view e não definirá o quadro que você pode querer fazer. Além disso, nessa implementação, não há um tableView padrão fornecido a você quando a classe é instanciada, o que também é algo que você pode considerar adicionar. Eu não incluo aqui porque isso é caso a caso, e esta solução realmente se encaixa bem com a solução da Keller.


Em um palpite, e baseado na inspiração do DrummerB, eu tentei simplesmente adicionar uma instância do UIRefreshControl como uma subview ao meu UITableView . E magicamente só funciona!

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

Isso adiciona um UIRefreshControl acima da exibição de tabela e funciona como esperado sem precisar usar um UITableViewController :)

EDIT: isso acima ainda funciona, mas como alguns têm apontado, há uma ligeira "gaguejar" ao adicionar o UIRefreshControl dessa maneira. Uma solução para isso é instanciar um UITableViewController e, em seguida, definindo seu UIRefreshControl e UITableView para isso, ou seja:

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;

O que você tentaria é usar a exibição de contêiner dentro do ViewController que estiver usando. você pode definir a subclasse UITableViewController limpa com visualização de tabela dedicada e colocar isso no ViewController.


Tente atrasar a chamada para o método refreshControl -endRefresh por uma fração de segundo depois que o tableView recarregar seu conteúdo, usando o -performSelector:withObject:afterDelay: do NSObject -performSelector:withObject:afterDelay: ou o dispatch_after do GCD.

Eu criei uma categoria no UIRefreshControl para isso:

@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

Eu testei e isso também funciona em visualizações de coleção. Eu notei que um atraso tão pequeno quanto 0,01 segundo é suficiente:

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

Tente isso

As soluções Above são boas, mas o tableView.refreshControl está disponível apenas para o UITableViewController, até o iOS 9.x e vem no UITableView a partir do iOS 10.x em diante.

Escrito em 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)