可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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.