How do I detect which way the user is swiping? - swift

I'm using the code below so that the user can swipe a UIImageView to whatever direction they want. I need to detect which way the user is swiping. If it's swiped for example to the left it needs to println "Swiped left!" Is there any way to do this?
let ThrowingThreshold: CGFloat = 1000
let ThrowingVelocityPadding: CGFloat = 35
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var redSquare: UIView!
#IBOutlet weak var blueSquare: UIView!
private var originalBounds = CGRect.zeroRect
private var originalCenter = CGPoint.zeroPoint
private var animator: UIDynamicAnimator!
private var attachmentBehavior: UIAttachmentBehavior!
private var pushBehavior: UIPushBehavior!
private var itemBehavior: UIDynamicItemBehavior!
func resetDemo() {
animator.removeAllBehaviors()
UIView.animateWithDuration(0.45) {
self.imageView.bounds = self.originalBounds
self.imageView.center = self.originalCenter
self.imageView.transform = CGAffineTransformIdentity
}
}
#IBAction func handleAttachmentGesture(sender: UIPanGestureRecognizer) {
let location = sender.locationInView(self.view)
let boxLocation = sender.locationInView(self.imageView)
switch sender.state {
case .Began:
println("Your touch start position is \(location)")
println("Start location in image is \(boxLocation)")
// 1
animator.removeAllBehaviors()
// 2
let centerOffset = UIOffset(horizontal: boxLocation.x - imageView.bounds.midX,
vertical: boxLocation.y - imageView.bounds.midY)
attachmentBehavior = UIAttachmentBehavior(item: imageView,
offsetFromCenter: centerOffset, attachedToAnchor: location)
// 3
redSquare.center = attachmentBehavior.anchorPoint
blueSquare.center = location
// 4
animator.addBehavior(attachmentBehavior)
case .Ended:
println("Your touch end position is \(location)")
println("End location in image is \(boxLocation)")
animator.removeAllBehaviors()
// 1
let velocity = sender.velocityInView(view)
let magnitude = sqrt((velocity.x * velocity.x) + (velocity.y * velocity.y))
if magnitude > ThrowingThreshold {
// 2
let pushBehavior = UIPushBehavior(items: [imageView], mode: .Instantaneous)
pushBehavior.pushDirection = CGVector(dx: velocity.x / 10, dy: velocity.y / 10)
pushBehavior.magnitude = magnitude / ThrowingVelocityPadding
self.pushBehavior = pushBehavior
animator.addBehavior(pushBehavior)
// 3
let angle = Int(arc4random_uniform(20)) - 10
itemBehavior = UIDynamicItemBehavior(items: [imageView])
itemBehavior.friction = 0.2
itemBehavior.allowsRotation = true
itemBehavior.addAngularVelocity(CGFloat(angle), forItem: imageView)
animator.addBehavior(itemBehavior)
// 4
let timeOffset = Int64(0.4 * Double(NSEC_PER_SEC))
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, timeOffset), dispatch_get_main_queue()) {
self.resetDemo()
}
} else {
resetDemo()
}
default:
attachmentBehavior.anchorPoint = sender.locationInView(view)
redSquare.center = attachmentBehavior.anchorPoint
}
override func viewDidLoad() {
super.viewDidLoad()
animator = UIDynamicAnimator(referenceView: view)
originalBounds = imageView.bounds
originalCenter = imageView.center

You can use the translationInView function of the gesture to get a CGPoint representing the movement of the gesture since it started (or since you last reset it). This tells you the relative movement in the x and y directions.
If you're interested in the overall movement then you can just read this and use the sign of the x and y values to determine the direction moved.
If you're interested in the instantaneous direction of movement then you can use the same approach, but after reading the translation you can use setTranslation:inView: to reset to CGPointZero. In this way the translation provided in the next callback is the delta movement.

Related

Shake animation of NSView

I am trying to implement a shaking NSView in my macOS app.
I tried following the example/tutorial from here, but I am not having much success.
I am attempting to not only shake the view left to right, but also up and down, and with a 2 degree positive and negative rotation.
I thought I could nest animations through the completion handler within a loop, but that is not working the way I expected so I am trying to use an if statement to implement the shake, which also is not working.
The intent here is to simulate a shake from an explosion and as the explosions get closer the intensity increases which would cause the shaking to increase in the number of shakes as well as the duration of the view shaking.
Here's example code I am working with to master this task:
import Cocoa
class ViewController: NSViewController {
#IBOutlet weak var shakeButton1: NSButton!
#IBOutlet weak var shakeButton2: NSButton!
#IBOutlet weak var shakeButton3: NSButton!
#IBOutlet weak var shakeButton4: NSButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
#IBAction func shakeButtonClicked(button: NSButton) {
switch button.identifier {
case shakeButton1.identifier:
shakeView(on: self.view, shakes: 2)
case shakeButton2.identifier:
shakeView(on: self.view, shakes: 4)
case shakeButton3.identifier:
shakeView(on: self.view, shakes: 6)
case shakeButton4.identifier:
shakeView(on: self.view, shakes: 8)
default: break
}
}
/**
Method simulates a shaking of a view
intensity value is an integer for the number of times the animation is repeated.
*/
func shakeView(on theView: NSView, shakes: Int) {
let originalOrigin = self.view.frame.origin
let rotation: CGFloat = 2
let moveValue: CGFloat = 5
let contextDuration: Double = 0.05
for shake in 0..<shakes {
if shake.isMultiple(of: 2) {
print("Even")
// Perform the first animation
NSAnimationContext.runAnimationGroup({ firstAnimation in
firstAnimation.duration = contextDuration
// Animate to the new origin and angle
theView.animator().frame.origin.x = moveValue // positive origin moves right
theView.animator().frame.origin.y = moveValue // positive origin moves up
theView.animator().rotate(byDegrees: -rotation) // apply a negative rotation
print("First rotation \(shake) of \(shakes) = \(self.view.animator().frameRotation)")
}) { // First completion handler
theView.frame.origin = originalOrigin
theView.rotate(byDegrees: rotation) // apply a positive rotation to rotate the view back to the original position
print("Second rotation \(shake) of \(shakes) = \(self.view.frameRotation)")
}
} else {
print("Odd")
// Perform the opposite action
NSAnimationContext.runAnimationGroup({ secondAnimation in
secondAnimation.duration = contextDuration
theView.animator().frame.origin.x = -moveValue // negative origin moves left
theView.animator().frame.origin.y = -moveValue // negative origin moves down
theView.animator().rotate(byDegrees: rotation) // apply positive rotation
print("Third rotation \(shake) of \(shakes) = \(self.view.frameRotation)")
}) { // Second completion handler
theView.frame.origin = originalOrigin
theView.rotate(byDegrees: -rotation) // apply a negative rotation to rotate the view back to the original position
print("Fourth rotation \(shake) of \(shakes) = \(self.view.frameRotation)")
}
}
}
}
}
I set up four buttons in storyboard and linked them all to the same IBAction method for simplicity. Button identifiers in storyboard are "shake1Button", "shake2Button", "shake3Button", and "shake4Button".
Thank you for any help.
EDIT
I think I nearly have it. The only issue I am now having is when the rotation occurs, the center of rotation does not appear to be centered on the view. I am now using NotificationCenter to handle the animation. It looks pretty good the way it is, but I would really like to get the center of rotation set on the center of the view.
import Cocoa
class ViewController: NSViewController {
#IBOutlet weak var shake1Button: NSButton!
#IBOutlet weak var shake2Button: NSButton!
#IBOutlet weak var shake3Button: NSButton!
#IBOutlet weak var shake4Button: NSButton!
let observatory = NotificationCenter.default
var shaken: CGFloat = 0
var intensity: CGFloat = 0
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
setupObservatory()
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
#IBAction func shakeButtonClicked(button: NSButton) {
switch button.identifier {
case shake1Button.identifier:
intensity = 1
case shake2Button.identifier:
intensity = 2
case shake3Button.identifier:
intensity = 3
case shake4Button.identifier:
intensity = 4
default: break
}
observatory.post(name: .shakeView, object: nil)
}
func setupObservatory() {
observatory.addObserver(forName: .shakeView, object: nil, queue: nil, using: shakeView)
observatory.addObserver(forName: .moveDownAnimation, object: nil, queue: nil, using: moveDownAnimation)
observatory.addObserver(forName: .moveLeftAnimation, object: nil, queue: nil, using: moveLeftAnimation)
observatory.addObserver(forName: .moveRightAnimation, object: nil, queue: nil, using: moveRightAnimation)
observatory.addObserver(forName: .moveUpAnimation, object: nil, queue: nil, using: moveUpAnimation)
observatory.addObserver(forName: .rotateLeftAnimation, object: nil, queue: nil, using: rotateLeftAnimation)
observatory.addObserver(forName: .rotateRightAnimation, object: nil, queue: nil, using: rotateRightAnimation)
}
func shakeView(notification: Notification) {
if shaken != intensity {
shaken += 1
observatory.post(name: .moveDownAnimation, object: nil)
} else {
shaken = 0
}
}
func moveDownAnimation(notification: Notification) {
//guard let theView = notification.object as? NSView else { return }
let originalOrigin = self.view.frame.origin
NSAnimationContext.runAnimationGroup({ verticalAnimation in
verticalAnimation.duration = 0.05
// Animate to the new angle
self.view.animator().frame.origin.y -= intensity // subtracting value moves the view down
}) { // Completion handler
self.view.frame.origin = originalOrigin
self.observatory.post(name: .moveLeftAnimation, object: nil)
}
}
func moveLeftAnimation(notification: Notification) {
//guard let theView = notification.object as? NSView else { return }
let originalOrigin = self.view.frame.origin
// Perform the animation
NSAnimationContext.runAnimationGroup({ firstAnimation in
firstAnimation.duration = 0.05
// Animate to the new origin
self.view.animator().frame.origin.x -= intensity // subtracting value moves the view left
}) { // Completion handler
self.view.frame.origin = originalOrigin
self.observatory.post(name: .rotateLeftAnimation, object: nil)
}
}
func moveRightAnimation(notification: Notification) {
//guard let theView = notification.object as? NSView else { return }
let originalOrigin = self.view.frame.origin
// Perform the animation
NSAnimationContext.runAnimationGroup({ horizontalAnimation in
horizontalAnimation.duration = 0.05
// Animate to the new origin
self.view.animator().frame.origin.x += intensity // adding value moves the view right
}) { // Completion handler
self.view.frame.origin = originalOrigin
self.observatory.post(name: .moveUpAnimation, object: nil)
}
}
func moveUpAnimation(notification: Notification) {
//guard let theView = notification.object as? NSView else { return }
let originalOrigin = self.view.frame.origin
NSAnimationContext.runAnimationGroup({ verticalAnimation in
verticalAnimation.duration = 0.05
// Animate to the new angle
self.view.animator().frame.origin.y += intensity // adding value moves the view up
}) { // Completion handler
self.view.frame.origin = originalOrigin
self.observatory.post(name: .rotateRightAnimation, object: nil)
}
}
func rotateLeftAnimation(notification: Notification) {
// Prepare the anchor point to rotate around the center
let newAnchorPoint = CGPoint(x: 0.5, y: 0.5)
view.layer?.anchorPoint = newAnchorPoint
// Prevent the anchor point from modifying views location on screen
guard var position = view.layer?.position else { return }
position.x += view.bounds.maxX * newAnchorPoint.x
position.y += view.bounds.maxY * newAnchorPoint.y
view.layer?.position = position
// Configure the animation
let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
rotateAnimation.byValue = intensity / 180 * CGFloat.pi
rotateAnimation.duration = 0.05;
let unRotateAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
unRotateAnimation.byValue = -(intensity / 180 * CGFloat.pi)
unRotateAnimation.duration = 0.05;
// Trigger the animation
CATransaction.begin()
view.layer?.add(rotateAnimation, forKey: "rotate")
CATransaction.setCompletionBlock {
// Handle animation completion
self.view.layer?.add(unRotateAnimation, forKey: "rotate")
self.observatory.post(name: .moveRightAnimation, object: nil)
}
CATransaction.commit()
}
func rotateRightAnimation(notification: Notification) {
// Prepare the anchor point to rotate around the center
let newAnchorPoint = CGPoint(x: 0.5, y: 0.5)
view.layer?.anchorPoint = newAnchorPoint
// Prevent the anchor point from modifying views location on screen
guard var position = view.layer?.position else { return }
position.x += view.bounds.maxX * newAnchorPoint.x
position.y += view.bounds.maxY * newAnchorPoint.y
view.layer?.position = position
// Configure the animation
let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
rotateAnimation.byValue = -(intensity / 180 * CGFloat.pi)
rotateAnimation.duration = 0.05;
let unRotateAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
unRotateAnimation.byValue = intensity / 180 * CGFloat.pi
unRotateAnimation.duration = 0.05;
// Trigger the animation
CATransaction.begin()
view.layer?.add(rotateAnimation, forKey: "rotate")
CATransaction.setCompletionBlock {
// Handle animation completion
self.view.layer?.add(unRotateAnimation, forKey: "rotate")
self.observatory.post(name: .shakeView, object: nil)
}
CATransaction.commit()
}
}
extension Notification.Name {
static var shakeView: Notification.Name {
.init(rawValue: "ViewController.shakeView")
}
static var moveDownAnimation: Notification.Name {
.init(rawValue: "ViewController.moveDownAnimation")
}
static var moveLeftAnimation: Notification.Name {
.init(rawValue: "ViewController.moveLeftAnimation")
}
static var moveRightAnimation: Notification.Name {
.init(rawValue: "ViewController.moveRightAnimation")
}
static var moveUpAnimation: Notification.Name {
.init(rawValue: "ViewController.moveUpAnimation")
}
static var rotateLeftAnimation: Notification.Name {
.init(rawValue: "ViewController.rotateLeftAnimation")
}
static var rotateRightAnimation: Notification.Name {
.init(rawValue: "ViewController.rotateRightAnimation")
}
}

Detect intersect with masked image (not rectangular)

How to not get detected with intersects func when moving around masked image in UIImageView frame?
explanation image
Code that I am using to detect collision:
movingPerson.frame.intersects(camera.frame)
Where movingPerson is just a regular UIImage that I am moving around with touchesmoved func and camera is a masked Image.
I've tried .bounds instead of .frame but it's not working.
Is there any easy way?
If you want the "Very easy way" then UIDynamicItem and provide a path that bounds your image and let UIDynamicAnimator handle the collisions for you. If you do not know how to get the path for your artwork (yours is pretty easy and you should be able to extract the Bezier coordinates automatically in Paintcode, by hand in Illustrator, or from a SVG file) you can use SpriteKit instead since it will actually generate the bounding polygon for your sprite automatically.
EDIT Sample (note I didn't want to write a whole app for this so I took an existing playground and just added the colliders. the collision works, but the reset after collision doesn't):
import UIKit
import PlaygroundSupport
class ObstacleView: UIView {
override var collisionBoundsType: UIDynamicItemCollisionBoundsType {
return .path
}
override var collisionBoundingPath: UIBezierPath {
return UIBezierPath(ovalIn: bounds)
}
override func layoutSubviews() {
super.layoutSubviews()
layer.cornerRadius = bounds.size.width / 2
}
}
class ViewController: UIViewController {
var dynamicAnimator: UIDynamicAnimator!
var gravityBehavior: UIGravityBehavior!
var pushBehavior: UIPushBehavior!
var collisionBehavior: UICollisionBehavior!
lazy var player: UILabel = {
let label = UILabel()
label.text = "🐥"
label.sizeToFit()
label.isUserInteractionEnabled = true
label.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(label)
return label
}()
var isPanning = false
var anchor: CGPoint = .zero
let maxDistance = CGFloat(100)
lazy var obstacles: [UIView] = {
return (0..<2).map { index in
let view = ObstacleView(frame: CGRect(x:100 + CGFloat(index)*200,y:200, width: 40, height: 40))
view.backgroundColor = .blue
view.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(view)
return view
}
}()
override func viewDidLoad() {
super.viewDidLoad()
dynamicAnimator = UIDynamicAnimator(referenceView: view)
dynamicAnimator.delegate = self
gravityBehavior = UIGravityBehavior(items: [player])
let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(pan))
_ = obstacles
view.addGestureRecognizer(panGestureRecognizer)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
anchor = view.center
player.center = anchor
}
#objc private func pan(recognizer: UIPanGestureRecognizer) {
switch recognizer.state {
case .began:
guard player.bounds.contains(recognizer.location(in: player)) else {
isPanning = false
return
}
isPanning = true
case .changed:
guard isPanning else {
return
}
player.center = recognizer.location(in: view)
let dx = player.center.x - anchor.x
let dy = player.center.y - anchor.y
let distance = CGFloat(hypotf(Float(dx), Float(dy)))
if distance > maxDistance {
player.center.x = anchor.x + dx / distance * maxDistance
player.center.y = anchor.y + dy / distance * maxDistance
}
case .ended:
guard isPanning else {
return
}
isPanning = false
let dx = player.center.x - anchor.x
let dy = player.center.y - anchor.y
let distance = CGFloat(hypotf(Float(dx), Float(dy)))
guard distance > 10 else {
player.center = anchor
return
}
if pushBehavior != nil {
dynamicAnimator.removeBehavior(pushBehavior)
}
pushBehavior = UIPushBehavior(items: [player], mode: .instantaneous)
pushBehavior.pushDirection = CGVector(dx: -dx, dy: -dy)
pushBehavior.magnitude = distance / maxDistance * 0.75
dynamicAnimator.addBehavior(pushBehavior)
dynamicAnimator.addBehavior(gravityBehavior)
collisionBehavior = UICollisionBehavior(items: [player] + obstacles)
collisionBehavior.translatesReferenceBoundsIntoBoundary = true
collisionBehavior.collisionDelegate = self
dynamicAnimator.addBehavior(collisionBehavior)
case .cancelled:
isPanning = false
player.center = anchor
default:
break
}
}
}
extension ViewController: UIDynamicAnimatorDelegate {
func dynamicAnimatorDidPause(_ animator: UIDynamicAnimator) {
dynamicAnimator.removeAllBehaviors()
player.center = anchor
}
}
extension ViewController: UICollisionBehaviorDelegate {
func collisionBehavior(_ behavior: UICollisionBehavior, beganContactFor item1: UIDynamicItem, with item2: UIDynamicItem, at p: CGPoint) {
print("contact")
}
}
PlaygroundPage.current.liveView = ViewController()
PlaygroundPage.current.needsIndefiniteExecution = true

Smooth object rotation in ARSCNView

I'm trying to make my pan gesture to be as smooth as the jigspace app when rotating 3d objects in AR. Here's what I have right now:
#objc func rotateObject(sender: UIPanGestureRecognizer) {
let sceneView = sender.view as! ARSCNView
var currentAngleY: Float = 0.0
let translation = sender.translation(in: sceneView)
var newAngleY = Float(translation.x)*Float(Double.pi)/180
sceneView.scene.rootNode.enumerateChildNodes { (node, stop) in
if sender.state == .changed {
newAngleY -= currentAngleY
node.eulerAngles.y = newAngleY
} else if sender.state == .ended {
currentAngleY = newAngleY
node.removeAllActions()
}
}
}
There seems to be a delay when I'm using it and I'm trying to figure out how to make the rotation as smooth as possible, again, kinda like jigspace or the Ikea app.
I've also noticed that when I try to rotate the object when it's in a certain angle, it could get quite awkward.
Looking at your rotate object function it seems like some of the logic is not quite right.
Firstly, I believe that the var currentAngleY: Float = 0 should be outside of your function body.
Secondly you should be adding the currentAngleY to the newAngleY variable e.g:
/// Rotates The Models On Their YAxis
///
/// - Parameter gesture: UIPanGestureRecognizer
#objc func rotateModels(_ gesture: UIPanGestureRecognizer) {
let translation = gesture.translation(in: gesture.view!)
var newAngleY = (Float)(translation.x)*(Float)(Double.pi)/180.0
newAngleY += currentAngleY
DispatchQueue.main.async {
self.sceneView.scene.rootNode.enumerateChildNodes { (node, _) in
node.eulerAngles.y = newAngleY
}
}
if(gesture.state == .ended) { currentAngleY = newAngleY }
}
An example therefore of this in a working context would be like so:
class ViewController: UIViewController {
#IBOutlet var augmentedRealityView: ARSCNView!
var currentAngleY: Float = 0
//-----------------------
// MARK: - View LifeCycle
//-----------------------
override func viewDidLoad() {
super.viewDidLoad()
//1. Generate Our Three Box Nodes
generateBoxNodes()
//2. Create Our Rotation Gesture
let rotateGesture = UIPanGestureRecognizer(target: self, action: #selector(rotateModels(_:)))
self.view.addGestureRecognizer(rotateGesture)
//3. Run The Session
let configuration = ARWorldTrackingConfiguration()
augmentedRealityView.session.run(configuration)
}
//------------------------
// MARK: - Node Generation
//------------------------
/// Generates Three SCNNodes With An SCNBox Geometry
func generateBoxNodes(){
//1. Create An Array Of Colours For Each Face
let colours: [UIColor] = [.red, .green, .blue, .purple, .cyan, .black]
//2. Create An SCNNode Wih An SCNBox Geometry
let boxNode = SCNNode()
let boxGeometry = SCNBox(width: 0.1, height: 0.1, length: 0.1, chamferRadius: 0.01)
boxNode.geometry = boxGeometry
//3. Create A Different Material For Each Face
var materials = [SCNMaterial]()
for i in 0..<5{
let faceMaterial = SCNMaterial()
faceMaterial.diffuse.contents = colours[i]
materials.append(faceMaterial)
}
//4. Set The Geometries Materials
boxNode.geometry?.materials = materials
//5. Create Two More Nodes By Cloning The First One
let secondBox = boxNode.flattenedClone()
let thirdBox = boxNode.flattenedClone()
//6. Position Them In A Line & Add To The Scene
boxNode.position = SCNVector3(-0.2, 0, -1.5)
secondBox.position = SCNVector3(0, 0, -1.5)
thirdBox.position = SCNVector3(0.2, 0, -1.5)
self.augmentedRealityView.scene.rootNode.addChildNode(boxNode)
self.augmentedRealityView.scene.rootNode.addChildNode(secondBox)
self.augmentedRealityView.scene.rootNode.addChildNode(thirdBox)
}
//----------------------
// MARK: - Node Rotation
//----------------------
/// Rotates The Models On Their YAxis
///
/// - Parameter gesture: UIPanGestureRecognizer
#objc func rotateModels(_ gesture: UIPanGestureRecognizer) {
let translation = gesture.translation(in: gesture.view!)
var newAngleY = (Float)(translation.x)*(Float)(Double.pi)/180.0
newAngleY += currentAngleY
DispatchQueue.main.async {
self.augmentedRealityView.scene.rootNode.enumerateChildNodes { (node, _) in
node.eulerAngles.y = newAngleY
}
}
if(gesture.state == .ended) { currentAngleY = newAngleY }
}
}
Hope it helps...

Game crash only on iPhone X and iPhone 8 Plus

I'm working on game and the game works perfect on all iPhones and iPad expect iPhone X and iPhone 8 Plus
When I add sprite to the scene the game crash (only on iPhone X and iPhone 8 Plus) because the sprite is already added to scene, On others iPhone the app not crash, I think the bug is in my timer, maybe the bug it's the iPhone simulator? What do you think?
Code:
class GameScene: SKScene, SKPhysicsContactDelegate {
var finger = SKSpriteNode(imageNamed: "Finger") //Finger to swipe the biscuit
var biscuit = SKSpriteNode(imageNamed: "Biscuit")
var glass = SKSpriteNode(imageNamed: "Glass")
var defaultbiscuitpos:CGPoint = CGPoint()
var defaultfingerpos:CGPoint = CGPoint()
/*** Drag Biscuit vars ***/
var touchpoint:CGPoint = CGPoint()
var istouching:Bool = false
var fadeoutfinger = SKAction()
var fadeinfinger = SKAction()
var fingergroup = SKAction()
var burnanimtion = SKAction()
var movefinger = SKAction()
let fingertimer:String = "fingertimer"
var isGameover:Bool = false
//Game mode enum
enum gamemode {
case dip
case ready
case out
case gameover
}
//Game mode (Dip,Ready,Out or game over by enum) **Now is Dip
var mymode = gamemode.dip
override func didMove(to view: SKView) {
//Finger
finger.name = "Finger"
finger.position = CGPoint(x: biscuit.position.x + finger.frame.width, y: biscuit.position.y)
defaultfingerpos = finger.position
finger.alpha = 1.0
finger.zPosition = 5
//Start finger timer to make animation
createTimer(name: fingeranimation, waitt: 3.0, sprite: finger, actioname: fingertimer)
}
//Finger timer func
func fingeranimation () {
//Check if timer is over 4 seconds and the title is dip
if mymode == gamemode.dip {
//Add finger to screen
addChild(finger)
//Set fade in animation for finger
fadeinfinger = SKAction.fadeIn(withDuration: 2.0)
//Set move animation for finger
movefinger = SKAction.moveTo(y: glass.frame.midX, duration: 2.0)
//Set fade out animation for finger
fadeoutfinger = SKAction.fadeOut(withDuration: 2.0)
fingergroup = SKAction.group([fadeinfinger,movefinger,fadeoutfinger])
finger.run(fingergroup, completion: {
//Remove finger from screen
self.finger.removeFromParent()
//Return the finger to apper and return the finger to default position
self.finger.alpha = 1.0
self.finger.position = self.defaultfingerpos
})
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first! as UITouch
let location = touch.location(in: self)
//Tap on biscuit
if biscuit.contains(location) && mymode != gamemode.gameover {
touchpoint = location
istouching = true
biscuit.physicsBody?.pinned = false
//Stop the finger animation timer
stopTimer(actioname: fingertimer, sprite: finger)
}
}
//Make timer function
func createTimer (name:#escaping os_block_t , waitt:TimeInterval, sprite:SKSpriteNode?,actioname: String) {
let myaction = SKAction.sequence([SKAction.wait(forDuration: waitt), SKAction.run(name)])
run(SKAction.repeatForever(myaction), withKey: actioname)
}
//Stop timer function
func stopTimer(actioname:String, sprite:SKSpriteNode?) {
removeAction(forKey: actioname)
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
if istouching && isGameover == false {
let dt:CGFloat = 1.0/15
let distance = CGVector(dx: touchpoint.x - biscuit.position.x, dy: touchpoint.y - biscuit.position.y * 1.65)
let velocity = CGVector(dx: distance.dx/dt, dy: distance.dy/dt)
self.biscuit.physicsBody!.velocity = velocity
}
}
}
This problem is now fixed with the release of Xcode 9.1.

Return image view in ios 9 swift 2.1 to original position once it has been dragged but then released

I am building a simple app similar to tinder as a test. It allows users to drag a photo (image view) around to either the left or right side and a new one will load in. However when the imageview is let go of before hitting either the left or right side it is sent to the middle of the screen instead of its original starting position. I think i need to somehow store the original starting position of the image view and call on it later but am unsure of how to do it
here is my relevant code:
class SwipingViewController: UIViewController {
#IBOutlet var userImage: UIImageView!
#IBOutlet var infoLabel: UILabel!
var displayedUserId = ""
//set image position
func wasDragged(gesture: UIPanGestureRecognizer) {
let translation = gesture.translationInView(self.view)
let imageDrag = gesture.view!
imageDrag.center = CGPoint(x: self.view.bounds.width / 2 + translation.x, y: self.view.bounds.height / 2 + translation.y)
let xFromCenter = imageDrag.center.x - self.view.bounds.width / 2
let scale = min(100 / abs(xFromCenter), 1)
var rotation = CGAffineTransformMakeRotation(xFromCenter / 200)
var stretch = CGAffineTransformScale(rotation, scale, scale)
imageDrag.transform = stretch
if gesture.state == UIGestureRecognizerState.Ended {
var acceptedOrRejected = ""
if imageDrag.center.x < 100 {
acceptedOrRejected = "rejected"
print("not chosen" + displayedUserId)
} else if imageDrag.center.x > self.view.bounds.width - 100 {
acceptedOrRejected = "accepted"
print("Chosen")
}
if acceptedOrRejected != "" {
PFUser.currentUser()?.addUniqueObjectsFromArray([displayedUserId], forKey: acceptedOrRejected)
PFUser.currentUser()?.saveInBackgroundWithBlock({
(succeeded: Bool, error: NSError?) -> Void in
if succeeded {
} else {
print(error)
}
})
}
//Resets image position after it has been let go of
rotation = CGAffineTransformMakeRotation(0)
stretch = CGAffineTransformScale(rotation, 1, 1)
imageDrag.transform = stretch
imageDrag.center = CGPoint(x: self.view.bounds.width / 2, y: self.view.bounds.height / 2)
updateImage()
}
}
Here are screenshots of the issue:
Image starting postion
Image Ending Position
Thanks in advance for any help, I'm still pretty new to swift