can't access object within UIPanGestureRecognizer - uiview

I am doing a bezier animation with programmatically generated objects. The number of objects could be bigger than 100. These objects are also responsive to touch.
The animation works fine. Now I would like to make some objects drag-able.
However, I can't access the individual objects in the UIPanGestureRecognizer function. I assume I m doing something wrong with class / subclass calling, but can't think of it..
The tutorials I looked into had IBOutlets or dedicated class variables for every animated object on screen.. I do have a potential high number of objects that are generated within the FOR loop..
What approach do you suggest?
func panning(pan: UIPanGestureRecognizer) {
self.view.bringSubviewToFront(pan.view!)
var translation = pan.translationInView(self.view)
pan.view.center = CGPointMake(pan.view.center.x + translation.x, pan.view.center.y + translation.y)
pan.setTranslation(CGPointZero, inView: self.view)
let panGesture = UIPanGestureRecognizer(target: self, action: "panning:");
}
}
.. within viewDidLoad the function gets called:
// loop from 0 to 50
for i in 0...50 {
// create a square object
let square = UIView()
square.frame = CGRect(x: 55, y: 300, width: 40, height: 40)
square.backgroundColor = UIColor.redColor()
self.view.addSubview(square)
let recognizer = UIPanGestureRecognizer(target: self, action: "panning:");
recognizer.delegate = self;
square.addGestureRecognizer(recognizer)
panGesture.delegate = self;
Thanks to the feedback from Rob, here's an updated version:
I don't really know how to do add the AllowUserInteraction to CAKeyframeAnimation (Rob's answer 3a), as it does not have an "option" field (I m a noob).
Stopping the animation for the object being moved (Rob's answer 3b) completely eludes me. But its something definitely necessary in here.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
srand48(Int(NSDate().timeIntervalSince1970))
// loop from 0 to 5
for i in 0...5 {
// create a square
let square = UIView()
square.frame = CGRect(x: 55, y: 300, width: 40, height: 40)
square.backgroundColor = UIColor.redColor()
self.view.addSubview(square)
//square.userInteractionEnabled = true;
let recognizer = UIPanGestureRecognizer(target: self, action: "panning:");
square.addGestureRecognizer(recognizer)
// randomly create a value between 0.0 and 150.0
let randomYOffset = CGFloat( drand48() * 150)
// for every y-value on the bezier curve
// add our random y offset so that each individual animation
// will appear at a different y-position
let path = UIBezierPath()
path.moveToPoint(CGPoint(x: -16,y: 239 + randomYOffset))
path.addCurveToPoint(CGPoint(x: 375, y: 239 + randomYOffset), controlPoint1: CGPoint(x: 136, y: 373 + randomYOffset), controlPoint2: CGPoint(x: 178, y: 110 + randomYOffset))
// create the animation
let anim = CAKeyframeAnimation(keyPath: "position")
anim.path = path.CGPath
anim.rotationMode = kCAAnimationRotateAuto
anim.repeatCount = Float.infinity
//anim.duration = 5.0
// each square will take between 4.0 and 8.0 seconds
// to complete one animation loop
anim.duration = 4.0 + 3 * drand48()
// stagger each animation by a random value
// `290` was chosen simply by experimentation
anim.timeOffset = 290 * drand48()
// add the animation
square.layer.addAnimation(anim, forKey: "animate position along path")
}
}
func panning(pan: UIPanGestureRecognizer) {
if pan.state == .Began {
println("pan began")
self.view.bringSubviewToFront(pan.view!)
} else if pan.state == .Changed {
println("pan state changed")
var translation = pan.translationInView(self.view)
pan.view?.center = CGPointMake(pan.view!.center.x + translation.x, pan.view!.center.y + translation.y)
pan.setTranslation(CGPointZero, inView: self.view)
println("translation")
}
}

Two things jump out at me:
Your for loop is setting delegates for recognizer (which is not needed unless you're doing something particular which you haven't shared with us) and panGesture (which should be removed because panGesture is not a variable that you have set in this for loop and seems to be completely unrelated). Thus it would be:
for i in 0...50 {
let square = UIView()
square.frame = CGRect(x: 55, y: 300, width: 40, height: 40)
square.backgroundColor = UIColor.redColor()
self.view.addSubview(square)
let recognizer = UIPanGestureRecognizer(target: self, action: "panning:");
square.addGestureRecognizer(recognizer)
}
In panning you are instantiating a new panGesture: You definitely want to get rid of that. A gesture handler has no business creating new gestures. So get rid of the line that starts with let panGesture = ....
In your original question, you hadn't shared the animation you're doing. If you were animating using block-based animation, you have to specify the UIViewAnimationOptions.AllowUserInteraction options. Also, if you'd start dragging it around a view that is being animated, you'd also want to (a) stop the animation for that view; and (b) reset the frame in accordance with the presentationLayer.
But, it's now clear that you're using CAKeyframeAnimation, in which case, there is no AllowUserInteraction mechanism. So, instead, you have to add the gesture recognizer to the superview, and then iterate through the frames of the squares (as represented by their presentation layer, i.e. the location mid-animation) and test to see if you get any hits.
It's up to you, but I find the pan gesture is a little slow to start recognizing the gesture (as it differentiates between a pan and a tap. In this case, you want something a little more responsive, methinks, so I might use a long press gesture recognizer with a
So, pulling that all together, you end up with something like:
var squares = [UIView]() // an array to keep track of all of the squares
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
srand48(Int(NSDate().timeIntervalSince1970))
// loop from 0 to 5
for i in 0...5 {
// create a square
let square = UIView()
square.frame = CGRect(x: 55, y: 300, width: 40, height: 40)
square.backgroundColor = UIColor.redColor()
self.view.addSubview(square)
squares.append(square)
// randomly create a value between 0.0 and 150.0
let randomYOffset = CGFloat( drand48() * 150)
// for every y-value on the bezier curve
// add our random y offset so that each individual animation
// will appear at a different y-position
let path = UIBezierPath()
path.moveToPoint(CGPoint(x: -16,y: 239 + randomYOffset))
path.addCurveToPoint(CGPoint(x: 375, y: 239 + randomYOffset), controlPoint1: CGPoint(x: 136, y: 373 + randomYOffset), controlPoint2: CGPoint(x: 178, y: 110 + randomYOffset))
// create the animation
let anim = CAKeyframeAnimation(keyPath: "position")
anim.path = path.CGPath
anim.rotationMode = kCAAnimationRotateAuto
anim.repeatCount = Float.infinity
//anim.duration = 5.0
// each square will take between 4.0 and 8.0 seconds
// to complete one animation loop
anim.duration = 4.0 + 3 * drand48()
// stagger each animation by a random value
// `290` was chosen simply by experimentation
anim.timeOffset = 290 * drand48()
// add the animation
square.layer.addAnimation(anim, forKey: "animate position along path")
}
let recognizer = UILongPressGestureRecognizer(target: self, action: "handleLongPress:");
recognizer.minimumPressDuration = 0
view.addGestureRecognizer(recognizer)
}
var viewToDrag: UIView! // which of the squares are we dragging
var viewToDragCenter: CGPoint! // what was the `center` of `viewToDrag` when we started to drag it
var originalLocation: CGPoint! // what was gesture.locationInView(gesture.view!) when we started dragging
func handleLongPress(gesture: UILongPressGestureRecognizer) {
let location = gesture.locationInView(gesture.view!)
if gesture.state == .Began {
for square in squares {
let presentationLayer = square.layer.presentationLayer() as CALayer
let frame = presentationLayer.frame
if CGRectContainsPoint(frame, location) {
viewToDrag = square
viewToDragCenter = CGPointMake(CGRectGetMidX(frame), CGRectGetMidY(frame))
viewToDrag.layer.removeAnimationForKey("animate position along path")
viewToDrag.center = viewToDragCenter
originalLocation = location
return
}
}
} else if gesture.state == .Changed {
if viewToDrag != nil {
var translation = CGPointMake(location.x - originalLocation.x, location.y - originalLocation.y)
viewToDrag.center = CGPointMake(viewToDragCenter.x + translation.x, viewToDragCenter.y + translation.y)
}
} else if gesture.state == .Ended {
viewToDrag = nil
}
}

Related

Pan Gesture making view jump away from finger on drag

I'm having a problem when trying to drag a view using a pan gesture recognizer. The view is a collectionViewCell and the dragging code is working, except when the drag starts the view jumps up and to the left. My code is below.
In the collectionViewCell:
override func awakeFromNib() {
super.awakeFromNib()
let panRecognizer = UIPanGestureRecognizer(target:self, action:#selector(detectPan))
self.gestureRecognizers = [panRecognizer]
}
var firstLocation = CGPoint(x: 0, y: 0)
var lastLocation = CGPoint(x: 0, y: 0)
#objc func detectPan(_ recognizer:UIPanGestureRecognizer) {
switch recognizer.state {
case .began:
firstLocation = recognizer.translation(in: self.superview)
lastLocation = recognizer.translation(in: self.superview)
case .changed:
let translation = recognizer.translation(in: self.superview)
self.center = CGPoint(x: lastLocation.x + translation.x, y: lastLocation.y + translation.y)
default:
UIView.animate(withDuration: 0.1) {
self.center = self.firstLocation
}
}
}
The first image is before the drag starts, the second is what happens when dragging up.
You're using self.center instead of using self.frame.origin.x and self.frame.origin.y then later you're setting your translation and adding it to the lastLocation.
Effectively what's happening is that your view is calculating the position changed from the center of the view, as if you were perfectly dragging from that location and then translating + lastLocation. I'm sure just by reading that you're aware of the issue.
The fix is simple.
self.frame.origin.x = translation.x
self.frame.origin.y = translation.y
The difference is the starting calculation with the translation. Origin will grab the x/y position based on where the touch event begins. Whereas the .center always goes from the center.

Move a circular UIView inside another circular UIView

I'm trying to do a joystick swift, and I'm almost there.
But I have a problem, the movement of the joystick is smooth when I move it "in the middle", but when the joystick touches the edges of "its container" it becomes laggy.
But I know why, it's because I allow the joystick to move only if it doesn't touch the edges, and I don't know how to correct this problem (what code to put in the else).
Here's my code and a GIF so you can see better.
import UIKit
import SnapKit
class ViewController: UIViewController {
let joystickSize = 150
let substractSize = 200
let joystickOffset = 10
let joystickSubstractView = UIView()
let joystickView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
joystickSubstractView.backgroundColor = .gray
joystickSubstractView.layer.cornerRadius = CGFloat(substractSize / 2)
self.view.addSubview(joystickSubstractView)
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(dragJoystick))
joystickView.isUserInteractionEnabled = true
joystickView.addGestureRecognizer(panGesture)
joystickView.backgroundColor = .white
joystickView.layer.cornerRadius = CGFloat(joystickSize / 2)
joystickSubstractView.addSubview(joystickView)
joystickSubstractView.snp.makeConstraints {
$0.width.height.equalTo(substractSize)
$0.centerX.equalToSuperview()
$0.bottom.equalToSuperview().inset(150)
}
joystickView.snp.makeConstraints {
$0.width.height.equalTo(joystickSize)
$0.center.equalToSuperview()
}
}
#objc func dragJoystick(_ sender: UIPanGestureRecognizer) {
self.view.bringSubviewToFront(joystickView)
let translation = sender.translation(in: self.view)
let joystickCenter = joystickView.convert(joystickView.center, to: self.view)
let futureJoystickCenter = CGPoint(x: joystickCenter.x - joystickView.frame.minX + translation.x,
y: joystickCenter.y - joystickView.frame.minY + translation.y)
let distanceBetweenCenters = hypot(futureJoystickCenter.x - joystickSubstractView.center.x,
futureJoystickCenter.y - joystickSubstractView.center.y)
if CGFloat(substractSize / 2 + joystickOffset) >= (distanceBetweenCenters + CGFloat(joystickSize / 2)) {
joystickView.center = CGPoint(x: joystickView.center.x + translation.x,
y: joystickView.center.y + translation.y)
} else {
// I don't know what to put here to make the joystick "smoother"
}
sender.setTranslation(CGPoint.zero, in: self.view)
}
}
Thank you for your help
Here is one approach...
calculate the maximum available distance from the center of the outer circle to the center of the inner circle, as a radius
track the touch / pan gesture's location relative to the center of the outer circle
if the new distance from the center of the inner circle (the touch point) to the center of the outer circle is greater than the max radius, move the inner circle center to the intersection of the touch-to-center line and the edge of the radius circle
Here's how it would look, with the center of the "joystick" view identified with a green dot, and the radius circle shown as a red outline:
You can give it a try with this code:
class JoyStickViewController: UIViewController {
let joystickSize: CGFloat = 150
let substractSize: CGFloat = 200
var innerRadius: CGFloat = 0.0
let joystickSubstractView = UIView()
let joystickView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
joystickSubstractView.backgroundColor = .gray
joystickSubstractView.layer.cornerRadius = CGFloat(substractSize / 2)
self.view.addSubview(joystickSubstractView)
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(dragJoystick(_:)))
joystickView.isUserInteractionEnabled = true
joystickView.addGestureRecognizer(panGesture)
joystickView.backgroundColor = .yellow
joystickView.layer.cornerRadius = CGFloat(joystickSize / 2)
joystickSubstractView.addSubview(joystickView)
joystickSubstractView.snp.makeConstraints {
$0.width.height.equalTo(substractSize)
$0.centerX.equalToSuperview()
$0.bottom.equalToSuperview().inset(150)
}
joystickView.snp.makeConstraints {
$0.width.height.equalTo(joystickSize)
$0.center.equalToSuperview()
}
// if you want the "joystick" circle to overlap the "outer circle" a bit, adjust this value
innerRadius = (substractSize - joystickSize) * 0.5
// start debugging / clarification...
// add a center "dot" to the joystick view
// add a red circle showing the inner radius - where we want to restrict the center of the joystick view
let jsCenterView = UIView()
jsCenterView.backgroundColor = .green
jsCenterView.layer.cornerRadius = 2.0
joystickView.addSubview(jsCenterView)
jsCenterView.snp.makeConstraints {
$0.width.height.equalTo(4.0)
$0.center.equalToSuperview()
}
let v = UIView()
v.backgroundColor = .clear
v.layer.borderColor = UIColor.red.cgColor
v.layer.borderWidth = 2
v.layer.cornerRadius = innerRadius
v.isUserInteractionEnabled = false
joystickSubstractView.addSubview(v)
v.snp.makeConstraints {
$0.width.height.equalTo(innerRadius * 2.0)
$0.center.equalToSuperview()
}
// end debugging / clarification
}
func lineLength(from pt1: CGPoint, to pt2: CGPoint) -> CGFloat {
return hypot(pt2.x - pt1.x, pt2.y - pt1.y)
}
func pointOnLine(from startPt: CGPoint, to endPt: CGPoint, distance: CGFloat) -> CGPoint {
let totalDistance = lineLength(from: startPt, to: endPt)
let totalDelta = CGPoint(x: endPt.x - startPt.x, y: endPt.y - startPt.y)
let pct = distance / totalDistance;
let delta = CGPoint(x: totalDelta.x * pct, y: totalDelta.y * pct)
return CGPoint(x: startPt.x + delta.x, y: startPt.y + delta.y)
}
#objc func dragJoystick(_ sender: UIPanGestureRecognizer) {
let touchLocation = sender.location(in: joystickSubstractView)
let outerCircleViewCenter = CGPoint(x: joystickSubstractView.bounds.width * 0.5, y: joystickSubstractView.bounds.height * 0.5)
var newCenter = touchLocation
let distance = lineLength(from: touchLocation, to: outerCircleViewCenter)
// if the touch would put the "joystick circle" outside the "outer circle"
// find the point on the line from center to touch, at innerRadius distance
if distance > innerRadius {
newCenter = pointOnLine(from: outerCircleViewCenter, to: touchLocation, distance: innerRadius)
}
joystickView.center = newCenter
}
}
Note: you can delete (or comment-out) the lines of code in viewDidLoad() between the // start debugging and // end debugging comments to remove the green center-dot and the red circle.

How to convert CATransform3DTranslate to CGAffineTransform so it can Mimic a Carousel View

My question pertains to how to mimic this Carousel view Youtube video only using a UIView not it's layer or a CALayer, which means actually transforming the UIViews its self.
I found a stack overflow question that actually is able to convert a
CATransform3D into a CGAffineTransform. That was written by some genius here as an answer, but my problem is a little unique.
The animation you see below is using CALayer to create. I need to create this same animation but transforming the UIView instead of its layer.
What it's Supposed to look like:
Code (Creates animation using Layers):
This takes an image card which is a CALayer() with a image attached to it and transforms which places it in the Carousel of images.
Note: turnCarousel() is also called when the user pans which moves / animates the Carousel.
let transformLayer = CATransformLayer()
func turnCarousel() {
guard let transformSubLayers = transformLayer.sublayers else {return}
let segmentForImageCard = CGFloat(360 / transformSubLayers.count)
var angleOffset = currentAngle
for layer in transformSubLayers {
var transform = CATransform3DIdentity
transform.m34 = -1 / 500
transform = CATransform3DRotate(transform, degreeToRadians(deg: angleOffset), 0, 1, 0)
transform = CATransform3DTranslate(transform, 0, 0, 175)
CATransaction.setAnimationDuration(0)
layer.transform = transform
angleOffset += segmentForImageCard
}
}
What It Currently Looks Like:
So basically it's close, but it seems as though there is a scaling issue with the cards that are supposed to be seen as in the front and the cards that are in the back of the carousel.
Fo this what I did is use a UIImageView as the base view for the carousel and then added more UIImageViews as cards to it. So now we are trying do a transformation on a UIImageView/UIView
Code:
var carouselTestView = UIImageView()
func turnCarouselTestCarousel() {
let segmentForImageCard = CGFloat(360 / carouselTestView.subviews.count)
var angleOffset = currentAngleTestView
for subview in carouselTestView.subviews {
var transform2 = CATransform3DIdentity
transform2.m34 = -1 / 500
transform2 = CATransform3DRotate(transform2, degreeToRadians(deg: angleOffset), 0, 1, 0)
transform2 = CATransform3DTranslate(transform2, 0, 0, 175)
CATransaction.setAnimationDuration(0)
// m13, m23, m33, m43 are not important since the destination is a flat XY plane.
// m31, m32 are not important since they would multiply with z = 0.
// m34 is zeroed here, so that neutralizes foreshortening. We can't avoid that.
// m44 is implicitly 1 as CGAffineTransform's m33.
let fullTransform: CATransform3D = transform2
let affine = CGAffineTransform(a: fullTransform.m11, b: fullTransform.m12, c: fullTransform.m21, d: fullTransform.m22, tx: fullTransform.m41, ty: fullTransform.m42)
subview.transform = affine
angleOffset += segmentForImageCard
}
}
the sub image that actually make up the Carousel are add with this function which simply goes through a for loop of image named 1...6 in my assets folder.
Code:
func CreateCarousel() {
carouselTestView.frame.size = CGSize(width: self.view.frame.width, height: self.view.frame.height / 2.9)
carouselTestView.center = CGPoint(self.view.frame.width * 0.5, self.view.frame.height * 0.5)
carouselTestView.alpha = 1.0
carouselTestView.backgroundColor = UIColor.clear
carouselTestView.isUserInteractionEnabled = true
self.view.insertSubview(carouselTestView, at: 0)
for i in 1 ... 6 {
addImageCardTestCarousel(name: "\(i)")
}
// Set the carousel for the first time. So that now we can see it like an actual carousel animation
turnCarouselTestCarousel()
let panGestureRecognizerTestCarousel = UIPanGestureRecognizer(target: self, action: #selector(self.performPanActionTestCarousel(recognizer:)))
panGestureRecognizerTestCarousel.delegate = self
carouselTestView.addGestureRecognizer(panGestureRecognizerTestCarousel)
}
The addImageCardTestCarousel function is here:
Code:
func addImageCardTestCarousel(name: String) {
let imageCardSize = CGSize(width: carouselTestView.frame.width / 2, height: carouselTestView.frame.height)
let cardPanel = UIImageView()
cardPanel.frame.size = CGSize(width: imageCardSize.width, height: imageCardSize.height)
cardPanel.frame.origin = CGPoint(carouselTestView.frame.size.width / 2 - imageCardSize.width / 2 , carouselTestView.frame.size.height / 2 - imageCardSize.height / 2)
guard let imageCardImage = UIImage(named: name) else {return}
cardPanel.image = imageCardImage
cardPanel.contentMode = .scaleAspectFill
cardPanel.layer.masksToBounds = true
cardPanel.layer.borderColor = UIColor.white.cgColor
cardPanel.layer.borderWidth = 1
cardPanel.layer.cornerRadius = cardPanel.frame.height / 50
carouselTestView.addSubview(cardPanel)
}
Purpose:
The purpose of this is that I want to build a UI that can take UIViews on the rotating cards you see, and a CALayer cannot add a UIView as a subview. It can only add the UIView's layer to its own Layer. So to solve this problem I need to actually achieve this animation with UIViews not CALayers.
I solved it the view that Appears to be behind the front most view are actually grabbing all the touch even if you touch a card right in the front the back card will prevent touches for the front card. So i made a function that can calculate. Which views are in the front. Than disable and enable touches for then. Its like when 2 cards get stacked on top of each other the card to the back will stop the card from the front from taking/userInteraction.
Code:
func DetermineFrontViews(view subview: UIView, angle angleOffset: CGFloat) {
let looped = Int(angleOffset / 360) // must round down to Int()
let loopSubtractingReset = CGFloat(360 * looped) // multiply 360 how ever many times we have looped
let finalangle = angleOffset - loopSubtractingReset
if (finalangle >= -70 && finalangle <= 70) || (finalangle >= 290) || (finalangle <= -260) {
print("In front of view")
if subview.isUserInteractionEnabled == false {
subview.isUserInteractionEnabled = true
}
} else {
print("Back of view")
if subview.isUserInteractionEnabled == true {
subview.isUserInteractionEnabled = false
}
}
}
I added this to the turn function to see if it could keep track of the first card being either in the back of the carousel or the front.
if subview.layer.name == "1" {
DetermineFrontViews(view: subview, angle: angleOffset)
}

Swift: Detect when SKSpriteNode has even touched

I have a game where an object falls from the top of the screen to the bottom randomly. I want to have it to where if the object is tapped, I can tell it to do something, like add points to the score.
Can you help me try to figure this out? I'll be on to answer any questions if you happen to have any. Here is the code I'm using:
class GameSceneTest: SKScene, SKPhysicsContactDelegate {
override func didMoveToView(view: SKView) {
backgroundColor = SKColor.clearColor()
physicsWorld.gravity = CGVectorMake(0, 0)
physicsWorld.contactDelegate = self
//Change duration
runAction(SKAction.repeatActionForever(SKAction.sequence([SKAction.runBlock(addObject), SKAction.waitForDuration(1)])
))
//AddMusicHere
}
func random() -> CGFloat {
return CGFloat(Float(arc4random()) / 0xFFFFFFFF)
}
func random(min min: CGFloat, max: CGFloat) -> CGFloat {
return random() * (max - min) + min
}
func add Object() {
let Object = SKSpriteNode(imageNamed: "Object\(arc4random_uniform(6) + 1).png")
Object.userInteractionEnabled = true
Object.size = CGSize(width: 50, height: 50)
Object.physicsBody = SKPhysicsBody(rectangleOfSize: Object.size)
Object.physicsBody?.dynamic = true
//Determine where to spawn gem across y axis
let actually = random(min: Object.size.width/2, max: size.width - Object.size.width/2)
//Position Object slightly off screen along right edge
//and along random y axis point
//Object.position = CGPoint(x: size.width + Object.size.width/2 , y: actually)
Object.position = CGPoint(x: actually, y: size.height + Object.size.height/2)
//Add the Object to the scene
addChild(Object)
//Determines speed of Object (edit later to where speed depends on type of Object)
let actualDuration = random(min: CGFloat(4), max: CGFloat(5))
//Create the Actions
let actionMove = SKAction.moveTo(CGPoint(x: actually, y: gem.size.height/2), duration: NSTimeInterval(actualDuration))
let actionMoveDone = SKAction.removeFromParent()
let loseAction = SKAction.runBlock() {
let reveal = SKTransition.flipHorizontalWithDuration(0.5) //Change to flipe vertical
let gameOverScene = GameOverScene(size: self.size, won: false)
self.view?.presentScene(gameOverScene, transition: reveal)
}
Object.runAction(SKAction.sequence([actionMove, loseAction, actionMoveDone]))
}
Take a look at "Moving The Cards Around The Board" in the following tutorial: Ray Wenderlicht SpriteKit Tutorial.
Then read a bit more around handling touch events in iOS (e.g. Handling Touches...).
Then try something in code and if you get stuck post a more specific question - because this one is a little too general.

UICollisions in Swift

I'm required to make a Breakout app in a Single-View Application in the Swift language. However, I'm having trouble getting the "ball" to respond to hitting the barrier. Additionally, I'm having trouble getting the barrier to disappear after it is hit. Does anyone have the solution to this, or does anyone have an example app I can look off of? This is in Single View Application, not Sprite.
var dynamicAnimatior = UIDynamicAnimator()
override func viewDidLoad() {
super.viewDidLoad()
dynamicAnimatior = UIDynamicAnimator(referenceView: view)
setupViews()
}
func setupViews() {
let blueSquare = UIView(frame: CGRectMake(100, 100, 50, 50))
blueSquare.backgroundColor = UIColor.blueColor()
view.addSubview(blueSquare)
let barrier = UIView(frame: CGRect(x: 0, y: 300, width: 130, height: 20))
barrier.backgroundColor = UIColor.redColor()
view.addSubview(barrier)
addDynamicBehaviors([blueSquare])
}
func addDynamicBehaviors(array: [UIView]) {
let dynamicItemBehavior = UIDynamicItemBehavior(items: array)
dynamicItemBehavior.density = 1.0
dynamicItemBehavior.friction = 0.0
dynamicItemBehavior.resistance = 0.0
dynamicItemBehavior.elasticity = 1.0
dynamicAnimatior.addBehavior(dynamicItemBehavior)
let pushBehavior = UIPushBehavior(items: array, mode: .Instantaneous)
pushBehavior.magnitude = 1.0
pushBehavior.pushDirection = CGVectorMake(0.5, 0.5)
dynamicAnimatior.addBehavior(pushBehavior)
let collisionBehavior = UICollisionBehavior(items: array)
collisionBehavior.translatesReferenceBoundsIntoBoundary = true
collisionBehavior.collisionMode = .Everything
collisionBehavior.collisionDelegate = self
dynamicAnimatior.addBehavior(collisionBehavior)
}
You need to have both the objects in your UICollisionBehavior if you want them to collide. Depending on what you need there is several possibilities. I suppose that you need your ball to bounce on barrier, so create the behavior:
let collisionBehavior = UICollisionBehavior(blueSquare: array)
and then add a rigid boundary that correspond to your barrier:
let edge = CGPointMake(barrier.frame.origin.x + barrier.frame.size.width,
barrier.frame.origin.y + barrier.frame.size.heigth);
collisionBehavior.addBoundaryWithIdentifier("barrier",
fromPoint:barrier.frame.origin,
toPoint:rightEdge];
Now if you want to capture the collision you need to add a delegate to collisionDelegate. This delegate should be able to respond to several methods when a hit occurs, (read documentation about UICollisionBehaviorDelegate.

Resources