I have 2 UIImageView who are placed on storyboard and setup in code via frames. I have also a UIButton and an optional draggableView: UIView?. What I'm trying to do:
Drag a view - seems to be working fine
Highlight my button when I hover draggableView over it and disable highlight if we moving away from it. Highlight seems to be happening, but my draggableView goes kinda underneath a button and does a very noticeable jump into its starting position before dragging. After that if we continue dragging - position continues to update properly.
Here is the code for touches
override func viewDidLoad() {
...
img01.isUserInteractionEnabled = true
img02.isUserInteractionEnabled = true
...
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else {return}
if img01.bounds.contains(touch.location(in: img01)) {
draggableView = img01
} else if img02.bounds.contains(touch.location(in: img02)) {
draggableView = img02
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let draggableView = draggableView else {return}
guard let touch = touches.first else {return}
draggableView.center = touch.location(in: view)
if btnMain.bounds.contains(touch.location(in: btnMain)) {
print("Case 4")
btnMain.isHighlighted = true
} else {
print("Case 5")
btnMain.isHighlighted = false
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
draggableView = nil
btnMain.isHighlighted = false
}
What causes that jump to initial position and is there a way to kinda... make draggableView noticeably on top of anything? Thanks.
Related
I have these buttons on my cameraView that I want to hide when pressing on the view. I got that to work but I want to unhide the buttons when I press on the view again. How would I be able to do that?
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
if touch?.view == self.cameraView {
flipCamera.isHidden = true
lockButton.isHidden = true
print("Hide buttons")
} else if touch?.view == self.cameraView && flipCamera.isHidden == true {
print("show buttons")
}
If you want to change the isHidden value to its opposite on each touch, you can simply use the toggle() function, which toggles a Bool value - it assigns false if the value was true and assigns true if the value was false.
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
if touch?.view == self.cameraView {
flipCamera.isHidden.toggle()
lockButton.isHidden.toggle()
}
}
I want to make the image jump by clicking on the sprite, rather than clicking the screen to make it jump.
func jump() {
Football?.texture = SKTexture(imageNamed: "Football")
Football?.physicsBody?.applyImpulse(CGVector(dx: 100, dy: 2000))
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches { self.touchUp(atPoint: t.location(in: self)) }
}
func touchUp(atPoint pos: CGPoint) {
Football?.texture = SKTexture(imageNamed: "Football")
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
this is a really basic way of testing if the sprite is touched
BTW your variable is named Football proper nomenclature says thay you should be using lower case variable names "football"
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first as UITouch! {
let touchLocation = touch.location(in: self)
if Football.contains(touchLocation) {
//jump code here
}
}
}
I have a subclassed node called LocationNode, where touch is enabled. It looks like this.
class LocationNode: SKSpriteNode, CustomNodeEvents {
func didMoveToScene() {
self.isUserInteractionEnabled = true
}
}
I haven't overriden touchesBegan in this subclass file, because I want to control all touch events like this in the actual scene file:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touchLocation = touches.first!.location(in: self)
let touchedNode = self.atPoint(touchLocation)
//if touchedNode.name != nil {
print("touched")
//}
}
When I try do this, however, it doesn't work. How can I control touch events for a subclassed node in the scene file?
EDIT:
Here's what my override function looks like in the scene.
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touchLocation = touches.first!.location(in: self)
let touchedNode = self.atPoint(touchLocation)
print("TOUCHED")
}
You could implement this code below in your custom class, in this case LocationNode, to have a "return of touches" to the parent class, then you can handle the touches in both places (also to the main scene) :
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
print("touched!")
}
guard let parent = self.parent else { return }
parent.touchesBegan(touches, with: event)
}
I´m making a game with Swift.
I need to add nodes where I press the screen, I know this is not from other planet but I can't do it.
Highly simplified example could look like this:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
let location = touch.location(in: self)
let node = SKNode()
node.position = location
addChild(node)
}
Here is how you can add an remove labels by tapping:
class ViewController: UIViewController{
private var labels = [UILabel]()
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else {
return
}
for label in labels {
if label.frame.contains(touch.location(in: view)) {
label.removeFromSuperview()
return
}
}
let label = UILabel()
view.addSubview(label)
labels.append(label)
label.text = "touch"
label.sizeToFit()
label.center = touch.location(in: view)
}
}
All i'm trying to do is be able to drag and drop a sprite across the screen. I've tried the following code:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in (touches ) {
let location = touch.locationInNode(self)
if ball.containsPoint(location) {
ball.position = location
}
}
}
This code does work, however, when I drag the ball quite fast, I guess it detects that the "ball" no longer contains the point "location" and the ball stops, meaning I have pick the ball up again. I want the ball to be able to respond to my touches quickly, so that the ball wont stop moving. How would I do this?
This is the correct way to do it in Sprite Kit. Like I said in my comment, you need to assign the moving node to an activate state, in this case I use a variable called movableNode to act is my activate state. When you touch the ball, it becomes activated by assigning it to movableNode. Then as you drag your finger, movableNode will go with the drag, Then once you release, you enter a deactivate state by setting movableNode to nil. Note that this code will only work on single touch applications, if you want to handle multitouch, then you need to track which touch is doing the dragging.
var movableNode : SKNode?
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let location = touch.locationInNode(self)
if ball.containsPoint(location) {
movableNode = ball
movableNode!.position = location
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first where movableNode != nil {
movableNode!.position = touch.locationInNode(self)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first where movableNode != nil {
movableNode!.position = touch.locationInNode(self)
movableNode = nil
}
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
movableNode = nil
}
}
KnightOFDragon solution works just fine. I just added few lines if you don't want to move sprite centre position to position where your finger touched the screen and you would like to move sprite from its centre original position.
var movableNode : SKNode?
var ballStartX: CGFloat = 0.0
var ballStartY: CGFloat = 0.0
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let location = touch.location(in: self)
if (map?.contains(location))! {
movableNode = ball
ballStartX = (movableNode?.position.x)! - location.x // Location X of your sprite when touch started
ballStartY = (movableNode?.position.y)! - location.y // Location Y of your sprite when touch started
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first, movableNode != nil {
let location = touch.location(in: self)
movableNode!.position = CGPoint(x: location.x+ballStartX, y: location.y+ballStartY) // Move node around with distance to your finger
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if let _ = touches.first, movableNode != nil {
movableNode = nil
}
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
movableNode = nil
}
I have an implementation where I've subclassed a UIImageView and called it a "DraggableImage"
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
originalPosition = self.center
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let position = touch.location(in: self.superview)
self.center = CGPoint(x: position.x, y: position.y)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
self.center = originalPosition
}