How to detect touches in status bar

匿名 (未验证) 提交于 2019-12-03 01:48:02

问题:

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

回答1:

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, 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") } 


回答2:

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 it's 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; } 


回答3:

Adding this to your AppDelegate.swift will do what you want:

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

Now you can subscribe to the event where ever you need:

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


回答4:

Found a much better solution which is iOS7 compatible here :http://ruiaureliano.tumblr.com/post/37260346960/uitableview-tap-status-bar-to-scroll-up

Add this method to your 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:

I implemented this by adding a clear UIView over the status bar and then intercepting the touch events

First in your Application delegate application:didFinishLaunchingWithOptions: add these 2 lines of code:

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

Then in the view controller you wish to intercept status bar taps (or in the application delegate) add the following code

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

In the statusBarClicked selector, do what you need to do, in my case I posted a notification to the notification center so that other view controllers can respond to the status bar tap.



回答6:

Thanks Max, your solution worked for me after spending ages looking.

For information :

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

then

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

Note that I had a UIWebView also which I had to hack a bit with a solution I found somewhere :

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


回答7:

You can track status bar tap by using following code:

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


回答8:

Use an invisible UIScrollView. Tested at 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 } 


回答9:

One way, might not be the best, could be to put a clear UIView on top of the status bar and intercept the touches of the UIView, might help you out if nothing else comes up...



回答10:

If you're just trying to have a UIScrollView scroll to the top when the status bar is tapped, it's worth noting that this is the default behavior IF your view controller has exactly one UIScrollView in its subviews that has scrollsToTop set to YES.

So I just had to go and find any other UIScrollView (or subclasses: UITableView, UICollectionView, and set scrollsToTop to be NO.

To be fair, I found this info in the post that was linked to in the original question, but it's also dismissed as no longer working so I skipped it and only found the relevant piece on a subsequent search.



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!