SWIFT Attach a image to bezierPath - path

i need to make a application where i have curved bezier like /\ and i have a small object at the left bottom of it, for example a plane >
use your imagination ;)
I have a value from 0.0 to 1.0 and base on it i want put my plane on the path, for example
0.0 plane is on the beginning of the path: >/\
0.5 is at halfway: />\
1.0 is at the end: />
I know how to do it with animation.
CAKeyframeAnimation(keyPath: "position") then .path = bezierPath
its easy!
But I don't need animation! I want to put this plane on the right place when my viewDidAppear.
How to stick UIImage to UIBezierPath and move it along this curve?

Ok i found solution
All you need is to set :
let flightAnimation = CAKeyframeAnimation(keyPath: "position")
flightAnimation.path = ovalShapeLayer.path
flightAnimation.speed = 0
flightAnimation.timeOffset = 0.5 // 50% of the path
flightAnimation.duration = 1.0

Related

Two CABasicAnimations inside of a group animation issue

I am trying to animate something to the below effect:
https://dribbble.com/shots/10885801-Tab-Bar-Simple-Animation
My below code produces something close to what I'm looking for, however once the animation group completes a solid line is drawn between the two specified points, instead of the layer just ending after the second animation.
let start = fromTab.convert(fromLineView.center, to: backgroundView)
let end = toTab.convert(toLineView.center, to: backgroundView)
let path = UIBezierPath()
path.move(to: start)
path.addLine(to: end)
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
shapeLayer.strokeColor = ColorConstants.green.cgColor
shapeLayer.lineWidth = 4
shapeLayer.lineCap = .round
backgroundView.layer.addSublayer(shapeLayer)
let startAnimation = CABasicAnimation(keyPath: "strokeEnd")
startAnimation.duration = 1
startAnimation.fromValue = 0
startAnimation.toValue = 1
let endAnimation = CABasicAnimation(keyPath: "strokeStart")
endAnimation.beginTime = startAnimation.duration
endAnimation.duration = 1
endAnimation.fromValue = 0
endAnimation.toValue = 1
let animation = CAAnimationGroup()
animation.animations = [startAnimation, endAnimation]
animation.duration = startAnimation.duration + endAnimation.duration
shapeLayer.add(animation, forKey: "lineAnimation")
Here's a video of current behavior (I've slowed down the animation to see what's happening):
Any suggestions on achieving my intended result would be greatly appreciated. Thanks in advance.
Your shape layer’s “base state” is to have a line drawn.
You create an animation that animates strokeStart and strokeEnd, but after the animation ends, it is removed, and your layer returns to it’s base state. (With your line visible.)
Add code to set strokeEnd to 0 before you submit your animations. That will set the “base state” to not showing a line, which is what you want.
Edit:
(In order for the second animation in the animation group to work correctly, you’ll also have to do the below, as discussed in the comments. Moving it to the answer for clarity.)
Also add a third animation to your animation group that starts at the same time as your endAnimation, and for the same duration, that animates the strokeEnd value and has a fromValue and TwoValue both of 1. That will set strokeEnd to 1 for the entire duration of phase 2 of the animation (what you call the "endAnimation") so the line will be visible for phase 2.

How to "center" SKTexture in SKSpriteNode

I'm trying to make Jigsaw puzzle game in SpriteKit. To make things easier I using 9x9 squared tiles board. On each tile is one childNode with piece of image from it area.
But here's starts my problem. Piece of jigsaw puzzle isn't perfect square, and when I apply SKTexture to node it just place from anchorPoint = {0,0}. And result isn't pretty, actually its terrible.
https://www.dropbox.com/s/2di30hk5evdd5fr/IMG_0086.jpg?dl=0
I managed to fix those tiles with right and top "hooks", but left and bottom side doesn't care about anything.
var sprite = SKSpriteNode()
let originSize = frame.size
let textureSize = texture.size()
sprite.size = originSize
sprite.texture = texture
sprite.size = texture.size()
let x = (textureSize.width - originSize.width)
let widthRate = x / textureSize.width
let y = (textureSize.height - originSize.height)
let heightRate = y / textureSize.height
sprite.anchorPoint = CGPoint(x: 0.5 - (widthRate * 0.5), y: 0.5 - (heightRate * 0.5))
sprite.position = CGPoint(x: frame.width * 0.5, y: frame.height * 0.5)
addChild(sprite)
Can you give me some advice?
I don't see a way you can get placement right without knowing more about the piece texture you are using because they will all be different. Like if the piece has a nob on any of the sides and the width width/height the nob will add to the texture. Hard to tell in the pic but even if it doesn't have a nob and instead has an inset it might add varying sizes.
Without knowing anything about how the texture is created I am not able to offer help on that. But I do believe the issue starts with that. If it were me I would create a square texture with additional alpha to center the piece correctly. So the center of that texture would always be placed in the center of a square on the grid.
With all that being said I do know that adding that texture to a node and then adding that node to a SKNode will make your placement go smoother with the way you currently have it. The trick will then only be placing that textured piece correctly within the empty SKNode.
For example...
let piece = SKSpriteNode()
let texturedPiece = SKSpriteNode(texture: texture)
//positioning
//offset x needs to be calculated with additional info about the texture
//for example it has just a nob on the right
let offsetX : CGFloat = -nobWidth/2
//offset y needs to be calculated with additional info about the texture
//for example it has a nob on the top and bottom
let offsetY : CGFloat = 0.0
texturedPiece.position = CGPointMake(offsetX, offsetY)
piece.addChild(texturedPiece)
let squareWidth = size.width/2
//Now that the textured piece is placed correctly within a parent
//placing the parent is super easy and consistent without messing
//with anchor points. This will also make rotations nice.
piece.position = CGPoint(x: squareWidth/2, y: squareWidth/2)
addChild(piece)
Hopefully that makes sense and didn't confuse things further.

Animate View with multiple arguments

I am trying to animate the opacity of a CALayer but the values are more than one value to begin with and one to end with.
For example: I want it to animate throw these values: 0.0, 0.7, 0.3, 1.0, 0.5, 0.0
I also want the animation to repeat with auto reverse.
This is what I have for now:
let redLineAnimation = CABasicAnimation(keyPath: "opacity")
redLineAnimation.duration = 0.4
redLineAnimation.autoreverses = true
redLineAnimation.fromValue = 0.0
redLineAnimation.toValue = 1.0
redLineAnimation.repeatCount = Float.infinity
movingRedLineLayer.addAnimation(redLineAnimation, forKey: nil)
I am new to iOS development. What can I do? Thanks!
Take a look at CAKeyframeAnimation. That will give you what you want.
And if what you are animating is a view's layer, you could use the easier-to-use UIView keyframe based animation methods. (Core Animation is pretty tricky, and the docs are spotty)

How do I make this animation go counterclockwise?

I have already got part of my animation completed. What I am trying to do is create the line border animation like this, where the line travels across the top and right side of the rectangle. What I need in addition to this is creating another line that mirrors the line shown in the video (traveling across the left side then bottom of the rectangle),counterclockwise. I tried doing this using the -bezierPathByReversingPath(_:) function, but it made the origin for the line begin in the bottom left of the rectangle. I am not sure what to do, any help would be appreciated!
Here’s my code:
// Create layer
rightLine = CAShapeLayer()
rightLine.bounds = centerButton.bounds
rightLine.position = view.center
rightLine.path = UIBezierPath(rect: rightLine.bounds).CGPath
rightLine.lineWidth = 3
rightLine.strokeColor = UIColor(hex: "3D424E").CGColor
rightLine.fillColor = UIColor.clearColor().CGColor
rightLine.strokeStart = 0
rightLine.strokeEnd = 0
// Create animation
let rightStart = CABasicAnimation(keyPath: "strokeStart")
rightStart.toValue = 0
let rightEnd = CABasicAnimation(keyPath: "strokeEnd")
rightEnd.toValue = 0.5
rightLineGroup = CAAnimationGroup()
rightLineGroup.animations = [rightStart, rightEnd]
rightLineGroup.duration = 1.5
rightLineGroup.autoreverses = true
rightLineGroup.repeatCount = HUGE // repeat forever
self.view.layer.addSublayer(rightLine)
rightLine.addAnimation(rightLineGroup, forKey: nil)
Animating strokeStart from zero to zero does nothing.
You probably want to leave strokeEnd at 1.0 (the default) and animate strokeStart from 1.0 to 0.5. That should have the desired effect.

CATransform3D breaks layer order?

Basically I want to build the scrollView like the one in iOS7 safari tab switcher. I use CATransform3D to get the feeling of oblique. However when I employ transform the layers just don't display in proper order.(They are in correct order before transform, seems like a total reversal in order.Back layer jumps to the front). How can I fix this thorny problem?
By the way in my case superView.bringSubViewToFront doesn't work.
My code:
//Create imageView
...
scroller.addSubview(imageView)
let layer = imageView.layer
//Set shadow
layer.shadowOpacity = 0.2
layer.shadowRadius = 2
layer.shadowOffset = CGSizeMake(6, -10)
//Transform, if without following code the layer order is correct
var transform = CATransform3DIdentity
transform.m34 = 0.0009
let radiants = 0.11*M_PI
transform = CATransform3DRotate(transform, CGFloat(radiants), 1.0, 0.0, 0.0)
scroller.bringSubviewToFront(imageView)//It doesn't work.
I just found that if you set the z axis to 1.0 in CATransform3DTranslate the layer become in front of other layers, so just make it 0.

Resources