Detect when a touch begins anywhere on screen even on a UIButton - ios

I am looking for a simple way to detect when the user starts to touch the screen regardless of whether they are touching on a UIButton or elsewhere on the screen.
If I use touchesBegan(... on my ViewController, it does not detect touches on controls like UIButtons.
There is UITapGesturReconizer on ViewController but that would fire only when the tap has completed. I am looking to detect when any touch begins.

Use a UILongPressGestureRecognizer and set its minimumPressDuration to 0. It will act like a touch down during the UIGestureRecognizerStateBegan state.
func setupTap() {
let touchDown = UILongPressGestureRecognizer(target:self, action: #selector(didTouchDown))
touchDown.minimumPressDuration = 0
view.addGestureRecognizer(touchDown)
}
#objc func didTouchDown(gesture: UILongPressGestureRecognizer) {
if gesture.state == .began {
doSomething()
}
}

Related

How to cancel LongPressGestureRecognizer?

I have a view that has a LongPressGestureRecognizer assigned to it which calls the following method:
#IBAction func longPressOnView1Recognized(_ sender: UIGestureRecognizer) {
if sender.state == .began {
// this runs when user's finger is down a "long time"
}
if sender.state == .ended {
// this runs when user's finger goes up again after the .began state
}
}
This all works as expected, but I'm trying to find a (good/proper) way of being able to programmatically cancel the long press recognizer (in certain circumstances) while the user's finger is still down.
That is, while a user's finger is still down on the view, and the recognizer has entered the .began state, (but before the user has lifted their finger -- before recognizer enters the .ended state)... is there some code we can run that will prevent the method above from firing when the user lifts their finger... like prematurely telling IOS to no longer listen for UP events for the remainder of this gesture?
I've read these docs, but I don't have that much experience with IOS touch, and I can't seem to find any method that is designed for this purpose.
my GestureRecognizer.reset() does not seem to do what I'm describing.
I can think of two possibilities:
1) A boolean flag, that would go inside the if sender.state == .ended {} closure
2) this:
myLongPressRecognizer.isEnabled = false
myLongPressRecognizer.isEnabled = true
Both of these work but seem not so great.
You are all good with disabling and reenabling the gesture recognizer so doing
myLongPressRecognizer.isEnabled = false
myLongPressRecognizer.isEnabled = true
is completely correct.
What I am worried about is you don't completely understand gesture recognizers. You should always use switch statement when handling gesture recognizer. Check the comments:
func handleLongPressGestureRecognizer(_ sender: UIGestureRecognizer) {
switch sender.state {
case .began:
// This will be called only once when the gesture starts
print("Long press did begin at \(sender.location(in: sender.view))")
case .changed:
// This will be called whenever your finger moves (at some frequency obviously).
// At this point your long press gesture is acting exactly the same as pan gesture
print("Long press changed position to \(sender.location(in: sender.view))")
case .ended:
// This is when user lifts his finger assuming the gesture was not canceled
print("Long press ended at \(sender.location(in: sender.view))")
case .cancelled:
// This is equally important as .ended case. You gesture may be canceled for many reasons like a system gesture overriding it. Make sure to implement logic here as well.
print("Long press canceled at \(sender.location(in: sender.view))")
case .failed, .possible:
// These 2 have been added additionally at some point. Useless as far I am concerned.
break
}
}
So at least you should handle cancelled status. But also note that the changed status will be triggered whenever the gesture is moved.
You have your solution on hand already. Toggling the UILongPressGestureRecognizer isEnabled is the best way to go. Setting the state property isn't possible, because it's a get-only property.
open var state: UIGestureRecognizer.State { get } // the current state of the gesture recognizer
isEnabled property is documented as:
default is YES. disabled gesture recognizers will not receive touches. when changed to NO the gesture recognizer will be cancelled if it's currently recognizing a gesture.
You can import the gesture recognizer header:
import UIKit.UIGestureRecognizer
That will make the state property a readwrite property. Thus, to cancel the gesture, just change its state to .cancelled.
So, for example, you can cancel the long press gesture recognizer one second after it was recognized with something like:
weak var timer: Timer?
#objc func handleLongPress(_ gesture: UILongPressGestureRecognizer) {
switch gesture.state {
case .began:
print("began")
timer?.invalidate()
timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { _ in
gesture.state = .cancelled
}
case .ended, .cancelled:
print(gesture.state == .ended ? "Ended" : "Cancelled")
timer?.invalidate()
default:
break
}
}

change the color of uiview when pressed/released

Is there a way to change color of the uiview when pressed and released.
I tried it using touchesBegan and touchesEnded by overriding it but it effected the existing functionality. so without effecting the existing functionality can we achieve it ?
You can use UIGestureRecognizer to detect touch events on your view. Please refer below links for more info.
https://www.raywenderlich.com/433-uigesturerecognizer-tutorial-getting-started
https://developer.apple.com/documentation/uikit/uipangesturerecognizer
Example:
let myView = UIView()
let gesture = UITapGestureRecognizer(target: self, action: #selector(tapEventDetected(gesture:)))
myView.addGestureRecognizer(gesture)
#objc func tapEventDetected(gesture:UITapGestureRecognizer){
if gesture.state == .began{
//Set touch began color
}
if gesture.state = .ended{
//Set touch ended color
}
}
Thanks!

In swift, how do I detect when a press begins and end on a UILabel?

I want to be able to detect when a press begins so I can perform an action while it is pressed only, stopping that action when press ends.
I know there is a pressesBegan function on UILabels but I am not sure how to use it and can't seem to find examples.
You can use UILongPressGestureRecongizer.
Initialize the pressGestureRecongizer in the viewDidLoad method of your viewController and add it to the label:
let pressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: "handlePress:")
label.addGestureRecognizer(pressGestureRecognizer)
and then you define the handlePress-function
func handlePress(sender: UILongPressGestureRecognizer) {
if sender.state == UIGestureRecognizerState.Began {
// handle start of pressing
}
else if sender.state == UIGestureRecognizerState.Ended {
// handle end of pressing
}
}

xcode swift: how to drag a button?

I am working with xcode to create a view that allows users to drag buttons. with the code below, I can move the button to the touch and drag from there, but I cant click the button and drag.
override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
for obj in touches {
let touch = obj as! UITouch
let location = touch.locationInView(self.view)
word1Button.center = location
}
}
Buttons respond to touch events, so when the user touches down within the bounds of a button the view underneath will not receive those touch events. You can get around this by using a gesture recogniser on your button instead of relying on the lower level touch delivery methods. A long press gesture recognizer would probably work best:
// Where you create your button:
let longPress = UILongPressGestureRecognizer(target: self, action: "handleLongPress:")
word1Button.addGestureRecognizer(longPress)
//...
func handleLongPress(longPress: UILongPressGestureRecognizer) {
switch longPress.state {
case .Changed:
let point = longPress.locationInView(view)
button.center = point
default:
break
}
}
Note that by default, the UILongPressGestureRecognizer needs the user to hold down for 0.5 seconds before the gesture starts recognizing (and therefore starts dragging). You can change this with the minimumPressDuration property of UILongPressGestureRecognizer. Be careful not to make it too short though - as soon as the gesture recognizes it will cancel other touches to the button, preventing the button action from being fired when the touch is lifted.

How to swipe multi buttons with one touch

I have 5 buttons; each allows touchUpInside-action and touchDragOutside-action...as well as doubleTap-action and longPress-action via tapGestureRecognizers.
I would also like to allow user to start a swipe from any UIButton and Any addition UIButton that the swipe touches, these buttons (including first touch) perform their #IBAction func Swipe.
so a continuously swipe like so
would perform #IBAction func swipe for UIButtons 1,2,3,4 and 5.
You can try something like this:
// Create an array to hold the buttons you've swiped over
var buttonArray:NSMutableArray!
override func viewDidLoad() {
super.viewDidLoad()
// Make your view's UIPanGestureRecognizer call panGestureMethod:
// (don't use a UISwipeGestureRecognizer since it's a discrete gesture)
panGesture.addTarget(self, action: "panGestureMethod:")
}
func panGestureMethod(gesture:UIPanGestureRecognizer) {
// Initialize and empty array to hold the buttons at the
// start of the gesture
if gesture.state == UIGestureRecognizerState.Began {
buttonArray = NSMutableArray()
}
// Get the gesture's point location within its view
// (This answer assumes the gesture and the buttons are
// within the same view, ex. the gesture is attached to
// the view controller's superview and the buttons are within
// that same superview.)
let pointInView = gesture.locationInView(gesture.view)
// For each button, if the gesture is within the button and
// the button hasn't yet been added to the array, add it to the
// array. (This example uses 4 buttons instead of 9 for simplicity's
// sake
if !buttonArray.containsObject(button1) && CGRectContainsPoint(button1.frame, pointInView){
buttonArray.addObject(button1)
}
else if !buttonArray.containsObject(button2) && CGRectContainsPoint(button2.frame, pointInView){
buttonArray.addObject(button2)
}
else if !buttonArray.containsObject(button3) && CGRectContainsPoint(button3.frame, pointInView){
buttonArray.addObject(button3)
}
else if !buttonArray.containsObject(button4) && CGRectContainsPoint(button4.frame, pointInView){
buttonArray.addObject(button4)
}
// Once the gesture ends, trigger the buttons within the
// array using whatever control event would otherwise trigger
// the button's method.
if gesture.state == UIGestureRecognizerState.Ended && buttonArray.count > 0 {
for button in buttonArray {
(button as UIButton).sendActionsForControlEvents(UIControlEvents.TouchUpInside)
}
}
}
(Edit: Here are a few answers I've written in the past explaining what I meant by UISwipeGestureRecognizer being a discrete gesture: stackoverflow.com/a/27072281/2274694, stackoverflow.com/a/25253902/2274694)

Resources