ios objective-c uitableview uinavigationcontroller uirefreshcontrol

ios - UIRefreshControl-beginRefreshing no funciona cuando UITableViewController está dentro de UINavigationController



objective-c (13)

Para Swift 4 / 4.1

Una combinación de respuesta existente hace el trabajo por mí:

refreshControl.beginRefreshing() tableView.setContentOffset(CGPoint(x: 0, y: tableView.contentOffset.y - (refreshControl.frame.size.height)), animated: true)

¡Espero que esto ayude!

He configurado un UIRefreshControl en mi UITableViewController (que está dentro de un UINavigationController) y funciona como se esperaba (es decir, desplegar dispara el evento correcto). Sin embargo, si invoco mediante programación el método de instancia beginRefreshing en el control de actualización como:

[self.refreshControl beginRefreshing];

No pasa nada. Debería animarse hacia abajo y mostrar el spinner. El método endRefreshing funciona correctamente cuando lo llamo después de la actualización.

Hice un prototipo básico con este comportamiento y funciona correctamente cuando mi UITableViewController se agrega directamente al controlador de vista raíz del delegado de la aplicación, por ejemplo:

self.viewController = tableViewController; self.window.rootViewController = self.viewController;

Pero si agrego TableViewController primero a un UINavigationController, entonces agregue el controlador de navegación como el control rootViewController , el método beginRefreshing ya no funciona. P.ej

UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:tableViewController]; self.viewController = navController; self.window.rootViewController = self.viewController;

Mi sensación es que esto tiene algo que ver con las jerarquías de vista anidadas dentro del controlador de navegación que no funcionan bien con el control de actualización, ¿alguna sugerencia?

Gracias


Además de la solución @Dymitry Shevchenko.

Encontré una buena solución a este problema. Puede crear una extensión para UIRefreshControl que sobrescribe el método:

// Adds code forgotten by Apple, that changes content offset of parent scroll view (table view). - (void)beginRefreshing { [super beginRefreshing]; if ([self.superview isKindOfClass:[UIScrollView class]]) { UIScrollView *view = (UIScrollView *)self.superview; [view setContentOffset:CGPointMake(0, view.contentOffset.y - self.frame.size.height) animated:YES]; } }

Puede usar una nueva clase configurando una clase personalizada en Identity Inspector para el control de actualización en Interface Builder.


Aquí está Swift 3 extensión que muestra spinner, así como animarlo.

import UIKit extension UIRefreshControl { func beginRefreshingWithAnimation() { DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) { if let scrollView = self.superview as? UIScrollView { scrollView.setContentOffset(CGPoint(x: 0, y: scrollView.contentOffset.y - self.frame.height), animated: true) } self.beginRefreshing() } } }


Aquí hay una extensión de Swift usando las estrategias descritas arriba.

extension UIRefreshControl { func beginRefreshingManually() { if let scrollView = superview as? UIScrollView { scrollView.setContentOffset(CGPoint(x: 0, y: scrollView.contentOffset.y - frame.height), animated: true) } beginRefreshing() } }


El enfoque ya mencionado:

[self.refreshControl beginRefreshing]; [self.tableView setContentOffset:CGPointMake(0, self.tableView.contentOffset.y-self.refreshControl.frame.size.height) animated:YES];

Haría que el spinner sea visible. Pero no animaría. Lo único que cambié es el orden de estos dos métodos y todo funcionó:

[self.tableView setContentOffset:CGPointMake(0, self.tableView.contentOffset.y-self.refreshControl.frame.size.height) animated:YES]; [self.refreshControl beginRefreshing];


Es un trabajo perfecto para mí:

Swift 3:

self.tableView.setContentOffset(CGPoint(x: 0, y: -self.refreshControl!.frame.size.height - self.topLayoutGuide.length), animated: true)


Fort Swift 2.2+

self.tableView.setContentOffset(CGPoint(x: 0, y: -refreshControl.frame.size.height), animated: true)


Ninguna de las otras respuestas funcionó para mí. Harían que la ruleta se muestre y gire, pero la acción de actualización en sí misma nunca ocurrirá. Esto funciona:

id target = self; SEL selector = @selector(example); // Assuming at some point prior to triggering the refresh, you call the following line: [self.refreshControl addTarget:target action:selector forControlEvents:UIControlEventValueChanged]; // This line makes the spinner start spinning [self.refreshControl beginRefreshing]; // This line makes the spinner visible by pushing the table view/collection view down [self.tableView setContentOffset:CGPointMake(0, -1.0f * self.refreshControl.frame.size.height) animated:YES]; // This line is what actually triggers the refresh action/selector [self.refreshControl sendActionsForControlEvents:UIControlEventValueChanged];

Tenga en cuenta que este ejemplo utiliza una vista de tabla, pero podría haber sido una vista de colección.


Parece que si comienza a actualizar mediante programación, tiene que desplazarse por la vista de tabla, digamos, cambiando contentoffset

[self.tableView setContentOffset:CGPointMake(0, -self.refreshControl.frame.size.height) animated:YES];

Supongo que la razón para esto es que no sería conveniente desplazarse al control de actualización cuando el usuario está en la mitad / parte inferior de la vista de tabla.

Swift 2.2 versión por @muhasturk

self.tableView.setContentOffset(CGPoint(x: 0, y: -refreshControl.frame.size.height), animated: true)


Si usa Rxswift para swift 3.1, puede usar a continuación:

func manualRefresh() { if let refreshControl = self.tableView.refreshControl { self.tableView.setContentOffset(CGPoint(x: 0, y: -refreshControl.height), animated: true) self.tableView.refreshControl?.beginRefreshing() self.tableView.refreshControl?.sendActions(for: .valueChanged) } }

Este trabajo para swift 3.1, iOS 10.


UITableViewController tiene la propiedad automaticallyAdjustsScrollViewInsets después de iOS 7. La vista de tabla ya puede tener contentOffset, generalmente (0, -64).

Entonces, la forma correcta de mostrar refreshControl después de comenzar a actualizar de forma programada es agregar la altura de refreshControl a contentOffset existente.

[self.refreshControl beginRefreshing]; [self.tableView setContentOffset:CGPointMake(0, self.tableView.contentOffset.y-self.refreshControl.frame.size.height) animated:YES];


Utilizo la misma técnica para mostrar al usuario el signo visual "datos actualizados". Un usuario del resultado trae la aplicación desde el fondo y los feeds / lists se actualizarán con la interfaz de usuario, como los usuarios que usan las tablas para refrescarse. Mi versión contiene 3 cosas

1) ¿Quién envía "despertar"

- (void)applicationDidBecomeActive:(UIApplication *)application { [[NSNotificationCenter defaultCenter] postNotificationName:kNotificationHaveToResetAllPages object:nil]; }

2) Observador en UIViewController

- (void)viewDidLoad { [super viewDidLoad]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(forceUpdateData) name:kNotificationHaveToWakeUp:nil]; }

3) El protocolo

#pragma mark - ForcedDataUpdateProtocol - (void)forceUpdateData { self.tableView.contentOffset = CGPointZero; if (self.refreshControl) { [self.refreshControl beginRefreshing]; [self.tableView setContentOffset:CGPointMake(0, -self.refreshControl.frame.size.height) animated:YES]; [self.refreshControl performSelector:@selector(endRefreshing) withObject:nil afterDelay:1]; } }


Ver también esta pregunta

UIRefreshControl no se muestra espinoso al llamar a beginRefreshing y contentOffset es 0

Me parece un error, porque solo se produce cuando la propiedad contentOffset de tableView es 0

Lo arreglé con el siguiente código (método para UITableViewController):

- (void)beginRefreshingTableView { [self.refreshControl beginRefreshing]; if (self.tableView.contentOffset.y == 0) { [UIView animateWithDuration:0.25 delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^(void){ self.tableView.contentOffset = CGPointMake(0, -self.refreshControl.frame.size.height); } completion:^(BOOL finished){ }]; } }