Cómo detectar toques en la barra de estado

Tengo una vista personalizada en mi aplicación que puede ser desplazada por el usuario. Esta vista, sin embargo, no hereda de UIScrollView. Ahora quiero que el usuario pueda desplazar esta vista a la parte superior, tal como lo permite cualquier otra vista desplazable. Pensé que no hay una forma directa de hacerlo.

Google presentó una solución: http://cocoawithlove.com/2009/05/intercepting-status-bar-touches-on.html Esto ya no funciona en iOS 4.x. Eso es un no-go.

Tuve la idea de crear una vista de desplazamiento y mantenerla en algún lugar, solo para captar sus notificaciones y luego reenviarlas a mi control. Esta no es una buena manera de resolver mi problema, entonces estoy buscando soluciones “más limpias”. Me gusta el enfoque general del enlace mencionado anteriormente a la subclase UIApplication. Pero, ¿qué API puede darme información confiable?

¿Hay pensamientos, ayuda, etc.?

Editar: Otra cosa que no me gusta de mi solución actual es que solo funciona siempre que la vista actual no tenga vistas de desplazamiento. El gesto scroll-to-top solo funciona si hay exactamente una vista de desplazamiento. Tan pronto como se agrega el maniquí (ver mi respuesta más abajo para más detalles) a una vista con otra vista de desplazamiento, el gesto está completamente deshabilitado. Otra razón para buscar una mejor solución …

Finalmente, he reunido la solución de trabajo de las respuestas aquí. Gracias chicos.

Declara el nombre de la notificación en algún lugar (por ejemplo, AppDelegate.h):

static NSString * const kStatusBarTappedNotification = @"statusBarTappedNotification"; 

Agregue las siguientes líneas a su AppDelegate.m:

 #pragma mark - Status bar touch tracking - (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [super touchesBegan:touches withEvent:event]; CGPoint location = [[[event allTouches] anyObject] locationInView:[self window]]; CGRect statusBarFrame = [UIApplication sharedApplication].statusBarFrame; if (CGRectContainsPoint(statusBarFrame, location)) { [self statusBarTouchedAction]; } } - (void)statusBarTouchedAction { [[NSNotificationCenter defaultCenter] postNotificationName:kStatusBarTappedNotification object:nil]; } 

Observe la notificación en el controlador necesario (por ejemplo, en viewWillAppear):

 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarTappedAction:) name:kStatusBarTappedNotification object:nil]; 

Retire el observador correctamente (por ejemplo, en viewDidDisappear):

 [[NSNotificationCenter defaultCenter] removeObserver:self name:kStatusBarTappedNotification object:nil]; 

Implementar la callback para el manejo de notificaciones:

 - (void)statusBarTappedAction:(NSNotification*)notification { NSLog(@"StatusBar tapped"); //handle StatusBar tap here. } 

Espero que ayude


Swift 3 actualización

Probado y funciona en iOS 9+.

Declara el nombre de la notificación en alguna parte:

 let statusBarTappedNotification = Notification(name: Notification.Name(rawValue: "statusBarTappedNotification")) 

La barra de estado de la pista toca y envía una notificación. Agregue las siguientes líneas a su AppDelegate.swift:

 override func touchesBegan(_ touches: Set, with event: UIEvent?) { super.touchesBegan(touches, with: event) let statusBarRect = UIApplication.shared.statusBarFrame guard let touchPoint = event?.allTouches?.first?.location(in: self.window) else { return } if statusBarRect.contains(touchPoint) { NotificationCenter.default.post(statusBarTappedNotification) } } 

Observe la notificación cuando sea necesario:

 NotificationCenter.default.addObserver(forName: statusBarTappedNotification.name, object: .none, queue: .none) { _ in print("status bar tapped") } 

Así que esta es mi solución actual, que funciona increíblemente bien. Pero por favor, ven con otras ideas, ya que realmente no me gusta …

  • Agregue una vista de desplazamiento en algún lugar de su vista. Tal vez esconderlo o colocarlo debajo de otra vista, etc.
  • Establecer su contentSize para ser más grande que los límites
  • Establecer un contentOffset distinto de cero
  • En su controlador, implemente un delegado de la vista de desplazamiento como se muestra a continuación.

Al devolver siempre NO , la vista de desplazamiento nunca se desplaza hacia arriba y se recibe una notificación cada vez que el usuario toca la barra de estado. El problema es, sin embargo, que esto no funciona con una vista de desplazamiento de contenido “real”. (ver pregunta)

 - (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView { // Do your action here return NO; } 

Agregar esto a su AppDelegate.swift hará lo que quiera:

 override func touchesBegan(touches: Set, withEvent event: UIEvent?) { super.touchesBegan(touches, withEvent: event) let events = event!.allTouches() let touch = events!.first let location = touch!.locationInView(self.window) let statusBarFrame = UIApplication.sharedApplication().statusBarFrame if CGRectContainsPoint(statusBarFrame, location) { NSNotificationCenter.defaultCenter().postNotificationName("statusBarSelected", object: nil) } } 

Ahora puede suscribirse al evento donde lo necesite:

 NSNotificationCenter.defaultCenter().addObserverForName("statusBarSelected", object: nil, queue: nil) { event in // scroll to top of a table view self.tableView!.setContentOffset(CGPointZero, animated: true) } 

Encontré una solución mucho mejor que es compatible con iOS7 aquí: http://ruiaureliano.tumblr.com/post/37260346960/uitableview-tap-status-bar-to-scroll-up

Agregue este método a su AppDelegate:

 - (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [super touchesBegan:touches withEvent:event]; CGPoint location = [[[event allTouches] anyObject] locationInView:[self window]]; if (CGRectContainsPoint([UIApplication sharedApplication].statusBarFrame, location)) { NSLog(@"STATUS BAR TAPPED!"); } } 

Implementé esto agregando una UIView clara sobre la barra de estado y luego interceptando los eventos táctiles

Primero en su aplicación de delegado de aplicación: didFinishLaunchingWithOptions: agregue estas 2 líneas de código:

 self.window.backgroundColor = [UIColor clearColor]; self.window.windowLevel = UIWindowLevelStatusBar+1.f; 

Luego, en el controlador de vista que desea interceptar los grifos de la barra de estado (o en el delegado de la aplicación) agregue el siguiente código

 UIView* statusBarInterceptView = [[[UIView alloc] initWithFrame:[UIApplication sharedApplication].statusBarFrame] autorelease]; statusBarInterceptView.backgroundColor = [UIColor clearColor]; UITapGestureRecognizer* tapRecognizer = [[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(statusBarClicked)] autorelease]; [statusBarInterceptView addGestureRecognizer:tapRecognizer]; [[[UIApplication sharedApplication].delegate window] addSubview:statusBarInterceptView]; 

En el selector statusBarClicked, haz lo que necesites, en mi caso publiqué una notificación al centro de notificaciones para que otros controladores de vista puedan responder a la barra de estado.

Gracias Max, tu solución funcionó para mí después de pasar siglos buscando.

Para información :

 dummyScrollView = [[UIScrollView alloc] init]; dummyScrollView.delegate = self; [view addSubview:dummyScrollView]; [view sendSubviewToBack:dummyScrollView]; 

entonces

  dummyScrollView.contentSize = CGSizeMake(view.frame.size.width, view.frame.size.height+200); // scroll it a bit, otherwise scrollViewShouldScrollToTop not called dummyScrollView.contentOffset = CGPointMake(0, 1); //delegate : - (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView { // DETECTED! - do what you need to NSLog(@"scrollViewShouldScrollToTop"); return NO; } 

Tenga en cuenta que también tuve UIWebView, que tuve que hackear un poco con una solución que encontré en alguna parte:

 - (void)webViewDidFinishLoad:(UIWebView *)wv { [super webViewDidFinishLoad:wv]; UIScrollView *scroller = (UIScrollView *)[[webView subviews] objectAtIndex:0]; if ([scroller respondsToSelector:@selector(setScrollEnabled:)]) scroller.scrollEnabled = NO; } 

Puedes rastrear la barra de estado tocando usando el siguiente código:

 [[NSNotificationCenter defaultCenter] addObserverForName:@"_UIApplicationSystemGestureStateChangedNotification" object:nil queue:nil usingBlock:^(NSNotification *note) { NSLog(@"Status bar pressed!"); }]; 

Use un UIScrollView invisible. Probado en iOS 10 y Swift 3.

 override func viewDidLoad() { let scrollView = UIScrollView() scrollView.bounds = view.bounds scrollView.contentOffset.y = 1 scrollView.contentSize.height = view.bounds.height + 1 scrollView.delegate = self view.addSubview(scrollView) } func scrollViewShouldScrollToTop(_ scrollView: UIScrollView) -> Bool { debugPrint("status bar tapped") return false } 

Una forma, quizás no sea la mejor, podría ser colocar una UIView clara en la parte superior de la barra de estado e interceptar los toques de la UIView, podría ayudarlo si no surge nada más …

Si solo está tratando de tener un desplazamiento UIScrollView hacia la parte superior cuando se toca la barra de estado, vale la pena señalar que este es el comportamiento predeterminado SI su controlador de vista tiene exactamente un UIScrollView en sus subvistas que tiene scrollsToTop establecido en YES .

Así que solo tuve que ir a buscar cualquier otro UIScrollView (o subclases: UITableView , UICollectionView y configurar scrollsToTop como NO .

Para ser justos, encontré esta información en la publicación a la que estaba vinculada en la pregunta original, pero también se descarta porque ya no funciona, así que me salté y solo encontré la pieza relevante en una búsqueda posterior.