I have set up 2 text by autolayouts and constraints. Now i would like to move text 1 to the right and text 2 to the left but it didnt work. Both the text should end on the center of the view (horizontally)
#IBOutlet weak var Text1: UILabel!
#IBOutlet weak var Text2: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
UIView.animate(withDuration: 5, delay: 0, options: [.beginFromCurrentState],
animations: {
self.Text1.frame.origin.x += 300
self.Text2.frame.origin.x -= 300
self.view.layoutIfNeeded()
}, completion: nil)
// Do any additional setup after loading the view.
}
Did i did anything wrong in the code?
You can use CGAffineTransform(translationX: y:) to achieve something like that:
UIView.animate(withDuration: 1.0, delay: 0.0, options: .curveEaseInOut, animations: {
self.breathingStatusLabel.transform = CGAffineTransform(translationX: self.breathingStatusLabel.bounds.origin.x + 300, y: self.breathingStatusLabel.bounds.origin.y)
}, completion: nil)
To move it back to it's originial place add this in another animation block:
self.breathingStatusLabel.transform = .identity
Note: CGAffineTransform(translationX: y:) translates the view to a provided location but it doesn't change the frame of that. So be careful about that while using this, to just animate labels you can use it.
But for example you want to move UITextFields when keyboard appears, you should change the constraints if its autolayout or change its frame. If you use CGAffineTransform(translationX: y:) you will notice that it will move the UITextField but when you tap on it nothing will work coz its frame didn't change, just the location changed.
You need to play with constraints of labels.
UIView.animate(withDuration: 5, delay: 0, options: [.beginFromCurrentState],
animations: {
//here modify constraints constant or may need to add some new constraints dynamically.
// self.Text1.frame.origin.x += 300
// self.Text2.frame.origin.x -= 300
// self.view.layoutIfNeeded()
}, completion: nil)
#IBOutlet weak var Text1: UILabel!
#IBOutlet var Text1LeftConstrin:NSLayoutConstraint!
// (Connect this with your Text1 label left constrain)
#IBOutlet weak var Text2: UILabel!
#IBOutlet var Text1LeftConstrin:NSLayoutConstraint!
//(Connect this with your Text1 label left constrain)
override func viewDidLoad() {
super.viewDidLoad()
UIView.animate(withDuration: 5, delay: 0, options: [.beginFromCurrentState],
animations: {
Text1.constant += 300
Text2.constant -= 300
self.view.layoutIfNeeded()
}, completion: nil)
// Do any additional setup after loading the view.
}
UIView.transition(with:self.yourLabel,
duration: 5.0,
options: [.autoreverse,.repeat],
animations: {
self.yourLabel.transform = CGAffineTransform(translationX: (-1 * (self.view.frame.size.width / 2) + 20), y:0)
self.yourLabel.transform = CGAffineTransform(translationX: ((self.view.frame.size.width / 2) - 20), y:0)
},
completion: nil)
Related
I'm trying to add a scale-up animation to the marker using UIView.animate() but it doesn't seem to work. Here's what I did...
class BubbleMarkerView: MarkerView {
#IBOutlet weak var gradeLabel: UILabel!
#IBOutlet weak var bubbleView: UIView!
override open func awakeFromNib() {
self.offset.x = -self.frame.size.width / 2.0
self.offset.y = -self.frame.size.height + 1.0
}
override func layoutIfNeeded() {
super.layoutIfNeeded()
bubbleView.transform = CGAffineTransform(scaleX: 0, y: 0)
UIView.animate(withDuration: 1, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: [], animations: { [weak self] in
self?.bubbleView.transform = .identity
}, completion: nil)
}
override func refreshContent(entry: ChartDataEntry, highlight: Highlight) {
gradeLabel.text = "\(Int(entry.y))"
layoutIfNeeded()
}
}
also I'm loading this marker from a xib like this:
let marker = BubbleMarkerView.viewFromXib() as! BubbleMarkerView
marker.chartView = self
self.marker = marker
Also tried adding CABasicAnimation to bubbleView but no luck
Anyone else faced this issue? Please advice...
I am trying to make an animation of a UILabel from off the screen to the center with a spring animation block. The first part of my code below in viewDidLoad() works perfectly, but when I add the animation block, it's like the code in the closure of the animation gets read first and it doesn't animate because the label is already in the place where I want the label to animate to. I have also tried to put this exact code in viewDidAppear() but the same thing happens.
#IBOutlet weak var follow: UILabel!
#IBOutlet weak var followX: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
self.follow.translatesAutoresizingMaskIntoConstraints = false
self.followX.constant = self.view.frame.width/2 + follow.frame.width/2
self.view.layoutIfNeeded()
UIView.animate(withDuration: 2, delay: 2, usingSpringWithDamping: 5, initialSpringVelocity: 5, options: .curveEaseOut, animations: {
self.followX.constant = 0
self.view.layoutIfNeeded()
})
}
This is the correct way to do it (although I would try to move that animation to viewWillAppear or viewDidAppear):
#IBOutlet weak var follow: UILabel!
#IBOutlet weak var followX: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
self.follow.translatesAutoresizingMaskIntoConstraints = false
self.followX.constant = self.view.frame.width/2 + follow.frame.width/2
self.view.layoutIfNeeded()
self.followX.constant = 0
self.view.setNeedsLayout()
UIView.animate(withDuration: 2, delay: 2, usingSpringWithDamping: 5, initialSpringVelocity: 5, options: .curveEaseOut, animations: {
self.view.layoutIfNeeded()
})
}
To work with autolayout:
You first make sure the to begin animation is ready:
self.followX.constant = self.view.frame.width/2 + follow.frame.width/2
self.view.layoutIfNeeded()
Then you change constraints to the final state of the animation:
self.followX.constant = 0
Then you tell autolayout that the constraints were changed:
self.view.setNeedsLayout()
And finally you call UIView.animate with layoutIfNeeded() to animate the change:
UIView.animate(withDuration: 2, delay: 2, usingSpringWithDamping: 5, initialSpringVelocity: 5, options: .curveEaseOut, animations: {
self.view.layoutIfNeeded()
})
override func viewDidLayoutSubviews() {
if(once)
{
once = false
self.follow.translatesAutoresizingMaskIntoConstraints = false
self.followX.constant = self.view.frame.width/2 + follow.frame.width/2
self.view.layoutIfNeeded()
}
}
and in viewDidAppear
override func viewDidAppear() {
self.followX.constant = 0
UIView.animate(withDuration: 2, delay: 0, usingSpringWithDamping: 5, initialSpringVelocity: 5, options: .curveEaseOut, animations: {
self.view.layoutIfNeeded()
})
}
I am using CGAffineTransform to shrink a button on click. The button shrinks in width but from the left and right meeting in the middle. However, I want the button to shrink from the right. A weird analogy is like the button is a piece of paper, and I lit the right side with a match and it burns the paper, destroying it towards the left. How would I achieve such functionality?
func animateStartButtonExit() {
UIView.animate(withDuration: 0.5,
animations: {
self.startButtonOutlet.transform = CGAffineTransform(scaleX: 0.1, y: 1)
},
completion: { _ in
UIView.animate(withDuration: 0.5) {
self.startButtonOutlet.isHidden = true
self.startButtonOutlet.transform = CGAffineTransform.identity
}
})
}
It works fine. I have just tested.
class ViewController: UIViewController {
#IBOutlet weak var button: UIButton!
#IBOutlet weak var widthConstraint: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func click(_ sender: UIButton) {
widthConstraint.constant = 10
UIView.animate(withDuration: 5, animations: {
self.view.layoutIfNeeded()
}, completion: { _ in
self.button.isHidden = true
// For example, back to the normal width
self.widthConstraint.constant = 168
})
}
}
You can try this which is animate the button from Right,
UIView.animate(withDuration: 5,
animations: {
button.frame = CGRect(x: button.frame.minX, y: button.frame.minY, width: button.frame.size.width / 2, height: button.frame.size.height / 2)
},
completion: { _ in
UIView.animate(withDuration: 0.5) {
button.isHidden = true
button.frame = CGRect(x: button.frame.minX, y: button.frame.minY, width: button.frame.size.width * 2, height: button.frame.size.height * 2)
}
})
If you are using autolayout, set the trailing space constraint to add up to make the width 0.
ie
rightConstraint.constant = rightConstraint.constant + width
UIView.animate(withDuration: 5,
animations: {
button.layoutIfNeeded()
},
completion: nil
)
I've been learning swift for one week now and after creating my first app which uses Weather API I wanted to create simple animation: there is an image in LaunchScreen.storyboard with background and I wanted to animate the image to shrink to 0 so that my other ViewController would appear as normal. I've made something like this but there is problem, after the animation is finished Main ViewController appears and there is no animation inside of it. Moreover I wanted this image to increase its size slightly and then shrink - maybe there is another way to do it in one UIView.animate?
afterLaunchVC.swift:
class afterLaunchVC: UIViewController {
#IBOutlet weak var logoImg: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
//increasing the size
UIView.animate(withDuration: 0.1, animations: ({
self.logoImg.transform = CGAffineTransform(scaleX: 1.05, y: 1.05)
}), completion: nil)
//shrinking the image after increasing
UIView.animate(withDuration: 1.0, animations: ({
self.logoImg.transform = CGAffineTransform(scaleX: 0.00001, y: 0.00001)
}), completion:{ //performingSegue
finished in self.performSegue(withIdentifier: "MainVC", sender: self)
})
}
}
As you can see as a UIView.animate completion I've set performSegue and turned off the animation of it but can't get animation in MainVC.swift (IBOutlets appear normally but without animation)
MainVC.swift:
class MainVC: UIViewController {
#IBOutlet weak var topLabel: UILabel!
#IBOutlet weak var hourLabel: UILabel!
#IBOutlet weak var remainingTimeLabel: UILabel!
var time : TimeTrack!
override func viewDidLoad() {
super.viewDidLoad()
topLabel.center.y = self.view.frame.height + 100
hourLabel.center.y = self.view.frame.height + 100
remainingTimeLabel.center.y = self.view.frame.height + 100
UIView.animate(withDuration: 2.6, delay: 0.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 3.0, options: [], animations: ({
self.topLabel.center.y = self.view.frame.height/2
}), completion: nil)
UIView.animate(withDuration: 2.5, delay: 0.1, usingSpringWithDamping: 1.0, initialSpringVelocity: 3.0, options: [], animations: ({
self.hourLabel.center.y = self.view.frame.height/2
}), completion: nil)
UIView.animate(withDuration: 2.4, delay: 0.2, usingSpringWithDamping: 1.0, initialSpringVelocity: 3.0, options: [], animations: ({
self.remainingTimeLabel.center.y = self.view.frame.height/2 - 30
}), completion: nil)
// further code...
}
I hope someone will explain what's wrong here because I'm new to this language
I suggest you familiarize yourself with the UIViewController lifecycle.
It would be best to put these animations in viewDidAppear because of this.
When the view APPEARS, you want these things to animate, there is a difference between when a view loads and when it appears.
View did Load: When a view loads into memory
View did appear: Called after view did load (and after viewWillAppear, Right when the view appears on screen
I have an IBOutlet Collection of buttons that I am trying to present on screen sequentially. They all start off screen fine, but as they animate in, I'd like each button to be presented on screen 0.05 seconds after the previous button. I can't figure out how to increment the delay in UIView.animateWithDuration. With the code below, they are all animating on screen at the same time.
//outlet collection
#IBOutlet var options: [UIButton]!
let increment = 0.25
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
for button in options {
button.center.y += view.bounds.height
}
}
override func viewDidLoad() {
super.viewDidLoad()
for button in options {
UIView.animateWithDuration(1.0, delay: increment, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.0, options: nil, animations: {
button.center.y -= self.view.bounds.height
self.increment + 0.05
}, completion: nil)
}
}
for button in options {
UIView.animateWithDuration(1.0, delay: increment, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.0, options: nil, animations: {
button.center.y -= self.view.bounds.height
}, completion: nil)
}
increment = increment + 0.05
}
Besides:
Change this
let increment = 0.25
To
var increment = 0.25
Increase the increment outside animation. Because animateWithDuration is an async method,it will return first. So,all your button have same delay.
#IBOutlet weak var button1: UIButton!
#IBOutlet weak var button2: UIButton!
#IBOutlet var options: [UIButton]!
let increment = 0.25
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
for button in options {
button.center.y += view.bounds.height
}
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
for button in options {
UIView.animateWithDuration(1.0, delay: increment, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.0, options: nil, animations: {
button.center.y -= self.view.bounds.height
}, completion: nil)
self.increment + 0.05
}
}
Also getting error "Cannot invoke '+=' with an argument list of type '(Double, FloatLiteralConvertible)'" when using += but it will take just +
Here is the way to achieve the required delay between animations:
var i! as UInt64;
i = 0
for button in options {
// your animation with delay
UIView.animateWithDuration(1.0, delay: (i *0.05), usingSpringWithDamping: 0.7, initialSpringVelocity: 0.0, options: nil, animations: {
button.center.y -= self.view.bounds.height
}, completion: nil)
})
++i
}