I have this big UICollectionView occupying major part of screen and there is a UIButton that shows a menu. I want to hide the menu when the user taps on any side of the screen which becomes unfortunately any part of UICollectionView for me. Tried on other view the below code, it works well...but not for UICollectionView. The function does not gets called.
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
hidemenu()
self.view.endEditing(true)
}
What is the problem? Thanks for your time.
OR
How to trigger touchesBegan in the UIViewController where UICollectionView resides
Here is the project.
While tapping on scrolling controllers (like UIScrollView, UITableView, UICollection etc) does not call touchesBegan method. because they have their own selector method. To handle such situation, you need to create UITapGesture on UICollectionView. While tapping on UICollectionView, its selector method called and do what ever you want.
Here are the link that guide you. how to create double Tap Gesture on UICollectionView. with help of this you can created single Tap gesture as well.
Collection View + Double Tap Gesture
Edit :
Do the following changes, it work fine.
Step 1 : Declare handleTap in SwipeMenuViewController.
func handleTap(sender: UITapGestureRecognizer) {
println("called swipe")
}
Step 2 : Create global variable of SwipeMenuViewController controller. that is out side of viewDidLoad()
var vc2 = SwipeMenuViewController()
Step 3 : Declare TapGesture in viewDidLoad()
var tap = UITapGestureRecognizer(target: vc2, action : "handleTap:")
tap.numberOfTapsRequired = 1
self.collectionView.addGestureRecognizer(tap)
Output :
called swipe
Hope this help you.
You can add a UITapGestureRecognizer to the UICollectionView and create an action to dismiss the view if the user touch outside your collection or anywhere.
Step 1 Create a tap gesture for dismiss the view
override func viewDidLoad() {
super.viewDidLoad()
...
let tap = UITapGestureRecognizer(target: self, action: #selector(didTapOutsideCollectionView(recognizer:)))
tap.numberOfTapsRequired = 1
self.collectionView.addGestureRecognizer(tap)
}
Step 2 With the tap location implement your desired action
#objc func didTapOutsideCollectionView(recognizer: UITapGestureRecognizer){
let tapLocation = recognizer.location(in: self.view)
//The point is outside of collection cell
if collectionView.indexPathForItem(at: tapLocation) == nil {
dismiss(animated: true, completion: nil)
}
}
You can directly use this method , whenever user taps on any part of collection view , your menu will be automatically hidden:-
func collectionView(_ collectionView: UICollectionView,
didSelectItemAtIndexPath indexPath: NSIndexPath)
{
hidemenu()
self.view.endEditing(true)
}
To detect tap on spaces between the cell, here is the code
self.collectionView.backgroundView = [[UIView alloc] initWithFrame:self.collectionView.frame];
self.collectionView.backgroundView.userInteractionEnabled = YES;
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(onTap:)];
self.collectionView.backgroundView.gestureRecognizers = #[tapRecognizer];
Related
I wrote a UITableView extension that would allow the reordering of cells after holding the tableview.
So the tableView goes edit mode as expected but what I would like to do is to add a Done button in the navigation bar of a viewController, when the tableView is being edited and that would end the edit mode when tapped.
How can I show/hide this button in viewController according to if the tableView being edited or not in UITableView extension?
that is my extension:
import UIKit
extension UITableView {
func addLongPressToTableView() {
let longPress = UILongPressGestureRecognizer(target: self, action: #selector(onLongPressGesture(sender:)))
longPress.minimumPressDuration = 0.8 // optional
self.addGestureRecognizer(longPress)
}
#objc func onLongPressGesture(sender: UILongPressGestureRecognizer) {
if (sender.state == .began) {
self.isEditing = true
self.setEditing(true, animated: false)
UIImpactFeedbackGenerator(style: .light).impactOccurred()
}
}
}
// ViewController, hide/show editButtonItem (done button?)
// navigationItem.rightBarButtonItems = [addButton, editButtonItem]
The UITableViewDelegate provides support for coordinating editing. Use the following two delegate methods to respond to changes in the editing state.
func tableView(UITableView, willBeginEditingRowAt: IndexPath)
func tableView(UITableView, didEndEditingRowAt: IndexPath?)
I am trying to use an MDCTextField and a table view together. The MDCTextField filters the table view, then the user should tap a cell and the keyboard should dismiss and the table view should be hidden. The issue is that the keyboard dismisses and the table view is hidden before the table view registers that a cell was tapped. How can I register that a cell was tapped before the MDCTextField registers the tap?
I use this extension you can place in a helpers.swift file but this way you can use on any UIViewController
extension UIViewController {
func hideKeyboardWhenTappedAround() {
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(UIViewController.dismissKeyboard))
tap.cancelsTouchesInView = false
view.addGestureRecognizer(tap)
}
#objc func dismissKeyboard() {
view.endEditing(true)
}
}
To use it, on viewDidLoad() just add this self.hideKeyboardWhenTappedAround()
EDIT Based on comment
In theory the following code should help, but I haven't got around to test it, I've read that people couldn't get the touchBegan function to trigger but I also don't know any other way to do what you want.
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let position = touch.location(in: view)
if let ip = tableView.indexPathForRow(at: position) {
let cell = tableView.cellForRow(at: ip)
print(cell.text)
}
}
}
The theory is the following
First we get the position where the tap occurred then we get the IndexPath for the row witch was taped (if exists) as we have the IndexPath we just get the cell.
In the example I gave you I print the cell text, but you can do any other operation that you want
I hope this is what you want and you can get it to work as I did not got around to test it and seems that some people are getting problems triggering the touchesBegan function
I want to remove UIView from screen after user tap something except that view. (to visualize it for you I will upload sketch of my view)
And I want to remove blue UIView after user tap on something except buttons in this view. What should I use?
EDIT:
In blue UIView are two buttons and I want to remove that view when user tap on background image
I did what #yerpy told me to do but it isn't working.
func test(gestureRecognizer: UITapGestureRecognizer) {
print("test")
}
func setUpBackgroundImageView() {
self.view.addSubview(backgroundImageView)
backgroundImageView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
backgroundImageView.heightAnchor.constraint(equalTo: view.heightAnchor).isActive = true
backgroundImageView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
backgroundImageView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
let tap = UITapGestureRecognizer(target: self, action: #selector(test(gestureRecognizer:)))
backgroundImageView.addGestureRecognizer(tap)
tap.delegate = self
}
And I also add shouldReceiveTouch function to UIGestureRecognizerDelegate. What am I doing wrong?
Add UIGestureRecognizer to the super view :
As you said you have image view as a background.
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(tapped(gestureRecognizer:)))
imageView.addGestureRecognizer(tapRecognizer)
tapRecognizer.delegate = self
Adding target action :
func tapped(gestureRecognizer: UITapGestureRecognizer) {
// Remove the blue view.
}
And then inside UITapGestureRecognizerDelegate :
extension ViewController : UIGestureRecognizerDelegate {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
if touch.view!.superview!.superclass! .isSubclass(of: UIButton.self) {
return false
}
return true
}
}
Hope it helps !
EDIT
Make sure that user can touch on the view by enabling : self.view.userInteractionEnabled = true
1- Add a view below your view, let call it overlay (gray one)
2- Add your container view with all your buttons inside (green one)
3- Add a tap gesture to the overlay (drag tap to overlay view)
4- Create a #IBAction of the tap to the viewcontroller
5- Write code to hide your green view inside the #IBAction
Image
Image
you can use UITapGestureRecognizer for that view
override func viewDidLoad() {
super.viewDidLoad()
// Add "tap" press gesture recognizer
let tap = UITapGestureRecognizer(target: self, action: #selector(tapHandler))
tap.numberOfTapsRequired = 1
backgroundimage.addGestureRecognizer(tap)
}
// called by gesture recognizer
func tapHandler(gesture: UITapGestureRecognizer) {
// handle touch down and touch up events separately
if gesture.state == .began {
} else if gesture.state == .ended {
}
}
You can
subclass with that background view, and implement an inherited method -beganTouches:(there is another para but I forget what it is)
Add a UITapGestureRecognizer to that view
Add a giant button that covers the entire screen under all the buttons. Anytime the user presses on the giant button underneath the smaller buttons, do what you want it to do.
Not the most elegant but it works :P
I want to override the default behavior of double tapping the mapView.
In my swift app I have a mapView in a static cell, so in the method cellForRowAt I've decided to add a UITapGestureRecognizer. This is how I do it:
func tableView(_ myTable: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if (indexPath as NSIndexPath).row == 0 {
let cell = myTable.dequeueReusableCell(withIdentifier: "cellStatic") as! MyTableDetailsCell
cell.mapView.isScrollEnabled = false //this works
//this does not:
let tap = UITapGestureRecognizer(target: self, action: #selector(doubleTapped))
tap.numberOfTapsRequired = 2
cell.mapView.addGestureRecognizer(tap)
...
And then I have a simple function:
func doubleTapped() {
print("map tapped twice")
}
But when I tap twice the map - it zooms in and there's no print in the console log - so my code doesn't work. What did I miss?
You had to enforce that your own double tap gesture recognizer disables the standard double tap gesture recognizer of the mapView.
This can be done using a delegate method:
Declare your view controller as a delegate of a gesture recognizer, using UIGestureRecognizerDelegate
Define a property for your own double tap gesture recognizer:
var myDoubleTapGestureRecognizer: UITapGestureRecognizer?
Set up your double tap gesture recognizer, e.g. in viewDidLoad:
myDoubleTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(doubleTapped))
myDoubleTapGestureRecognizer!.numberOfTapsRequired = 2
myDoubleTapGestureRecognizer!.delegate = self
mapView.addGestureRecognizer(myDoubleTapGestureRecognizer!)
Note, that the delegate is set here.
Implement the following delegate method:
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer,
shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool {
if ((gestureRecognizer == myDoubleTapGestureRecognizer) && (otherGestureRecognizer is UITapGestureRecognizer)) {
let otherTapGestureRecognizer = otherGestureRecognizer as! UITapGestureRecognizer
return otherTapGestureRecognizer.numberOfTapsRequired == 2
}
return true
}
So, when you double tap the mapView, this delegate method returns true if the other gesture recognizer is the built-in double tap recognizer of the mapView. This means that the built-in double tap recognizer can only fire if your own double tap recognizer fails to recognize a double tap, which it won’t.
I tested it: The map is no longer zoomed, and method doubleTapped is called.
Try using touchesBegan to identify the touch event, and you can call your custom handler when the event is trigerred
Add the mapview as a subview of a container view in the tableViewCell. Set constraints so that the mapview fills the entier container view. Disable the user interaction of the mapview and add the double tap gesture to the container view. This code will help.
let cell = myTable.dequeueReusableCell(withIdentifier: "cellStatic") as! MyTableDetailsCell
let tap = UITapGestureRecognizer(target: self, action: #selector(doubleTapped))
cell.mapView.isUserInteractionEnabled = false
cell.containerView.addGestureRecognizer(tap)
tap.numberOfTapsRequired = 2
Now the "doubleTapped" selector will be called when the mapview is tapped twice. All other user interactions including the rotation gesture of the mapview are disabled.
How would you dismiss the keyboard from the UITextField when tapping outside of the keyboard. I have tried resignFirstResponder() and it only exits after typing one number. I have also tried textField.inputView = UIView.frame(frame: CGRectZero). I have seen many Obj-C verisons of what I'm asking but I need the Swift equivalent because I have no programming experience in Objective-C
Thank you for your time and patience.
The best way to add a tap gesture recognizer to the view and calling either resignFirstResponder() or self.view.endEditing(true). I prefer endEditing() since resignFirstResponder has to be done for each text field separately unlike endEditing which is done for the view itself.
In viewDidLoad, write the below code:
let tapRecognizer = UITapGestureRecognizer()
tapRecognizer.addTarget(self, action: "didTapView")
self.view.addGestureRecognizer(tapRecognizer)
Now write the didTapView method to dismiss the keyboard:
func didTapView(){
self.view.endEditing(true)
}
Now when you tap outside the keyboard on the main view of the controller, it will call the didTapView method and dismiss the keyboard.
Swift 3.x
The code in viewDidLoad should be:
let tapRecognizer = UITapGestureRecognizer()
tapRecognizer.addTarget(self, action: #selector(ViewController.didTapView))
self.view.addGestureRecognizer(tapRecognizer)
where ViewController should be the name of your view controller.
Thanks
Swift 3 tested and working
// dismiss keyboard on touch outside textfield
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for txt in self.view.subviews {
if txt.isKind(of: UITextField.self) && txt.isFirstResponder {
txt.resignFirstResponder()
}
}
}
Enjoy
Swift 2.3 tested and working
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for txt in self.view.subviews {
if txt.isKindOfClass(UITextField.self) && txt.isFirstResponder() {
txt.resignFirstResponder()
}
}
}
Enjoy
You could also use this method to dismiss the keyboard when pressing 'Return'
func textFieldShouldReturn(textField: UITextField!) -> Bool {
self.view.endEditing(true);
return false;
}
Make sure to set your delegate
If you don't want to define an extra method, there is a slightly simpler way that will also work
let tapRecognizer = UITapGestureRecognizer(target: self, action: "endEditing:")
view.addGestureRecognizer(tapRecognizer)
I found this code on a site and it works great for me!
//FUNCTION TO DISMISS THE KEYBOARD
func initializeHideKeyboard(){
//Declare a Tap Gesture Recognizer which will trigger our dismissMyKeyboard() function
let tap: UITapGestureRecognizer = UITapGestureRecognizer(
target: self,
action: #selector(dismissMyKeyboard))
//Add this tap gesture recognizer to the parent view
view.addGestureRecognizer(tap)
}
#objc func dismissMyKeyboard(){
//endEditing causes the view (or one of its embedded text fields) to resign the first responder status.
//In short- Dismiss the active keyboard.
view.endEditing(true)
}
Then just call this function in a button action or similar:
dismissMyKeyboard()