I have a UIView, placed in the middle of the screen. When the user presses a button, I want it to move up near top of the screen as it shrinks to about a fifth of its size.
I have tried this:
UIView.animateWithDuration(0.7) { () -> Void in
self.main.transform = CGAffineTransformMakeScale(0.2, 0.2)
self.main.transform = CGAffineTransformMakeTranslation(0, -250)
}
But for some reason, that only scales the view. I have also tried to put this in the animateWithDuration:
self.main.frame = CGRectMake(self.view.frame.width/2 - 10, 50, 50, 50)
How can I get both animations to work?
Joe's answer above does exactly as his GIF describes but it doesn't really answer your question since it translates then scales the view (as opposed to both translating and scaling at the same time). Your issue is that you're setting the view's transform in your animation block then immediately overwritting that value with another transform. To achieve both translation and scale at the same time, you'll want something like this:
#IBAction func animateButton(_ sender: UIButton) {
let originalTransform = self.main.transform
let scaledTransform = originalTransform.scaledBy(x: 0.2, y: 0.2)
let scaledAndTranslatedTransform = scaledTransform.translatedBy(x: 0.0, y: -250.0)
UIView.animate(withDuration: 0.7, animations: {
self.main.transform = scaledAndTranslatedTransform
})
}
You can achieve basic UIView animation in several ways in Swift. Below code is just a simple approach...
class ViewController: UIViewController {
#IBOutlet weak var animationButton: UIButton!
#IBOutlet weak var myView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
// I added seperate colour to UIView when animation move from one animation block to another.So, You can get better understanding how the sequence of animation works.
self.myView.backgroundColor = .red
}
#IBAction func animateButton(_ sender: UIButton) {
UIView.animate(withDuration: 0.5, delay: 0.0, options: UIViewAnimationOptions.curveEaseIn, animations: {
//Frame Option 1:
self.myView.frame = CGRect(x: self.myView.frame.origin.x, y: 20, width: self.myView.frame.width, height: self.myView.frame.height)
//Frame Option 2:
//self.myView.center = CGPoint(x: self.view.frame.width / 2, y: self.view.frame.height / 4)
self.myView.backgroundColor = .blue
},completion: { finish in
UIView.animate(withDuration: 1, delay: 0.25,options: UIViewAnimationOptions.curveEaseOut,animations: {
self.myView.backgroundColor = .orange
self.myView.transform = CGAffineTransform(scaleX: 0.25, y: 0.25)
self.animationButton.isEnabled = false // If you want to restrict the button not to repeat animation..You can enable by setting into true
},completion: nil)})
}
}
Output:
Related
Is it possible to Increase the size of the indicator in UIPageViewController?
I have this:
And my requirement is this:
Scaling the page control will scale the dots, but will also scale the spacing in between them.
pageControl.transform = CGAffineTransform(scaleX: 2, y: 2)
If you want to keep the same spacing between dots, you'll need to transform the dots individually:
pageControl.subviews.forEach {
$0.transform = CGAffineTransform(scaleX: 2, y: 2)
}
However, if you do this in viewDidLoad, the transform has been reset by the time the view appears, so you should do this in viewDidLayoutSubviews…
override func viewDidLayoutSubviews() {
pageControl.subviews.forEach {
$0.transform = CGAffineTransform(scaleX: 2, y: 2)
}
}
You can use an UIPageControl and scale it like this :
#IBOutlet weak var pageControl: UIPageControl!
override func viewDidLoad() {
super.viewDidLoad()
pageControl.transform = CGAffineTransform(scaleX: 2, y: 2); //set value here
}
The problem with that is that you space between your dots will be increase too. If you want to have an accurate design with your dot you have to use 3party controls : https://www.cocoacontrols.com/
For swift 2.0 to increase or decrease size of pageControl Indicator
self.pageControl.transform = CGAffineTransformMakeScale(0.8, 0.8)
OR
self.pageControl.transform = CGAffineTransformMakeScale(1.3, 1.3)
Swift 4, 4.2 and 5
First create an outlet of page control
#IBOutlet weak var pageControl: UIPageControl!
If you want to keep the original spacing.
override func viewDidLayoutSubviews() {
pageControl.transform = CGAffineTransform(scaleX: 2, y: 2)
}
If you don't want to keep the original spacing.
override func viewDidLayoutSubviews() {
pageControl.subviews.forEach {
$0.transform = CGAffineTransform(scaleX: 2, y: 2)
}
}
Firstly, create an uiPageControl object inside inside viewDidLoad() and then set it's y position as per your requirement then apply required scale using CAAffiniteTransform as below:
var pageControl = UIPageControl()
pageControl.pageIndicatorTintColor = UIColor.gray
pageControl.currentPageIndicatorTintColor = UIColor.yellow
pageControl.transform = CGAffineTransform(scaleX: 1.3, y: 1.3) // set dot scale of pageControl
pageControl.backgroundColor = UIColor.darkGray
pageControl.numberOfPages = 3
pageControl.center = self.view.center
self.view.addSubview(pageControl) // add pageControl to view
pageControl.layer.position.y = self.view.frame.height - 100; // y position of the pageControl
Add an extension to the page controller
extension UIPageControl {
func customPageControl(dotWidth: CGFloat) {
for (pageIndex, dotView) in self.subviews.enumerated() {
dotView.frame.size = CGSize.init(width: dotWidth, height: dotWidth)
}
}
}
I want an image of a red circle to move from the top of the screen to the bottom, as though it were a meteor traveling across the screen. It does move, but it moves in a millisecond with no animation. How do I make it move smoothly like a meteor with a tail?
class ViewController: UIViewController {
let meteorImage = UIImageView()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.meteorImage.backgroundColor = .clear
self.meteorImage.frame = CGRect(x: 50, y: 0, width: 50, height: 50)
self.meteorImage.isHidden = false
self.meteorImage.image = #imageLiteral(resourceName: "success")
view.addSubview(self.meteorImage)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func buttonPressed(_ sender: UIButton) {
let totalHeight = Int(view.frame.height)
var currentHeight = 0
while currentHeight < totalHeight {
self.meteorImage.backgroundColor = .clear
self.meteorImage.frame = CGRect(x: 50, y: currentHeight, width: 50, height: 50)
self.meteorImage.isHidden = false
self.meteorImage.image = #imageLiteral(resourceName: "success")
view.addSubview(self.meteorImage)
currentHeight += 1
}
}
}
Animations have to be done with an animate call.
This is a simple way to do an animation.
UIView.animate(withDuration: 0.1, animations: {
// Apply Changes to frame.
})
Consider also: instead of doing the while loop simply set the final location of the meteor's frame and set the duration to the amount of time you want it to take.
let newFrame= CGRect(x: 50,
y: currentHeight,
width: 50,
height: 50)
UIView.animate(withDuration: 3.0, animations: {
// Apply Changes to frame.
self.meteorImage.frame = newFrame
})
If you want to make your animation more complicated you can look into keyframe animations on a UIView.
Is it possible to Increase the size of the indicator in UIPageViewController?
I have this:
And my requirement is this:
Scaling the page control will scale the dots, but will also scale the spacing in between them.
pageControl.transform = CGAffineTransform(scaleX: 2, y: 2)
If you want to keep the same spacing between dots, you'll need to transform the dots individually:
pageControl.subviews.forEach {
$0.transform = CGAffineTransform(scaleX: 2, y: 2)
}
However, if you do this in viewDidLoad, the transform has been reset by the time the view appears, so you should do this in viewDidLayoutSubviews…
override func viewDidLayoutSubviews() {
pageControl.subviews.forEach {
$0.transform = CGAffineTransform(scaleX: 2, y: 2)
}
}
You can use an UIPageControl and scale it like this :
#IBOutlet weak var pageControl: UIPageControl!
override func viewDidLoad() {
super.viewDidLoad()
pageControl.transform = CGAffineTransform(scaleX: 2, y: 2); //set value here
}
The problem with that is that you space between your dots will be increase too. If you want to have an accurate design with your dot you have to use 3party controls : https://www.cocoacontrols.com/
For swift 2.0 to increase or decrease size of pageControl Indicator
self.pageControl.transform = CGAffineTransformMakeScale(0.8, 0.8)
OR
self.pageControl.transform = CGAffineTransformMakeScale(1.3, 1.3)
Swift 4, 4.2 and 5
First create an outlet of page control
#IBOutlet weak var pageControl: UIPageControl!
If you want to keep the original spacing.
override func viewDidLayoutSubviews() {
pageControl.transform = CGAffineTransform(scaleX: 2, y: 2)
}
If you don't want to keep the original spacing.
override func viewDidLayoutSubviews() {
pageControl.subviews.forEach {
$0.transform = CGAffineTransform(scaleX: 2, y: 2)
}
}
Firstly, create an uiPageControl object inside inside viewDidLoad() and then set it's y position as per your requirement then apply required scale using CAAffiniteTransform as below:
var pageControl = UIPageControl()
pageControl.pageIndicatorTintColor = UIColor.gray
pageControl.currentPageIndicatorTintColor = UIColor.yellow
pageControl.transform = CGAffineTransform(scaleX: 1.3, y: 1.3) // set dot scale of pageControl
pageControl.backgroundColor = UIColor.darkGray
pageControl.numberOfPages = 3
pageControl.center = self.view.center
self.view.addSubview(pageControl) // add pageControl to view
pageControl.layer.position.y = self.view.frame.height - 100; // y position of the pageControl
Add an extension to the page controller
extension UIPageControl {
func customPageControl(dotWidth: CGFloat) {
for (pageIndex, dotView) in self.subviews.enumerated() {
dotView.frame.size = CGSize.init(width: dotWidth, height: dotWidth)
}
}
}
So ive been doing some reading about UIViewPropertyAnimator and in the examples I've been looking at, they do something like this:
animator = UIViewPropertyAnimator(duration: 2.0, curve: .easeInOut, animations: {
[unowned self, redBox] in
redBox.center.x = self.view.frame.width
redBox.transform = CGAffineTransform(rotationAngle: CGFloat.pi).scaledBy(x: 0.001, y: 0.001)
})
I dont understand the '[unowned self, redBox] in' part of it. Can anyone explain what we use it for?
I know that unowned is usually used to decide how the reference count is determined and that it can not be set to nil as the references are going to not exist one without the other(as an alternative to weak), but i dont understand the use here, and I dont understand the bracket part. It looks to me to be an array of the item im animating and the view its located in?
Full code is as follows:
import UIKit
class ViewController: UIViewController {
var animator: UIViewPropertyAnimator!
override func viewDidLoad() {
super.viewDidLoad()
//redBox
let redBox = UIView(frame: CGRect(x: 10, y: 100, width: 100, height: 100))
redBox.translatesAutoresizingMaskIntoConstraints = false// lar oss redigere posisjon og sånn selv, uten at xcode setter posisjon/størrelse i stein.
redBox.backgroundColor = .red
redBox.center.y = view.center.y
view.addSubview(redBox)
animator = UIViewPropertyAnimator(duration: 2.0, curve: .easeInOut, animations: {
[unowned self, redBox] in
redBox.center.x = self.view.frame.width
redBox.transform = CGAffineTransform(rotationAngle: CGFloat.pi).scaledBy(x: 0.001, y: 0.001)
})
// slider
let slider = UISlider()
slider.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(slider)
slider.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
slider.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
slider.addTarget(self, action: #selector(sliderChanged), for: .valueChanged)
}
func sliderChanged(_ sender: UISlider){
animator.fractionComplete = CGFloat(sender.value)
}
}
We need to use either weak or unowned otherwise an ownership (reference) cycle would be created (self => animator => animations => self).
We can use unowned instead of weak because we can be sure that self and animator are destroyed together and when self is deallocated, the animations won't be running any more.
I have a uilabel with a text suppose " This is a label." I want this label to be displayed one word at a time flying from outside screen to the UILable position..
something like
label.
a label.
is a label.
This is a label.
How can i get such animation
I have found a way to do as i wished.
class ViewController: UIViewController {
#IBOutlet var sampleLabel: UILabel!
var slogan = "This is a slogan."
var xdir = 250
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
let sampleLabelFrame = sampleLabel.frame
let ypos = sampleLabelFrame.origin.y
var sloganArray = slogan.componentsSeparatedByString(" ")
sloganArray = sloganArray.reverse()
var i = 0.0
for word in sloganArray{
let label: UILabel = UILabel(frame: CGRect(x: -100, y:ypos, width: 60, height: 20))
label.text = word
view.addSubview(label)
let width = label.intrinsicContentSize().width
var labelFramewidth = label.frame
labelFramewidth.size.width = width
label.frame = labelFramewidth
self.xdir = self.xdir - Int(width)-4
UIView.animateWithDuration(0.7, delay: i, options: .CurveEaseOut, animations: {
var labelframe = label.frame
labelframe.origin.x = CGFloat(self.xdir)
label.frame = labelframe
}, completion: { finished in
})
i+=0.5
}
}
}
hope this helps others in need of something like this.
This might help you on the way to achieve what you want:
http://helpmecodeswift.com/animation/creating-animated-labels
EDIT:
Flying label code:
meme1.text = "Brace yourself!"
meme1.font = UIFont.systemFontOfSize(25)
meme1.textColor = UIColor.whiteColor() // This is all just styling the Label
meme1.sizeToFit()
meme1.center = CGPoint(x: 200, y: -50) // Starting position of the label
view.addSubview(meme1) // Important! Adding the Label to the Subview, so we can actually see it.
//Animation options. Play around with it.
UIView.animateWithDuration(0.9, delay: 0.0, usingSpringWithDamping: 0.3, initialSpringVelocity: 0.0, options: .CurveLinear, animations: {
self.meme1.center = CGPoint(x: 200, y:50 ) // Ending position of the Label
}, completion: nil)
UIView.animateWithDuration(0.6, delay: 1.1, usingSpringWithDamping: 0.3, initialSpringVelocity: 0.0, options: .CurveLinear, animations: {
self.meme1.center = CGPoint(x: 200, y:150+90 )
}, completion: nil)
I have no experience with it myself, but it seems to get your job done. You can eventually se the starting point outside the screen and let it animate over to the screen. That should give the desired effect.