2010-09-20 10 views
26

Tengo una vista personalizada en mi aplicación que puede recorrer 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.Cómo detectar toques en la barra de estado

Google apareció 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 guardarla 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 alguna idea, 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 ninguna vista 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 ...

+0

Me pregunto por qué las intercepciones de la barra de estado si toca ni siquiera se hace nada, – bobobobo

Respuesta

24

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 ...

  • Añade una vista de desplazamiento en algún lugar de tu vista. Tal vez ocultarlo o colocarlo por debajo de algún otro punto de vista, etc.
  • Set es contentSize a ser más grandes que los límites
  • conjunto A que no sea cero contentOffset
  • En el controlador de implementar un delegado de la ScrollView 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". (vea la pregunta)

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

Esto es bastante brillante. – overboming

+0

Hola, ¿esto también se aplica a los sitios web? ver: http://stackoverflow.com/questions/21803234/ios7-status-bar-tap-not-working-on-mobile-website-if-webkit-overflow-scrolling-i –

1

Una forma, podría no ser la mejor, podría ser poner una UIView clara en la parte superior de la barra de estado e interceptar los toques de la UIView, podría ayudarlo si nada más aparece ...

+0

Sí, eso es una idea. Pero eso sería incluso más complicado que agregar una vista de desplazamiento arbitraria en algún lugar y seguir su comportamiento, ¿no? –

8

Gracias Max, su 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]; 

continuación

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; 
} 

Nota que tenía un UIWebView también, que yo tenía que cortar 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; 
} 
+0

Me temo que esto no parece que funciona con la configuración que tengo. Tengo una UIView principal sobre la cual tengo una vista de desplazamiento que contiene todas las páginas diferentes de la aplicación. He insertado el código en la vista principal, así como en 'AppDelegate' (para' UIWindow') ¡pero no funciona! – p0lAris

+0

No funciona si hay una vista de tabla en la pantalla. Incluso con 'tableView.scrollsToTop = false' – SoftDesigner

9

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

Primero en su aplicación delegado Aplicación: didFinishLaunchingWithOptions: añadir 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 grifos barra de estado (o en el delegado de la aplicación) añadir 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, hacer lo que tiene que hacer, en mi caso he publicado una notificación al centro de información para que otros controladores de vista pueden responder a la llave de barra de estado.

+0

intenté un par de estos y esta es la solución más confiable. – amleszk

+0

funciona maravillosamente. – jsd

+0

efectos secundarios !!! apenas notado - el controlador de vista de compartir de Facebook (SLComposeViewController) no muestra el teclado si te metes con el UIWindowLevel. Esta solución en realidad no es tan buena como se pensaba. – amleszk

4

Puede realizar un seguimiento de la barra de estado del grifo mediante el uso de código siguiente:

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

Esto funciona para mí, sin embargo, la notificación no es solo para touchUpInside, es para cualquier cambio de estado. Como tal, cualquier acción que solicite aquí se llama dos veces, en contacto y en lanzamiento. –

+8

¿Hay alguna posibilidad de que una aplicación que observa "_UIApplicationSystemGestureStateChangedNotification" se rechace en la tienda de aplicaciones? Los nombres de notificación con _ en el frente parecen algo privados para mí. – chadbag

+1

¡Muchas gracias! Esto acaba de resolver mi problema con un UIWebView que contiene un div que abarca toda su altura que tiene desbordamiento: conjunto de desplazamiento. – mwidmann

11

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

Añadir este método para 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!"); 
    } 
} 
+5

Su código fallará en los casos en que la barra de estado sea de doble altura o deje de ser de 20 píxeles. Intente algo como esto en su lugar: if (CGRectContainsPoint ([UIApplication sharedApplication] .statusBarFrame, location)) {/ * touched * /} – zadr

+1

Esto falla si se gira, incluso al revés. Sin embargo, usar 20 píxeles es mejor que statusBarFrame, porque statusBarFrame incluye la parte "in-call" de la barra de estado, que creo que probablemente quieras ignorar. – EricS

+0

Gracias hombre! gran sulotion! Le sugiero que utilice la "[UIApplication sharedApplication] .statusBarFrame.size.height" insteal de "20". ;) –

44

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

declare el nombre de notificación alguna parte (por ejemplo AppDelegate.h):

static NSString * const kStatusBarTappedNotification = @"statusBarTappedNotification"; 

Agregar 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]; 
} 

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

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

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

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

Implementar función de notificación de manejo:

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

esperan que ayude.


3 actualización Swift

probado y funciona en iOS 9+.

declare el nombre de notificación alguna parte:

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

Pista toques barra de estado y notificación posterior. Añadir siguientes líneas a su AppDelegate.swift:

override func touchesBegan(_ touches: Set<UITouch>, 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 notificación cuando sea necesario:

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

Asegúrese de que su AppDelegate extienda UIResponder para que se llame a -touchesBegan: withEvent: (proyecto antiguo no). – leolobato

+2

Es posible que desee agregar '' 'touch.window.windowLevel == UIWindowLevelStatusBar''' de lo contrario, detectará grifos en la parte superior de un UIAlertView –

+0

¿Funciona este código para iOS7 y versiones posteriores? – Vyacheslav

1

Si usted está tratando de tener un desplazamiento UIScrollView a la parte superior cuando se golpea la barra de estado, que es Vale la pena señalar que este es el comportamiento predeterminado IF su controlador de vista tiene exactamente un UIScrollView en sus subvistas que tiene scrollsToTop establecido en YES.

Así que sólo tenían que ir a buscar cualquier otra UIScrollView (o subclases: UITableView, UICollectionView y establezca scrollsToTop ser NO

Para ser justos, he encontrado esta información en la post that was linked to en la pregunta original. , pero también ha descartado como ya no funciona así que salté y sólo encontré la pieza relevante en una búsqueda posterior

12

Añadiendo esto a su AppDelegate.swift va a hacer lo que quiere:.

override func touchesBegan(touches: Set<UITouch>, 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 cada vez que necesita:

NSNotificationCenter.defaultCenter().addObserverForName("statusBarSelected", object: nil, queue: nil) { event in 

    // scroll to top of a table view 
    self.tableView!.setContentOffset(CGPointZero, animated: true) 
} 
+1

¡Gracias! Esto es exactamente lo que quería. Y SWIFT es realmente limpio. ¡Espero que más personas voten esta respuesta! –

2

utilizar un invisible UIScrollView. Probado en iOS 10 & 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 
} 
+0

Esta es una estrategia excelente, simple e infalible. El scrollView ni siquiera tiene que ser la vista superior. Puse el mío detrás de mi webView y lo dimensioné para llenar la vista principal y funciona muy bien. –

+0

Esto es asombroso –

Cuestiones relacionadas