How to ignore all UIControl Taps, when using the tap to close UIKeyboard - ios

This issue is stemming from my previous question dismiss keyboard on tapping on any UIControl object.
So when user has a keyboard present, I want the user to be able to tap anywhere outside of keyboard or UITextView to close keyboard. This works...But I also want that "first" tap that closes the keyboard to not cause any other actions.
i.e. UIKeyboard is present...user taps anywhere to close and happens to tap on a UIButton. UIKeyboard is closed and UIButton's action is NOT sent.
I tried to place
#IBAction func blahblah(sender: UIButton){
if keyboardShow == false {//UIButtons action here
}
}
But with some println() tests, it's showing that the keyboard is closed(thus keyboardShow is set to false) before UIButton registers the tap.

But I also want that "first" tap that closes the keyboard to not cause any other actions.
You can do it the way you are doing it, but that's very inflexible because you have to deal with all those other controls separately and individually.
The simplest way to do this is simply to put an invisible view on top of your entire "screen" (i.e., add it to your view controller's view, in front of all views) — except that it should be behind the text view.
text view
secret invisible view
all other views (buttons etc.)
Normally, your secret invisible view's userInteractionEnabled is false, so taps just fall through to whatever is behind it, as if the invisible view were not even there. The user can tap buttons and so on.
But when you show the keyboard, you also set your secret invisible view's userInteractionEnabled to true and give it a tap gesture recognizer. Thus, when the user taps outside the text view, the tap gesture recognizer responds, and you dismiss the keyboard.
When you dismiss the keyboard, you reverse all of that: remove the tap gesture recognizer and turn the invisible view's userInteractionEnabled to false.
I use this kind of trick all the time in my apps.
It is also possible to be more sophisticated: put your interfering invisible view in front of everything and override its touch handling (hitTest:). Here's an example of a view that blocks all touches except touches that would land on one particular passthruView located behind it:
class BlockerView: UIView {
weak var passthruView : UIView!
override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
if let pv = self.passthruView {
let pt = pv.convertPoint(point, fromView: self)
if pv.pointInside(pt, withEvent: event) {
return nil // let the touch fall thru
}
}
return super.hitTest(point, withEvent: event)
}
}

Related

Handling UIPanGestureRecognizer gestures for multiple Views (one covers the other)

Sorry for such a long question, but felt I should convey what I have tried.
I've got a view viewA within a navigation controller. I am then adding a subview viewB (that contains a UITableView) to viewA and offsetting its origin height so that it covers only half the screen (with the other half overflowing off out the bottom of the screen). I want to be able to then drag this viewB upwards but it get stopped when it hits the bottom of the navigation bar and similarly get stopped when dragged back down when it hits the origin offset point. This I have achieved successfully.
However, I want the UITableView interaction to only be enabled when viewB is in its upper position and thus not respond to gestures in any other position. Essentially, dragging viewB up so that it completely covers viewA should enable interaction with the UITableView.
The tricky part here is that I want it to do the following:
If viewB is in its upper position so that it is covering the screen, the UITableView content offset is 0 (i.e. we are at the top of the table) and the user makes a pan gesture downwards, the gesture should not interact with the UITableView but should move viewB downwards.
Any other pan gesture in the above condition should be an interaction with the UITableView.
If viewB is in its upper position so that it is covering the screen, the UITableView content offset is NOT at 0 (i.e. we are NOT at the top of the table) and the user makes a pan gesture downwards, the gesture should interact with the UITableView.
I've been very close to achieving this but I can't get it quite right.
Attempts So Far
I'm using a UIPanGestureRecognizer to handle the dragging of the view. I have tried adding this to:
viewB with the UITableView user interaction initially disabled. This allows me to drag viewB up and down without interfering with the UITableView. Once viewB is in its upper position I enable UITableView user interaction which then correctly allows me to interact with the UITableView without moving viewB.
However, by enabling UITableView user interaction, this means touches never reach the UIPanGestureRecognizer, meaning I can never detect for the scenario described in point (1.) above and thus can't re-disable UITableView user interaction to make viewB movable again.
Maybe it is possible to do it this way by overriding the gesture recognition methods used by the UITableView? If this is possible can anyone point me in the right direction?
a new view added in front of the UITableView. I thought maybe I could forward the touch gestures to the UITableView behind it when necessary but I still haven't found a way to do this.
All I have been able to do is disable the gesture recognizer which allows me to interact with the UITableView, but then I have the same issue as above. I can't detect when to re-enable it.
the UITableView within viewB. This seemed to be the most promising way so far. By setting the return values of the following methods I can enable and disable recognition of either viewB and the UITableView.
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
if pulloverVC.view.frame.origin.y == bottomNavbarY &&
pulloverVC.tableView?.contentOffset.y == 0 { // need to add gesture direction check to this condition
viewBisAtTop = true
return false // disable pullover control
}
return true // enable pullover control
}
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
if (gestureRecognizer as! UIPanGestureRecognizer).velocityInView(view).y < 0 && viewBisAtTop { // gesture direction check not wanted here
return true // enable tableview control
}
viewBisAtTop = false
return false // disable tableview control
}
The top method is called first when a gesture is made (I have checked with print statements) followed by the bottom method. By making different combinations of true/false for the 2 methods I can alternate interaction between viewB and the UITableView.
To detect whether the user is swiping downwards I am calling velocityInView() on the recognizer (as shown in the bottom method). I was intending on making this check in the top methods if statement and I think this would work, however, although velocityInView() works fine in the bottom method, it does not in the top one (velocity is always 0).
I have scoured SO for some solution and find many similar queries about gesture handling for views that cover each other, but these all seem to be regarding one gesture type, e.g. pinch, on one view, and another type, e.g. pan, on the other. In my case the gesture type is the same for both.
Maybe someone has a clever idea? Or maybe this is actually very simple to do and I have made this incredibly complicated? xD
Managed to get this working.
Of the methods described in my question above I removed the top one keeping just this (it has a few changes):
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
if ((gestureRecognizer as! UIPanGestureRecognizer).velocityInView(view).y < 0
|| pulloverVC.tableView.contentOffset.y > 0)
&& pulloverVC.view.frame.origin.y == bottomNavbarY {
return true // enable tableview control
}
return false
}
The if statement checks that the covering UITableView is in its upper position AND that either the user is is not dragging downwards or the table content is offset (we are not at the top of the table). If this is true, then we return true to enable the tableview.
After this method is called, the standard method implemented to handle my pan gesture is called. In here I have an if statement that sort of checks the opposite to above, and if that's true, it prevents control over the covering viewB from moving:
func handlePanGesture(recognizer: UIPanGestureRecognizer) {
let gestureIsDraggingFromTopToBottom = (recognizer.velocityInView(view).y > 0)
if pulloverVC.view.frame.origin.y != bottomNavbarY || (pulloverVC.view.frame.origin.y == bottomNavbarY && gestureIsDraggingFromTopToBottom && pulloverVC.tableView.contentOffset.y == 0) {
...
This now keeps the UITableView interaction off unless its parent view viewB is in the correct position, and when it is, disables the movement of viewB so that only interaction with the UITableView works.
Then when, we are at the top of the table, and drag downwards, interaction with the UITableView is re-disabled and interaction with its parent view viewB is re-enabled.
A wordy post and answer, but if someone can make sense of what I'm saying, hopefully it will help you.

UIScrollView scroll over background view

I have a UIScrollView (with a clear background) and behind it I have a UIImage that takes up about 1/3 of the devices height. In order to initial display the image which is sitting being the scroll view I set the scrollviews contentInset to use the same height as the image. This does exactly what I want, initialing showing the image, but scrolling down will eventually cover the image with the scroll views content.
The only issue is I added a button onto of the image. However it cannot be touched because the UIScrollView is actually over the top of it (even though the button can be seen due to the clear background). How can I get this to work.
Edit:
The following solved the problem:
//viewdidload
self.scrollView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: "onScrollViewTapped:"))
...
func onScrollViewTapped(recognizer:UITapGestureRecognizer)
{
var point = recognizer.locationInView(self.view)
if CGRectContainsPoint(self.closeButton.frame, point) {
self.closeButton.sendActionsForControlEvents(UIControlEvents.TouchUpInside)
}
}
Thanks for the screenshots and reference to Google maps doing what you're looking for, I can see what you're talking about now.
I noticed that the image is clickable and is scrolled over but there is no button showing on the image itself. What you can do is put a clear button in your UIScrollView that covers the image in order to make it clickable when you're able to see it. You're not going to be able to click anything under a UIScrollView as far as I can tell.
Please let me know if that works for you.
a simple solution is to reorder the views in the document out line. The higher the view in the outline, the lower the view is as a layer
Two things to test:
1) Make sure the image that contains the button has its userInteractionEnabled set to true (the default is false). Although, since the button is a subview and added on top of the ImageView (I assume) then this might not help.
2) If that doesn't help, can you instead add the button as a subview of the UIScrollView and set its position to be where the image is? This way it should stay on the image and will be hidden as the user scrolls down, but clickable since it is a child of the ScrollView.
Some code and/or images would help as well.
I think the way to do this is to subclass whatever objects are in your UIScrollView and override touches began / touches ended. Then figure out which coordinates are being touched and whether they land within the bounds of your button
e.g. in Swift this would be:
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent?) {
println("!!! touchesBegan")
if var touch = touches.first {
var touchObj:UITouch = touch as! UITouch
println("touchesBegan \(touchObj.locationInView(self))") //this locationInView should probably target the main screen view and then test coordinates against your button bounds
}
super.touchesBegan(touches, withEvent:event!)
}
See :
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIResponder_Class/index.html#//apple_ref/occ/instm/UIResponder/touchesBegan:withEvent:
And:
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITouch_Class/index.html#//apple_ref/occ/instm/UITouch/locationInView:
You should subclass UIScrollView and override -hitTest:withEvent: like so, to make sure it only eats touches it should.
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
UIView *const inherited = [super hitTest:point withEvent:event];
if (inherited == self) return nil;
return inherited;
}
Also make sure to set userInteractionEnabled to YES in your image view.
There is 2 way you can checked that weather touch event is fire on UIButton or not?
Option 1 : You need to add UITapGesture on UIScrollView. while tapping on UIScrollView. Tap gesture return touch point with respect to UIScrollView. you need to convert that touch point with respect to main UIView(that is self.view) using following method.
CGPoint originInSuperview = [superview convertPoint:CGPointZero fromView:subview];
after successfully conversation, you can checked that weather touch point is interact with UIButton frame or what. if it interact then you can perform you action that you are going to perform on UIButton selector.
CGRectContainsPoint(buttonView.frame, point)
Option 2 : Received first touch event while user touch on iPhone screen. and redirect touch point to current UIViewController. where you can check interact as like in option 1 describe. and perform your action.
Option 2 is already integrated in one of my project successfully but i have forgot the library that received first tap event and redirect to current controller. when i know its name i will remind you.
May this help you.

UIScrollView with touch interceptor for contentSize to control subviews

assume I've got the following view hierarchy;
Those views are actually ShinobiCharts, which are subclasses of UIView. View1 acts as a main chart that can be touched by the user (pinch, pan, long press and so on). This view in turn controls the other views (View2, View3 and so on) with regards to specific, touch dependent properties (the user pans the View1, so views 2 and 3... have to act accordingly and pan as well).
However, once the user scrolls the UIScrollView, View1 may disappear from the screen, leaving only views 2, 3 etc. visible, which don't have the corresponding gesture recognizers, so the user can't interact with the charts any more, bad user experience.
I sure could add recognizers to these additional charts as well, but during pinch, that would mean that both fingers would have to be located within a single view, the user can't just touch where he wants in order to pinch and zoom, again, bad user experience.
To cut things short, I need some sort of touch interceptor that covers the entire content area of the UIScrollView so that when a user pinches or pans, the corresponding touches will be forwarded to the main chart (View1) which in turn can update the other subviews.
Vertical scrolling the UIScrollView should be possible at all times.
First experiment:
I've tried adding a transparent UIView to the ViewController that covers the UIScrollView.
However, even with a direct reference to View1, the touches wouldn't get forwarded.
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self.chartReference touchesBegan:touches withEvent:event];
}
I'm not so sure if this is the correct way to achieve this anyways.
Second experiment:
Disabling userInteraction on the chart subviews in UIScrollView and add my own pinch and pan gestures (in UIViewController) to the UIScrollView.
Issues;
1. The UIScrollView doesn't scroll any more.
2. How to forward these gestures to View1?
I'm afraid I can't provide more example code at this point since there isn't really much relevant code to show yet.
Edit:
Small note to experiment two;
The gesture recognisers have been added as follows;
UIPinchGestureRecognizer *pinchGestureRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(pinchGesture:)];
pinchGestureRecognizer.cancelsTouchesInView = NO;
[scrollView addGestureRecognizer:pinchGestureRecognizer];
Simultaneous gesture recognition on the UIViewController has been enabled;
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
Edit:
Problem 2 for experiment 2 has been solved. I didn't set the correct delegate for the pan/pinch gestures. D'oh!
What remains is how to forward the pinch/pan gestures.
You're close with placing a transparent view on top of the others. But, you need to override one more method.
You want to override hitTest:withEvent on the transparent view. This method is used while traversing the responder chain to see which view handles a touch in a particular area. If a view handles that touch, it returns Itself. If it wants to pass it on to the next view below it, it returns nil. If it knows another view handles that touch, it can return that view.
So, in your case, if the point is in the target area of your top transparent view, you return view1. View1's gesture recognizer should then be called.
Example:
InterceptorView is a transparent view which lies on top of the scrollView. TargetView is a view inside the scrollView, and has a TapGestureRecognizer attached to it.
class InterceptorView: UIView {
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
#IBOutlet weak var targetView1: UIView?
override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
print("[Interceptor] Testing point: \(point) ")
if self.pointInside(point, withEvent: event) {
println("Hit")
return targetView1
}
else {
println()
return nil;
}
}
}
--
class TargetView: UIView {
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
#IBAction func handleGesture(gestureRecognizer: UIGestureRecognizer) {
let location = gestureRecognizer.locationInView(self)
print("[TargetView] Got gesture. Location \(location) ")
if (pointInside(location, withEvent: nil)) {
println("Inside");
}
else {
println("Outside");
}
}
}
Here's the project:
https://github.com/annabd351/GestureForwarding
(there's some other stuff in there too, but it works!)

presenting a modal view controller while the keyboard is active

So I basically have a form, consisting of several text fields. The user types into the fields as usual. But the user also has the option of double-tapping a text field, which presents a modal view controller, allowing the user to choose from a number of options relating to that field.
Can I somehow present the modal "over" the keyboard, such that when it is dismissed, the keyboard is still active for the field that had been first responder before I presented the modal?
Right now, the keyboard dismisses while the modal appears, and reappears as the modal is dismissed. It looks clunky to me, and distracting. Would love to streamline it, and reduce the amount of animation onscreen.
Edit: I've updated this answer for iOS 12 and Swift. The revised example project (containing new Swift and updated Objective-C implementations) is here.
You can create a new UIWindow and place that over the default window while hiding the keyboard's window.
I have an example project on Github here, but the basic process is below.
Create a new UIViewController class for your modal view. I called mine OverlayViewController. Set up the corresponding view as you wish. Per your question you need to pass back some options, so I made a delegate protocol OverlayViewController and will make the primary window's root view controller (class ViewController) our delegate.
protocol OverlayViewControllerDelegate: class {
func optionChosen(option: YourOptionsEnum)
}
Add some supporting properties to our original view controller.
class ViewController: UIViewController {
/// The text field that responds to a double-tap.
#IBOutlet private weak var firstField: UITextField!
/// A simple label that shows we received a message back from the overlay.
#IBOutlet private weak var label: UILabel!
/// The window that will appear over our existing one.
private var overlayWindow: UIWindow?
Add a UITapGestureRecognizer to your UITextField.
override func viewDidLoad() {
super.viewDidLoad()
// Set up gesture recognizer
let doubleTapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleDoubleTap))
doubleTapRecognizer.numberOfTapsRequired = 2
doubleTapRecognizer.delegate = self
firstField.addGestureRecognizer(doubleTapRecognizer)
firstField.becomeFirstResponder()
}
UITextField has a built-in gesture recognizer, so we need to allow multiple UIGestureRecognizers to operate simultaneously.
extension ViewController: UIGestureRecognizerDelegate {
// Our gesture recognizer clashes with UITextField's.
// Need to allow both to work simultaneously.
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer,
shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
}
This is the interesting part. When the gesture recognizer is triggered, create the new UIWindow, assign your OverlayViewController as the root view controller, and show it. Note that we set the window level to UIWindowLevelAlert so it will appear in front. However, the keyboard will still be in front despite the alert window level, so we have to manually hide its window, too.
It is important to not set the new UIWindow as key or to change the first responder from the UITextField or the keyboard will be dismissed.
Previously (before iOS 10?) we could get away with overlayWindow.makeKeyAndVisible(), but now setting it as key will dismiss the keyboard. Also, the keyboard's window now has a non-standard UIWindow.Level value that is in front of every publicly defined value. I've worked around that by finding the keyboard's window in the hierarchy and hiding it instead.
#objc func handleDoubleTap() {
// Prepare the overlay window
guard let overlayFrame = view?.window?.frame else { return }
overlayWindow = UIWindow(frame: overlayFrame)
overlayWindow?.windowLevel = .alert
let overlayVC = OverlayViewController.init(nibName: "OverlayViewController", bundle: nil)
overlayWindow?.rootViewController = overlayVC
overlayVC.delegate = self
// The keyboard's window always appears to be the last in the hierarchy.
let keyboardWindow = UIApplication.shared.windows.last
keyboardWindow?.isHidden = true
}
The overlay window is now the original window. The user can now select whatever options you built into the overlay view. After your user selects an option, your delegate should take whatever action you intend and then dismiss the overlay window and show the keyboard again.
func optionChosen(option: YourOptionsEnum) {
// Your code goes here. Take action based on the option chosen.
// ...
// Dismiss the overlay and show the keyboard
overlayWindow = nil;
UIApplication.shared.windows.last?.isHidden = false
}
The overlay window should disappear, and your original window should appear with the keyboard in the same position as before.
I can't try this right now, but have implemented similar for other purposes. In the action for presenting the modal controller, I assume gesture recognizer or delegate method, first take a screenshot and place it in an imageView over the current subviews. Later when returning, simply remove the imageView.
Might sound crazy but I remember having done this for a transition where the keyboard moving during the transition caused similar clunky behavior. It was not difficult to implement at all.
If you have trouble trying it, perhaps someone will provide some code. I can reference my own work later and add an example, but not now.
#Rob Bajorek's answer is excellent.
For iOS 9,10 there are small changes.
Instead of the code:
[self.overlayWindow setWindowLevel:UIWindowLevelAlert];
[self.overlayWindow makeKeyAndVisible];
Put the following code:
NSArray *windows = [[UIApplication sharedApplication] windows];
UIWindow *lastWindow = (UIWindow *)[windows lastObject];
[self.overlayWindow setWindowLevel:lastWindow.windowLevel + 1];
[self.overlayWindow setHidden:NO];
In order to keyboard to visible any of text accepting fields such UITextField or UITextView or UISearchBar should be the first responder and they should be visible in the view. Meaning responding view should be in the top level hierarchy in the window.
If you don't need this effect, Instead of presenting a ViewController you can add ViewController.view as a subview of your self.view with animation.
You have access to the frame of the keyboard in iOS.
You need to implement code to listen to the keyboard notifications (like UIKeyboardWillShowNotification and UIKeyboardWillChangeFrameNotification). The notification will send you informations about the frame of the keyboard.
Giva a look to the description of the "Keyboard Notification User Info Keys" in the windows reference.
You'll find useful for you purpose:
UIKeyboardBoundsUserInfoKey The key for an NSValue object containing a CGRect that identifies the bounds rectangle of the
keyboard in window coordinates. This value is sufficient for obtaining
the size of the keyboard. If you want to get the origin of the
keyboard on the screen (before or after animation) use the values
obtained from the user info dictionary through the
UIKeyboardCenterBeginUserInfoKey or UIKeyboardCenterEndUserInfoKey
constants.
With the information of the keyboard frame you can show there you modal view.
Just add an tap gesture in your textfield and a UITextfield *flagTextfield;
UITapGestureRecognizer* doubleTap = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(DoubleTapMethod:)];
doubleTap.numberOfTapsRequired = 2;
[self.txtTest addGestureRecognizer:doubleTap];
-(void)DoubleTapMethod:(UITapGestureRecognizer *)gesture
{
[flagTextfield resignFirstResponder];
NSLog(#"DoubleTap detected");
//Set your logic on double tap of Textfield...
//presents a modal view controller
}
- (void)textFieldDidBeginEditing:(UITextField *)textField{
flagTextfield = textfield;
}

touches methods not getting called on UIView placed inside a UIScrollView

I have a Custom Scroll View, subclassing UIScrollView. I have added a scroll view in my viewcontroller nib file and changed its class to CustomScrollView. Now, this custom scroll view (made from xib) is added as a subview on self.view.
In this scroll view, I have 3 text fields and 1 UIImageView(named signImageView) added from xib. On clicking UIImageView (added a TapGestureRecogniser), a UIView named signView is added on the custom scroll view. I want to allow User to sign on this view, So I have created a class Signature.m and .h, subclassing UIView and implemented the touches methods (touchesBegan, touchesMoved and touchesEnded) and initialised the signView as follows:
signView = [[Signature alloc]initWithFrame:signImageView.frame];
[customScrollView addSubview:signView];
But when I start signing on the signView, the view gets scrolled and hence the touches methods don't get called.
I have tried adding signView on self.view instead of custom scroll view, but in that case the view remains glued to a fixed position when I start scrolling. (Its frame remains fixed in this case)
Try setting canCancelContentTouches of the scrollView to NO and delaysContentTouches to YES.
EDIT:
I see that similiar question was answered here Drag & sweep with Cocoa on iPhone (the answer is exactly the same).
If the user tap-n-holds the signView (for about 0.3-0.5 seconds) then view's touchesBegan: method gets fired and all events from that moment on go to the signView until touchesEnded: is called.
If user quickly swipes trough the signView then UIScrollView takes over.
Since you already have UIView subclassed with touchesBegan: method implemented maybe you could somehow indicate to user that your app is prepared for him to sign ('green light' equivalent).
You could also use touchesEnded: to turn off this green light.
It might be better if you add signImageView as as subView of signView (instead of to customScrollView) and hide it when touchesBegan: is fired). You would add signView to customScrollview at the same place where you add signImageView in existing code instead.
With this you achieve that there is effectively only one subView on that place (for better touch-passing efficiency. And you could achieve that green light effect by un-hiding signImageView in touchesBegan:/touchesEnded:
If this app-behaviour (0.3-0.5s delay) is unacceptable then you'd also need to subclass UIScrollView. There Vignesh's method of overriding UIScrollView's touchesShouldBegin: could come to the rescue. There you could possibly detect if the touch accoured in signView and pass it to that view immediately.
When ever you add a scrollview in your view hierarchy it swallows all touches.Hence you are not getting the touches began. So to get the touches in your signon view you will have to pass the touches to signon view. This is how you do it.
We achieved this with a UIScrollView subclass that disables the pan gesture recogniser for a list of views that you provide.
class PanGestureSelectiveScrollView: UIScrollView {
var disablePanOnViews: [UIView]?
override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
guard let disablePanOnViews = disablePanOnViews else {
return super.gestureRecognizerShouldBegin(gestureRecognizer)
}
let touchPoint = gestureRecognizer.location(in: self)
let isTouchingAnyDisablingView = disablePanOnViews.first { $0.frame.contains(touchPoint) } != nil
if gestureRecognizer === panGestureRecognizer && isTouchingAnyDisablingView {
return false
}
return true
}
}

Resources