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) {}];
}];
}
Related
Is there a way to remove the delay on start and end of the animateKeyframes animation?
As you can see there is a slight delay before the animation starts; after tapping on the Animate button and also at the end of the animation. What I would like to be able to do is start the animation as soon as the Animate button is tapped since this is meant to provide feedback to the user.
Is this the normal behavior when using animateKeyframes animations with the calculationModeCubic? Is there a way to make the animation starts as soon as the button is tapped?
Sorry about the misspelling error (Aniamate).
Here is the code:
#IBAction func startAnimation(_ sender: Any) {
addMyView()
UIView.animateKeyframes(withDuration: 3.0, delay: 0.0, options: [.calculationModeCubic], animations: {
UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.2, animations: {
self.myView!.center = CGPoint(x: self.pointA.center.x, y: self.pointA.center.y)
})
UIView.addKeyframe(withRelativeStartTime: 0.2, relativeDuration: 0.2, animations: {
self.myView!.center = CGPoint(x: self.pointB.center.x + 55, y: self.pointB.center.y - 5 )
self.myView!.transform = CGAffineTransform(scaleX: 0.75, y: 0.75)
})
UIView.addKeyframe(withRelativeStartTime: 0.4, relativeDuration: 0.2, animations: {
self.myView!.center = CGPoint(x: self.pointB.center.x, y: self.pointB.center.y)
self.myView!.transform = CGAffineTransform(scaleX: 0.5, y: 0.5)
})
}, completion: { _ in
self.myView?.removeFromSuperview()
})
}
func addMyView(){
myView = UIView(frame: CGRect(x: pointA.center.x - 25, y: pointA.center.y - 25, width: 50, height: 50))
myView?.backgroundColor = .blue
myView?.layer.cornerRadius = myView!.frame.height / 2
view.addSubview(myView!)
}
The key is to keep tweaking the withRelativeStartTime: and the relativeDuration: parameters.
#IBAction func startAnimation(_ sender: Any) {
addMyView()
UIView.animateKeyframes(withDuration: 1.5, delay: 0.0, options: [.calculationModeCubic], animations: {
UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.2, animations: {
self.myView!.center = CGPoint(x: self.pointA.center.x, y: self.pointA.center.y)
})
UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: 0.9, animations: {
self.myView!.center = CGPoint(x: self.pointB.center.x + 75, y: self.pointB.center.y - 50 )
self.myView!.transform = CGAffineTransform(scaleX: 0.75, y: 0.75)
})
UIView.addKeyframe(withRelativeStartTime: 0.1, relativeDuration: 0.7, animations: {
self.myView!.center = CGPoint(x: self.pointB.center.x, y: self.pointB.center.y)
self.myView!.transform = CGAffineTransform(scaleX: 0.3, y: 0.3)
})
}, completion: { _ in
self.myView?.removeFromSuperview()
})
}
I have the following code in Swift. The problem is when it is executed, it performs the animation, but the window stays in its original position as shown in the gif I attached.
UIView.animateKeyframes(withDuration: 0.5, delay: 0, options: .calculationModePaced, animations: {
self.view.viewWithTag(123)?.frame = CGRect(x: screenWidth5/2, y: screenHeight, width: screenWidth5 * 4, height: screenHeight5 * 3 )
}, completion: .none)
self.view.viewWithTag(123)?.removeFromSuperview()
Thank you for your time and answers.
You need to put removeFromSuperview in your completion handler.
UIView.animateKeyframes(withDuration: 0.5, delay: 0, options: .calculationModePaced, animations: {
self.view.viewWithTag(123)?.frame = CGRect(x: screenWidth5/2, y: screenHeight, width: screenWidth5 * 4, height: screenHeight5 * 3 )
}) { _ in
self.view.viewWithTag(123)?.removeFromSuperview()
}
My question is similar to this one. However, I want an updated version for swift, and I want it to go endlessly. Here is my animation now:
bubble.frame.origin = CGPoint(x: 75, y: -120)
UIView.animate(
withDuration: 2.5,
animations: {
self.bubble.transform = CGAffineTransform(translationX: 0, y: self.view.frame.height * -1.3)
}
)
After the bubble goes off screen, I want it to go back on from the bottom and go up off screen again and again endlessly.
Use the option from the UIViewAnimationOptions set:
UIViewAnimationOptions.Repeat
Should look like this with your code:
bubble.frame.origin = CGPoint(x: 75, y: -120)
UIView.animate(withDuration: 2.5, options: [UIViewAnimationOptions.Repeat], animations: {
self.bubble.transform = CGAffineTransform(translationX: 0, y: self.view.frame.height * -1.3)
})
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
}
My label is disappearing before the animation runs.
The animation should run after a button is pressed and slide out a label and the Button. But when I press the button they are both hidden before the animation can slide them out.
func nextGame() {
UIView.animate(withDuration: 3, delay: 0.0, options: .allowAnimatedContent, animations: {
NSLog("Animation started")
self.labelWinningPlayer.center = CGPoint(x: self.labelWinningPlayer.center.x + 500, y: self.labelWinningPlayer.center.y)
self.buttonNextGameLabel.center = CGPoint(x: self.buttonNextGameLabel.center.x + 500, y: self.buttonNextGameLabel.center.y)
}, completion: { (finished: Bool) in
NSLog("Animation stopped")
self.labelWinningPlayer.isHidden = true
self.buttonNextGameLabel.isHidden = true
})
//sets buttons to start position
labelWinningPlayer.center = CGPoint(x: labelWinningPlayer.center.x - 1000, y: labelWinningPlayer.center.y)
buttonNextGameLabel.center = CGPoint(x: buttonNextGameLabel.center.x - 1000, y: buttonNextGameLabel.center.y)
//hides buttons that lay under the animated buttons
for i in 1...9 {
if let button = view.viewWithTag(i) as? UIButton {
button.setImage(nil, for: .normal)
}
}
activeGame = true
}
//animation should get started with this button
#IBAction func buttenNextGameAction(_ sender: Any) {
nextGame()
}
//slides buttons in
func slideNextGameButtons() {
labelWinningPlayer.isHidden = false
buttonNextGameLabel.isHidden = false
UIView.animate(withDuration: 0.5, animations: {
self.labelWinningPlayer.center = CGPoint(x: self.labelWinningPlayer.center.x + 500, y: self.labelWinningPlayer.center.y)
self.buttonNextGameLabel.center = CGPoint(x: self.buttonNextGameLabel.center.x + 500, y: self.buttonNextGameLabel.center.y)
})
}
Here is the NSLog. According to that it's running the full animation...
2017-11-15 23:26:17.321465 [2442:245232] Animation started
2017-11-15 23:26:20.325137 [2442:245232] Animation stopped
Thanks for your help in advance!
Your code works perfectly:
Therefore something that you have not told us about is causing whatever the problem is.
EDIT: And yes indeed, it was the code you didn't tell us about that causes the problem. You told us about this:
UIView.animate(withDuration: 3, delay: 0.0, options: .allowAnimatedContent, animations: {
self.labelWinningPlayer.center = CGPoint(x: self.labelWinningPlayer.center.x + 500, y: self.labelWinningPlayer.center.y)
self.buttonNextGameLabel.center = CGPoint(x: self.buttonNextGameLabel.center.x + 500, y: self.buttonNextGameLabel.center.y)
}, completion: { (finished: Bool) in
self.labelWinningPlayer.isHidden = true
self.buttonNextGameLabel.isHidden = true
})
But you didn't tell us about the next lines, which are this:
labelWinningPlayer.center = CGPoint(x: labelWinningPlayer.center.x - 1000, y: labelWinningPlayer.center.y)
buttonNextGameLabel.center = CGPoint(x: buttonNextGameLabel.center.x - 1000, y: buttonNextGameLabel.center.y)
Those lines cancel the animation!
It seems you have not understood what an animation is. You animate first, and anything you want done after the animation, you put into the completion function. But those lines are something you want done after the animation. So put them into the completion function! Like this:
UIView.animate(withDuration: 3, delay: 0.0, options: .allowAnimatedContent, animations: {
self.labelWinningPlayer.center = CGPoint(x: self.labelWinningPlayer.center.x + 500, y: self.labelWinningPlayer.center.y)
self.buttonNextGameLabel.center = CGPoint(x: self.buttonNextGameLabel.center.x + 500, y: self.buttonNextGameLabel.center.y)
}, completion: { (finished: Bool) in
self.labelWinningPlayer.isHidden = true
self.buttonNextGameLabel.isHidden = true
labelWinningPlayer.center = CGPoint(x: labelWinningPlayer.center.x - 1000, y: labelWinningPlayer.center.y)
buttonNextGameLabel.center = CGPoint(x: buttonNextGameLabel.center.x - 1000, y: buttonNextGameLabel.center.y)
// ... and everything else in the method goes here too
})
And everything else in the method needs to be moved in there too. Everything that is to happen after the completion of the animation goes into the completion function. That is what completion means!
set the label and the button's super view clipsToBounds property to true
UIView.animate(withDuration: 3, delay: 0.0, options: [], animations: {
NSLog("Animation started")
self.labelWinningPlayer.center = CGPoint(x: (self.labelWinningPlayer.center.x) * 3, y: self.labelWinningPlayer.center.y)
self.buttonNextGameLabel.center = CGPoint(x: (self.buttonNextGameLabel.center.x) * 3, y: self.buttonNextGameLabel.center.y)
}, completion: { (finished: Bool) in
NSLog("Animation stopped")
self.labelWinningPlayer.isHidden = true
self.buttonNextGameLabel.isHidden = true
})