Swift: how to detect if swipe occurred in subview? - ios

Assume a UIView, ParentView, contains a subview, ChildView, and other subviews.
The UIViewController attaches a swipe gesture recognizer to ParentView.
Swipes on ChildView trigger this swipe handler.
Inside of ParentView's swipe handler, is there a way to detect if the swipe occurred on ChildView?
Per Josh's answer, here's the attempted code, which does not work:
class TestViewController: UIViewController {
#IBOutlet weak var targetView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
initSwipeGestures()
}
fileprivate func initSwipeGestures() {
let leftSwipe = UISwipeGestureRecognizer(target: self, action: #selector(didViewSwipe))
let rightSwipe = UISwipeGestureRecognizer(target: self, action: #selector(didViewSwipe))
let upSwipe = UISwipeGestureRecognizer(target: self, action: #selector(didViewSwipe))
let downSwipe = UISwipeGestureRecognizer(target: self, action: #selector(didViewSwipe))
leftSwipe.direction = .left
rightSwipe.direction = .right
upSwipe.direction = .up
downSwipe.direction = .down
view.addGestureRecognizer(leftSwipe)
view.addGestureRecognizer(rightSwipe)
view.addGestureRecognizer(upSwipe)
view.addGestureRecognizer(downSwipe)
}
func didViewSwipe(_ sender:UISwipeGestureRecognizer) {
let location = sender.location(in: view)
let touchedView = view.hitTest(location, with: nil)
// Ignore swipes if targetView was swiped
if touchedView == targetView {
print("YO YO YO")
}
}
}

You can use the UIView method hitTest(_ point: CGPoint, with event: UIEvent?) to get the subview under the gesture's location.
func swipe(_ recognizer: UISwipeGestureRecognizer)
{
let location = recognizer.location(in: self)
let touchedView = self.hitTest(location, with: nil)
}

Related

Swift -How to make a passthrough view recognize a down swipe gesture

I have a second window that has a view with a passthrough view inside of it. The passthrough works fine but I need it to recognize downSwipe gestures while still passing all other touch events to the view below it. How can I do this?
class PassThroughView: UIView {
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
print("Passing all touches to the next view (if any), in the view stack.")
return false
}
}
class MyVC: UIViewController {
lazy var backdropView: PassThroughView = {
let v = PassThroughView(frame: self.view.bounds)
v.backgroundColor = .clear
v.isUserInteractionEnabled = true
return v
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .clear
addGesture()
}
func addGesture() {
let swipeDown = UISwipeGestureRecognizer(target: self, action: #selector(handleGesture))
swipeDown.direction = .down
backdropView.addGestureRecognizer(swipeDown)
}
#objc func handleGesture(gesture: UISwipeGestureRecognizer) -> Void {
if gesture.direction == .down {
print("Swipe Down")
}
}
}

Rotate UIImageView inside UIScrollView get X and Y inverted. Why?

I have this code, which makes my image rotate, scale and pan to positioning it on screen.
The problem is, when I rotate the image more than 90º, the X and Y are inverted. i.e. if I move left, the image goes up and if I move to right, the image goes down. But when image is at 0º I can move it normally.
class ViewController: UIViewController, UIGestureRecognizerDelegate {
#IBOutlet weak var imgLogo: UIImageView!
#IBOutlet weak var scrollLogo: UIScrollView!
var identity = CGAffineTransform.identity
override func viewDidLoad() {
super.viewDidLoad()
self.setupLayout()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.setNavigationBarHidden(true, animated: false)
self.navigationItem.backBarButtonItem = UIBarButtonItem.init(title: " ", style: .plain, target: nil, action: nil)
self.navigationItem.backBarButtonItem?.tintColor = .white
}
func setupLayout(){
let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(scale))
let rotationGesture = UIRotationGestureRecognizer(target: self, action: #selector(rotate))
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(positioningImage))
pinchGesture.delegate = self
rotationGesture.delegate = self
panGesture.delegate = self
view.addGestureRecognizer(pinchGesture)
view.addGestureRecognizer(rotationGesture)
view.addGestureRecognizer(panGesture)
}
#objc func scale(_ gesture: UIPinchGestureRecognizer) {
switch gesture.state {
case .began:
identity = imgLogo.transform
case .changed,.ended:
imgLogo.transform = identity.scaledBy(x: gesture.scale, y: gesture.scale)
case .cancelled:
break
default:
break
}
}
#objc func rotate(_ gesture: UIRotationGestureRecognizer) {
imgLogo.transform = imgLogo.transform.rotated(by: gesture.rotation)
}
#objc func positioningImage(_ gesture: UIPanGestureRecognizer) {
let points = gesture.translation(in: scrollLogo)
imgLogo.transform = imgLogo.transform.translatedBy(x: points.x, y: points.y)
gesture.setTranslation(CGPoint.zero, in: scrollLogo)
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
}
Can someone help me indicate what is wrong? Where/how to fix?
Thanks
Your panning is currently relative to scrollLogo, if you change the points declaration to
let points = gesture.translation(in: imgLogo)
then the panning will be done relative to the image’s current translation, and it should work as intended!

How do I create a UISwipeGestureRecognizer programatically

private let swipeUp: UISwipeGestureRecognizer = {
let swiper = UISwipeGestureRecognizer(target: self, action: #selector(movedUp))
swiper.direction = .up
return swiper
}()
private let swipeDown: UISwipeGestureRecognizer = {
let swiper = UISwipeGestureRecognizer(target: self, action: #selector(movedDown))
swiper.direction = .down
return swiper
}()
creating my swipeUp and swipeDown gesture recognizers
func movedUp(sender: UISwipeGestureRecognizer){
print("UP")
}
func movedDown(sender: UISwipeGestureRecognizer){
print("DOWN")
}
my functions to be called when the swipe is recieved
override func viewDidLoad() {
self.view.addGestureRecognizer(swipeUp)
self.view.addGestureRecognizer(swipeDown)
}
adding my gesture recognizers in viewDidLoad
my program runs, but nothing happens when i swipe, what am i doing wrong?
Thankyou everyone
Try this:
private lazy var swipeUp: UISwipeGestureRecognizer = {
let swiper = UISwipeGestureRecognizer(target: self, action: #selector(movedUp))
swiper.direction = .up
return swiper
}()
private lazy var swipeDown: UISwipeGestureRecognizer = {
let swiper = UISwipeGestureRecognizer(target: self, action: #selector(movedDown))
swiper.direction = .down
return swiper
}()
override func viewDidLoad()
{
super.viewDidLoad()
self.view.addGestureRecognizer(swipeUp)
self.view.addGestureRecognizer(swipeDown)
}
func movedUp(sender: UISwipeGestureRecognizer)
{
print("UP")
}
func movedDown(sender: UISwipeGestureRecognizer)
{
print("DOWN")
}
Have you tried func movedUp(sender: UIGestureRecognizer)

How to use pan gesture and a swipe gesture alternatively on the same view?

I have a view that contains a counter.
I have implemented two different gesture recognizers,
a UISwipeGesture to increase the count by one,
and a UIPanGesture in order to increase the count by multiple numbers every
time the user swipes up.
Both gestures work, but my problem is they don't work at the same time.
I want them to work alternatively, for example if I do small swipes the
counter should increase by one, and if I keep dragging upwards the counter
should increase by multiple numbers.
Here's part of my code:
```
override func viewDidLoad() {
super.viewDidLoad()
setupSwipeGestures()
setupPanGestures()
}
private func setupSwipeGestures() {
let swipeUp = UISwipeGestureRecognizer(target: self, action: Selector("handleSwipes:"))
let swipeDown = UISwipeGestureRecognizer(target: self, action: Selector("handleSwipes:"))
swipeUp.direction = .Up
swipeDown.direction = .Down
circleView.addGestureRecognizer(swipeUp)
circleView.addGestureRecognizer(swipeDown)
}
private func setupPanGestures() {
let panGesture = UIPanGestureRecognizer(target: self, action: Selector("handleThePan:"))
circleView.addGestureRecognizer(panGesture)
}
extension UIPanGestureRecognizer {
func isDown(circleView: UIView) -> Bool {
let velocity : CGPoint = velocityInView(circleView)
if velocity.y < 0 {
print("ex Gesture went up")
return false
} else {
print("ex Gesture went down")
return true
}
}
}
```
P.S: The methods "handleSwipe" and "handlePan" contain the logic of the counter.
A swipe and a pan gesture can not be recognized at the same time because a swipe gesture is just a special type of pan that has to be faster, short lived, and in one direction.
What you should do is setup the pan gestures to require the swipe gesture to fail before they can start. You will need to set each of the UISwipeGestureRecognizer to be a requireGestureRecognizerToFail on the UIPanGestureRecognizer
override func viewDidLoad() {
super.viewDidLoad()
let swipeGestures = setupSwipeGestures()
setupPanGestures(swipeGestures: swipeGestures)
}
private func setupSwipeGestures() -> [UISwipeGestureRecognizer] {
let swipeUp = UISwipeGestureRecognizer(target: self, action: Selector("handleSwipes:"))
let swipeDown = UISwipeGestureRecognizer(target: self, action: Selector("handleSwipes:"))
swipeUp.direction = .Up
swipeDown.direction = .Down
circleView.addGestureRecognizer(swipeUp)
circleView.addGestureRecognizer(swipeDown)
return [swipeUp, swipeDown]
}
private func setupPanGestures(swipeGestures: [UISwipeGestureRecognizer]) {
let panGesture = UIPanGestureRecognizer(target: self, action: Selector("handleThePan:"))
for swipeGeature in swipeGestures {
panGesture.requireGestureRecognizerToFail(swipeGesture)
}
circleView.addGestureRecognizer(panGesture)
}
Swift 3
override func viewDidLoad() {
super.viewDidLoad()
let swipeGestures = setupSwipeGestures()
setupPanGestures(swipeGestures: swipeGestures)
}
private func setupSwipeGestures() -> [UISwipeGestureRecognizer] {
let swipeUp = UISwipeGestureRecognizer(target: self, action: #selector(self.handleSwipes))
let swipeDown = UISwipeGestureRecognizer(target: self, action: #selector(self.handleSwipes))
swipeUp.direction = .up
swipeDown.direction = .down
circleView.addGestureRecognizer(swipeUp)
circleView.addGestureRecognizer(swipeDown)
return [swipeUp, swipeDown]
}
private func setupPanGestures(swipeGestures: [UISwipeGestureRecognizer]) {
let panGesture = UIPanGestureRecognizer.init(target: self, action:#selector(self.handleThePanUp))
for swipeGesture in swipeGestures {
panGesture.require(toFail: swipeGesture)
}
circleView.addGestureRecognizer(panGesture)
}

Change iOS view / view controller using swipe gesture

How can I implement a swipe gesture to change view to and fro?
The best example I've seen so far is the Soundcloud application but I couldn't figure out how to make it work.
Compatible with Swift 5
override func viewDidLoad()
{
super.viewDidLoad()
let leftSwipe = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipes(_:)))
let rightSwipe = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipes(_:)))
leftSwipe.direction = .left
rightSwipe.direction = .right
view.addGestureRecognizer(leftSwipe)
view.addGestureRecognizer(rightSwipe)
}
#objc func handleSwipes(_ sender: UISwipeGestureRecognizer)
{
if sender.direction == .left
{
print("Swipe left")
// show the view from the right side
}
if sender.direction == .right
{
print("Swipe right")
// show the view from the left side
}
}
Use this code...
override func viewDidLoad() {
super.viewDidLoad()
var swipeRight = UISwipeGestureRecognizer(target: self, action: "respondToSwipeGesture:")
swipeRight.direction = UISwipeGestureRecognizerDirection.Right
self.view.addGestureRecognizer(swipeRight)
}
func respondToSwipeGesture(gesture: UIGestureRecognizer) {
if let swipeGesture = gesture as? UISwipeGestureRecognizer {
switch swipeGesture.direction {
case UISwipeGestureRecognizerDirection.Right:
println("Swiped right")
//change view controllers
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let resultViewController = storyBoard.instantiateViewControllerWithIdentifier("StoryboardID") as ViewControllerName
self.presentViewController(resultViewController, animated:true, completion:nil)
default:
break
}
}
}
You can a UISwipeGestureRecognizer to your UIView and add to this gesture a target and an action to perform when the gesture occurs
var swipeGesture = UISwipeGestureRecognizer(target: self, action: "doSomething")
myView.addGestureRecognizer(swipeGesture)
func doSomething() {
// change your view's frame here if you want
}
This tutorial might be helpful to you: http://www.avocarrot.com/blog/implement-gesture-recognizers-swift/
Basically, you'll need to add a gesture recognizer to your view that listens for swipe gestures. Then when it detects a swipe, push to the next view.

Resources