简体   繁体   English

如何检测状态栏中的触摸

[英]How to detect touches in status bar

I have custom view in my application which can be scrolled by the user.我的应用程序中有自定义视图,可以由用户滚动。 This view, however, does not inherit from UIScrollView.但是,此视图不是从 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.谷歌提出了一种解决方案: http : //cocoawithlove.com/2009/05/intercepting-status-bar-touches-on.html这不再适用于 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.我喜欢上述链接到 UIApplication 子类的一般方法。 But what API can give me reliable info?但是什么 API 可以给我可靠的信息?

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...寻找更好解决方案的另一个原因......

Finally, i've assembled the working solution from answers here.最后,我从这里的答案中收集了工作解决方案。 Thank you guys.谢谢你们。

Declare notification name somewhere (eg AppDelegate.h):在某处声明通知名称(例如 AppDelegate.h):

static NSString * const kStatusBarTappedNotification = @"statusBarTappedNotification";

Add following lines to your AppDelegate.m:将以下行添加到您的 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 (eg in viewWillAppear):观察所需控制器中的通知(例如在 viewWillAppear 中):

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

Remove observer properly (eg in viewDidDisappear):正确移除观察者(例如在 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斯威夫特 3 更新

Tested and works on iOS 9+.经过测试并适用于 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:将以下行添加到您的 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")
}

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将其contentSize设置为大于边界
  • Set a non-zero contentOffset设置一个非零的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.通过始终返回NO ,滚动视图永远不会向上滚动,并且每当用户点击状态栏时都会收到通知。 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;
}

Adding this to your AppDelegate.swift will do what you want:将此添加到您的 AppDelegate.swift 将执行您想要的操作:

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

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

Found a much better solution which is iOS7 compatible here : http://ruiaureliano.tumblr.com/post/37260346960/uitableview-tap-status-bar-to-scroll-up在这里找到了一个与 iOS7 兼容的更好的解决方案: http ://ruiaureliano.tumblr.com/post/37260346960/uitableview-tap-status-bar-to-scroll-up

Add this method to your AppDelegate:将此方法添加到您的 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!");
    }
}

I implemented this by adding a clear UIView over the status bar and then intercepting the touch events我通过在状态栏上添加一个清晰的 UIView 然后拦截触摸事件来实现这一点

First in your Application delegate application:didFinishLaunchingWithOptions: add these 2 lines of code:首先在您的应用程序委托 application:didFinishLaunchingWithOptions: 添加以下两行代码:

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.在 statusBarClicked 选择器中,做你需要做的事情,在我的例子中,我向通知中心发布了一个通知,以便其他视图控制器可以响应状​​态栏点击。

Thanks Max, your solution worked for me after spending ages looking.谢谢 Max,你的解决方案在我花了很长时间寻找之后对我有用。

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 :请注意,我也有一个 UIWebView,我不得不用我在某处找到的解决方案来破解一下:

- (void)webViewDidFinishLoad:(UIWebView *)wv
{  
  [super webViewDidFinishLoad:wv];  

  UIScrollView *scroller = (UIScrollView *)[[webView subviews] objectAtIndex:0];
  if ([scroller respondsToSelector:@selector(setScrollEnabled:)])
    scroller.scrollEnabled = NO; 
}

Use an invisible UIScrollView .使用一个不可见的UIScrollView Tested at iOS 10 & Swift 3.在 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
}

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!");
}];

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 .如果您只是想在点击状态栏时让UIScrollView滚动到顶部,那么值得注意的是,如果您的视图控制器在其子视图中恰好有一个UIScrollView并且将scrollsToTop设置为YES ,那么这是默认行为

So I just had to go and find any other UIScrollView (or subclasses: UITableView , UICollectionView , and set scrollsToTop to be NO .所以我只需要找到任何其他UIScrollView (或子类: UITableViewUICollectionView ,并将scrollsToTop设置为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.公平地说,我在原始问题中链接到帖子中找到了此信息,但它也因不再有效而被驳回,因此我跳过了它,仅在随后的搜索中找到了相关部分。

一种方法,可能不是最好的,可能是在状态栏顶部放置一个清晰的 UIView 并拦截 UIView 的触摸,如果没有其他问题,可能会帮助你......

For iOS 13 this has worked for me, Objective-C category of UIStatusBarManager对于 iOS 13,这对我UIStatusBarManagerUIStatusBarManager Objective-C 类别

@implementation UIStatusBarManager (CAPHandleTapAction)
-(void)handleTapAction:(id)arg1 {
    // Your code here
}
@end

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM