如何检测状态栏中的触摸

时间:2010-09-20 15:44:38

标签: iphone ios

我的应用程序中有自定义视图,可以由用户滚动。但是,此视图不会从UIScrollView继承。现在我希望用户能够将此视图滚动到顶部,就像任何其他可滚动视图所允许的那样。我认为没有直接的方法可以这样做。

Google提出了一个解决方案:http://cocoawithlove.com/2009/05/intercepting-status-bar-touches-on.html这不再适用于iOS 4.x.那是不行的。

我有想法创建一个scrollview并将其保存在某处,只是为了捕获它的通知,然后将它们转发给我的控件。这不是解决我的问题的好方法,所以我正在寻找“更清洁”的解决方案。我喜欢前面提到的UIApplication子类链接的一般方法。但是什么API可以给我可靠的信息?

有任何想法,帮助等......?

编辑:我不喜欢当前解决方案的另一件事是,只有当前视图没有任何滚动视图时,它才有效。滚动到顶部的手势只有在周围只有一个滚动视图时才有效。只要将虚拟添加到具有另一个滚动视图的视图(请参阅下面的详细说明),手势就会完全禁用。寻找更好解决方案的另一个原因......

11 个答案:

答案 0 :(得分:50)

最后,我从这里的答案中汇总了工作解决方案。感谢你们。

在某处声明通知名称(例如AppDelegate.h):

static NSString * const kStatusBarTappedNotification = @"statusBarTappedNotification";

将以下行添加到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];
}

观察所需控制器中的通知(例如在viewWillAppear中):

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

正确删除观察者(例如在viewDidDisappear中):

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

实施通知处理回调:

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

希望它会有所帮助。


Swift 3更新

经过测试并适用于iOS 9 +。

在某处声明通知名称:

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

跟踪状态栏触摸和发布通知。将以下行添加到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)
    }
}

在必要时观察通知:

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

答案 1 :(得分:28)

所以这是我目前的解决方案,效果非常好。但请带上其他想法,因为我不喜欢它......

  • 在视图中的某处添加滚动视图。也许隐藏它或将它放在其他视图之下等。
  • 将其contentSize设置为大于边界
  • 设置非零contentOffset
  • 在您的控制器中实现scrollview的委托,如下所示。

通过始终返回NO,滚动视图永远不会向上滚动,只要用户点击状态栏,就会收到通知。但问题是,这不适用于“真实”内容滚动视图。 (见问题)

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

答案 2 :(得分:14)

将此添加到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)
    }
}

现在您可以根据需要订阅活动:

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

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

答案 3 :(得分:11)

找到了一个更好的iOS7兼容解决方案:http://ruiaureliano.tumblr.com/post/37260346960/uitableview-tap-status-bar-to-scroll-up

将此方法添加到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!");
    }
}

答案 4 :(得分:10)

我通过在状态栏上添加一个清晰的UIView然后拦截触摸事件

来实现这一点

首先在Application委托应用程序中:didFinishLaunchingWithOptions:添加以下两行代码:

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

然后在视图控制器中,您希望拦截状态栏点击(或在应用程序委托中)添加以下代码

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

在statusBarClicked选择器中,执行您需要执行的操作,在我的情况下,我向通知中心发布了通知,以便其他视图控制器可以响应状​​态栏点击。

答案 5 :(得分:8)

谢谢Max,您的解决方案在花了很多年的时间后仍适合我。

有关信息:

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

然后

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

请注意,我还有一个UIWebView,我不得不使用我在某个地方找到的解决方案:

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

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

答案 6 :(得分:4)

您可以使用以下代码跟踪状态栏点按:

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

答案 7 :(得分:2)

使用不可见的UIScrollView。在iOS 10和iOS测试斯威夫特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
}

答案 8 :(得分:1)

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

答案 9 :(得分:1)

如果您只是在点击状态栏时尝试将UIScrollView滚动到顶部,则值得注意的是,这是默认行为IF 您的视图控制器在其子视图中只有一个UIScrollViewscrollsToTop设置为YES

所以我必须去查找任何其他UIScrollView(或子类:UITableViewUICollectionView,并将scrollsToTop设置为NO。< / p>

公平地说,我在原始问题的post that was linked to中找到了这个信息,但它也因为不再工作而被驳回,所以我跳过它并且只在随后的搜索中找到了相关的部分。

答案 10 :(得分:1)

对于iOS 13来说,这对我来说很有效,UIStatusBarManager的Objective-C类别

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