Swift: Animating stretch & shrink of an image - ios

I am trying to animate the stretch and shrink of a lightsaber in my app. Currently I have everything working except for the stretching/shrinking of the lightsaber.
Ideally, I would like to be able to have the bottom anchor of actual saber be flush below the top anchor of saber handle. I've been able to achieve somewhat well with Auto Layout constraints. However, I'm struggling to find the right code for animations. As you can see from my code, I have tried frame.size.height/width, however that only moves the frame of the saber (not the handle). The line with frame.size.height achieves what I want in terms of making it stretch/grow, but it's a very bootleg way of doing it.
The current frame.size.height/width method only shifts the images, how would I achieve the stretch/shrink capability? Is it possible to achieve it with the multiplier function from Auto Layout constraints?
#objc func lightsaberTapped() {
print("lightsaber open!")
if mainView.saberImage.isHidden == true {
mainView.saberImage.isHidden = false
UIView.animate(withDuration: 0.5, animations: {
self.mainView.saberImage.frame.size.height -= 500
// self.mainView.saberImage.frame.size.width += 20
}, completion: nil)
lightsaberModel.turnSaberOnAudio.play()
} else {
mainView.saberImage.isHidden = true
UIView.animate(withDuration: 0.5, animations: {
self.mainView.saberImage.frame.size.height += 500
// self.mainView.saberImage.frame.size.width += 20
}, completion: nil)
}
}

Your main issue is the frame animation while you have constraints, you need 1 of 2 or animate constrains or remove it and animate with frame property, in that case you need to make sure that your UIImageView is added with translatesAutoresizingMaskIntoConstraints = false
To animate width and height in Autolayout system you need to get the constraint reference as IBOutlet and animate the constant property inside an animation block,
this is a very small example
class ViewController: UIViewController {
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var imageHeightConstraint: NSLayoutConstraint!
#IBOutlet weak var imageWidthConstraint: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
}
#IBAction func action(_ sender: Any) {
UIView.animate(withDuration: 0.5, animations: {
self.imageHeightConstraint.constant = self.imageHeightConstraint.constant - 10
self.imageWidthConstraint.constant = self.imageWidthConstraint.constant - 10
self.view.layoutIfNeeded()
})
}
}

First add constraints for width and height
#IBOutlet weak var saberImageHeightConstraint: NSLayoutConstraint!
#IBOutlet weak var saberImageWidthConstraint: NSLayoutConstraint!
Try this to get animation while resize frame.
if mainView.saberImage.isHidden == true
{
mainView.saberImage.isHidden = false
self.saberImageHeightConstraint.constant -= 500
UIView.animate(withDuration: 0.5, animations: {
self.view.layoutIfNeeded()
self.viewDidLayoutSubviews()
}, completion: nil)
lightsaberModel.turnSaberOnAudio.play()
}
else
{
mainView.saberImage.isHidden = true
self.saberImageHeightConstraint.constant += 500
UIView.animate(withDuration: 0.5, animations: {
self.view.layoutIfNeeded()
self.viewDidLayoutSubviews()
}, completion: nil)
}

Related

swift: fix buttons animation

When my app stat I want to show buttons animation. In storyboard my buttons have height = 50 and y = -50. I have this action in code.
#IBOutlet weak var imageView: UIImageView!
override func viewDidLoad()
{
super.viewDidLoad()
buttonAnimation()
}
func buttonAnimation()
{
self.buttonTopConstraint.constant += self.ButtonHeightConstraint.constant
UIView.animate(withDuration: 1.5, delay: 0.0, options: [], animations:
{
self.view.layoutIfNeeded()
}
)
}
Animation work well. But when app start my background imageView in storyboard and different imageView animated too. But I need to have only animation for button. How to fix it?
Don't call animation in viewDidLoad because at that time all views of your controller are going to set.
Use viewDidAppear to call animation method.
override func viewDidAppear()
{
buttonAnimation()
}
Use CGAffineTransform to animate your button. Remove y=-50 from storyboard and keep it at its desired place. Then call this function in viewWillAppear and viewDidAppear :
func animateButton() {
yourButton.transform = CGAffineTransform(translationX: yourButton.frame.origin.x, y: yourButton.frame.origin.y - 50)
UIView.animate(withDuration: 1.5, delay: 0.0, options: [], animations: {
self.yourButton.transform = .identity
}, completion: nil)
}

how to make a sliding door animation in Xcode

I am very new to Xcode, so this could be something very basic that i'm just not finding, but i'm trying to create an app that moves 2 images away from each other (at least one width away) when the user taps the screen, and then have the images return to their original positions when the user taps the screen again, however, i have been unable to find out how to move an image in a specific direction, a specific distance.
i'm also new to Stack Overflow, so i'm sorry if i'm doing something wrong
I am breaking the rules by giving up the answer when it appears you have not googled enough. The general rule is to show the code you have attempted and indicate what part you are having issues with.
The code below will achieve what you are attempting to do.
import UIKit
class slidingIamgesViewController: UIViewController {
#IBOutlet weak var topImage: UIImageView!
#IBOutlet weak var bottomImage: UIImageView!
var doubleTap: Bool! = false
//MARK: View LifeCycle
override func viewDidLoad() {
super.viewDidLoad()
let singleFingerTap = UITapGestureRecognizer(target: self, action: #selector(slidingIamgesViewController.handleSingleTap(_:)))
self.view.addGestureRecognizer(singleFingerTap)
}
// MARK: gestutre recognizer
func handleSingleTap(_ recognizer: UITapGestureRecognizer) {
if (doubleTap) {
UIView.animate(withDuration: 0.7, delay: 1.0, options: .curveEaseOut, animations: {
var basketTopFrame = self.topImage.frame
basketTopFrame.origin.y += basketTopFrame.size.height
var basketBottomFrame = self.bottomImage.frame
basketBottomFrame.origin.y -= basketBottomFrame.size.height
self.topImage.frame = basketTopFrame
self.bottomImage.frame = basketBottomFrame
}, completion: { finished in
print("Images Moved back!")
})
doubleTap = false
} else {
UIView.animate(withDuration: 0.7, delay: 1.0, options: .curveEaseOut, animations: {
var basketTopFrame = self.topImage.frame
basketTopFrame.origin.y -= basketTopFrame.size.height
var basketBottomFrame = self.bottomImage.frame
basketBottomFrame.origin.y += basketBottomFrame.size.height
self.topImage.frame = basketTopFrame
self.bottomImage.frame = basketBottomFrame
}, completion: { finished in
print("Images sperated!")
})
doubleTap = true
}
}
}
Make sure in your storyboard to added a Tap Gesture Recognizer.

invert place of the UIView, with animation

I'm looking for the best way to invert the place of two UIView, with animation if possible (first i need to change the place, animation is optionnal).
viewController :
So, i want view1 to invert his place with the view2. The views are set with autolayout in the storyboard at the init.
if state == true {
view1 at the top
view2 at the bot
} else {
view 2 at the top
view 1 at the top
}
I've tried to take view.frame.(x, y, width, height) from the other view and set it to the view but it doesn't work.
I think the best way to do this would be to have a topConstraint for both views connected to the header and then change their values and animate the transition. You can do it in a way similar to this:
class MyViewController: UIViewController {
var view1TopConstraint: NSLayoutConstraint!
var view2TopConstraint: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
view1TopConstraint = view1.topAnchor.constraintEqualToAnchor(header.bottomAnchor, constant: 0)
view1TopConstraint.isActive = true
view2TopConstraint = view2.topAnchor.constraintEqualToAnchor(header.bottomAnchor, constant: view1.frame.height)
view2TopConstraint.isActive = true
}
func changeView2ToTop() {
UIView.animateWithDuration(0.2, animations: { //this will animate the change between 2 and 1 where 2 is at the top now
self.view2TopConstraint.constant = 0
self.view1TopConstraint.constant = view2.frame.height
//or knowing that view2.frame.height represents 30% of the total view frame you can do like this as well
// self.view1TopConstraint.constant = view.frame.height * 0.3
self.view.layoutIfNeeded()
})
}
You could also create the NSLayoutConstraint in storyboard and have an outlet instead of the variable I have created or set the top constraint for both views in storyboard at "remove at build time" if you are doing both. This way you won't have 2 top constraints and no constraint warrning
I Made an Example: https://dl.dropboxusercontent.com/u/24078452/MovingView.zip
I just created a Storyboard with 3 View, Header, View 1 (Red) and View 2 (Yellow). Then I added IBoutlets and animated them at viewDid Appear, here is the code:
import UIKit
class ViewController: UIViewController {
var position1: CGRect?
var position2: CGRect?
#IBOutlet weak var view1: UIView!
#IBOutlet weak var view2: UIView!
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
position1 = view1.frame
position2 = view2.frame
UIView.animate(withDuration: 2.5) {
self.view1.frame = self.position2!
self.view2.frame = self.position1!
}
}
}
Hello #Makaille I have try to resolve your problem.
I have made an example, which will help you for your required implementation.
Check here: Source Code
I hope, it will going to help you.
#IBAction func toggle(_ sender: UIButton) {
sender.isUserInteractionEnabled = false
if(sender.isSelected){
let topPinConstantValue = layout_view2TopPin.constant
layout_view1TopPin.constant = topPinConstantValue
layout_view2TopPin.priority = 249
layout_view1BottomPin_to_View2Top.priority = 999
layout_view1TopPin.priority = 999
layout_view2BottomPin_ToView1Top.priority = 249
UIView.animate(withDuration: 0.3, animations: {
self.view.layoutIfNeeded()
}, completion: { (value) in
if(value){
sender.isUserInteractionEnabled = true
}
})
} else {
let topPinConstantValue = layout_view1TopPin.constant
layout_view2TopPin.constant = topPinConstantValue
layout_view1TopPin.priority = 249
layout_view2BottomPin_ToView1Top.priority = 999
layout_view2TopPin.priority = 999
layout_view1BottomPin_to_View2Top.priority = 249
UIView.animate(withDuration: 0.3, animations: {
self.view.layoutIfNeeded()
}, completion: { (value) in
if(value){
sender.isUserInteractionEnabled = true
}
})
}
sender.isSelected = !sender.isSelected
}

Why are the animated UI Labels in my code starting from within the view?

I'm a very new-to-code learner. I've been taking some online courses, and came across this project recently.
I basically copied the code as I saw it on the screen, as the project files weren't available to download.
The animation is supposed to bring the UILabels from outside the view and position them within the view.
What seems to happen however, is the labels are starting within the view screen and animate outward and beyond.
I've copied the project below. Any help is so very appreciated.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var helloWorld: UILabel!
#IBOutlet weak var secondLabel: UILabel!
#IBOutlet weak var hiddenLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
helloWorld.center.y -= view.bounds.height
secondLabel.center.y += view.bounds.height
hiddenLabel.alpha = 0.0
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
// Animate Hello World label
UIView.animateWithDuration(1.5, animations: {
self.helloWorld.center.y += self.view.bounds.height
}, completion: { finished in
self.secondAnimation()
})
// Animate background color change
UIView.animateWithDuration(2.0, delay: 1.5, options: [], animations: {
self.view.backgroundColor = UIColor.yellowColor()
}, completion:nil)
}
// Animate second Label
func secondAnimation() {
UIView.animateWithDuration(1.5, delay: 0.0, options: [], animations: { () -> Void in
self.secondLabel.center.y -= self.view.bounds.height
}, completion:nil)
}
func backgroundColor() {
UIView.animateWithDuration(2.5, animations: {
self.view.backgroundColor = UIColor.blackColor()
}, completion: nil)
UIView.animateWithDuration(1.0, delay: 1.5, options: [], animations: {
self.hiddenLabel.alpha = 1.0
}, completion:nil)
}
}
I would check hello world.center.y value in viewWillAppear, before you subtract the view.bounds.height. If center.y value is large and positive, then the subtraction might be setting the center.y value to be inside of the view. If this is the case, then you would want to change the subtraction in viewWillAppear to addition and then the addition in the viewDidAppear animation to subtraction.

Hide navigation bar without moving scrollView

I have a viewController where am showing image for adding the zooming functionality I added the scrollView in viewController and inside of ScrollView I added ImageView everything is working fine expect of one thing, am hiding, and showing the bars (navigation bar + tab bar) on tap but when hiding them my imageView moves upside see the below images
See here's the image and the bars.
Here I just tapped on the view and bars got hidden but as you can see my imageView is also moved from its previous place, this is what I want to solve I don't want to move my imageView.
This is how am hiding my navigation bar:
func tabBarIsVisible() ->Bool {
return self.tabBarController?.tabBar.frame.origin.y < CGRectGetMaxY(self.view.frame)
}
func toggle(sender: AnyObject) {
navigationController?.setNavigationBarHidden(navigationController?.navigationBarHidden == false, animated: true)
setTabBarVisible(!tabBarIsVisible(), animated: true)
}
any idea how can I hide and show the bars without affecting my other Views?
The problem is that you need to set the constraint of your imageView to your superView, not TopLayoutGuide or BottomLayoutGuide.
like so:
Then you can do something like this to make the it smootly with animation:
import UIKit
class ViewController: UIViewController {
#IBOutlet var imageView: UIImageView!
var barIsHidden = false
var navigationBarHeight: CGFloat = 0
var tabBarHeight: CGFloat = 0
override func viewDidLoad() {
super.viewDidLoad()
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(ViewController.hideAndShowBar))
view.addGestureRecognizer(tapGesture)
navigationBarHeight = (self.navigationController?.navigationBar.frame.size.height)!
tabBarHeight = (self.tabBarController?.tabBar.frame.size.height)!
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func hideAndShowBar() {
print("tap!!")
if barIsHidden == false {
UIView.animateWithDuration(0.5, delay: 0.0, options: .CurveEaseOut, animations: {
// fade animation
self.navigationController?.navigationBar.alpha = 0.0
self.tabBarController?.tabBar.alpha = 0.0
// set height animation
self.navigationController?.navigationBar.frame.size.height = 0.0
self.tabBarController?.tabBar.frame.size.height = 0.0
}, completion: { (_) in
self.barIsHidden = true
})
} else {
UIView.animateWithDuration(0.5, delay: 0.0, options: .CurveEaseOut, animations: {
// fade animation
self.navigationController?.navigationBar.alpha = 1.0
self.tabBarController?.tabBar.alpha = 1.0
// set height animation
self.navigationController?.navigationBar.frame.size.height = self.navigationBarHeight
self.tabBarController?.tabBar.frame.size.height = self.tabBarHeight
}, completion: { (_) in
self.barIsHidden = false
})
}
}
}
Here is the result:
I have created an example project for you at: https://github.com/khuong291/Swift_Example_Series
You can see it at project 37
Hope this will help you.

Resources