xcode swift: how to drag a button? - ios

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.

Related

Swift iOS: Detect when a user drags/slides finger over a UILabel?

I have a project where I’m adding three UILabels to the view controller’s view. When the user begins moving their finger around the screen, I want to be able to determine when they their finger is moving over any of these UILabels.
I’m assuming a UIPanGestureRecognizer is what I need (for when the user is moving their finger around the screen) but I’m not sure where to add the gesture. (I can add a tap gesture to a UILabel, but this isn’t what I need)
Assuming I add the UIPanGestureRecognizer to the main view, how would I go about accomplishing this?
if gesture.state == .changed {
// if finger moving over UILabelA…
// …do this
// else if finger moving over UILabelB…
// …do something else
}
You can do this with either a UIPanGestureRecognizer or by implementing touchesMoved(...) - which to use depends on what else you might be doing.
For pan gesture, add the recognizer to the view (NOT to the labels):
#objc func handlePan(_ g: UIPanGestureRecognizer) {
if g.state == .changed {
// get the location of the gesture
let loc = g.location(in: view)
// loop through each label to see if its frame contains the gesture point
theLabels.forEach { v in
if v.frame.contains(loc) {
print("Pan Gesture - we're panning over label:", v.text)
}
}
}
}
For using touches, no need to add a gesture recognizer:
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let t = touches.first {
// get the location of the touch
let loc = t.location(in: view)
// loop through each label to see if its frame contains the touch point
theLabels.forEach { v in
if v.frame.contains(loc) {
print("Touch - we're dragging the touch over label:", v.text)
}
}
}
}

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

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()
}
}

Other actions occurring when sprite is tapped; how to prevent

I'm building a game that is primarily played by tapping the screen (anywhere). I have currently added a settings button that when tapped, opens up a settings window.
Problem is, when the settings button is tapped, interaction happens in the game too. Is there any way to prevent this from happening?
My settings screen works in conjunction with a settings function and the override touches began function through a boolean expression.
The game is played through a UITapGestureRecognizer that was added to the view in didMoveToView.
/* Game Interaction & Playability */
override func didMoveToView(view: SKView) {
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: Selector("tapped:"))
view.addGestureRecognizer(tap)
}
/* Settings Button Interaction */
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let positionInScene = touch.locationInNode(self)
let touchedNode = self.nodeAtPoint(positionInScene)
if let name = touchedNode.name {
if name == "Settings" {
if settingsOpen == false {
settings()
} else {
settings()
}
}
}
}
}
Well, you've got a couple gesture recognizers working here. You don't actually need to add a recognizer to the view to know if someone is tapping it. That's handled already for you (hence the reason why touchesBegan works without adding another recognizer).
I would say remove that extra recognizer, and move the code for tapped: to your touchesBegan.

recognizing long press gesture in spriteKit

Hi I am currently moving a sprite by checking if the button was pressed in touchesBegan and then updating the position in update. I am doing this so that the user does not need to keep pressing the move up/down/left/right etc button over and over. The problem is that sometimes the sprite does not stop moving which im sure is due to this. Does anyone know a better solution to this? For brevity I will show you the way I am taking care of the up button.
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
for touch: AnyObject in touches {
// Get the location of the touch in this scene
let location = touch.locationInNode(self)
// Check if the location of the touch is within the button's bounds
if upButton.containsPoint(location) {
upButtonPressed = true
}
override func update(currentTime: CFTimeInterval) {
if upButtonPressed == true {
ball.position.y += 3
}
I kept it simple here but I do have all the conditions to stop movement in my code. I am simply wondering if there is an easier way to do this with maybe a long press gesture recognizer?

UIControl endTrackingWithTouch not called

I have a view with a tap gesture recognizer. A subview of this view is an instance of my custom class, which inherits from UIControl. I am having an issue where the UIControl subclass will sometimes allow touch events to pass through to the parent view when it shouldn't.
Within the UIControl subclass, I have overridden these functions (code is in Swift)
override func beginTrackingWithTouch(touch: UITouch, withEvent event: UIEvent) -> Bool
{
return true
}
override func continueTrackingWithTouch(touch: UITouch, withEvent event: UIEvent) -> Bool
{
// The code here moves this UIControl so its center is at the touchpoint
return true
}
override func endTrackingWithTouch(touch: UITouch,withEvent event: UIEvent)
{
// Something important happens here!
}
This system works just fine if the user touches down within the UIControl, drags the control around in both X and Y directions, and then lifts off the screen. In this case, all three of these functions are called, and the "something important" happens.
However, if the user touches down with the UIControl, drags the control around only in the X direction, and then lifts off the screen, we have a problem. The first two functions are called, but when the touchpoint lifts off the screen, the tap gesture recognizer is called, and endTrackingWithTouch is not called.
How do I make sure that endTrackingWithTouch is always called?
I fixed this in a way that I consider to be a hack, but there's really no alternative, given how UIGestureRecognizer works.
What was happening was that the tap gesture recognizer was canceling the control's tracking and registering a tap gesture. This was because when I was dragging horizontally, I just happened to be dragging short distances, which gets interpreted as a tap gesture.
The tap gesture recognizer must be disabled while the UIControl is tracking:
override func beginTrackingWithTouch(touch: UITouch, withEvent event: UIEvent) -> Bool
{
pointerToSuperview.pauseGestureRecognizer()
return true
}
override func continueTrackingWithTouch(touch: UITouch, withEvent event: UIEvent) -> Bool
{
// The code here moves this UIControl so its center is at the touchpoint
return true
}
override func endTrackingWithTouch(touch: UITouch,withEvent event: UIEvent)
{
// Something important happens here!
pointerToSuperview.resumeGestureRecognizer()
}
override func cancelTrackingWithEvent(event: UIEvent?)
{
pointerToSuperview.resumeGestureRecognizer()
}
In the superview's class:
pauseGestureRecognizer()
{
tapGestureRecognizer.enabled = false
}
resumeGestureRecognizer()
{
tapGestureRecognizer.enabled = true
}
This works because I'm not dealing with multitouch (it's OK for me not to receive tap touch events while tracking touches with the UIControl).
Ideally, the control shouldn't have to tell the view to pause the gesture recognizer - the gesture recognizer shouldn't be meddling with the control's touches to begin with! However, even setting the gesture recognizer's cancelsTouchesInView to false cannot prevent this.
There's a way to fix this that's nicely self-contained: instantiate your own TapGestureRecognizer and attach it to your custom control, e.g. in Objective-C,
_tapTest = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapped:)];
_tapTest.numberOfTapsRequired = 1;
[self addGestureRecognizer:_tapTest];
and then implement the tapped action handler to process the tap:
- (void)tapped:(UITapGestureRecognizer *)recognizer {...}
In my case, I handle tapped the same as endTrackingWithTouch:withEvent:; your mileage may vary.
This way, you get the tap before any superview can snatch it, and you don't have to worry about the view hierarchy behind your control.
When a UIControl is moved while tracking touches, it might cancel its tracking. Try overriding cancelTrackingWithEvent and see if this is the case. If you do see the cancel, you're going to have to track your touches in an unmoving view somewhere in the parent hierarchy of this control.
I know this is old, but I run into the same problem, check if one of your superviews has gesture recogniser, and deactivate them when you need to use the UIControl.
I actually ended changed the superview of the UIControl to the main window to avoid this conflicts (Because it was in a popup).

Resources