I got a problem with the animations from CGAffineTransform... I want to move the button and scale it up at the same time.
But if I do one animation alone everything works pretty straight forward. It scales up or moves the button by 50px.
But if I put both in the same animation its messed up. The button starts from far outside the screen to move in and doesn't scale as it should.
Also, after the movement of the button. The buttons scales up to the correct size i expected, but moves back to origin position before...
What do I miss here?
let shape = CAShapeLayer()
//shape.path = UIBezierPath(arcCenter: CGPoint(x: 25, y: 25), radius: 30, startAngle: 0, endAngle: .pi/2, clockwise: true).cgPath
shape.lineWidth = 5
shape.strokeColor = UIColor.black.cgColor
timerButton.layer.addSublayer(shape)
UIView.animate(withDuration: 2, animations: {
self.timerButton.transform = CGAffineTransform(translationX: -50, y: 0)
//self.timerButton.transform = CGAffineTransform(scaleX: 3, y: 3)
}) { (true) in
UIView.animate(withDuration: 1, animations: {
self.timerButton.transform = CGAffineTransform(scaleX: 3, y: 3)
}) { (true) in
print("later")
}
}
At completion of the transform of the x position, you need to change the constraint to make it permanent, the top constraint, after that also in the completion you need to remove the x position transformation,
If you follow these steps and after them animate your new transformation it should work
Related
I want my swift code to follow the gif I created. What is going is after the first animation there is not any change of the position. Like the gif I created it should first move down then move up again. I tried multiplying by the negative -0.15 thinking it would go in the north direction. Right now it is not having a effect.
UIView.animate(withDuration: 1, animations: {
self.block1.frame = CGRect(x: 0, y: self.view.frame.height * 0.15, width: self.view.frame.width, height: self.view.frame.height * 0.1)
self.block1.center = self.view.center
}) { done in
/// first animation finished, start second
if done {
UIView.animate(withDuration: 1, animations: {
self.block1.frame = CGRect(x: 0, y: self.view.frame.height * (-0.15), width: self.view.frame.width, height: self.view.frame.height * 0.1)
self.block1.center = self.view.center
})
}
}
Start by getting rid of self.block1.center = self.view.center as you're making the centre point of the block the same as the view in both cycles.
self.view.frame.height * (-0.15) is setting an absolute position out side of the view, not sure if that's intentional, but you might want to be aware of it. Instead, if you only wanted to move a given distance, say the height of the view, you should be using a relative position, such as block1.frame.origin.y -= block.frame.size.height (probably a much easier way to say that, but you get the idea)
So I created a really quick Playground, made up some values and got a "sort of" bouncy, returny thing going as an example
import UIKit
import XCPlayground
let view = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 300.0, height: 600.0))
// XCPShowView("container", view: container) // -> deprecated
let block1 = UIView(frame: CGRect(x: 0.0, y: 0.0, width: (view.frame.width) / 2, height: 150))
block1.backgroundColor = UIColor.green
block1.center.x = view.center.x
view.addSubview(block1)
UIView.animate(withDuration: 1, animations: {
let y = view.frame.height * 0.15
print(y)
block1.center.y = view.center.y
}) { done in
print("Done = \(done)")
/// first animation finished, start second
if done {
UIView.animate(withDuration: 1, animations: {
block1.frame.origin.y = 0
})
}
}
XCPlaygroundPage.currentPage.liveView = view
Caveat: There's probably a really neat and easy way to do "auto reversal" style animations using CALayer and I'd be keen to see other examples presenting the same concept
so my Problem is, that I want to have a motion animation of a circle, which is moving from left to right. The circle is created like this:
let point = CGPoint.init(x: 10, y: 10)
let circlePath = UIBezierPath(arcCenter: point, radius: CGFloat(20), startAngle: CGFloat(0), endAngle: CGFloat(Double.pi * 2), clockwise: true)
let shapeLayer = CAShapeLayer()
shapeLayer.path = circlePath.cgPath
How do I create a motion animation of a circle like this?
I have done moving animation on imageView, You can try on any View it will work.
private func animate(_ image: UIImageView) {
UIView.animate(withDuration: 1, delay: 0, options: .curveLinear, animations: {
image.transform = CGAffineTransform(translationX: self.view.frame.width/2-10, y: 0)
}) { (success: Bool) in
image.transform = CGAffineTransform.identity
self.animate(image) // imageAnime is outlet
self.imageAnim.layer.removeAllAnimations() // if you want to remove animation after its done animating once
}
}
call this function Where you want your animation to be done
I've set up an SKScene, that I fill with a bunch of blocks. Upon touch I want to move a random block to the 0,0 position of the SKScene. 0,0 is not the final goal, but I use it for test. My node is called a bixel and I naturally thought that using SKAction.move(to: ...) would move it to an absolute coordinate, as stated in the documentation, however I must be missing something, as moving to 0,0 does nothing, however moving to say 100,0 moves it 100 pixels to the right.
I've tried all the move(to: ...) (moveTo(x: ...) etc. And I've tried move(by: ...) which works as it should.
With the code provided below, if I switch between action and action2 (by rebuilding the project), the outcome is always the same, it's moved 100 pixels to the right.
I've also tried the point convert:
let point = self.convert(CGPoint(x: 0, y: 0), to: bixel)
but the result is the same.
let bixel = bixels[Int.random(in: 0..<bixels)] bixel.fillColor = .red
let action = SKAction.move(to: CGPoint(x: 100, y: 0), duration: 0.1)
let action2 = SKAction.move(by: CGVector(dx: 100, dy: 0), duration: 0.1)
bixel.zPosition = 1000
bixel.run(action)
EDIT: You are completely correct #Martin R. And as I wrote in my comment to your answer every ShapeNode thought that their position was 0,0. It was because of how I initialised them.
I did this:
let bixel = SKShapeNode(
rect: CGRect(
x: xOffset + (xF * shapeSize.width),
y: self.size.height - yOffset - (yF * shapeSize.height),
width: shapeSize.width - space,
height: shapeSize.height - space
),
cornerRadius: 2
)
Which made it think it's position was 0,0. So when I just changed it to this:
let bixel = SKShapeNode(rectOf: shapeSize, cornerRadius: 2)
bixel.position = CGPoint(
x: xOffset + (xF * shapeSize.width),
y: self.size.height - yOffset - (yF * shapeSize.height)
)
everything worked. Thanks!
SKAction.move(to:duration:) creates an action that moves a node to the given position.
SKAction.move(to: CGPoint(x: 100, y: 0), duration: 0.1)
creates an action that moves a node to position (100, 0).
SKAction.move(by:duration:) creates an action that moves a node relative to its current position.
SKAction.move(by: CGVector(dx: 100, dy: 0), duration: 0.1)
creates an action that moves a node from its current position (x0, y0) to the new position (x0+100, y0).
Both actions have the same effect if (and only if) the current node position is (0, 0).
I have 4 playing cards on the screen. At the press of a button, I want one of the cards at random to move to the middle of the top half of the screen. Is it possible to create an instance of a constraint (eg: centerXAnchor with constant 0, and centerYAnchor with constant -200) so that I can use CGAffineTransform and move the random image to this point?
Ive tried creating an instance of a CGRect Frame:
let destination = CGPoint(x: 10, y: 10)
but this does not move evenly across devices.
An affine transformation matrix is used to rotate, scale, translate, or skew the objects you draw in a graphics context.
I don't think CGAffineTransform is the ideal thing to use for this task. You aren't doing any the above things (rotate, scale, translate, or skew).
I think you would likely be best using UIView.animateWithDuration
let cardSize = CGSize(width: 100, height: 100)
let card = UIView(frame: CGRect(origin: CGPoint(x: 0, y: 0), size: cardSize))
UIView.animate(withDuration: 1.0) {
card.frame = CGRect(origin: CGPoint(x: 100, y: 100), size: cardSize)
}
I'm trying to get a view to shrink down to nothing in the bottom right corner when its cancel button is pressed. Currently when i use:
UIView.animate(withDuration: 0.7, delay: 0, options: .beginFromCurrentState, animations: {
self.transform = CGAffineTransform(scaleX: 0.01, y: 0.01)
})
the view shrinks into its center.
I understand that I should be setting the anchor point of the layer so it shrinks to that point but when I do this it moves the view center to that point then shrinks:
UIView.animate(withDuration: 0.7, delay: 0, options: .beginFromCurrentState, animations: {
self.layer.anchorPoint = CGPoint(x: 0.0, y: 0.0)
self.transform = CGAffineTransform(scaleX: 0.01, y: 0.01)
})
Any ideas?
Edit: More info would be that im using cartography to set the view's constraints to the viewController.view edges, I'm wondering if this has something to do with it?
Frame of UIView or CALayer are actually derived properties which are calculated from layers position and anchorpoint. So, when you change either one of these, the layer frame also changes and hence you see the effect of view movement.
When you change the anchor point of the CALayer, you could also immediately set the position of the layer to compromise for the change. You can offset the view position with amount of anchor point changes relative to views bounds.
Here is a simple extension on UIView, which you could use to change the layers anchor point without actually changing the position.
extension UIView {
func set(anchorPoint: CGPoint) {
let originalPosition = CGPoint(x: frame.midX, y: frame.midY)
let width = bounds.width
let height = bounds.height
let newXPosition = originalPosition.x + (anchorPoint.x - 0.5) * width
let newYPosition = originalPosition.y + (anchorPoint.y - 0.5) * height
layer.anchorPoint = anchorPoint
layer.position = CGPoint(x: newXPosition,
y: newYPosition)
}
}
The code above changes position of CALayer as soon as you make change to the anchorPoint. You could as well do this without having to change the position.
Whenever you set the anchorPoint, simply set back previous frame,
let originalFrame = view.frame
view.layer.anchorPoint = CGPoint(x: 0, y: 0)
view.frame = originalFrame
This will prevent your view from moving and stay at the same position, despite of the change in anchorPoint.
Also, for your particular case, setting anchorPoint to layer triggers intrinsic animation. So, if you want to make it like shrink to lower or upper edge, perform anchorPoint change before animation like so,
UIView.performWithoutAnimation {
self.set(anchorPoint: CGPoint(x: 0, y: 0))
}
UIView.animate(withDuration: 0.7, delay: 0, options: .beginFromCurrentState, animations: {
self.transform = CGAffineTransform(scaleX: 0.01, y: 0.01)
})