How to animate UIView from top to bottom and vice versa? - ios

I'm trying to animate UIImageView from top to bottom and vice versa.
I tried the code:
Previous value was(y == 0):
laserImageView.frame = CGRect(x: 0, y: 0.0, width: 300.0, height: 300.0)
and want to animate it to 500.0:
UIView.animate(withDuration: 3.0, delay: 0.2, options: [.curveEaseInOut], animations: {
laserImageView.frame = CGRect(x: 0, y: 500.0, width: 300.0, height: 300.0)
}) { (finished) in
if finished {
// Repeat animation from bottom to top
}
}
but when I run my app the UIImageView appears at the last point(y == 500) without any animation, like the initial position was 500.0 but not 0.0.
I do not understand why animation does not work. Any idea?

Try this code:
class ViewController: UIViewController {
let laserImageView = UIImageView(frame: CGRect(x: 0, y: 0.0, width: 300.0, height: 300.0))
override func viewDidLoad() {
super.viewDidLoad()
laserImageView.backgroundColor = UIColor.blue
self.view.addSubview(laserImageView)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
bottom()
}
func bottom() {
UIView.animate(withDuration: 3.0, delay: 0.2, options: [.curveEaseInOut], animations: {
self.laserImageView.frame = CGRect(x: 0, y: 500.0, width: 300.0, height: 300.0)
}) { (finished) in
if finished {
// Repeat animation from bottom to top
self.Up()
}
}
}
func Up() {
UIView.animate(withDuration: 3.0, delay: 0.2, options: [.curveEaseInOut], animations: {
self.laserImageView.frame = CGRect(x: 0, y: self.laserImageView.bounds.origin.y, width: 300.0, height: 300.0)
}) { (finished) in
if finished {
// Repeat animation to bottom to top
self.bottom()
}
}
}
}

One way to do so is take a IBOutlet of top constraint of view which you want to animate and update the value of constraint using the below method:-
- (IBAction)animateButton:(UIButton *)sender {
[UIView transitionWithView:_topView duration:0.8 options:UIViewAnimationOptionRepeat animations:^{
_headerViewTopConstraint.constant = self.view.frame.size.height - 20;
[self.view layoutIfNeeded];
} completion:^(BOOL finished) {
}];
}

For those who wants to do it using code inspite of interface builder can use below answer:-
__weak FirstViewController *weakSelf = self;
[UIView transitionWithView:_topView duration:0.4 options:UIViewAnimationOptionCurveEaseIn animations:^{
//
_topView.frame = CGRectMake(0, 300, _topView.frame.size.width, _topView.frame.size.height);
[weakSelf.view layoutIfNeeded];
} completion:^(BOOL finished) {
//
}];
Please try to post complete question inspite of downvoting the answer :) Thanks

Related

UIView bottom to top animation issue

This is a simple function I am using for animating a view from top to bottom and vice versa (if is top to bottom animation and else is bottom to top animation) :
#objc func openMenu(sender: UIButton) {
if sender.tag == 1 {
self.buttonView.tag = 2
self.moduleView = ModulesCollectionView(frame: CGRect(x: 0, y: self.frame.origin.y + self.frame.size.height + 20, width: UIScreen.main.bounds.size.width, height: 0), collectionViewLayout: UICollectionViewLayout())
self.window?.addSubview(self.moduleView)
UIView.animate(withDuration: 0.7, animations: {
self.moduleView.frame = CGRect(x: 0, y: self.frame.origin.y + self.frame.size.height + 20, width: UIScreen.main.bounds.size.width, height: UIScreen.main.bounds.size.height - self.frame.size.height - 22)
}, completion: { _ in
})
} else {
self.buttonView.tag = 1
UIView.animate(withDuration: 3, animations: {
self.moduleView.frame = CGRect(x: 0, y: self.frame.origin.y + self.frame.size.height + 20, width: UIScreen.main.bounds.size.width, height: 0)
}, completion: { _ in
self.moduleView.removeFromSuperview()
})
}
}
Top animation works fine and the view is animated from top to bottom pleasantly in 0.7 seconds. However, bottom to top animation does not happen. The view is removed instantly. This is the result I am getting :
But I want the animation to be clean while going from bottom to top as well. Just like here.
Secondary : What I finally plan to achieve is PullUpController with the exact reverse animation. So if anyone knows a similar library (pull down drag) can share there inputs.
Update : The issue is coming only with UICollectionView. I replaced collectionView with a simple UIView and it worked perfect.
You should try to use layoutSubViews() method after at the end of animation block. Change the animation block like this.
For if block:
self.moduleView.frame = CGRect(x: 0, y: self.frame.origin.y + self.frame.size.height + 20, width: UIScreen.main.bounds.size.width, height: UIScreen.main.bounds.size.height - self.frame.size.height - 22)
self.moduleView.layoutSubviews()
For else block:
self.moduleView.frame = CGRect(x: 0, y: self.frame.origin.y + self.frame.size.height + 20, width: UIScreen.main.bounds.size.width, height: 0)
self.moduleView.layoutSubviews()
Here is example code, hope it will help.
Show view:
self.containerView = ModulesCollectionView(frame: UIScreen.main.bounds)
self.containerView.center = CGPoint(x: self.view.center.x,
y: self.view.center.y + self.view.frame.size.height)
self.window?.addSubview(self.moduleView)
self.window?.bringSubview(toFront: self.moduleView)
self.window?.endEditing(true)
UIView.animate(withDuration: 0.3, delay: 0.0,
usingSpringWithDamping: 0.7, initialSpringVelocity: 3.0,
options: .allowAnimatedContent, animations: {
self.moduleView.center = self.view.center
}) { (isFinished) in
self.view.layoutIfNeeded()
}
hide view:
UIView.animate(withDuration: 0.7, delay: 0.0,
usingSpringWithDamping: 1, initialSpringVelocity: 1.0,
options: .allowAnimatedContent, animations: {
self.moduleView.center = CGPoint(x: self.view.center.x,
y: self.view.center.y + self.view.frame.size.height)
}) { (isFinished) in
self.moduleView.removeFromSuperview
}

Slide in Menu for iOS

I'm trying to create a slide in menu that comes in from the left but I've done something and its coming in onto the right instead. I've programmatically done this not used storyboards as most of the time the constraints messes up.
for example heres a screenshot (probably something I've done):
Slide Menu - Gone Wrong
import UIKit
class SideInMenu: NSObject {
let blackView = UIView()
let collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
return cv
}()
#objc func showSlideMenu() {
//Show slide in menu Here
if let window = UIApplication.shared.keyWindow {
blackView.backgroundColor = UIColor(white: 0, alpha: 0.5)
blackView.addGestureRecognizer(UITapGestureRecognizer(target:
self, action: #selector(handleDismiss)))
window.addSubview(blackView)
window.addSubview(collectionView)
let width: CGFloat = 194
let x = window.frame.width - width
collectionView.frame = CGRectMake(x, 0, width, window.frame.height)
//collectionView.frame = CGRectMake(x, 0, width, window.frame.height)
blackView.frame = window.frame
blackView.alpha = 0
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping:
1, initialSpringVelocity: 1, options: .curveEaseOut,
animations: {
self.blackView.alpha = 1
self.collectionView.frame = self.CGRectMake(width, 0, self.collectionView.frame.width, self.collectionView.frame.height)
}, completion: nil)
}
}
#objc func handleDismiss() {
UIView.animate(withDuration: 0.5) {
self.blackView.alpha = 0
if let window = UIApplication.shared.keyWindow {
self.collectionView.frame = self.CGRectMake(window.frame.width, 0, self.collectionView.frame.width, self.collectionView.frame.height)
}
}
}
override init() {
super.init()
}
func CGRectMake(_ x: CGFloat, _ y: CGFloat, _ width: CGFloat, _ height: CGFloat) -> CGRect {return CGRect(x: x, y: y, width: width, height: height)
}
}
Always remember that 0, 0 for x and y is always the top left of the screen and if the width and height is 20, 20, it will go from the top left, right 20px across and down 20px. The first thing you have to do inside you viewDidLoad is to set the collectionViews initial frame, which is off screen like this. DON'T FORGET MINUS WIDTH:
self.collectionView.frame = self.CGRectMake(-width, 0, self.collectionView.frame.width, self.collectionView.frame.height)
Animate it in from the left like so:
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping:
1, initialSpringVelocity: 1, options: .curveEaseOut,
animations: {
self.blackView.alpha = 1
self.collectionView.frame = self.CGRectMake(0, 0, self.collectionView.frame.width, self.collectionView.frame.height)
}, completion: nil)
Dismiss with:
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping:
1, initialSpringVelocity: 1, options: .curveEaseOut,
animations: {
self.blackView.alpha = 1
self.collectionView.frame = self.CGRectMake(-width, 0, self.collectionView.frame.width, self.collectionView.frame.height)
}, completion: nil)

Repeat while loop doesn't work - why?

I want an imageView in my viewController to "pulsate", that means become bigger and then tinier again and repeat that until the "start"-button was pressed. The problem is, that every time I call that function in the viewDidLoad() with a repeat-while-loop, it has got a breakpoint for whatever reason.
That's the part of my viewDidLoad():
repeat {
pulsate()
} while hasStarted == false
When I press the start button, hasStarted becomes true and it should stop pulsating. But it doesn't even start the function, it immediately calls an error. Where's the problem?
Here's my pulsate()-function
func pulsate()
{
frameOR = imageView.frame
frameNext = CGRect(x: imageView.frame.midX - 35, y: imageView.frame.midY - 35, width: 80, height: 80)
let frameVDL = CGRect(x: imageView.frame.midX - 150, y: imageView.frame.midY - 150, width: 300, height: 300)
if start == false
{
UIView.animate(withDuration: 2, animations: {
self.imageView.frame = frameVDL
}) { (finished) in
UIView.animate(withDuration: 2, animations: {
self.imageView.frame = self.frameOR
}, completion: nil)
}
}
}
Thank your for your help! Have a great day!
You can use UIView.animate's built-in options to really simplify what you're trying to do, and avoid locking up the thread.
Give this a try:
let frameOR = imageView.frame
let frameVDL = CGRect(x: imageView.frame.midX - 150, y: imageView.frame.midY - 150, width: 300, height: 300)
UIView.animate(withDuration: 2.0,
delay: 0.0,
options: [UIViewAnimationOptions.autoreverse, UIViewAnimationOptions.repeat],
animations: {
imageView.frame = frameVDL
},
completion: nil)
Your imageView should now be in a full "pulsing" loop, without needing any additional code. When you're ready to stop the animation, just call:
imageView.layer.removeAllAnimations()

Move UIButtons back when I click on image in swift

I need to move UIButtons inside the view when I tap on image, and need to move UIButtons out of the screen when I tap on same image again.
When I tap on a UIImage, 4 UIButtons move inside screen with following code without a problem.
func imageTapped(img: AnyObject)
{UIView.animateWithDuration(0.5,
delay: 0,
options: [.CurveEaseInOut, .AllowUserInteraction],
animations: {
self.imageReceivedTrashCan.center = CGPoint(x: 30, y: 530)
self.imageReceivedRightArrow.center = CGPoint(x: 285, y: 530)
self.imageReceivedForward.center = CGPoint(x: 160, y: 530)
self.imageReceivedCross.center = CGPoint(x: 30, y: 30)
},
completion: { finished in
print("Objects moved!")
})
}
But, I couldn't find a way how can I move UIButtons back to original locations when I click on same image again. Appreciate your help.
Even if this seems really old-school, w/o any autolayout and I randomly guessed you don't change the center's elsewhere and all center's at once:
func imageTapped(img: AnyObject) {
if (self.imageReceivedTrashCan.center == CGPoint(x: 30, y: 530)) {
UIView.animateWithDuration(0.5,
delay: 0,
options: [.CurveEaseInOut, .AllowUserInteraction],
animations: {
self.imageReceivedTrashCan.center = CGPoint(x: -30, y: -530)
self.imageReceivedRightArrow.center = CGPoint(x: -285, y: -530)
self.imageReceivedForward.center = CGPoint(x: -160, y: -530)
self.imageReceivedCross.center = CGPoint(x: -30, y: -30)
},
completion: { finished in
print("Objects moved!")
})
} else {
UIView.animateWithDuration(0.5,
delay: 0,
options: [.CurveEaseInOut, .AllowUserInteraction],
animations: {
self.imageReceivedTrashCan.center = CGPoint(x: 30, y: 530)
self.imageReceivedRightArrow.center = CGPoint(x: 285, y: 530)
self.imageReceivedForward.center = CGPoint(x: 160, y: 530)
self.imageReceivedCross.center = CGPoint(x: 30, y: 30)
},
completion: { finished in
print("Objects moved!")
})
}
}
Firstly, to answer your question :
You can save the initial position for each button then on the second tap you call animateWithDuration:delay:options:animations:completion: and set the position to self.imageReceivedTrashCan.center = initialPosition
You also can try with translation methods from CoreGraphics.
UIView.animateWithDuration(0.5,
delay: 0,
options: [.CurveEaseInOut, .AllowUserInteraction],
animations: {
self.imageReceivedTrashCan.transform = CGAffineTransformMakeTranslation(300, 0)
....
},
completion: { finished in
print("Objects moved!")
})
}
Then on the second tap get back to the Identity transform
UIView.animateWithDuration(0.5,
delay: 0,
options: [.CurveEaseInOut, .AllowUserInteraction],
animations: {
self.imageReceivedTrashCan.transform = CGAffineTransformIdentity
....
},
completion: { finished in
print("Objects moved!")
})
}
But this method is hard coded, you should use autoLayout to make this kind of animations. Look at here
Hope this help you ;)
Select imageView, goto attribute inspector and Check user interaction enabled
UITapGestureRecognizer *TapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(moveMyView:)];
[imageview addGestureRecognizer: TapRecognizer];
- (void) moveMyView:(UITapGestureRecognizer*)sender {
[UIView animateWithDuration:1.0 delay:0.0 options:
UIViewAnimationOptionCurveEaseIn animations:^{
self.imageReceivedTrashCan.center = CGPoint(x: -30, y: -530)
self.imageReceivedRightArrow.center = CGPoint(x: -285, y: -530)
self.imageReceivedForward.center = CGPoint(x: -160, y: -530)
self.imageReceivedCross.center = CGPoint(x: -30, y: -30)
} completion:^ (BOOL completed) {}];
}];
}

UIView having Animation from right to left

how to make uiview animated so it appears slowly from right to left and having two UIButtons on it having images on buttons and one of them image on button will change on button click
[UIView animateWithDuration:0.7f
animations:^ {
CGRect frame = YourView.frame;
frame.origin.x = 0;
YourView.frame = frame;
YourView.transform = CGAffineTransformScale(CGAffineTransformIdentity, 1.0, 1.0);
}
completion:^(BOOL finished) {
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:5.3];
[UIView commitAnimations];
}];
You can try this code and make changes according to your requirements .
here i create a baseview which moves from right to left and vice versa.
In that baseview you put whatever u want to animate.
set bounceDistance,bounceDuration,animateWithDuration,delay variable according to your requirement.
-(IBAction)rightClicked:(id)sender {
CGRect initalFrame = self.baseView.frame;
initalFrame.origin.x = initalFrame.size.width;
self.baseView.frame = initalFrame;
[NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(moveFromLeftOrRight:) userInfo:#0 repeats:NO];
}
-(IBAction)leftClicked:(id)sender {
CGRect initalFrame = self.baseView.frame;
initalFrame.origin.x = -initalFrame.size.width;
self.baseView.frame = initalFrame;
[NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(moveFromLeftOrRight:) userInfo:#1 repeats:NO];
}
-(void)moveFromLeftOrRight:(NSTimer *) timer {
BOOL isLeft = [timer.userInfo boolValue];
CGFloat bounceDistance = 10;
CGFloat bounceDuration = 0.2;
[UIView animateWithDuration:.2 delay:0.0 options:UIViewAnimationOptionAllowAnimatedContent
animations:^{
CGFloat direction = (isLeft ? 1 : -1);
self.baseView.center = CGPointMake(self.baseView.frame.size.width/2 + direction*bounceDistance, self.baseView.center.y);}
completion:^(BOOL finished){
[UIView animateWithDuration:bounceDuration animations:^{
self.baseView.center = CGPointMake(self.baseView.frame.size.width/2, self.baseView.center.y);
}];
}];
}
Assuming you are targeting iPhone.
Set the view's x co ordinate to, say 320(max visible in iPhone) in the storyboard (IB).
x-320 y-0 width-320 height-568
In the viewDidAppear: you can animate and bring the view to the starting co ordinate, say 0. You can do this as follows
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[UIView animateWithDuration:10.f animations:^{
self.view.frame = CGRectMake(0.f, 0.f, 320.f, 568.f);
}];
}
For the buttons you will have to be specific.
Right to Left animation & Left to right animation as-
let firstViewTowardsLeft = UIView(frame: CGRect(x: 0, y: 150, width: 50, height: 50))
firstViewTowardsLeft.backgroundColor = .red
view.addSubview(firstViewTowardsLeft)
UIView.animate(withDuration: 1, delay: 0, options: .repeat, animations: {Bool in
firstViewTowardsLeft.frame = CGRect(x: 100, y: 150, width: 50, height: 50)
}, completion: nil)
let secondViewTowardsLeft = UIView(frame: CGRect(x: 100, y: 100, width: 50, height: 50))
secondViewTowardsLeft.backgroundColor = .red
view.addSubview(secondViewTowardsLeft)
UIView.animate(withDuration: 1, delay: 0, options: .repeat, animations: {Bool in
secondViewTowardsLeft.frame = CGRect(x: 0, y: 100, width: 50, height: 50)
}, completion: nil)
https://developer.apple.com/documentation/uikit/uiview/1622418-animate

Resources