Prevent or detect events sent from the iOS 8 keyboard

In our iOS 8 app, the search screen, similar to the app storeโ€™s search screen, no longer works reliably. When the user presses a key, the keyboard sometimes closes or even an action is performed.

Part of the reason is that the transition event is sent to lower levels that close the keyboard (smoke screen), go to the search result (UITableView with the search result) or search (UITableView with search suggestions).

For some unknown reason, it works correctly while the user remains in the application. However, if it switches to another application and then returns, events are sent. This behavior affects all versions of iOS 8 (8.0.x, 8.1).

How can we prevent keyboard transmission during tap events or how can we detect such an event (e.g. from tableView:didSelectRowAtIndexPath: ?

The question โ€œ Keyboard intermittently disappears when editing using iOS 8 โ€ seems to refer to the same problem, although I can't figure out how to apply this ugly hack to my situation.


I just found a similar post in the Apple Developer Forum. Unfortunately, it has no answers and was archived on average:

I have overriden -hitTest: withEvent: when viewing on my view hierarchy where I check to see if it has been touched, and forward the touch to its subviews and trigger the selector to reject the keyboard.

In iOS 7 (and, even stranger, when the application is running on iOS 8), this works fine and -hitTest: withEvent: will never be called if the view is behind the keyboard and the user clicks on the keyboard.

But on iOS 8, if the user sends the application to the background and brings it back to the forefront by pressing something on the keyboard, trigger -hitTest: withEvent: as if the view was above the keyboard in a hierarchy of views. I used Reveal.app to verify that it is not above the keyboard; it is lagging as expected.

Anyone have any ideas on what might happen? I created a sample project and tied it to a radar for Apple, because it looks like an error on iOS 8 so that they do not work the same way.

Update

My search screen contains two views (on top of each other): the background view is displayed when the search results are not available, and the table view is displayed if the search results are available. In addition, I dynamically add two additional views if the search bar becomes active: the smoking class view, which you can use to complete the search text input and the table view, which displays the search text hints. All four views are directly contained in the main window of the view controller and cover the entire area.

Interestingly, the keyboard forwards events to two dynamically added views, but not to the two lower views that always exist.

+5
source share
4 answers

I believe that an error related to events is passed to the views under the keyboard. Since at the same time I realized that only dynamically added views are affected, and not those that are part of the Interface Builder file, now I came up with a work: if the keyboard appears, I reduce these two so that they do not expand under the keyboard. And I grow them again when the keyboard disappears.

So this is the code. The upper part before (and excluding) [[NSNotificationCenter defaultCenter] existed before. The rest is a workaround.

 - (BOOL) searchBarShouldBeginEditing: (UISearchBar*) searchBar { // calculate from for entire area CGRect frame = ... // omitted // add smoke screen _smokeScreen = [UIButton buttonWithType: UIButtonTypeCustom]; _smokeScreen.frame = frame; _smokeScreen.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; _smokeScreen.backgroundColor = [UIColor colorWithWhite: 0.0f alpha: 0.5f]; [_smokeScreen addTarget: self action: @selector(smokeScreenPressed:) forControlEvents: UIControlEventTouchDown]; [self.view addSubview: _smokeScreen]; // add table view for search term suggestions _suggestionTableView = [[SearchControllerTableView alloc] initWithFrame:frame style:UITableViewStylePlain]; _suggestionTableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; _suggestionTableView.dataSource = self; _suggestionTableView.delegate = self; _suggestionTableView.searchController = self; _suggestionTableView.hidden = YES; [self.view addSubview:_suggestionTableView]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidShow:) name:UIKeyboardDidShowNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil]; return YES; } - (void) keyboardDidShow:(NSNotification *) notification { // shrink the smoke screen area and the table view because otherwise they'll receive tap events from the keyboard CGRect screenRect = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]; CGRect windowRect = [self.view.window convertRect:screenRect fromWindow:nil]; CGRect viewRect = [self.view convertRect:windowRect fromView:nil]; [self setBottom: viewRect.origin.y]; } - (void) keyboardWillHide:(NSNotification *) notification { // grow the views again [self setBottom: self.view.frame.size.height]; } - (void) setBottom: (CGFloat)y { CGRect frame = _suggestionTableView.frame; frame.size.height = y - frame.origin.y; _suggestionTableView.frame = frame; frame = _smokeScreen.frame; frame.size.height = y - frame.origin.y; _smokeScreen.frame = frame; } 
+2
source

You can try playing with the view of the userInteractionEnabled or exclusiveTouch property when the search view is called.

0
source

I ran into this problem with Xamarin.iOS , this is a solution for Xamarin.iOS use C# .

In the WillMoveToSuperview method WillMoveToSuperview watch the show / hide event keyboard and save the keyboard frame in the KeyboardEndFrame variable:

  if (newsuper != null) { var wrThis = new WeakReference<XInterceptTouchView> (this); // add notification observe keyboardDidShow = UIKeyboard.Notifications.ObserveDidShow ((sender, e) => { XInterceptTouchView interceptTouchView; if (wrThis.TryGetTarget (out interceptTouchView)) { interceptTouchView.KeyboardEndFrame = e.FrameEnd; } }); keyboardDidHide = UIKeyboard.Notifications.ObserveDidHide ((sender, e) => { XInterceptTouchView interceptTouchView; if (wrThis.TryGetTarget (out interceptTouchView)) { interceptTouchView.KeyboardEndFrame = e.FrameEnd; } }); } else { // remove notification observe keyboardDidShow?.Dispose (); keyboardDidHide?.Dispose (); } 

In HitTest , get the keyboard window and handle the touch event with the window:

 if (KeyboardEndFrame.Contains (point)) { IntPtr handle = ObjCRuntime.Class.GetHandle ("UITextEffectsWindow"); if (handle != IntPtr.Zero) { var keyboardWindow = UIApplication.SharedApplication.Windows.FirstOrDefault (w => w.IsKindOfClass (new ObjCRuntime.Class ("UITextEffectsWindow"))); if (keyboardWindow != null) return keyboardWindow.HitTest (point, uievent); } } var hitTestView = base.HitTest (point, uievent); this.EndEditing (true); return hitTestView; 
0
source

I found that the keyboard touches the hitTest trigger, if I show the keyboard, send the application to the background and come back. This should not happen, so it seems like an iOS bug. I see two solutions:

# 1 Send a message

Solution # 1: hitTest should not be caused by touching the keyboard, so check on the keyboard frame and release the sensor.

 var keyboardFrame: CGRect? override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { if let keyboardFrame = keyboardFrame { if keyboardFrame.contains(point){ return super.hitTest(CGPoint(x:-1,y:-1), with: event) } } return super.hitTest(point, with: event) } deinit { NotificationCenter.default.removeObserver(self) } func subscribeKeyboard(){ NotificationCenter.default.addObserver(self, selector: #selector(MyView.keyboardDidShow(_:)), name: NSNotification.Name.UIKeyboardDidShow, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(MyView.keyboardWillHide(_:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil) } func keyboardDidShow(_ notification: NSNotification) { keyboardFrame = (notification.userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue } func keyboardWillHide(_ notification: NSNotification) { keyboardFrame = nil } 

# 2 Pull the keyboard before resigning

Solution 2 #: Before canceling activation, pull the keyboard. Two flaws: you touch the delegate to fix something else, and the user is surprised to find the keyboard when the application returns.

 func applicationWillResignActive(_ application: UIApplication) { UIApplication.shared.sendAction(#selector(self.resignFirstResponder), to: nil, from: nil, for: nil) } 

Or if you want this to be done only for a specific controller of the form:

 if let controller = UIApplication.shared.keyWindow?.topmostViewController { if controller is MyViewController { NSLog("Pulling the keyboard to prevent touches from falling through after coming back from the background.") UIApplication.shared.sendAction(#selector(self.resignFirstResponder), to: nil, from: nil, for: nil) } } 

where topmostViewController:

 extension UIViewController { static var topmostViewController: UIViewController? { return UIApplication.shared.keyWindow?.topmostViewController } var topmostViewController: UIViewController? { return presentedViewController?.topmostViewController ?? self } } extension UINavigationController { override var topmostViewController: UIViewController? { return visibleViewController?.topmostViewController } } extension UITabBarController { override var topmostViewController: UIViewController? { return selectedViewController?.topmostViewController } } extension UIWindow { var topmostViewController: UIViewController? { return rootViewController?.topmostViewController } } 

Leonardo Cardozo wrote the extensions above.

0
source

Source: https://habr.com/ru/post/1205860/


All Articles