I have a subclass of UIView called TitleView, all this subclass does is override layerClass to return CATransformLayer.
My titleView property has some subviews; a titleBackgroundView and a titleLabel.
When I run my code the titleView’s top layer is visible (green background), but when I run my flip animation, there’s no animation. The code just jumps to the end state. Furthermore there’s no bottom layer visible (red background), just a reversed version of the titleView (a transformed titleLabel).
In the IBOutlet setter I have the following code:
#IBOutlet private weak var titleView: TitleView! {
didSet {
titleView.backgroundColor = UIColor.clearColor()
let topLayer = CALayer()
topLayer.backgroundColor = UIColor.greenColor().CGColor
topLayer.frame = titleView.bounds
topLayer.doubleSided = false
topLayer.zPosition = 3
titleView.layer.addSublayer(topLayer)
let bottomLayer = CALayer()
bottomLayer.backgroundColor = UIColor.redColor().CGColor
bottomLayer.frame = titleView.bounds
bottomLayer.doubleSided = false
bottomLayer.zPosition = 2
bottomLayer.transform = CATransform3DMakeRotation(CGFloat(M_PI), 1, 0, 0)
titleView.layer.addSublayer(bottomLayer)
}
}
titleView animation code:
private func setIsCategoriesShowing(showCategories: Bool, animated: Bool)
{
let alreadyInFinishState = (isShowingCategories == showCategories) ? true : false
if alreadyInFinishState
{
return
}
// Setup animations
isAnimatingCategories = true
headerView.superview?.bringSubviewToFront(headerView)
titleView.layer.setAnchorPointDynamically(CGPoint(x: 0.5, y: 1)) // Change position when updating anchor point
// Animate
let duration: NSTimeInterval = animated ? 0.8 : 0
let options: UIViewAnimationOptions = (showCategories == true) ? [.CurveEaseIn] : [.CurveEaseOut]
let newRotationValue: CGFloat = (showCategories == true) ? -179 : 0
let damping: CGFloat = (showCategories == true) ? 0.7 : 1
let initialSpringVelocity: CGFloat = (showCategories == true) ? 0.5 : 1
UIView.animateWithDuration(duration,
delay: 0,
usingSpringWithDamping: damping,
initialSpringVelocity: initialSpringVelocity,
options: options,
animations: { () -> Void in
var rotationAndPerspectiveTransform = CATransform3DIdentity
rotationAndPerspectiveTransform.m34 = 1 / -500
rotationAndPerspectiveTransform = CATransform3DRotate(rotationAndPerspectiveTransform, newRotationValue, 1, 0, 0);
self.titleView.layer.sublayerTransform = rotationAndPerspectiveTransform;
}) { (success) -> Void in
if showCategories == false
{
self.titleView.layer.sublayerTransform = CATransform3DIdentity
}
self.isAnimatingCategories = false
self.isShowingCategories = showCategories
}
}
Okay, so given a series of trial and error I’ve managed to (it seems) fix my problem. Feel free to take a look…
Here is the working code:
#IBOutlet private weak var titleView: TitleView! {
didSet {
let bottomLayer = CALayer()
bottomLayer.backgroundColor = UIColor.redColor().CGColor
bottomLayer.frame = titleView.bounds
bottomLayer.doubleSided = false
bottomLayer.zPosition = 2
bottomLayer.transform = CATransform3DMakeRotation(CGFloat(M_PI), 0, 1, 0) // Reverse bottom layer, so when flipped it's visible.
titleView.layer.addSublayer(bottomLayer)
// All subviews are one-sided
for subview in titleView.subviews
{
subview.layer.doubleSided = false
}
}
}
#IBOutlet private weak var titleViewBackgroundView: UIView!
Animation code:
private func setIsCategoriesShowing(showCategories: Bool, animated: Bool)
{
let alreadyInFinishState = (isShowingCategories == showCategories) ? true : false
if alreadyInFinishState
{
return
}
// Housekeeping
headerView.superview?.bringSubviewToFront(headerView)
titleView.layer.setAnchorPointDynamically(CGPoint(x: 0.5, y: 1))
// Animate
isAnimatingCategories = true
let isOpening = (showCategories == true)
let duration: NSTimeInterval = animated ? 3 : 0
let damping: CGFloat = isOpening ? 0.7 : 1
let initialSpringVelocity: CGFloat = isOpening ? 0.5 : 1
let options: UIViewAnimationOptions = isOpening ? [.CurveEaseIn] : [.CurveEaseOut]
let newRotationValue: CGFloat = isOpening ? -179 : 0
var rotationAndPerspectiveTransform = CATransform3DIdentity
rotationAndPerspectiveTransform.m34 = 1 / -500
rotationAndPerspectiveTransform = CATransform3DRotate(rotationAndPerspectiveTransform, newRotationValue, 1, 0, 0);
UIView.animateWithDuration(duration,
delay: 0,
usingSpringWithDamping: damping,
initialSpringVelocity: initialSpringVelocity,
options: options,
animations: {
self.titleView.layer.transform = rotationAndPerspectiveTransform;
}) { (success) -> Void in
if !isOpening
{
self.titleView.layer.transform = CATransform3DIdentity
}
self.isAnimatingCategories = !success
self.isShowingCategories = showCategories
}
}
Related
I am trying to perform a transition animation whenever a user scrolls on a paginated view, i.e: From Page 1 to Page 2.
Unfortunately, I've not been able to replicate that.
Attach below is what I've done:
class OnboardingParallaxImageView: BaseUIView, UIScrollViewDelegate {
let allImages = [#imageLiteral(resourceName: "onboarding_handshake_icon"), #imageLiteral(resourceName: "onboarding_paylinc_icon")]
var activeCurrentPage = 1
let bgView: UIImageView = {
let image = #imageLiteral(resourceName: "onboard_bg_gradient")
let view = UIImageView(image: image)
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
let firstImageView: UIImageView = {
let view = UIImageView()
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
let secondImageView: UIImageView = {
let view = UIImageView()
view.isHidden = true
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
var firstImageHeightAnchor: NSLayoutConstraint?
var firstImageWidthAnchor: NSLayoutConstraint?
var secondImageHeightAnchor: NSLayoutConstraint?
var secondImageWidthAnchor: NSLayoutConstraint?
override func setupViews() {
super.setupViews()
addSubview(bgView)
addSubview(firstImageView)
addSubview(secondImageView)
bgView.widthAnchor.constraint(equalTo: widthAnchor).isActive = true
bgView.heightAnchor.constraint(equalTo: heightAnchor).isActive = true
bgView.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
bgView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
firstImageWidthAnchor = firstImageView.widthAnchor.constraint(equalTo: widthAnchor)
firstImageWidthAnchor?.isActive = true
firstImageHeightAnchor = firstImageView.heightAnchor.constraint(equalTo: heightAnchor)
firstImageHeightAnchor?.isActive = true
firstImageView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
firstImageView.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
firstImageView.image = allImages[0]
secondImageWidthAnchor = secondImageView.widthAnchor.constraint(equalTo: widthAnchor)
secondImageWidthAnchor?.isActive = true
secondImageHeightAnchor = secondImageView.heightAnchor.constraint(equalTo: heightAnchor)
secondImageHeightAnchor?.isActive = true
secondImageView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
secondImageView.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
secondImageView.image = allImages[1]
}
override func layoutSubviews() {
super.layoutSubviews()
let frameWidth = frame.size.width
secondImageHeightAnchor?.constant = -(frameWidth - 32)
secondImageWidthAnchor?.constant = -(frameWidth - 32)
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let offSet = scrollView.contentOffset.x
let frameWidth = frame.size.width / 2
let toUseConstant = (CGFloat(abs(offSet)) / frameWidth)
if activeCurrentPage == 1 {
if offSet <= 0 {
firstImageHeightAnchor?.constant = 0
firstImageWidthAnchor?.constant = 0
firstImageView.isHidden = false
secondImageView.isHidden = true
} else {
firstImageHeightAnchor?.constant += -(toUseConstant)
firstImageWidthAnchor?.constant += -(toUseConstant)
firstImageView.isHidden = false
secondImageHeightAnchor?.constant += -(toUseConstant)
secondImageWidthAnchor?.constant += -(toUseConstant)
secondImageView.isHidden = false
secondImageView.alpha = toUseConstant
}
}
UIView.animate(withDuration: 0.5) {
self.layoutSubviews()
}
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
self.activeCurrentPage = scrollView.currentPage
}
}
This is the result of what I've been able to achieve:
How can I go about transitioning from A to B without any funny behaviour.
Thanks
The easiest way to achieve smooth behaviour is using UIViewPropertyAnimator.
Setup the initial values for each image view:
override func viewDidLoad() {
super.viewDidLoad()
firstImageView.image = // your image
secondImageView.image = // your image
secondImageView.transform = CGAffineTransform(scaleX: 0.01, y: 0.01)
secondImageView.alpha = 0
}
Then create property animator for each imageView
lazy var firstAnimator: UIViewPropertyAnimator = {
// You can play around with the duration, curve, damping ration, timing
let animator = UIViewPropertyAnimator(duration: 2, curve: .easeIn, animations: {
self.firstImageView.image.transform = CGAffineTransform(scaleX: 0.01, y: 0.01)
self.firstImageView.image.alpha = 0
})
return animator
}()
and the second one
lazy var secondAnimator: UIViewPropertyAnimator = {
let animator = UIViewPropertyAnimator(duration: 2, curve: .easeIn, animations: {
self.secondImageView.transform = CGAffineTransform(scaleX: 1, y: 1)
self.secondImageView.alpha = 1
})
return animator
}()
now on scrollViewDidScroll when you have calculated the completed percent just update the animators:
firstAnimator.fractionComplete = calculatedPosition
secondAnimator.fractionComplete = calculatedPosition
You can apply the same approach for multiple views
You might want to consider using a Framework for this instead of reinventing the wheel. Something like Hero for example.
There as some basic examples to get you started. Essentially this provides you an easy to use option to code transition behavior between two UIViewControllers by defining how each UI-Component should behave base on the transition progress.
I have achieved it using the keyframe animation on ImageView's frame. But you can also achieve it using the constraint based animation as you tried. Just replace the frames size variations that I have done here with the change in constraint's constants as per your convenience.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var imgView1: UIImageView! //HANDSHAKE image on TOP
#IBOutlet weak var imgView2: UIImageView! //CREDIT_CARD and PHONE image
#IBOutlet weak var imgView3: UIImageView! //SINGLE_HAND image
let minimumFrameSize = CGSize(width: 0.0, height: 0.0)
let meetingPointFrameSize = CGSize(width: 150.0, height: 150.0)
let maximumFrameSize = CGSize(width: 400.0, height: 400.0)
override func viewDidLoad() {
super.viewDidLoad()
let centerOfImages = CGPoint(x: self.view.frame.width/2 , y: self.view.frame.height/2)
//initial state
self.imgView1.frame.size = maximumFrameSize
self.imgView1.alpha = 1.0
self.imgView2.frame.size = minimumFrameSize
self.imgView2.alpha = 0.0
self.imgView3.frame.size = minimumFrameSize
self.imgView3.alpha = 0.0
self.imgView1.center = centerOfImages
self.imgView2.center = centerOfImages
self.imgView3.center = centerOfImages
UIView.animateKeyframes(withDuration: 5.0, delay: 2.0, options: [UIView.KeyframeAnimationOptions.repeat,
UIView.KeyframeAnimationOptions.calculationModeLinear],
animations: {
UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: 0.25, animations: {
self.imgView1.frame.size = self.meetingPointFrameSize
self.imgView1.alpha = 0.3
self.imgView1.center = centerOfImages
self.imgView2.frame.size = self.meetingPointFrameSize
self.imgView2.alpha = 0.3
self.imgView2.center = centerOfImages
/*image 1 and image 2 meets at certain point where
image 1 is decreasing its size and image 2 is increasing its size simultaneously
*/
})
UIView.addKeyframe(withRelativeStartTime: 0.25, relativeDuration: 0.25, animations: {
self.imgView1.frame.size = self.minimumFrameSize
self.imgView1.alpha = 0.0
self.imgView1.center = centerOfImages
self.imgView2.frame.size = self.maximumFrameSize
self.imgView2.alpha = 1.0
self.imgView2.center = centerOfImages
/* image 1 has decreased its size to zero and
image 2 has increased its size to maximum simultaneously
*/
})
UIView.addKeyframe(withRelativeStartTime: 0.4, relativeDuration: 0.1, animations: {
self.imgView2.frame.size = self.maximumFrameSize
self.imgView2.alpha = 1.0
self.imgView2.center = centerOfImages
/* Hold for a moment
*/
})
UIView.addKeyframe(withRelativeStartTime: 0.6, relativeDuration: 0.15, animations: {
self.imgView2.frame.size = self.meetingPointFrameSize
self.imgView2.alpha = 0.3
self.imgView2.center = centerOfImages
self.imgView3.frame.size = self.meetingPointFrameSize
self.imgView3.alpha = 0.3
self.imgView3.center = centerOfImages
/*image 2 and image 3 meets at certain point where
image 2 is decreasing its size and image 3 is increasing its size simultaneously
*/
})
UIView.addKeyframe(withRelativeStartTime: 0.75, relativeDuration: 0.25, animations: {
self.imgView2.frame.size = self.minimumFrameSize
self.imgView2.alpha = 0.0
self.imgView2.center = centerOfImages
self.imgView3.frame.size = self.maximumFrameSize
self.imgView3.alpha = 1.0
self.imgView3.center = centerOfImages
/* image 2 has decreased its size to zero and
image 3 has increased its size to maximum simultaneously
*/
})
}) { (finished) in
/*If you have any doubt or need more enhancement or in case you need my project. Feel free to ask me.
Please ping me on skype/email:
ojhashubham29#gmail.com
or connect me on Twitter
#hearthackman
*/
}
}
}
I built a class to implement a circular transition between view controllers. When I hit the button to navigate to the other view controller a circle starts growing from the button until it fills the screen with the new controller. When I dismiss the view controller I expected this circle to shrink down back to the original position. It's also working. The only problem is that when the dismiss is underway the back of the screen while the circle is shrinking is completely black and after the animation is completed the new viewController appears abruptly.
Here are some photos of the effect:
Here's the code of the custom class:
class customTransition: NSObject, UIViewControllerAnimatedTransitioning{
var duration: TimeInterval = 0.5
var startPoint = CGPoint.zero
var circle = UIView()
var circleColor = UIColor.white
enum transitMode: Int {
case presenting, dismissing
}
var transitionMode: transitMode = .presenting
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return duration
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
let container = transitionContext.containerView
guard let to = transitionContext.view(forKey: UITransitionContextViewKey.to) else {return}
guard let from = transitionContext.view(forKey: UITransitionContextViewKey.from) else {return}
circleColor = to.backgroundColor ?? UIColor.white
if transitionMode == .presenting {
to.translatesAutoresizingMaskIntoConstraints = false
to.center = startPoint
circle = UIView()
circle.backgroundColor = circleColor
circle.frame = getFrameForCircle(rect: to.frame)
circle.layer.cornerRadius = circle.frame.width / 2
circle.transform = CGAffineTransform(scaleX: 0.01, y: 0.01)
circle.alpha = 0
circle.addSubview(to)
to.centerXAnchor.constraint(equalTo: circle.centerXAnchor).isActive = true
to.centerYAnchor.constraint(equalTo: circle.centerYAnchor).isActive = true
to.widthAnchor.constraint(equalToConstant: to.frame.width).isActive = true
to.heightAnchor.constraint(equalToConstant: to.frame.height).isActive = true
container.addSubview(circle)
UIView.animate(withDuration: duration, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 0, options: UIView.AnimationOptions.curveLinear, animations: {
self.circle.center = from.center
self.circle.transform = CGAffineTransform.identity
self.circle.alpha = 1
}) { (sucess) in
transitionContext.completeTransition(sucess)
}
} else if transitionMode == .dismissing {
UIView.animate(withDuration: duration, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 0, options: UIView.AnimationOptions.curveLinear, animations: {
self.circle.center = self.startPoint
self.circle.transform = CGAffineTransform(scaleX: 0.001, y: 0.001)
self.circle.alpha = 0
}) { (sucess) in
transitionContext.completeTransition(sucess)
}
}
}
func getFrameForCircle(rect: CGRect) -> CGRect{
let width = Float(rect.width)
let height = Float(rect.height)
let diameter = CGFloat(sqrtf(width * width + height * height))
let x: CGFloat = rect.midX - (diameter / 2)
let y: CGFloat = rect.midY - (diameter / 2)
return CGRect(x: x, y: y, width: diameter, height: diameter)
}
}
and the implementation...
let circularTransition = customTransition()
the call for the present view controller... I tried to set secondVC.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext but when I set this line it ignores completely the animation transition I don't know why...
`
#objc func handlePresent(sender: UIButton){
let secondVC = nextVC()
secondVC.transitioningDelegate = self
present(secondVC, animated: true, completion: nil)
}
delegate methods:
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
circularTransition.startPoint = presentButton.center
circularTransition.transitionMode = .presenting
return circularTransition
}
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
circularTransition.transitionMode = .dismissing
circularTransition.startPoint = presentButton.center
return circularTransition
}
What am I missing here? Any suggestions?
No storyboard being used, just code.
If you don't use navigationController, it's necessary to use the .custom mode in the presentedviewController.
import UIKit
class TransViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
let circularTransition = customTransition()
#IBOutlet var presentButton : UIButton!
#IBAction func handlePresent(sender: UIButton){
if let secondVC = storyboard?.instantiateViewController(withIdentifier: "next"){
secondVC.modalPresentationStyle = .custom
secondVC.transitioningDelegate = self
present(secondVC, animated: true, completion: nil)
}
}
}
class BackViewController: UIViewController {
#IBAction func dismissMe(sender: UIButton){
self.dismiss(animated: true, completion: nil)
}
}
extension TransViewController: UIViewControllerTransitioningDelegate {
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
circularTransition.startPoint = presentButton.center
circularTransition.transitionMode = .presenting
return circularTransition
}
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
circularTransition.transitionMode = .dismissing
circularTransition.startPoint = presentButton.center
return circularTransition
}
}
If there is no from or to view, we have use the from and to view from containView.
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
let container = transitionContext.containerView
var to : UIView!
var from : UIView!
to = transitionContext.view(forKey: UITransitionContextViewKey.to)
if to == nil {to = container}
from = transitionContext.view(forKey: UITransitionContextViewKey.from)
if from == nil {from = container}
The rest is same:
circleColor = to.backgroundColor ?? UIColor.white
if transitionMode == .presenting {
to.translatesAutoresizingMaskIntoConstraints = false
to.center = startPoint
circle = UIView()
circle.backgroundColor = circleColor
circle.frame = getFrameForCircle(rect: to.frame)
circle.layer.cornerRadius = circle.frame.width / 2
circle.transform = CGAffineTransform(scaleX: 0.01, y: 0.01)
circle.alpha = 0
circle.addSubview(to)
to.centerXAnchor.constraint(equalTo: circle.centerXAnchor).isActive = true
to.centerYAnchor.constraint(equalTo: circle.centerYAnchor).isActive = true
to.widthAnchor.constraint(equalToConstant: to.frame.width).isActive = true
to.heightAnchor.constraint(equalToConstant: to.frame.height).isActive = true
container.addSubview(circle)
UIView.animate(withDuration: duration, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 0, options: UIView.AnimationOptions.curveLinear, animations: {
self.circle.center = from.center
self.circle.transform = CGAffineTransform.identity
self.circle.alpha = 1
}) { (sucess) in
transitionContext.completeTransition(sucess)
}
} else if transitionMode == .dismissing {
UIView.animate(withDuration: duration, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 0, options: UIView.AnimationOptions.curveLinear, animations: {
self.circle.center = self.startPoint
self.circle.transform = CGAffineTransform(scaleX: 0.001, y: 0.001)
self.circle.alpha = 0
}) { (sucess) in
transitionContext.completeTransition(sucess)
}
}
}
I am trying to get 3d flip animations among view controllers like this
https://github.com/nicklockwood/CubeController
For the animation.. basing on reverse value I set clock wise direction or anti clock direction
This is the code I am using for animation..
class CusNavAnimController : NSObject, UIViewControllerAnimatedTransitioning {
var reverse: Bool = false
func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
return 0.25
}
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
let containerView = transitionContext.containerView()
let toViewController = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)!
let fromViewController = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)!
let toView = toViewController.view
let fromView = fromViewController.view
//
toView.frame = UIScreen.mainScreen().bounds
fromView.frame = UIScreen.mainScreen().bounds
let direction: CGFloat = reverse ? -1 : 1
let const: CGFloat = -0.005
toView.layer.anchorPoint = CGPointMake(direction == 1 ? 0 : 1, 0.5)
fromView.layer.anchorPoint = CGPointMake(direction == 1 ? 1 : 0, 0.5)
var viewFromTransform: CATransform3D = CATransform3DMakeRotation(direction * CGFloat(M_PI_2), 0.0, 1.0, 0.0)
var viewToTransform: CATransform3D = CATransform3DMakeRotation(-direction * CGFloat(M_PI_2), 0.0, 1.0, 0.0)
viewFromTransform.m34 = const
viewToTransform.m34 = const
containerView!.transform = CGAffineTransformMakeTranslation(direction * containerView!.frame.size.width / 2.0, 0)
toView.layer.transform = viewToTransform
containerView!.addSubview(toView)
UIView.animateWithDuration(transitionDuration(transitionContext), animations: {
containerView!.transform = CGAffineTransformMakeTranslation(-direction * containerView!.frame.size.width / 2.0, 0)
fromView.layer.transform = viewFromTransform
toView.layer.transform = CATransform3DIdentity
}, completion: {
finished in
containerView!.transform = CGAffineTransformIdentity
fromView.layer.transform = CATransform3DIdentity
toView.layer.transform = CATransform3DIdentity
fromView.layer.anchorPoint = CGPointMake(0.5, 0.5)
toView.layer.anchorPoint = CGPointMake(0.5, 0.5)
if (transitionContext.transitionWasCancelled()) {
toView.removeFromSuperview()
} else {
fromView.removeFromSuperview()
}
transitionContext.completeTransition(!transitionContext.transitionWasCancelled())
})
}}
I am getting the animation but It is not same as the git hub link providing..
For my code.. the fromview is slightly going back and toview coming to front from that point.. can anyone suggest me the changes
Check out TransitionFlipFromLeft and TransitionFlipFromRight in UIViewAnimationOptions
I've made a rotating button. But while button rotates I can't press it. So The question is how I can make it "pressable"?
My animation code is:
var max: Bool = true
func startAnimation() {
max = !max
let duration: Double = 1
let fullCircle = 2 * M_PI
let upOrDown = (max ? CGFloat(-1 / 16 * fullCircle) : CGFloat(1 / 16 * fullCircle))
let scale: (CGFloat, CGFloat) = (max ? (1.0, 1.0) : (1.3, 1.3))
UIView.animateWithDuration(duration, animations: { () -> Void in
let rotationAnimation = CGAffineTransformMakeRotation(upOrDown)
let scaleAnimation = CGAffineTransformMakeScale(scale)
self.startButton.transform = CGAffineTransformConcat(rotationAnimation, scaleAnimation)
}) { (finished) -> Void in
self.startAnimation()
}
So if I press button there is no any effect that it has happened. No text inside button text animation - nothing! But button keep rotating and don't perform segue to other scene. But if it is no animation I can do segue.
Use the animation option UIViewAnimationOptionAllowUserInteraction.
UIView.animateWithDuration(duration, delay:, options: UIViewAnimationOptions.AllowUserInteraction, animations: , completion: )
The final code works fine and looks like:
var max: Bool = true
func startAnimation() {
max = !max
let duration: Double = 1
let fullCircle = 2 * M_PI
let upOrDown = (max ? CGFloat(-1 / 16 * fullCircle) : CGFloat(1 / 16 * fullCircle))
let scale: (CGFloat, CGFloat) = (max ? (1.0, 1.0) : (1.3, 1.3))
UIView.animateWithDuration(duration, delay: 0, options: UIViewAnimationOptions.AllowUserInteraction, animations: { () -> Void in
let rotationAnimation = CGAffineTransformMakeRotation(upOrDown)
let scaleAnimation = CGAffineTransformMakeScale(scale)
self.startButton.transform = CGAffineTransformConcat(rotationAnimation, scaleAnimation)
}) { (finished) -> Void in
self.startAnimation()
}
If you are using UIViewPropertyAnimator the following might also help you
let animator = UIViewPropertyAnimator(duration: 2, curve: .easeInOut) {
// animation code
}
animator.isUserInteractionEnabled = true
animator.startAnimation()
I have a UITableView with some label and images on it.
And I want to show image fullscreen when user tap on image.
I'am trying to achieve animation effect like when you tap on images on twitter account.
I've tried two days, but i couldn't find desire effect. This is my code right now :
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
println("animating transition")
var containerView = transitionContext.containerView()
var toViewController = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)!
var fromViewController = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)!
var window = UIApplication.sharedApplication().keyWindow
var imageCenter = imageViewToSegue.center
var frame = window.convertRect(imageViewToSegue.frame, fromView: fromView)
var www = imageViewToSegue.image?.size.width
var hhh = imageViewToSegue.image?.size.height
var index = www! / self.view.frame.width
if (isPresenting) {
var copyImageView = UIImageView(frame: imageViewToSegue.frame)
copyImageView.frame.origin = view.convertPoint(imageViewToSegue.frame.origin, fromView: fromView)
copyImageView.image = imageViewToSegue.image
copyImageView.contentMode = imageViewToSegue.contentMode
copyImageView.clipsToBounds = true
copyImageView.userInteractionEnabled = true
window.addSubview(copyImageView)
containerView.addSubview(toViewController.view)
toViewController.view.alpha = 0
if let vc = toViewController as? ImageViewController {
vc.imageView.hidden = true
}
UIView.animateWithDuration(0.4, animations: { () -> Void in
copyImageView.frame = CGRect(x: 0, y: (self.view.bounds.height / 2) - ( hhh!/(index * 2)), width: www!/index , height: hhh!/index)
toViewController.view.alpha = 1
}) { (finished: Bool) -> Void in
copyImageView.removeFromSuperview()
if let vc = toViewController as? ImageViewController {
vc.imageView.hidden = false
}
transitionContext.completeTransition(true)
}
} else {
let vc = fromViewController as ImageViewController
var copyImageView = UIImageView(frame: vc.imageView.frame)
copyImageView.frame = CGRect(x: 0, y: (self.view.bounds.height / 2) - ( hhh!/(index * 2)), width: www!/index , height: hhh!/index)
copyImageView.image = imageViewToSegue.image
copyImageView.contentMode = imageViewToSegue.contentMode
copyImageView.clipsToBounds = true
copyImageView.userInteractionEnabled = true
window.addSubview(copyImageView)
vc.imageView.hidden = true
UIView.animateWithDuration(0.4, animations: { () -> Void in
fromViewController.view.alpha = 0
copyImageView.frame = frame
}) { (finished: Bool) -> Void in
copyImageView.removeFromSuperview()
transitionContext.completeTransition(true)
fromViewController.view.removeFromSuperview()
}
}
}