First of all, I'm pretty new to swift.
I'm doing an app where I have to recognize a lot of gesture (swipe up, down, left, right, single tap, double tap, single tap with 2 fingers, double tap with 2 fingers, rotations etc etc).
I'm able to recognize each gesture, but my problem occurs when I'm doing a double tap.
The app recognizes a single tap before the double tap (looks logical...), but as each gesture is dedicated to one action it's a problem: it sends the action linked to the single tap before the action linked to the double tap...
Do you have an idea how I can get rid of this ?
I clearly have no idea here :/
Thank you.
Have a nice day.
// SINGLE TAP
#IBOutlet var oneFingerTap: UITapGestureRecognizer!
#IBOutlet var twoFingersTap: UITapGestureRecognizer!
// DOUBLE TAP
#IBOutlet var doubleTapOneFinger: UITapGestureRecognizer!
#IBOutlet var doubleTapTwoFinger: UITapGestureRecognizer!
// SINGLE TAP
#IBAction func oneFingerTap(_ sender: UITapGestureRecognizer) {
statusLabel.text = "single Tap"
}
#IBAction func twoFingersTap(_ sender: UITapGestureRecognizer) {
statusLabel.text = "Two fingers Tap"
}
// DOUBLE TAP
#IBAction func doubleTapOneFinger(_ sender: UITapGestureRecognizer) {
statusLabel.text = "Double Tap 1 finger"
}
#IBAction func doubleTapTwoFingers(_ sender: UITapGestureRecognizer) {
statusLabel.text = "Double Tap 2 fingers"
}
EDIT :
I tried what's on [UITapGestureRecognizer - single tap and double tap
]1 but nothing's working.
Here is what I tried :
doubleTapOneFinger.require(toFail: oneFingerTap);
Doing this throw me an error "fatal error: unexpectedly found nil while unwrapping an Optional value", so I changed how I was declaring my variables to avoid using optional variables like this :
let oneFingerTap = UITapGestureRecognizer(target: self, action: #selector(self.oneFingerTap))
oneFingerTap.numberOfTapsRequired = 1
oneFingerTap.numberOfTouchesRequired = 1
self.view.addGestureRecognizer(oneFingerTap)
let twoFingerTap = UITapGestureRecognizer(target: self, action: #selector(self.twoFingersTap))
twoFingerTap.numberOfTapsRequired = 1;
twoFingerTap.numberOfTouchesRequired = 2;
self.view.addGestureRecognizer(twoFingerTap)
No more error, but nothing's happening... Do you have any idea what I can do ?
Related
I am trying to use two buttons to toggle between an animated sliding view. When the UIView loads, I want button1 to be UIColor.darkGrey and button2 to be UIColor.lightGrey. Then, when I press button2 I want button2 to become UIColor.darkGrey and button1 to become UIColor.lightGrey. If I press button1, I want button1 to be UIColor.darkGrey and button2 to be UIColor.lightGrey.
It seems simple; Using the storyboard, I connected a UIButton for button1 and button2 as an outlet. Then I connected each as actions. In each of the actions, I included the following code:
#IBAction func button1Action(_ sender: UIButton) {
button2.titleLabel?.textColor = UIColor.lightGray
button1.titleLabel?.textColor = UIColor.darkGray
UIView.animate(withDuration: 1){
self.side1.constant = 0
self.side2.constant = 0
self.sideA.constant = 400
self.sideB.constant = -400
self.view.layoutIfNeeded()
}
}
#IBAction func button2Action(_ sender: UIButton) {
button1.titleLabel?.textColor = UIColor.lightGray
button2.titleLabel?.textColor = UIColor.darkGray
view.layoutIfNeeded()
UIView.animate(withDuration: 1){
self.side1.constant = -400
self.side2.constant = 400
self.sideA.constant = 0
self.sideB.constant = 0
self.view.layoutIfNeeded()
}
}
When I press button1, everything works as expected; However, whenever I press button2 both buttons are UIColor.lightGrey. Am I missing something obvious?
You get some methods "for free" with buttons to manage their state. One of them is isSelected. Another is the tag property, so you can figure out which button is which. Since you've only got two buttons, you can get away with just using isSelected to figure out which is which. You can also use computed vars to make your life easier. With those things in mind, here's one approach you could utilize to managing the buttons' states:
Declare a buttons computed var, like so
#IBOutlet var firstButton: UIButton!
#IBOutlet var secondButton: UIButton!
// computed var to access your buttons
private var buttons: [UIButton] {
return [firstButton, secondButton]
}
Set up your buttons in viewDidLoad.
override func viewDidLoad() {
super.viewDidLoad()
// one setup to configure each button for selected or not selected
buttons.forEach { button in
button.setTitleColor(.darkGray,
for: .selected)
button.setTitleColor(.lightGray,
for: .normal)
}
firstButton.isSelected = true
}
func setTitleColor(_ color: UIColor?, for state: UIControl.State) will remain in effect for the lifetime of the button, so you don't need to fiddle with it after the initial declaration (unless you want to change the button's behavior later).
One #IBAction for both buttons and utilize the tag property to figure out which one is which. Since you've only got two buttons, I'm just using isSelected. Here's what your single #IBAction would look like:
#IBAction func buttonAction(_ sender: UIButton) {
UIView.animate(withDuration: 1.0) {
// flip buttons
self.buttons.forEach { button in
button.isSelected.toggle()
}
if self.firstButton.isSelected {
// tweak your constraints for firstButton.isSelected == true
// tweak your constraints for secondButton.isSelected == false
} else {
// tweak your constraints for firstButton.isSelected == false
// tweak your constraints for secondButton.isSelected == true
}
}
}
Based on your current implementation, you'll need to right click on the UIButtons on storyboard and nuke the existing IBAction connections and reconnect both buttons to the method above.
How can I count time on how long the button is pressed using UILongPressGestureRecognizer; I am looking to print the long-Pressed count time in the displayLabel.text
I've tried most possible way.
#IBOutlet weak var buttonPressed: UIButton!
#IBOutlet weak var displayLabel: UILabel!
var buttonPressedCount : Int = 0
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let longPressObj = UILongPressGestureRecognizer(target: self, action: #selector(longPressButton))
longPressObj.minimumPressDuration = 2
longPressObj.numberOfTouchesRequired = 1
self.view.addGestureRecognizer(longPressObj)
// below statement is not a right one but i tried many possiblity including this one.
if longPressObj.minimumPressDuration == TimeInterval(2) {
displayLabel.text = "Longpressed for 2 seconds"
} else if longPressObj.minimumPressDuration == TimeInterval(3) {
displayLabel.text = "3"
} else if longPressObj.minimumPressDuration == TimeInterval(3) {
displayLabel.text = "4"
}
}
#IBAction func longPressButton() {
displayLabel.text = "Button pressed for \(buttonPressedCount)"
}
I want to display time of the long-Pressed NOT BUTTON CLICKED.
enter image description here
Thank you in advance!
EDITS:-
1. I just want to show the Running duration while the user is performed Long-Press. I would really appreciate the real-time count in the
2. Also is it will be helpful to show the total duration after stop pressing.
(https://i.stack.imgur.com/ppr0W.png)
Your target function should include sender, then you can get the state of the UILongPressGestureRecognizer.
Here is the Apple official document.
First save the gesture begin time, then you can use the current time to subtract the begin pressed time to get the duration in state .ended (or/and .cancelled, .failed).
Sample code:
class ViewController: UIViewController {
var touchBeginTime: Double = 0
var touchCountTimer: Timer?
override func viewDidLoad() {
super.viewDidLoad()
let longPressObj = UILongPressGestureRecognizer(target: self, action: #selector(longPressButton(sender:)))
longPressObj.minimumPressDuration = 2
longPressObj.numberOfTouchesRequired = 1
self.view.addGestureRecognizer(longPressObj)
}
#IBAction func longPressButton(sender: UILongPressGestureRecognizer) {
switch sender.state {
case .began:
touchCountTimer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true, block: { (timer) in
print("User pressing \(Date().timeIntervalSince1970 - self.touchBeginTime) sec.")
})
touchBeginTime = Date().timeIntervalSince1970
break;
case .changed:
//print("Long pressed changed \(Date().timeIntervalSince1970 - touchBeginTime)")
break;
case .ended, .cancelled, .failed:
touchCountTimer?.invalidate()
print("User pressed total \(Date().timeIntervalSince1970 - touchBeginTime) sec.")
break;
default:
break;
}
}
}
Since you want to show the running duration while the long press is being performed, you need to use a timer.
Start the timer when the long press reaches the .began state and stop (invalidate) the timer when the long press reaches the .ended or .canceled state.
Your time should repeat every second and update the label based on the difference between the current date and the date the long press began.
Rather than using a long press gesture recognizer, why not attach button actions to the touchDownInside event and the touchUpInside and touchUpOutside events on your button. your touchDownInside code can start your timer, which would update the label. The touchUpInside/touchUpOutside action would stop the timer.
In a single VC app,
a info label is used to display textually the number of clicks of a button,
idea is to toggle the text on even/ uneven number of clicks. I did this ok,
but the problem came when I tried to link couple ( 9 ) UIButtons to the single IBAction where count and label updates happen. Here the label update lags one step behind the real count ( verified by print(counter) on Console).
May someone help why this happens?
I can do it of course with separate IBAction for each UIButton,
but one action and buttons tags works fine, so it would be nice to keep the code less by single IBAction.
import UIKit
class ViewController: UIViewController {
var player:Player = .cross
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.label.text = "Player \(player.rawValue) is on the move"
}
#IBAction func Move(_ sender: UIButton) {
if player == .cross {
player = .circle
} else {
player = .cross
}
print(sender.tag)
print("Player \(player.rawValue) is on the move")
self.label.text = "Player \(player.rawValue) is on the move"
}
#IBAction func button(_ sender: UIButton) {
if player == .cross {
player = .circle
} else {
player = .cross
}
print(sender.tag)
print("Player \(player.rawValue) is on the move")
self.label.text = "Player \(player.rawValue) is on the move"
}
}
// the move function is the common IBAction, which lags the label update
// the func button is action for a separate button, doing label updates correctly
// debug progress: similar code worked fine without lag on other desktop,
moreover working code there clean, lags when i run it on my desktop.
I have doubts it might be simulator issue.
For the record, my xcode did it's latest amend few days ago.
I have 3 buttons in my UI that ultimately will perform similar to keys on a piano. Either of the 3 buttons may get tapped to perform that particular buttons actions, but the user can 'slide' their finger off onto the next button to perform button 2's action.
I did some searching and it looks like a touchesBegan method detecting the location of the tap is best. My attempt is below:
#IBOutlet weak var button1: UIButton!
#IBOutlet weak var button2: UIButton!
#IBOutlet weak var button3: UIButton!
(I don't have any action events tied to the 3 buttons, because I thought touchesBegan will cover that.)
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
var touch: UITouch = event!.allTouches()!.first!
var location: CGPoint = touch.locationInView(touch.view!)
if CGRectContainsPoint(button1.frame, location){
print("button 1")
}
else if CGRectContainsPoint(button2.frame, location) {
print("button 2")
}
else if CGRectContainsPoint(button3.frame, location) {
print("button 3")
}
}
Nothing prints to the console when I test this. I tested both tapping and swiping across buttons. Should I be using UIViews instead of UIButtons? Should I be using actions on the buttons? This is for Swift 2.0. Thank you.
EDIT
I have a working prototype much closer to how I envisioned this functioning. This thread here: Using iOS pan gesture to highlight buttons in grid pointed me in the direction of using a pangesture on the buttons superview.
var touchPoint = CGPoint()
var myGesture = UIPanGestureRecognizer()
viewDidLoad:
myGesture = UIPanGestureRecognizer(target: self, action: Selector("panDetected:"))
myGesture.maximumNumberOfTouches = 1
view.addGestureRecognizer(myGesture)
.
func panDetected(sender : UIPanGestureRecognizer) {
touchPoint = sender.locationInView(self.view)
if CGRectContainsPoint(button1.frame, touchPoint) {
button1Func()
}
else if CGRectContainsPoint(button2.frame, touchPoint) {
button2Func()
}
else if CGRectContainsPoint(button3.frame, touchPoint) {
button3Func()
}
The above DOES work, however, button1Func / button2Func / button3Func all contain an animation block. When the finger is dragged within the buttons, it fires the method every time a movement is detected. I just need for this to happen once. I tried adding this to a state == .Began statement, but that prevents any functionality once the finger leaves the first tapped button.
Is there any way I can fire those methods (button1Func / button2Func / button3Func) just ONCE inside the pan gesture once the finger is inside the bounds of each of the 3 buttons? I will be happy to clarify this if it's confusing. THANKS.
There are a whole bunch of control events you can tie to IBActions. Take a look at the UIControlEvents enum. You probably want to add actions for UIControlEventTouchDragInside, UIControlEventTouchDragEnter and a few others.
override func viewDidAppear(animated: Bool) {
view.userInteractionEnabled = true
let pinchGesture:UIPinchGestureRecognizer = UIPinchGestureRecognizer(target: self, action: "pinchGesture")
view.addGestureRecognizer(pinchGesture)
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "dismissKeyboard")
view.addGestureRecognizer(tap)
}
func dismissKeyboard() {
view.endEditing(true)
}
func pinchGesture(){
self.performSegueWithIdentifier("trick1Segue", sender: self)
}
In my iOS app, i want to transition to a different view controller when a pinch gesture is performed on the screen.
The tap gesture is to dismiss the keyboard when tapped outside the keyboard area
While running the app, I get an error message:
"Attempting to present < ** > on < ** > while a presentation is in progress"
The new view controller appears but opens twice, with a very short time difference. Looked up a lot of blogs but couldn't find a solution, please help!!!
The problem is that pinchGesture can be called multiple times. You should add a property to your viewController to keep track of the fact that you already have acted upon the pinch gesture:
var segueInProcess = false
func pinchGesture() {
if !segueInProcess {
self.performSegueWithIdentifier("trick1Segue", sender: self)
segueInProcess = true
}
}
Gesture recognizers are called multiple times with different states. What you can do is check the state property of the UIPinchGestureRecognizer in pinchGesture().