I don't know how exactly to achieve the following in Swift:
I am displaying a modal form sheet popup in my iPad app. In order to dismiss this view I have added a button that dismisses it. But what I really want is for it to dismiss when you click outside of the view. Almost the same behaviour as the popover. I have tried achieving this by adding tap gestures, making it a popover, any of the presentation styles, nothing works.
Has someone maybe done this so that you can point me in the right direction?
I had to display some screens like a popup in my app, so I've made a
base class that looks like this:
class MyBasePopUp: UIViewController, UIGestureRecognizerDelegate {
var tap: UITapGestureRecognizer!
override func viewDidAppear(_ animated: Bool) {
tap = UITapGestureRecognizer(target: self, action: #selector(onTap(sender:)))
tap.numberOfTapsRequired = 1
tap.numberOfTouchesRequired = 1
tap.cancelsTouchesInView = false
tap.delegate = self
self.view.window?.addGestureRecognizer(tap)
}
internal func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
internal func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
let location = touch.location(in: self.view)
if self.view.point(inside: location, with: nil) {
return false
}
else {
return true
}
}
#objc private func onTap(sender: UITapGestureRecognizer) {
self.view.window?.removeGestureRecognizer(sender)
self.dismiss(animated: true, completion: nil)
}
This is just to dismiss the popup, be careful if you have multiple gestures.
Related
I have an app that manipulates view with gestures. View is connected to pan, pinch and rotate gestures. If I start interacting with the view with two fingers all gestures are working simultaneously (expected behavior). But if you start with one finger pan, pinch and rotate gestures do not work. None of the methods in the delegate are called when I am trying to start pinch or rotate while panning.
shouldRecognizeSimultaneouslyWith
is always true.
Expected behavior,
one finger pan on the view
add second finger and start pinch/rotate interaction
(similar to instagram stories editor)
From a clean project I added the following:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
view.addGestureRecognizer({
let gesture = UIPanGestureRecognizer(target: self, action: #selector(onPan))
gesture.delegate = self
return gesture
}())
view.addGestureRecognizer({
let gesture = UIPinchGestureRecognizer(target: self, action: #selector(onPinch))
gesture.delegate = self
return gesture
}())
view.addGestureRecognizer({
let gesture = UIRotationGestureRecognizer(target: self, action: #selector(onRotate))
gesture.delegate = self
return gesture
}())
}
#objc private func onPan(_ sender: UIPanGestureRecognizer) {
print("Did pan to \(sender.translation(in: sender.view))")
}
#objc private func onPinch(_ sender: UIPinchGestureRecognizer) {
print("Did pinch to \(sender.scale)")
}
#objc private func onRotate(_ sender: UIRotationGestureRecognizer) {
print("Did rotate to: \(sender.rotation)")
}
}
extension ViewController: UIGestureRecognizerDelegate {
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { true }
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { true }
}
All 3 methods report changes after the scenario you described:
I started with single touch drag gesture
I pressed another finger and started rotating gesture and pinching gesture
Does already this example not work for you? What is the behavior you are experiencing and what is expected behavior?
I presented a view controller and I have a subview in it. I added a swipe gesture on the subview. The swipe gesture is not being called. Instead, the presented view controller is trying to dismiss itself i.e. going down. How do I override the swipe gesture to be recognised by the subview instead of the super view.
When I implemented swipe on a static view controller, it works as expected.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var viewSwipe: UIView!
override func viewDidLoad() {
super.viewDidLoad()
let swipeUp = UISwipeGestureRecognizer(target: self, action: #selector(swipeUp(_:)))
swipeUp.direction = UISwipeGestureRecognizer.Direction.up
self.viewSwipe.addGestureRecognizer(swipeUp)
let swipedown = UISwipeGestureRecognizer(target: self, action: #selector(swipeDown(_:)))
swipedown.direction = UISwipeGestureRecognizer.Direction.down
self.viewSwipe.addGestureRecognizer(swipedown)
}
#objc func swipeUp(_ gesture: UISwipeGestureRecognizer) {
print("swiped up")
}
#objc func swipeDown(_ gesture: UISwipeGestureRecognizer) {
print("swiped down")
}
}
The red area needs to get swipe
Add Gesture only in view and you can also add any direction on gesture. Here i put Small example, you can refer this code for your problem.
Get 1 for Right Direction and 2 For Left Direction
Example :
import UIKit
class ViewController: UIViewController,UIGestureRecognizerDelegate{
#IBOutlet weak var viewGray: UIView!
#IBOutlet weak var viewRed: UIView!
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.interactivePopGestureRecognizer?.delegate = self
let directions: [UISwipeGestureRecognizer.Direction] = [.right, .left]
for direction in directions {
let gesture = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipe(sender:)))
gesture.direction = direction
gesture.delegate = self
self.viewRed.addGestureRecognizer(gesture)
}
}
#objc func handleSwipe(sender: UISwipeGestureRecognizer) {
print(sender.direction)
}
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
}
Your code should work. If not there, is probably conflict with different gesture recognizer. To solve this conflict, you can set delegate on your swipeUp and swipeDown recognizers:
class ViewController: UIViewController, UIGestureRecognizerDelegate
swipeUp.delegate = self
swipeDown.delegate = self
and try to investigate what is happening in this delegate funcs (with breakpoints or with print, it is up to you):
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return false
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
If none of this delegate funcs is called, when you are trying to swipe. Then there is probably different (ie invisible) view over you content.
you code will work. Just try to present your view controller as a full screen in case iOS 13. Refer following code.
let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let vc: UIViewController = storyboard.instantiateViewController(withIdentifier: "MyViewController") as! MyViewController
vc.modalPresentationStyle = .fullScreen
self.present(vc, animated: true, completion: nil)
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
}
I have a UIWebView in which I've implemented a single tap gesture recognizer. I don't have any recognizer for long hold, though my problem is in making a selection. Issues:
Select some text in a UIWebView, select copy from contextual menu. Menu should disappear.
Long hold to make a text selection, single tap gesture is also getting called.
If text is selected, a single tap elsewhere should remove the selection like in Safari.
…
func createGestureRecognizer() {
// single tap
let singleTapGesture = UITapGestureRecognizer(target: self, action: "handleSingleTap:")
singleTapGesture.numberOfTapsRequired = 1
singleTapGesture.delegate = self
singleTapGesture.cancelsTouchesInView = false
webView.addGestureRecognizer(singleTapGesture)
}
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
if touch.view.isDescendantOfView(self.webView) {
return true
}
return true
}
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
if otherGestureRecognizer.isKindOfClass(UITapGestureRecognizer) {
otherGestureRecognizer.requireGestureRecognizerToFail(gestureRecognizer)
// println("added failure requirement to: \(otherGestureRecognizer)")
}
return true
}
func handleSingleTap(tap: UITapGestureRecognizer) {
…
}
EDIT: It is sort of working. Problem: Making an initial text selection requires a longer hold than normal. A normal long hold that would normally make a selection calls my single tap recognizer without selecting any text.
func handleSingleTap(tap: UITapGestureRecognizer) {
let selection = webView.stringByEvaluatingJavaScriptFromString("window.getSelection().toString()")!
if selection == "" {
// do stuff
} else {
webView.userInteractionEnabled = false
webView.userInteractionEnabled = true
webView.stringByEvaluatingJavaScriptFromString("window.getSelection().removeAllRanges()")
}