Location of touch in UIButton action - ios

I'm trying to get the (x,y) location of where a button is tapped. I've found answers but they don't work for Swift 2.3.
Any suggestions?
That's what I have tried:
#IBAction func buttonTapped(sender: AnyObject, forEvent event: UIEvent) {
let buttonView = sender as! UIView;
// get any touch on the buttonView
if let touch = event.touchesForView(buttonView) as? UITouch{
// print the touch location on the button
print(touch.locationInView(buttonView))
}
}

Swift 2:
#IBAction func buttonPressed(button: UIButton, forEvent event: UIEvent) {
guard let touch = event.allTouches()?.first else { return }
let point = touch.locationInView(button)
print ("point: \(point)")
}
Swift 3:
#IBAction func buttonPressed(_ button: UIButton, forEvent event: UIEvent) {
guard let touch = event.allTouches?.first else { return }
let point = touch.location(in: button)
print ("point: \(point)")
}
To create an IBAction with event pick the right option in this popup in your storyboard:

func addGesture() {
let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(ViewController.buttonTapped(sender:)));
gestureRecognizer.numberOfTapsRequired = 1;
gestureRecognizer.numberOfTouchesRequired = 1;
button.addGestureRecognizer(gestureRecognizer) }
func buttonTapped(sender:UIGestureRecognizer) {
print(sender.location(in: button))
}
You can add Gesture recognizer to UIButton and it will work as well

override func touchesBegan(touches: NSSet!, withEvent event: UIEvent!) {
let touch = touches.allObjects[0] as UITouch
let touchLocation = touch.locationInView(self.view)
}
Hope this will be useful

Related

Function to detect if there's any touch occurring on the screen

I'm trying to create a simple function, similar to the touchesBegan, that detects if there's any touch occurring on the screen.
I've hit a brick wall trying it out myself because I'm not comfortable with UITouch class, but I really need some self made function, outside the touchesBegan default one.
I was trying to do something like this 'pseudo-code/swift'
func isTouchingTheScreen() -> Bool {
let someTouchHandleConstant: uitouch ???
if imTouchingTheScreen {
return true
} else {
return false
}
}
Do you have any hints?
PS: I know that code doesn't work, don't call that out, it was just to give you some 'image' of what I was trying to do (:
The idea
You can simply keep track of every touch begun, ended or cancelled by the user.
class GameScene: SKScene {
private var activeTouches = Set<UITouch>()
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
activeTouches.unionInPlace(touches)
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
activeTouches.subtractInPlace(touches)
}
override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) {
if let touches = touches {
activeTouches.subtractInPlace(touches)
}
}
var isTouchingTheScreen: Bool { return !activeTouches.isEmpty }
}
Keeping activeTouches updated
As you can see I am keeping updated the activeTouches Set.
Every time a touch does begin I add it to activeTouches. And every time a touch does end or is cancelled I remove it from activeTouches.
The isTouchingTheScreen computed variable
This allows me to define the isTouchingTheScreen computed property that simply returns true when the Set contains some element.
You can implement UITapGestureRecognizer as below:
var tapGesture :UITapGestureRecognizer!
override func didMoveToNode() {
// Add UITapGestureRecognizer to view
self.tapGesture = UITapGestureRecognizer(target: self, action: #selector(ViewController.touchedView(_:)))
view.addGestureRecognizer(tapGesture)
}
func touchedView(sender: UITapGestureRecognizer) {
print("view touched")
}
You could implement UITapGestureRecognizer:
// global var
var tapGesture :UITapGestureRecognizer!
override func didMoveToView() {
// Add tap gesture recognizer to view
self.tapGesture = UITapGestureRecognizer(target: self, action: #selector(GameScene.handleTap(_:)))
self.tapGesture.cancelsTouchesInView = false
view.addGestureRecognizer(tapGesture)
}
func handleTap(sender: UITapGestureRecognizer) {
print("GameScene tap")
if sender.state == .Ended {
var positionInScene: CGPoint = sender.locationInView(sender.view)
positionInScene = self.scene!.convertPointFromView(positionInScene)
let touchedNode = self.nodeAtPoint(positionInScene)
if touchedNode.name != "myHero" {
print("The SKSpriteNode myHero was tapped")
}
}
}
You can find more details in Apple docs here.

how to get tap point (coordinates) in iOS swift

I'm trying to get the coordinates of a touch on the screen, To show a popup menu
how to use in page view controller
What am I doing wrong here?
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
if let touch = touches.first {
let position = touch.locationInView(view)
print(position)
}
}
In PageViewController,if you want to get pop up
In viewDidLoad:
let tap = UITapGestureRecognizer(target: self, action: "showMoreActions:")
tap.numberOfTapsRequired = 1
view.addGestureRecognizer(tap)
Make the page view controller inherit UIGestureRecognizerDelegate then add:
func showMoreActions(touch: UITapGestureRecognizer) {
let touchPoint = touch.locationInView(self.view)
let DynamicView = UIView(frame: CGRectMake(touchPoint.x, touchPoint.y, 100, 100))
DynamicView.backgroundColor=UIColor.greenColor()
DynamicView.layer.cornerRadius=25
DynamicView.layer.borderWidth=2
self.view.addSubview(DynamicView)
}
UIPageViewController with TouchEvent
Swift 5:
override func viewDidLoad() {
super.viewDidLoad()
let tap = UITapGestureRecognizer(target: self, action: #selector(touchedScreen(touch:)))
view.addGestureRecognizer(tap)
}
#objc func touchedScreen(touch: UITapGestureRecognizer) {
let touchPoint = touch.location(in: self.view)
print(touchPoint)
}

Swift, touch down on UIButton and touch up on another

I'm making a calculator app that has several UIButtons for input of digits etc. I want the user to be able to touch down on one button and, if this was not the intended button, move the finger to another button and touch up inside that one. The button that the user has his/her finger on should change background color to indicate to the user what is happening, much like Apples built in calculator app.
I've tried to do this by using touch drag inside/outside and touch drag enter/exit on the buttons, but it only works for the button where the touch originated. Meaning I can touch down on one button, drag outside, back inside and touch up inside, but I can't touch down, drag outside and touch up inside another button.
Also the area that is recognized as being inside or outside the button is larger than the bounds of the button.
Here's an example of the code I've tried for one of the buttons:
#IBAction func didTouchDownThreeButton(sender: AnyObject) {
threeButton.backgroundColor = blueColor
}
#IBAction func didTouchUpInsideThreeButton(sender: AnyObject) {
inputTextView.text = inputTextView.text + "3"
threeButton.backgroundColor = lightGrayColor
}
#IBAction func didTouchDragExitThreeButton(sender: AnyObject) {
threeButton.backgroundColor = lightGrayColor
}
#IBAction func didTouchDragEnterThreeButton(sender: AnyObject) {
threeButton.backgroundColor = blueColor
}
Any help would be much appreciated!
I managed to create a suitable solution by overriding the functions below and keeping track of which button was touched last and second to last. The last button touched gets highlighted and the second to last gets unhighlighted. Here's my code for a two button test in case anyone should find it useful:
#IBOutlet weak var bottomButton: UIButton!
#IBOutlet weak var topButton: UIButton!
var lastTouchedButton: UIButton? = nil
var secondToLastTouchedButton: UIButton? = nil
override func touchesBegan(touches: Set<UITouch>?, withEvent event: UIEvent?) {
let touch = touches?.first
let location : CGPoint = (touch?.locationInView(self.view))!
if topButton.pointInside(self.view.convertPoint(location, toView: topButton.viewForLastBaselineLayout), withEvent: nil) {
topButton?.backgroundColor = UIColor.redColor()
}
else if bottomButton.pointInside(self.view.convertPoint(location, toView: bottomButton.viewForLastBaselineLayout), withEvent: nil) {
bottomButton?.backgroundColor = UIColor.redColor()
}
super.touchesBegan(touches!, withEvent:event)
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first
let location : CGPoint = (touch?.locationInView(self.view))!
if topButton.pointInside(self.view.convertPoint(location, toView: topButton.viewForLastBaselineLayout), withEvent: nil) {
secondToLastTouchedButton = lastTouchedButton
lastTouchedButton = topButton
lastTouchedButton?.backgroundColor = UIColor.redColor()
}
else if bottomButton.pointInside(self.view.convertPoint(location, toView: bottomButton.viewForLastBaselineLayout), withEvent: nil) {
secondToLastTouchedButton = lastTouchedButton
lastTouchedButton = bottomButton
lastTouchedButton?.backgroundColor = UIColor.redColor()
}
else {
lastTouchedButton?.backgroundColor = UIColor.whiteColor()
}
if secondToLastTouchedButton != lastTouchedButton {
secondToLastTouchedButton?.backgroundColor = UIColor.whiteColor()
}
super.touchesMoved(touches, withEvent: event)
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first
let location : CGPoint = (touch?.locationInView(self.view))!
if topButton.pointInside(self.view.convertPoint(location, toView: topButton.viewForLastBaselineLayout), withEvent: nil) {
topButton?.backgroundColor = UIColor.whiteColor()
}
else if bottomButton.pointInside(self.view.convertPoint(location, toView: bottomButton.viewForLastBaselineLayout), withEvent: nil) {
bottomButton?.backgroundColor = UIColor.whiteColor()
}
super.touchesEnded(touches, withEvent: event)
}
override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) {
lastTouchedButton?.backgroundColor = UIColor.whiteColor()
super.touchesCancelled(touches, withEvent: event)
}
I've tried to do this by using touch drag inside/outside and touch drag enter/exit on the buttons, but it only works for the button where the touch originated
Absolutely right. The touch "belongs" to a view, and for as long as you keep dragging, it will still only belong to that view. Thus, the only way something like what you describe can be possible is for the touch detection to take place in the common superview of these button views.

TextField Draggable in Swift iOS

I want to drag textField around my view. but im having small problem with my code.
here my code
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
var touch : UITouch! = touches.first as! UITouch
location = touch.locationInView(self.view)
bottomTextField.center = location
}
override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
var touch : UITouch! = touches.first as! UITouch
location = touch.locationInView(self.view)
bottomTextField.center = location
}
Problem is if i tried to touch on the TextField and drag then it doesn't work. but if i just touch somewhere else and drag my finger then TextField start to drag. i want t make it only if i touch on that textFiel.
You can achieve this by adding a gesture to the text field:
override func viewDidLoad() {
super.viewDidLoad()
var gesture = UIPanGestureRecognizer(target: self, action: Selector("userDragged:"))
bottomTextField.addGestureRecognizer(gesture)
bottomTextField.userInteractionEnabled = true
}
func userDragged(gesture: UIPanGestureRecognizer){
var loc = gesture.locationInView(self.view)
self.bottomTextField.center = loc
}
If it helps anyone, following is an updated working version of the accepted answer for swift 4.0 and 5.0
override func viewDidLoad() {
super.viewDidLoad()
//Draggable textfields
let gesture = UIPanGestureRecognizer(target: self, action: #selector(userDragged(gesture:)))
bottomTextView.addGestureRecognizer(gesture)
bottomTextView.isUserInteractionEnabled = true
}
#objc func userDragged(gesture: UIPanGestureRecognizer){
let loc = gesture.location(in: self.view)
self.bottomTextView.center = loc
}

UISegmentedControl unselect segment by pressing it again

How can one unselect a given segment in a UISegmented control, by pressing the same segment again?
E.g. Press segment 0, it becomes selected and remains highlighted. Press segment 0 again and it becomes unselected and unhighlighted.
The control only fires UIControlEventValueChanged events. Other events don't seem to work with it.
There is a property 'momentary' which when set to YES almost allows the above behavior, excepting that highlighting is only momentary. When momentary=YES pressing the same segment twice results in two UIControlEventValueChanged events, but when momentary=NO only the first press of a given segment results in a UIControlEventValueChanged event being fired. I.e. subsequent presses on the same segment will not fire the UIControlEventValueChanged event.
you can subclass UISegmentedControl:
Swift 3
class ReselectableSegmentedControl: UISegmentedControl {
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
let previousSelectedSegmentIndex = self.selectedSegmentIndex
super.touchesEnded(touches, withEvent: event)
if previousSelectedSegmentIndex == self.selectedSegmentIndex {
if let touch = touches.first {
let touchLocation = touch.locationInView(self)
if CGRectContainsPoint(bounds, touchLocation) {
self.sendActionsForControlEvents(.ValueChanged)
}
}
}
}
}
Swift 4
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
let previousSelectedSegmentIndex = self.selectedSegmentIndex
super.touchesEnded(touches, with: event)
if previousSelectedSegmentIndex == self.selectedSegmentIndex {
let touch = touches.first!
let touchLocation = touch.location(in: self)
if bounds.contains(touchLocation) {
self.sendActions(for: .valueChanged)
}
}
}
and then
#IBAction func segmentChanged(sender: UISegmentedControl) {
if (sender.selectedSegmentIndex == selectedSegmentIndex) {
sender.selectedSegmentIndex = UISegmentedControlNoSegment;
selectedSegmentIndex = UISegmentedControlNoSegment;
}
else {
selectedSegmentIndex = sender.selectedSegmentIndex;
}
}
Not EXACTLY what you ask for but I was searching for a similar feature and decided on this.
Add doubleTap gesture to UISegmentedControl that sets the selectedSegmentIndex
When you initialize your UISegmentedControl.
let doubleTap = UITapGestureRecognizer(target: self, action #selector(YourViewController.doubleTapToggle))
doubleTap.numberOfTapsRequired = 2
yourSegmentedControl.addGestureRecognizer(doubleTap)
Function to deselect segment
#objc func doubleTapToggle () {
yourSegmentedControl.selectedSegmentIndex = -1
yourSegmentedControl.sendActions(for: valueChanged)
}
Based on #protspace answer, a simpler way, without having to write anything in the #IBAction:
class DeselectableSegmentedControl: UISegmentedControl {
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
let previousSelectedSegmentIndex = selectedSegmentIndex
super.touchesEnded(touches, with: event)
if previousSelectedSegmentIndex == selectedSegmentIndex {
let touch = touches.first!
let touchLocation = touch.location(in: self)
if bounds.contains(touchLocation) {
selectedSegmentIndex = UISegmentedControl.noSegment
sendActions(for: .valueChanged)
}
}
}
}
UISegmentedControl can be programmatically deselected with Swift using #IBOutlet weak var mySegmentedControl: UISegmentedControl! and mySegmentedControl.selectedSegmentIndex = -1. As far as detecting the touch of the segment, I do not know. Hopefully someone else will have that answer.

Resources