objective-c - objective - swift ios pull to refresh
UIRefreshControl sin UITableViewController (12)
Solo por curiosidad, ya que no parece posible de inmediato, pero ¿hay alguna forma astuta de aprovechar la nueva clase UIRefreshControl
de iOS 6 sin utilizar una subclase UITableViewController
?
A menudo utilizo un UIViewController
con una subvista UITableViewDataSource
y UITableViewDelegate
UITableViewDataSource
a UITableViewDataSource
y UITableViewDelegate
lugar de utilizar un UITableViewController
directamente.
IOS 10 Swift 3.0
es sencillo
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
}
}
Si desea obtener más información sobre iOS 10 UIRefreshControl, lea here .
Al agregar el control de actualización como una subvista, se crea un espacio vacío sobre los encabezados de sección.
En cambio, incrusté un UITableViewController en mi UIViewController, luego cambié mi propiedad tableView
para que apunte hacia la incrustada, y viola! Cambios mínimos de código. :-)
Pasos:
- Cree un nuevo UITableViewController en el guión gráfico e insértelo en su UIViewController original
- Reemplace
@IBOutlet weak var tableView: UITableView!
con el del recientemente incorporado UITableViewController, como se muestra a continuación
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
}
...
}
Aquí hay otra solución que es un poco diferente.
Tuve que usarlo debido a algunos problemas de jerarquía de vistas que tenía: estaba creando una funcionalidad que requería pasar las vistas a diferentes lugares en la jerarquía de vistas, que se rompió al usar la vista de tabla de un UITableViewController. La tablaView es la vista raíz del UITableViewController. self.view) y no solo una vista normal, creó jerarquías de controlador / vista inconsistentes y causó un bloqueo.
Básicamente, cree su propia subclase de UITableViewController y anule loadView para asignarse self.view a una vista diferente, y anule la propiedad tableView para devolver una tableview separada.
por ejemplo:
@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
Cuando se combina con la solución de Keller, esto será más robusto en el sentido de que tableView ahora es una vista regular, no una vista de raíz de VC, y será más robusto para cambiar las jerarquías de vista. Ejemplo de usarlo de esta manera:
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;
Hay otro uso posible para esto:
Dado que la subclase de esta manera separa a self.view de self.tableView, ahora es posible usar este UITableViewController como un controlador más regular, y agregar otras subvistas a self.view sin las rarezas de agregar subviews a UITableView, por lo que uno puede considerar hacer su vea los controladores directamente una subclase de UITableViewController en lugar de tener hijos UITableViewController.
Algunas cosas a tener en cuenta:
Ya que estamos anulando la propiedad tableView sin llamar a super, puede haber algunas cosas a tener en cuenta y deben manejarse donde sea necesario. Por ejemplo, configurar la vista de tabla en mi ejemplo anterior no agregará la vista de tabla a self.view y no establecerá el marco que desee hacer. Además, en esta implementación no se le da una vista de tabla predeterminada cuando se crea una instancia de la clase, que también es algo que puede considerar agregar. No lo incluyo aquí porque es un caso por caso, y esta solución realmente encaja bien con la solución de Keller.
Bueno, UIRefreshControl es una subclase de UIView, por lo que puede usarlo por sí mismo. No estoy seguro de cómo se rinde. La representación podría simplemente depender del marco, pero también podría depender de un UIScrollView o del UITableViewController.
De cualquier manera, va a ser más un hack que una solución elegante. Te recomiendo que busques en uno de los clones de terceros disponibles o escribas el tuyo.
En una corazonada, y en base a la inspiración de DrummerB, intenté simplemente agregar una instancia de UIRefreshControl
como una subvista a mi UITableView
. ¡Y mágicamente simplemente funciona!
UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
[refreshControl addTarget:self action:@selector(handleRefresh:) forControlEvents:UIControlEventValueChanged];
[self.myTableView addSubview:refreshControl];
Esto agrega un UIRefreshControl
sobre su vista de tabla y funciona como se espera sin tener que usar un UITableViewController
:)
EDITAR: Esto arriba todavía funciona pero como algunos han señalado, hay un ligero "tartamudeo" cuando se agrega el Control UIRefresh de esta manera. Una solución para eso es crear una instancia de un UITableViewController, y luego configurar su UIRefreshControl y UITableView a eso, es decir:
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;
Intente retrasar la llamada al método refreshControl -endRefresh
una fracción de segundo después de que tableView vuelva a cargar su contenido, ya sea utilizando NSObject''s -performSelector:withObject:afterDelay:
o GCD''s dispatch_after
.
Creé una categoría en UIRefreshControl para esto:
@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
Lo probé y esto también funciona en vistas de colección. He notado que un retraso tan pequeño como 0.01 segundos es suficiente:
// My data refresh process here while the refresh control ''isRefreshing''
[self.tableView reloadData];
[self.refreshControl endRefreshingAfterDelay:.01];
La primera sugerencia de Keller causa un error extraño en iOS 7 donde la inserción de la tabla se incrementa después de que reaparece el controlador de vista. Cambiar a la segunda respuesta, usando el controlador uitableview, me solucionó las cosas.
Lo que intentarías es usar la vista de contenedor dentro de ViewController que estás usando. puede definir una subclase UITableViewController limpia con una vista de tabla dedicada y colocarla en el ViewController.
Para eliminar el tartamudeo causado por la respuesta aceptada, puede asignar su UITableView
a un 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;
EDITAR:
Una forma de agregar un UIRefreshControl
sin UITableViewController
sin tartamudear y conservar la buena animación después de actualizar los datos en la vista de tabla.
UIRefreshControl *refreshControl = [UIRefreshControl new];
[refreshControl addTarget:self action:@selector(handleRefresh:) forControlEvents:UIControlEventValueChanged];
[self.theTableView addSubview:refreshControl];
[self.theTableView sendSubviewToBack:refreshControl];
Más tarde, al manejar los datos actualizados ...
- (void)handleRefresh:(UIRefreshControl *)refreshControl {
[self.theTableView reloadData];
[self.theTableView layoutIfNeeded];
[refreshControl endRefreshing];
}
Prueba esto,
Las soluciones anteriores están bien, pero tableView.refreshControl está disponible solo para UITableViewController, hasta iOS 9.x y viene en UITableView desde iOS 10.x en adelante.
Escrito en 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)
Resulta que puede usar lo siguiente cuando usa un UIViewController con una subvista UITableView y se ajusta a UITableViewDataSource y UITableViewDelegate:
self.refreshControl = [[UIRefreshControl alloc]init];
[self.refreshControl addTarget:self action:@selector(refresh:) forControlEvents:UIControlEventValueChanged];
Para Swift 2.2.
Primero haga UIRefreshControl ().
var refreshControl : UIRefreshControl!
En su método viewDidLoad () agregue:
refreshControl = UIRefreshControl()
refreshControl.attributedTitle = NSAttributedString(string: "Refreshing..")
refreshControl.addTarget(self, action: #selector(YourUIViewController.refresh(_:)), forControlEvents: UIControlEvents.ValueChanged)
self.tableView.addSubview(refreshControl)
Y hacer la función de actualización.
func refresh(refreshControl: UIRefreshControl) {
// do something ...
// reload tableView
self.tableView.reloadData()
// End refreshing
refreshControl.endRefreshing()
}