Let's say I scale a UILabel using a CGAffineTransformScale like so:
let scale = 0.5
text = UILabel(frame: CGRectMake(100, 100, 100, 100))
text.text = "Test"
UIView.animateWithDuration(2.0, delay: 0.0, options: UIViewAnimationOptions.CurveEaseIn, animations: {
self.text.transform = CGAffineTransformScale(self.text.transform, scale, scale)
}, completion: {(value : Bool) in
print("Animation finished")
})
This works great when I want to scale the UILabel by half. But if I were to call this same code again, it would end up with a scale of 0.25, as it scales again by half.
Would it be possible to use the CGAffineTransformScale to always scale to a size of half the original UILabel frame, instead of a scaling it cumulatively?
Swift 3:
text.transform = CGAffineTransform.identity
UIView.animate(withDuration: 0.25, animations: {
self.text.transform = CGAffineTransform(scaleX: scale, y: scale)
})
You are scaling the existing transform. Just create a new transform:
self.text.transform = CGAffineTransformMakeScale(scale, scale)
Swift version:
self.text.transform = CGAffineTransform.init(scaleX: scaleFactorX, y: scaleFactorY)
Related
For my personal project, I try to make an image of a leaf go up for 5 seconds and then lower it for 5 seconds.
It works well, only after raising and lowering the sheet, it changes position and is transported well to the bottom of the screen.
I would like it to go up from the place of the end of the descent. I have tried several things but nothing works. I wish I could restore the original position after descent but I think it will make the image flicker on the screen.
Any ideas ?
func upLeaf(){
let xPosition = imageLeaf.frame.origin.x
let yPosition = imageLeaf.frame.origin.y - 100 // Slide Up - 20px
let width = imageLeaf.frame.size.width
let height = imageLeaf.frame.size.height
UIView.animate(withDuration: 5.0, animations: {
self.imageLeaf.frame = CGRect(x: xPosition, y: yPosition, width: width, height: height)
})
}
func downLeaf(){
let xPosition = imageLeaf.frame.origin.x
let yPosition = imageLeaf.frame.origin.y + 100 // Slide Up - 20px
let width = imageLeaf.frame.size.width
let height = imageLeaf.frame.size.height
UIView.animate(withDuration: 5.0, animations: {
self.imageLeaf.frame = CGRect(x: xPosition, y: yPosition, width: width, height: height)
})
secondUp = true
}
Instead of using frame to perform the animation you could use CGAffineTransform. It's more powerful and equally easy:
UIView.animate(withDuration: 5.0, animations: {
line3.transform = CGAffineTransform(translationX: 0, y: 100)
})
When you want to return to the initial state you should type:
UIView.animate(withDuration: 5.0, animations: {
line3.transform = CGAffineTransform.identity
})
CGAffineTransform remembers the initial parameters for your view, so that you don't have to do any calculations yourself.
If you only want the leaf to go up and down, you can just animate imageLeaf's frame.origin.y (no need to keep track of origin.x, width, or height).
func upDownLeaf() {
UIView.animate(withDuration: 5.0, animations: {
self.imageLeaf.frame.origin.y -= 100 /// go up
}) { _ in
UIView.animate(withDuration: 5.0, animations: {
self.imageLeaf.frame.origin.y += 100 /// go down
})
}
}
I have two shortcuts.
Each of them has initial properties, such as font size and opacity.
Before animation, the values are
labelOne.font = labelOne.font.withSize(109)
labelOne.alpha = 1.0
labelTwo.font = labelTwo.font.withSize(40)
labelTwo.alpha = 0.7
After the animation, they should have these properties:
labelOne.font = labelOne.font.withSize(40)
labelOne.alpha = 0.7
labelTwo.font = labelTwo.font.withSize(109)
labelTwo.alpha = 1.0
For transformation, I use CGAffineTransform()
example:
labelOne.transform = CGAffineTransform(scaleX: //some value? , y: //someValue?)
But I'm new to programming and don't quite understand how it works. Tell me how to write this animation with font size resizing?
To achieve an animation on both labels, as you wished by controlling font size and alpha, you could achieve it like this:
// Before animation
labelOne.font = labelOne.font.withSize(109)
labelOne.alpha = 1.0
labelTwo.font = labelTwo.font.withSize(40)
labelTwo.alpha = 0.7
UIView.animate(withDuration: 1, animations: {
// This will be values set during the animation of 1 second
self.labelOne.font = labelOne.font.withSize(40)
self.labelOne.alpha = 0.7
self.labelTwo.font = labelTwo.font.withSize(109)
self.labelTwo.alpha = 1.0
})
Meanwhile, CGAffineTransform(scaleX:, y:) lets you scale the X,Y coordinates value of your view (labelOne for example), but to make that happen with an animation you have to put that inside the animate block:
// normal state
labelOne.transform = CGAffineTransform(scaleX: 1, y: 1)
labelTwo.transform = CGAffineTransform(scaleX: 0.7, y: 0.7)
UIView.animate(withDuration: 1, animations: {
// State reached through the animation
labelOne.transform = CGAffineTransform(scaleX: 0.7, y: 0.7)
labelTwo.transform = CGAffineTransform(scaleX: 1, y: 1)
})
To achieve a smoother transition play with:
UIView.transition(with: labelOne, duration: 0.25, options: .transitionCrossDissolve, animations: {
self.labelOne.font = UIFont.systemFont(ofSize: 40)
}) { _ in }
I want to create similar animation for all screen sizes. I have UIView in center and want to move at right and distance between UIView and screen should be = 10.
How to do it?
My code for one screen(iPhone 5s):
UIView.animate(withDuration: 1.0, delay: 0.0, options: [], animations: {
self.imageRevealed.center.x += 200
})
in this case distance between UIView and screen = 10. But for other screens I should write new code with new coordinates. How to write universal code?
I think you need the trailing space between image view and view of 10px.
let trailingSpace: CGFloat = 10
UIView.animate(withDuration: 1.0, delay: 0.0, options: [], animations: {
let screenSize = UIScreen.main.bounds.size
let x = screenSize.width - self.imageRevealed.frame.size.width - trailingSpace
let y = self.imageRevealed.frame.origin.y
self.imageRevealed.frame = CGRect(x: x, y: y, width: self.imageRevealed.frame.size.width, height: self.imageRevealed.frame.size.height)
})
I'm trying to create a Star Wars style scrolling text for a viewController.
I have the following code:
override func viewDidAppear(_ animated: Bool) {
UIView.animate(withDuration: 60.0, animations: {
let width = self.scrollingTextLabel.frame.width
let height = self.scrollingTextLabel.frame.height
self.scrollingTextLabel.frame = CGRect(x: 5, y: (180 - height), width: width, height: height)
}, completion:{ _ in
self.buttonTextLabel.text = "Play"
})
}
It works perfectly except for 1 thing, it speeds up, then slows down. This means, it's hard to read at the mid point. Is there a way to make the speed constant?
Pass in .curveLinear in the animation options to make the animation have a constant velocity:
UIView.animate(withDuration: 60.0, delay: 0.0, options: .curveLinear, animations: {
let width = self.scrollingTextLabel.frame.width
let height = self.scrollingTextLabel.frame.height
self.scrollingTextLabel.frame = CGRect(x: 5, y: (180 - height), width: width, height: height)
}, completion:{ _ in
self.buttonTextLabel.text = "Play"
})
I try to animate an UILabel:
let label: UILabel = UILabel()
var transform = CGAffineTransform.identity
UIView.animate(withDuration: 2, animations: {
self.transform = self.transform.translatedBy(x: 0, y: -150)
self.transform = self.transform.scaledBy(x: 2, y: 2)
self.label.transform = self.transform
})
It works well. I have a button and I added a selector so that when it is pressed, the label animates again:
func performSearch() {
UIView.animate(withDuration: 2, animations: {
self.transform = self.transform.translatedBy(x: 0, y: -300)
self.label.transform = self.transform
})
}
But what it actually does is to scale down the label as in the original state, move it down to the bottom of the screen, then animate, although all I want it to do is to move up. Why?
Transforms can be a little confusing... Because you have scaled the transform, further transforms seem to be "auto-adjusted" by the scaling, but not in a really intuitive or obvious way.
This may help you get further:
// initial animation
UIView.animate(withDuration: 2, animations: {
self.transform = self.transform.translatedBy(x: 0, y: -150)
self.transform = self.transform.scaledBy(x: 2, y: 2)
self.label.transform = self.transform
})
// "resuming" animation
func performSearch() {
let distance: CGFloat = -300 / 2
self.transform = self.transform.translatedBy(x: 0, y: distance)
self.label.transform = self.transform
UIView.animate(withDuration: 2, animations: {
self.transform = self.transform.translatedBy(x: 0, y: distance)
self.label.transform = self.transform
})
}
You can flip the coordinate system to behave like iOS like this...
layer.sublayerTransform = CATransform3DMakeScale(1.0f, -1.0f, 1.0f);
If you want to move it up in performSearch() you need to use a positive y value:
self.transform = self.transform.translatedBy(x: 0, y: 300)