I'm trying to animate 3 squares from the left to the right. The squares should reappear from the left while they are disappearing from the right. The idea is that the squares represent clouds, so the idea is clear.
This is what I currently got:
class ViewController: UIViewController {
// contains the squares
#IBOutlet weak var containerView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
startAnimating()
}
func startAnimating() {
UIView.animateKeyframes(withDuration: 10, delay: 0, options: .repeat, animations: {
self.containerView.center.x += self.view.bounds.width
}, completion: nil)
}
}
This is the result:
The squares move from the left to the right. This is good. The problem is that once the animation is done the squares just reappear from where they started. It isn't a smooth continuous movement where the clouds move from the left to the right and slowly reappear from the left again. How do you achieve this? Should there be a second containerView? Or remove the containerView and animate individual clouds?
The answer Milan Nosáľ solved the problem. A note though:
I placed containerView2 above containerView1 with the exact same
constraints (they overlap each other). Just make sure IB doesn't
include containerView2 as a subview of containerView1.
I guess the simplest approach for you know is to use two exactly the same containers. You can use exactly the same code, and put the containerView2 above containerView to make sure that cover one another.
And then simply use:
class ViewController: UIViewController {
// contains the squares
#IBOutlet weak var containerView: UIView!
#IBOutlet weak var containerView2: UIView!
override func viewDidLoad() {
super.viewDidLoad()
// this will transform the containerView2 to the left to appear as if it was exactly to the left of the containerView
containerView2.transform = CGAffineTransform.identity.translatedBy(x: -self.view.bounds.width, y: 0)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
startAnimating()
}
func startAnimating() {
UIView.animateKeyframes(withDuration: 10, delay: 0, options: .repeat, animations: {
self.containerView.center.x += self.view.bounds.width
self.containerView2.center.x += self.view.bounds.width
}, completion: nil)
}
}
The containerView2 will represent the clouds coming back from the left.
You could try something like putting containerView.center.x -= 200 right before starting the animation. This way it first appears from the left side.
Related
I need to create a right slide animation for TextField 1 and left slide animation for TextField2 . I tried to accomplish first right slide animation, but there is a problem. I have an slide animation, but there is another textfield under my animation textfield which doesn't move from it's constraints. I do not really understand why.
here is the code:
class ViewController: UIViewController {
#IBOutlet weak var emailField: UITextField!
#IBOutlet weak var PasswordField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
emailField.center.x = self.view.frame.width + 50
UIView.animate(withDuration: 1.0, delay: 0.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 1.0, options: .allowAnimatedContent, animations: {
self.emailField.center.x = self.view.frame.width/2
self.view.layoutIfNeeded()
}, completion: nil)
}
}
Assuming your Storyboard looks similar to this:
Two text fields, with Top constraints, Width constraints equal to 80% of the view width, and CenterX constraints to the view's CenterX
Ctrl-Drag from each .centerX constraint to your view controller code to create #IBOutlet connections to the constraints:
#IBOutlet var emailCenterX: NSLayoutConstraint!
#IBOutlet var passwordCenterX: NSLayoutConstraint!
Your Storyboard will now look like this:
And you can move / animate the views by setting their .constant values at run-time:
class AViewController: UIViewController {
#IBOutlet var emailField: UITextField!
#IBOutlet var PasswordField: UITextField!
#IBOutlet var emailCenterX: NSLayoutConstraint!
#IBOutlet var passwordCenterX: NSLayoutConstraint!
var viewWidth: CGFloat = 0
override func viewDidLoad() {
super.viewDidLoad()
// move the textFields "off-screen"
emailCenterX.constant = UIScreen.main.bounds.width
passwordCenterX.constant = -UIScreen.main.bounds.width
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// reset the centerX constraints to the center of the view
emailCenterX.constant = 0
passwordCenterX.constant = 0
UIView.animate(withDuration: 1.0, delay: 0.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 1.0, options: .allowAnimatedContent, animations: {
self.view.layoutIfNeeded()
}, completion: nil)
}
}
I'm new to iOS development. I'm trying to make a very simple application that rotates an image on a button press. The code works fine, but only once - i.e. the image rotates one time and then the button can no longer be pressed.
I've searched and found many similar issues, but nothing seems to fix my problem. Below is my ViewController.swift code.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var rotateImage: UIButton!
#IBAction func rotateImage(_ sender: Any) {
UIView.animate(withDuration: 2.0, animations: {
self.imageName.transform = CGAffineTransform(rotationAngle: (90.0 * .pi) / 90.0)
})
}
#IBOutlet weak var imageName: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
}
}
Ideally, the code should allow for multiple button presses, enabling the user to rotate the image as much as they would like.
It gets called multiple times, but it does the same rotation each time so nothing visually changes.
You can look into using a variable for your rotationAngle based on the image orientation so that it keeps rotating in your desired direction with each click.
An easy example takes your code and then uses a random value for the angle.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var imageName: UIImageView!
#IBAction func rotateImage(_ sender: Any) {
UIView.animate(withDuration: 2.0, animations: {
self.imageName.transform = CGAffineTransform(rotationAngle: (CGFloat.random(in: 0 ..< 360) * .pi) / 90.0)
})
}
}
I have first ViewController and second ViewController. When I return to first ViewController from second ViewController after that I want to show buttons in first ViewController.
To returning I use this code:
self.dismiss(animated: true, completion: nil)
And this code to show buttons in first ViewController
#IBOutlet weak var infoButton: UIButton!
#IBOutlet weak var nextButton: UIButton!
#IBOutlet weak var infoButtonTopConstraint: NSLayoutConstraint!
#IBOutlet weak var nextButtonBottomConstraint: NSLayoutConstraint!
#IBOutlet weak var infoButtonHeightConstraint: NSLayoutConstraint!
#IBOutlet weak var nextButtonHeightConstraint: NSLayoutConstraint!
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
showButtonAnimation()
//animaton()
}
func showButtonAnimation() {
self.infoButtonTopConstraint.constant += self.infoButtonHeightConstraint.constant
self.nextButtonBottomConstraint.constant -= self.nextButtonHeightConstraint.constant
UIView.animate(withDuration: 1.0, delay: 0.0,
options: [], animations: {
self.view.layoutIfNeeded()
})
}
But buttons not showing when I return from second ViewController.
Replace this
func showButtonAnimation() {
self.view.layoutIfNeeded()
UIView.animate(withDuration: 1.0, delay: 0.0,
options: [], animations: {
self.infoButtonTopConstraint.constant += self.infoButtonHeightConstraint.constant
self.nextButtonBottomConstraint.constant -= self.nextButtonHeightConstraint.constant
self.view.layoutIfNeeded()
})
}
EDIT 1
viewWillAppear not getting called reason : you are presenting 2nd viewController as overCurrentContext modalPresentationStyle.
Just replace overCurrentContext with fullScreen and it works.
Whenever you call viewWillAppear keep in mind that view where not loaded in main view so animation will not work proper. You should use a viewDidAppear.
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
showButtonAnimation()
}
And in your animation method update as below because you change constraint value inside animation block that in need to out of it.
func showButtonAnimation() {
self.infoButtonTopConstraint.constant += self.infoButtonHeightConstraint.constant
self.nextButtonBottomConstraint.constant -= self.nextButtonHeightConstraint.constant
UIView.animate(withDuration: 1.0, delay: 0.0,
options: [], animations: {
self.view.layoutIfNeeded()
})
}
I have tried your code and it is working for the first time, but the problem might be every time the constant value of the button constraint are increased.
so they might be hiding behind your main view.
Solution:- you can take flag and change its value when animation is completed.
Now when you come back from the second controller then you have to check in the viewWillAppear that if the buttons are visible or not, if they are visible then you do not have to call the showButtonAnimation method.
You can see where the buttons are in current screen with clicking on
button to see the UI hierarchy.
Hope this will solve your issue.
You can add a delegate in your second viewController. like:
protocol SecondViewControllerDelegate {
func vcDismissed()
}
Class SecondViewController : UIViewController {
weak var delegate: SecondViewControllerDelegate
.....
//Where you are dismissing your viewController, call the delegate method in completion.
func someMethod() {
self.dismiss(animated: true, completion: {
self.delegate.vcDismissed()
})
}
}
Now in your FirstViewController, conform the protocol and implement the method like below:
extension FirstViewController: SecondViewControllerDelegate {
func vcDismissed() {
//Here call your showButton method
self.showButton()
}
}
And do not forget to set the delegate when your are creating the instance of your secondViewController where you are presenting it. like
objSecondViewController.delegate = self
Hope this helps. for any query feel free to leave a comment.
I am looking for the text to be scroll all the way down to the bottom that is it. Just like you would see in movie credits. I do not want to move the x, y location of the textview just the text inside from top to bottom.
import UIKit
class ViewController: UIViewController {
#IBOutlet var text: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func play(_ sender: Any) {
}
}
Use a UIScrollView and scroll a UILabel with the text in it.
You can use standard UIView animation with timing and optionally animation curve and set the contentOffset inside the closure.
UIView.animate(withDuration: 10.0) { // 10 seconds
scrollView.contentOffset = CGPoint(x: 0, y: maxDesiredScrollPosition)
}
I am doing a transition of an UIImageView's image and would also like to move the image view at the same time. The results so far is that the image transitions, but not while moving.
class ViewController: UIViewController {
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var imageViewTopConstraint: NSLayoutConstraint!
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
UIView.transition(with: imageView, duration: 2.0, options: .transitionCrossDissolve, animations: {
self.imageView.image = UIImage(named: "otherImage")
}, completion: nil)
imageViewTopConstraint.constant = 200
UIView.animate(withDuration: 2.0) {
self.view.layoutIfNeeded()
}
}
}
Any ideas on how to solve this?
You can put your imageView inside a container, then animate the constraints of the container while doing the transition on the imageView.