I would like to add multiple UIViews when the user taps the screen I am using the code below and it creates a UIView when I tap but removes the previous one.
What am I doing wrong?
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
let touch = touches.anyObject()! as UITouch
let location = touch.locationInView(self.view)
println(location)
rectangle.frame = CGRectMake(location.x, location.y, 20, 20)
rectangle.backgroundColor = UIColor.redColor()
self.view.addSubview(rectangle)
}
Assuming rectangle is a property, this code only changes the frame of the existing rectangle and re-adds it to the view hierarchy. If you want a new rectangle to be added each time the user begins touching the device, you have to create a new instance of UIView each time. For example:
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
let touch = touches.anyObject()! as UITouch
let location = touch.locationInView(self.view)
println(location)
let rectangle = UIView(frame: CGRectMake(location.x, location.y, 20, 20))
rectangle.backgroundColor = UIColor.redColor()
self.view.addSubview(rectangle)
}
Additionally, in case this wasn't your intention, the code you're using won't center the new view on the touch location, it's origin will be there, but the view will extent from there to 20 points down and 20 points to the right. If you want the view to be centered in the touch location, I suggest using the view's center property:
let rectangle = UIView(frame: CGRectMake(0, 0, 20, 20))
rectangle.center = location
self.view.addSubview(rectangle)
Related
At the moment I have a game in which the sprite can be controlled and moved along the x axis (with a fixed y position) using the accelerometer of the device. I wish to change this so the user can control the sprite by dragging on the screen like in popular games such as snake vs.block.
I've already tried using the touches moved method which gives the correct effect, although the sprite first moves to the location of the users touch which I don't want.
Below is the environment I've been using to experiment, the touches moved gives the correct type of control I want although I can't seem to figure out how to stop the sprite first moving to the location of the touch before the swipe/drag
import SpriteKit
import GameplayKit
var player = SKSpriteNode()
var playerColor = UIColor.orange
var background = UIColor.black
var playerSize = CGSize(width: 50, height: 50)
var touchLocation = CGPoint()
var shape = CGPoint()
class GameScene: SKScene {
override func didMove(to view: SKView) {
self.backgroundColor = background
spawnPlayer()
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches{
let touchLocation = touch.location(in: self)
player.position.x = touchLocation.x
}
}
func spawnPlayer(){
player = SKSpriteNode(color: playerColor, size: playerSize)
player.position = CGPoint(x:50, y: 500)
self.addChild(player)
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
In summary, I'm looking for the same method of controlling a sprite as in snakes vs blocks
Hey so you have the right idea, you need to store a previous touchesMovedPoint. Then use it to determine how far your finger has moved each time touchesMoved has been called. Then add to the player's current position by that quantity.
var lastTouchLoc:CGPoint = CGPoint()
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches{
let touchLocation = touch.location(in: self)
let dx = touchLocation.x - lastTouchLoc.x
player.position.x += dx // Change the players current position by the distance between the previous touchesMovedPoint and the current one.
lastTouchLoc.x = touchLocation.x // Update last touch location
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
// When releasing the screen from point "E" we don't want dx being calculated from point "E".
// We want dx to calculate from the most recent touchDown point
let initialTouchPoint = touch.location(in: self)
lastTouchLoc.x = initialTouchPoint.x
}
}
You want to use SKAction.move to perform what you want.
func distance(from lhs: CGPoint, to rhs: CGPoint) -> CGFloat {
return hypot(lhs.x.distance(to: rhs.x), lhs.y.distance(to: rhs.y))
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches{
let touchLocation = touch.location(in: self)
let speed = 100/1 //This means 100 points per second
let duration = distance(player.position,touchLocation) / speed //Currently travels 100 points per second, adjust if needed
player.run(SKAction.move(to:touchLocation, duration:duration),forKey:"move")
}
}
What this does is it will move your "snake" to the new location at a certain duration. To determine duration, you need to figure out how fast you want your "snake" to travel. I currently have it set up to where it should take 1 second to move 100 points, but you may want to adjust that as needed.
I'm subclassing nodes to use for touch detection. I have a box parent which has a line child directly next to it, with the gray space being just empty space:
The problem is when I click the gray space, it registers as a touch on the box, which is quite far away.
Here's the code where I show the problem, and my crappy workaround... I make two sets of boxes, the first being the one showing the problem, and the second being the one with the workaround:
import SpriteKit
class GameScene: SKScene {
enum sizez {
static let
box = CGSize(width: 50, height: 35),
line = CGSize(width: 200, height: 10)
}
class Box: SKSpriteNode {
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
print("touched box")
}
}
class Line: SKSpriteNode {
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
print("touched line")
}
}
override func didMove(to view: SKView) {
// The problem sprites that register touch despite empty space:
let box = Box(color: .red, size: sizez.box)
box.isUserInteractionEnabled = true
addChild(box)
let line = Line(color: .purple, size: sizez.line)
line.isUserInteractionEnabled = true
line.anchorPoint = CGPoint.zero
line.position = CGPoint(x: box.frame.maxX + 10, y: box.frame.minY)
///////////////////
box.addChild(line)
///////////////////
// These sprites detect touches properly (no detection in empty space)
let box2 = Box(color: .red, size: sizez.box)
box2.isUserInteractionEnabled = true
box2.position.y -= 100
addChild(box2)
let line2 = Line(color: .purple, size: sizez.line)
line2.isUserInteractionEnabled = true
line2.anchorPoint = CGPoint.zero
line2.position = CGPoint(x: box2.frame.maxX + 10, y: box2.frame.minY)
////////////////
addChild(line2)
////////////////
}
}
When you click directly above (or even farther out from) the top line, you get this:
When you do the same for the bottom line, you get this:
It will be a huge hassle to forgo SK's built in parent / child system, and then for me to keep track of them on my own manually... as well as it being a big performance hit for my app.
Any reasonable workaround or solution that lets me click in the gray space while using code similar to the first box would be greatly appreciated.
UPDATE:
By making an invisible background node and setting its zPositon 1 less, I can now click in the gray space and it registers as the background node, not the box.
let bkgSize = CGSize(width: 1000, height: 1000)
let bkg = Bkg(color: .gray, size: bkgSize)
bkg.isUserInteractionEnabled = true
bkg.zPosition -= 1
addChild(bkg)
But still, why is this empty space touch being registered as a box touch in the absence of a background node?
You're absolutely correct with a background node it works as expected, without a background sprite it gives the results as described by #Confused. I was able to get it to work as expected without a background by just fine tuning the TouchesBegan function like so...
class Box: SKSpriteNode {
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first as UITouch! {
let touchLocation = touch.location(in: parent!)
if frame.contains(touchLocation) {
print("touched box")
}
}
}
}
class Line: SKSpriteNode {
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first as UITouch! {
let touchLocation = touch.location(in: parent!)
if frame.contains(touchLocation) {
print("touched line")
}
}
}
}
From my understanding of how bounds work for parented objects, I think this is what's going on...
Addendum
This is slightly related, and may help in understanding why this is happening, and why Apple considers this to be how a parent and its children should use an accumulated (combined) rectangle/quad for touch response:
I am new to Swift and Sprite Kit.
I want in MyScene.swift to create a button that has a specific image. Every time I click on the button I want to change the button image with a new image.
How can I do that?
GameScene.swift
class GameScene: SKScene {
var rollButton:SKSpriteNode = SKSpriteNode(imageNamed: "dice1.png")
override func didMoveToView(view: SKView) {
rollButton.position = CGPoint(x: 79, y: 290) //this position is based to what? Is there an easier way to put a button using storyborard
rollButton.setScale(0.5) //what does setScale mean?
self.addChild(rollButton)
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
for touch: AnyObject in touches {
if CGRectContainsPoint(rollButton.frame, touch.locationInNode(self)) {
changeButtonImage()
}
}
}
func changeButtonImage() {
//radom get value beteween 1 - 6
change the rollButton image with a new value??? //how to do that
}
Is there a way i an add a UIButton on Storyboard and than connect
GameScene,swift with that storyboard? (adding child nodes
programatically can be really hard)
The CGPoint of a node is based to what? the current UIView?
How to programatically change the image of a button (in swift using
SpriteKit Node) with other images if for example I have the images:
dice1, dice2, dice3...
This is the objective C code:
NSString *fileName = [NSString stringWithFormat:#"dice%d.png", num];
// Setting the uiimageview to the appropriate image
self.rollButton.image = [UIImage imageNamed:fileName];
I didn't find any good example with how to set buttons from storyborard and than using uiviewcontrollers to each new GameScene created,
First, create an array of the images you want the button to have:
let arrayOfImages = [
"image1.png",
"image2.png",
"image3.png",
"image4.png",
"image5.png",
"image6.png"
]
Then initialize the button sprite and give it a texture to start:
var rollButton = SKSpriteNode()
rollButton.texture = SKTexture(imageNamed: arrayOfImages[0])
Then, each time changeButtonImage() is called, generate a random number and change the button's texture:
var randomNumberBetween0And5 = Int(arc4random_uniform(6))
rollButton.texture = SKTexture(imageNamed: arrayOfImages[randomNumberBetween0And5])
If you want to build a button or a switch for generic use, you should try this control I made, the use its pretty straight forward:
You just initialize the type of Button/Switch you want (ColoredSprite, Textured or TextOnly)
let control = TWButton(normalColor: SKColor.blueColor(), highlightedColor: SKColor.redColor(), size: CGSize(width: 160, height: 80))
And after initialize you add a closure to it (like addTargetForSelector on UIButton)
control.addClosureFor(.TouchUpInside, target: self, closure: { (scene, sender) -> () in
scene.testProperty = "Changed Property"
})
}
That’s it! There’s more info on the readme section on the GitHub page: https://github.com/txaidw/TWControls
But if you want to implement in a specific node here’s how I did:
internal override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
let touch = touches.first as! UITouch
let touchPoint = touch.locationInNode(self.parent)
if self.containsPoint(touchPoint) {
self.touchLocationLast = touchPoint
touchDown()
}
}
internal override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
let touch = touches.first as! UITouch
let touchPoint = touch.locationInNode(self.parent)
self.touchLocationLast = touchPoint
}
internal override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
let touch = touches.first as! UITouch
let touchPoint = touch.locationInNode(self.parent)
if let lastPoint = self.touchLocationLast where self.containsPoint(lastPoint) {
// Ended inside
touchUpInside()
}
else {
// Ended outside
touchUpOutside()
}
}
I am new in ios developement.i need to show scratch-card effect for an iPhone app after scratches if coupon number is visible i need to show alert message How can i do this?.i have download sample code from iPhone - Scratch and Win Example and also i have done showing below screen and scratches also works fine
if UILabel text is visible i want to show alert message How can i do this?
i have attached screen shot for your refference
i Found very nice Github example please take a look this and impliment as par you need hope this helps to you my Frnd.
CGScratch This is same thing that you want to apply.
review the code and check the visible area of Number else check che scration overImage is tatally removed of not if remove then show Alert.
A simple UIImageView subclass that allows your UIImageView become a scratch card.
In your storyboard or xib set custom class of your UIImageView that represents your scratch image to ScratchCardImageView. Change lineType or lineWidth to change the appearance of the scratch lines.
Download example
Swift 3:
import UIKit
class ScratchCardImageView: UIImageView {
private var lastPoint: CGPoint?
var lineType: CGLineCap = .square
var lineWidth: CGFloat = 20.0
override func awakeFromNib() {
super.awakeFromNib()
isUserInteractionEnabled = true
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else {
return
}
lastPoint = touch.location(in: self)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first, let point = lastPoint else {
return
}
let currentLocation = touch.location(in: self)
eraseBetween(fromPoint: point, currentPoint: currentLocation)
lastPoint = currentLocation
}
func eraseBetween(fromPoint: CGPoint, currentPoint: CGPoint) {
UIGraphicsBeginImageContext(self.frame.size)
image?.draw(in: self.bounds)
let path = CGMutablePath()
path.move(to: fromPoint)
path.addLine(to: currentPoint)
let context = UIGraphicsGetCurrentContext()!
context.setShouldAntialias(true)
context.setLineCap(lineType)
context.setLineWidth(lineWidth)
context.setBlendMode(.clear)
context.addPath(path)
context.strokePath()
image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
}
}
Updated
With this solution touch events will be tracked only inside the UIImageView bounds. If you need touch events to start already outside your scratchcard, see ScratchCardTouchContainer example
You can also check this github tutorial for scratch:
https://github.com/SebastienThiebaud/STScratchView
https://github.com/moqod/iOS-Scratch-n-See
Ok, so I have always wondered how do I actually pick up an image view and drag it. I was planning when I drag an image view and when user places it to correct location it locks there. I really don't have idea how to do this and it has bothered me for sometime.
Thanks so much in advance!
Or you can use a UIPanGestureRecognizer if you don't want to subclass the view and handle touches yourself.
Create a pan recognizer in any class (e.g. view controller) and add it to your view:
UIPanGestureRecognizer * panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self
action:#selector(handlePanGesture:)];
[myDraggedView addGestureRecognizer:panRecognizer];
And then simply:
- (void)handlePanGesture:(UIPanGestureRecognizer *)gestureRecognizer
{
CGRect frame = myDraggedView.frame;
frame.origin = [gestureRecognizer locationInView:myDraggedView.superview];
myDraggedView.frame = frame;
}
If you like Interface Builder as much as me you can also just drag a pan gesture recognizer over your image view, and then connect the recognizer to the handlePanGesture: that should be declared as an IBAction instead of void.
If you're one for reading, then check out the UIResponder Class Reference, and, in particular, touchesBegan and touchesMoved.
I already worked with this, but the user can drag imageview freely with position that they want. and this is my custom class with swift
class DraggableImageView: UIImageView {
var beganPoint: CGPoint? = nil
var originCenter: CGPoint? = nil
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let position = touches.first?.location(in: superview){
beganPoint = position
originCenter = center
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let position = touches.first?.location(in: superview){
let newPosition = CGPoint(x: position.x - (beganPoint?.x)!, y: position.y - (beganPoint?.y)!)
center = CGPoint(x: (originCenter?.x)! + newPosition.x, y: (originCenter?.y)! + newPosition.y)
}
}
}
Just user this custom class with your UIImageView