I have a "search" UITextField at the top of my view.
Below this, I have a UICollectionView which is populated with the search results as the user types.
When a user is typing into the UITextView, the keyboard is displayed. At first, I wanted to hide the keyboard if the user touched anywhere outside the UITextField. I accomplished this with the following:
func textFieldDidBeginEditing(textField: UITextField) {
if (textField == self.textFieldSearch) {
NSNotificationCenter.defaultCenter().addObserver(self, selector: "textFieldSearchDidChange:", name: UITextFieldTextDidChangeNotification, object: textField)
}
var tapGesture = UITapGestureRecognizer(target: self, action: "dismissKeyboard:")
self.view.addGestureRecognizer(tapGesture)
}
func dismissKeyboard(gesture: UIGestureRecognizer) {
self.textFieldSearch.resignFirstResponder()
self.view.removeGestureRecognizer(gesture)
}
However, if the user taps on a UICollectionViewCell, the dismissKeyboard func runs, and hides the keyboard, but the user has to tap on the cell again to run the func:
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
How do I do this all in one step? So if the user touches anywhere outside the UITextField, hide the keyboard...but if the user happens to touch a UICollectionViewCell, run the didSelectItemAtIndexPath function on the first touch as well as hide the keyboard and end editing on the UITextField?
Any help is appreciated!
Thanks
Please try this one
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
if touch.view == self.textFieldSearch{
return false;
}
else{
return true;
}
}
and add this line in your code
var tapGesture = UITapGestureRecognizer(target: self, action: "dismissKeyboard:")
tapGesture.delegate = self // add gesture delegate here
self.view.addGestureRecognizer(tapGesture)
You can implement the delegate method of UIGestureRecognizerDelegate
optional func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer,
shouldReceiveTouch touch: UITouch) -> Bool
Above method returns NO for the views on which gesture recognizer should not be called and in your case it should be collection view else for other things return YES.
example implementation-
/**
Disallow recognition of tap gestures on the collection view.
*/
func gestureRecognizer(recognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool
{
if touch.view == collectionView && recognizer == tapRecognizer {
return false
}
return true
}
Here's a solution. A little janky but it should do the trick. You might have to tinker with it a little. Also my Swift isn't top notch so excuse any syntax errors.
func dismissKeyboard(gesture: UIGestureRecognizer) {
let point : CGPoint = gesture.locationInView(self.collectionView)
let indexPath : NSIndexPath = self.collectionView.indexPathForItemAtPoint(point)
self.textFieldSearch.resignFirstResponder()
self.view.removeGestureRecognizer(gesture)
self.collectionView.selectItemAtIndexPath(indexPath, animated:YES, scrollPosition:UICollectionViewScrollPosition.Top)
}
I am providing here a Objective-C code to dismiss keyBoard on any touch performed outside. Kindly write it in Swift for yourself. The problem is you are adding A tap Gesture which is hiding the touch events of your collection cell.
So instead of tap gesture use the Swift-translated code of the following code.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self.view endEditing:YES];
}
Dont use your tap gesture. And I think things should get going for you...
EDIT
Some efforts to write the above code in swift for you...
override func touchesBegan(touches: NSSet, withEvent event: UIEvent){
self.view.endEditing(true)
}
This is the code that ended up finally working, with the help of #Sanjay above.
I still wanted to hide the keyboard if the user touched on the part of the collectionView that wasn't occupied by cells. I will still have to implement code for hiding the keyboard if a user 'swipes/scrolls/drags' on the UICollectionView instead of taps.
For now:
func gestureRecognizer(recognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
for cell:AnyObject in self.collectionView.visibleCells() {
if (touch.view.isDescendantOfView(cell as UIView)) {
self.resignAllResponders()
return false
}
}
return true
}
Related
I have a UICollectionView, which I can search through using a UISearchBar. I set it up so that when the user taps anywhere on the screen, the keyboard is dismissed.
In viewDidLoad():
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(ViewController.dismissKeyboard))
view.addGestureRecognizer(tap)
Then:
#objc override func dismissKeyboard() {
view.endEditing(true)
searchBar.endEditing(true)
}
It works at dismissing the keyboard but this tap gesture recognizer is getting in the way of selecting UICollectionView cells. The didSelectItemAt method just won't work.
Looking at another answer here, I managed to fix it somewhat by removing the gesture recognizer and just adding dismissKeyboard() in the didSelectItemAt. However, now it only dismisses if you tap the cell, and then the item selects (which I don't want, I just want the keyboard to dismiss).
How do I make it so that tapping anywhere on the screen when the keyboard is showing dismisses it, after which the UICollectionView cells work and can be selected?
Thanks!
you need to extend UIGestureRecognizerDelegate in your viewcontroller and add this snips of code. then tap gesture will not work for collectionview and act normally for rest of view.
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
if touch.view != self.yourCollectionView{
return false
}else{
return true
}
}
In didSelectItemAt You can check, is Your UISearchBar is first responder.
if searchBar.isFirstResponder {
searchBar.endEditing(true)
} else {
//do what You want
}
If You have another things, apart from cells, add Your gesture recognizer to dismiss keyboard
Ended up fixing it by adding a transparent view on top of everything and applying the gesture recognizer to it. On viewDidLoad() I set the view to isHidden = true.
Then added these:
func searchBarShouldBeginEditing(_ searchBar: UISearchBar) -> Bool {
gestureView.isHidden = false
return true
}
func searchBarShouldEndEditing(_ searchBar: UISearchBar) -> Bool {
gestureView.isHidden = true
return true
}
Perhaps try tapGesture.cancelsTouchesInView = false
override func viewWillAppear(_ animated: Bool) {
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(hideKeyboard))
tapGesture.cancelsTouchesInView = false
view.addGestureRecognizer(tapGesture)
}
#objc func hideKeyboard() {
searchController.searchBar.resignFirstResponder()
view.endEditing(true)
}
I have added GestureRecognizer for hide keyboard when user click to anywhere in view without textview. Its working well but CollectionView Cells need two tap for work, when I delete GestureRecognizer its working well but I need both of them. I have searched this in couple hours and tried so many solutions but anything is not worked.
GestureRecognizer for hide keyboard when user click to anywhere in view without textview;
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap))
tapRecognizer.cancelsTouchesInView = false
view.addGestureRecognizer(tapRecognizer)
#objc func handleTap() {
textBody.endEditing(true)
}
You can try this method:
public override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
view.endEditing(true) // or textBody.endEditing(true)
}
To recognize both gestures simultaneously use below code by removing cancelsTouchesInView line. You may need to add another tap gesture for your collectionView according to your need. Or use scrollViewDidScroll event of collectionView to hide the keyboard.
//tapRecognizer.cancelsTouchesInView = false
tapRecognizer.delegate = self
view.addGestureRecognizer(tapRecognizer)
extension MyViewController: UIGestureRecognizerDelegate {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
I am using both a tap and pan gesture in my View. The view has a UICollectionView where I am trying to call didSelectItemAthowever the method is not called.
I have tried the following, but with no luck.
override func viewDidLoad() {
panGesture.delegate = self
tapGesture.delegate = self
}
extension AddNotebookViewController: UIGestureRecognizerDelegate {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
}
Does anybody have any idea what the issue may be ?
The problem, as you've already guessed, is that the background view's gesture recognizer swallows the tap that would select the collection view cell. To solve the problem, implement this gesture recognizer delegate method in your view controller:
func gestureRecognizerShouldBegin(_ gr: UIGestureRecognizer) -> Bool {
let p = gr.location(in: self.view)
let v = self.view.hitTest(p, with: nil)
return v == gr.view
}
The result is that if the gesture is in the collection view, the background view's gesture recognizer won't begin and normal selection will be able to take place.
The google login button provided by google (GIDSignInButton), is not working in normal press but in long press. Otherwise evrything is normal.
Any idea guys?
That was due to a tap recognizer I had in the same viewcontroller. Issue got solved.
Google sign in default button not works in single tap, it works after 1
long press because of the Tap Gesture included in same
viewcontroller...
So the Solution is Handle touch event in sameViewcontroller:
override func viewDidLoad() {
super.viewDidLoad()
let touchRecognizer = UITapGestureRecognizer(target: self, action:
#selector(onBaseTapOnly))
touchRecognizer.numberOfTouchesRequired = 1
touchRecognizer.delegate = self
self.view.addGestureRecognizer(touchRecognizer)
}
func onBaseTapOnly(sender: UITapGestureRecognizer) {
if sender.state == .ended {
//react to tap
self.view.endEditing(true)
}
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer,
shouldReceive touch: UITouch) -> Bool {
return touch.view == gestureRecognizer.view
}
I am using this code to dismiss keyboard when user click's outside the TextField
override func viewDidLoad() {
...
let tapGesture = UITapGestureRecognizer(target: self, action: "tap:")
view.addGestureRecognizer(tapGesture)
...
}
func tap(gesture: UITapGestureRecognizer) {
txtName.resignFirstResponder()
}
It is working when the user click's anywhere outside the textfield but the datepicker. When he put's a name and then click on the DatePicker (just click, not roll) the tap is not recognized.
What should I do to make it work?
It's possible the gesture recogniser on the DatePicker is interfering with yours. See if modifying this function helps your case.
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true //Obviously think about the logic of what to return in various cases
}