Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to detect touches in status bar

Tags:

ios

iphone

I have custom view in my application which can be scrolled by the user. This view, however, does not inherit from UIScrollView. Now I want the user to be able to scroll this view to the top, just as any other scrollable view allows. I figured that there is no direct way to do so.

Google turned up one solution: http://cocoawithlove.com/2009/05/intercepting-status-bar-touches-on.html This no longer works on iOS 4.x. That's a no-go.

I had the idea of creating a scrollview and keeping it around somewhere, just to catch it's notifications and then forward them to my control. This is not a nice way to solve my problem, so I am looking for "cleaner" solutions. I like the general approach of the aforementioned link to subclass UIApplication. But what API can give me reliable info?

Are there any thoughts, help, etc...?

Edit: Another thing I don't like about my current solution is that it only works as long as the current view does not have any scroll views. The scroll-to-top gesture works only if exactly one scroll view is around. As soon as the dummy is added (see my answer below for details) to a view with another scrollview, the gesture is completely disabled. Another reason to look for a better solution...

like image 920
Max Seelemann Avatar asked Sep 20 '10 15:09

Max Seelemann


2 Answers

Finally, i've assembled the working solution from answers here. Thank you guys.

Declare notification name somewhere (e.g. AppDelegate.h):

static NSString * const kStatusBarTappedNotification = @"statusBarTappedNotification";

Add following lines to your 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 notification in the needed controller (e.g. in viewWillAppear):

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

Remove observer properly (e.g. in viewDidDisappear):

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

Implement notification-handling callback:

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

Hope it will help.


Swift 3 update

Tested and works on iOS 9+.

Declare notification name somewhere:

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

Track status bar touches and post notification. Add following lines to your 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 notification where necessary:

NotificationCenter.default.addObserver(forName: statusBarTappedNotification.name, object: .none, queue: .none) { _ in
    print("status bar tapped")
}
like image 144
Eugene Tartakovsky Avatar answered Oct 18 '22 03:10

Eugene Tartakovsky


So this is my current solution, which works amazingly well. But please come with other ideas, as I don't really like it...

  • Add a scrollview somewhere in your view. Maybe hide it or place it below some other view etc.
  • Set its contentSize to be larger than the bounds
  • Set a non-zero contentOffset
  • In your controller implement a delegate of the scrollview like shown below.

By always returning NO, the scroll view never scrolls up and one gets a notification whenever the user hits the status bar. The problem is, however, that this does not work with a "real" content scroll view around. (see question)

- (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView
{
    // Do your action here
    return NO;
}
like image 43
Max Seelemann Avatar answered Oct 18 '22 01:10

Max Seelemann