Swift: Is it possible to remove View Controller from stack? - ios

I found a Github repository where the user has created a launch animation like in twitters app: This
This works best if the icon is a solid shape and being centered in the screen.
My problem is that I can center my icon because how its designed. So when my animation is finished the app will still have some masking left on the screen from the previously View Controller that made the animation.
So is it possible to remove that View Controller completely once the animation is done and the app hits the table view View Controller?
Here is my code:
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window!.backgroundColor = UIColor(red: 241/255, green: 196/255, blue: 15/255, alpha: 1)
self.window!.makeKeyAndVisible()
// rootViewController from StoryBoard
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
var navigationController = mainStoryboard.instantiateViewControllerWithIdentifier("navigationController") as! UIViewController
self.window!.rootViewController = navigationController
// logo mask
navigationController.view.layer.mask = CALayer()
navigationController.view.layer.mask.contents = UIImage(named: "logo.png")!.CGImage
navigationController.view.layer.mask.bounds = CGRect(x: 0, y: 0, width: 10, height: 10)
navigationController.view.layer.mask.anchorPoint = CGPoint(x: 0.5, y: 0.5)
navigationController.view.layer.mask.position = CGPoint(x: navigationController.view.frame.width / 2, y: navigationController.view.frame.height / 2)
// logo mask background view
var maskBgView = UIView(frame: navigationController.view.frame)
maskBgView.backgroundColor = UIColor.whiteColor()
navigationController.view.addSubview(maskBgView)
navigationController.view.bringSubviewToFront(maskBgView)
// logo mask animation
let transformAnimation = CAKeyframeAnimation(keyPath: "bounds")
transformAnimation.delegate = self
transformAnimation.duration = 1
transformAnimation.beginTime = CACurrentMediaTime() + 1 //add delay of 1 second
let initalBounds = NSValue(CGRect: navigationController.view.layer.mask.bounds)
let secondBounds = NSValue(CGRect: CGRect(x: 0, y: 0, width: 50, height: 50))
let finalBounds = NSValue(CGRect: CGRect(x: 0, y: 0, width: 2000, height: 2000))
transformAnimation.values = [initalBounds, secondBounds, finalBounds]
transformAnimation.keyTimes = [0, 0.5, 1]
transformAnimation.timingFunctions = [CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut), CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)]
transformAnimation.removedOnCompletion = false
transformAnimation.fillMode = kCAFillModeForwards
navigationController.view.layer.mask.addAnimation(transformAnimation, forKey: "maskAnimation")
// logo mask background view animation
UIView.animateWithDuration(0.1,
delay: 1.35,
options: UIViewAnimationOptions.CurveEaseIn,
animations: {
maskBgView.alpha = 0.0
},
completion: { finished in
maskBgView.removeFromSuperview()
})
// root view animation
UIView.animateWithDuration(0.25,
delay: 1.3,
options: UIViewAnimationOptions.TransitionNone,
animations: {
self.window!.rootViewController!.view.transform = CGAffineTransformMakeScale(1.05, 1.05)
},
completion: { finished in
UIView.animateWithDuration(0.3,
delay: 0.0,
options: UIViewAnimationOptions.CurveEaseInOut,
animations: {
self.window!.rootViewController!.view.transform = CGAffineTransformIdentity
},
completion: nil
)
})
And the problem is worse on iPad where the screen is bigger = more masking left on the screen.

You need to remove the mask after the animation is done.
UIView.animateWithDuration(0.25,
delay: 1.3,
options: UIViewAnimationOptions.TransitionNone,
animations: {
self.window!.rootViewController!.view.transform = CGAffineTransformMakeScale(1.05, 1.05)
},
completion: { finished in
UIView.animateWithDuration(0.3,
delay: 0.0,
options: UIViewAnimationOptions.CurveEaseInOut,
animations: {
self.window!.rootViewController!.view.transform = CGAffineTransformIdentity
},
completion: {
// ADDED THIS LINE
self.window!.rootViewController!.view.layer.mask = nil
}
)
})

Related

Custom VC Transition not Dismissing Properly

I am trying to recreate the stock UIAlertAction in a VC with a custom transition. I currently have the presenting working perfectly(backgroundView fades in and the 'notification' VC slides from the bottom). The problem I am facing is when I dismiss the VC the backgroundView doesn't fade out. It's as if it's completely bypassing my animation block. Once completeTransition is called the backgroundView disappears completely. What is happening?
class AnimationController: NSObject {
let backgroundView = UIView()
}
extension AnimationController: UIViewControllerAnimatedTransitioning {
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
let containerView = transitionContext.containerView
guard let toViewController = transitionContext.viewController(forKey: .to),
let fromViewController = transitionContext.viewController(forKey: .from) else {
transitionContext.completeTransition(false)
return
}
switch animationType {
case .present:
backgroundView.frame = CGRect(x: 0, y: 0, width: toViewController.view.frame.width, height: fromViewController.view.frame.height)
backgroundView.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.4)
backgroundView.alpha = 0
containerView.addSubview(backgroundView)
let viewToAnimate = toViewController.view!
containerView.addSubview(viewToAnimate)
toViewController.view.clipsToBounds = true
viewToAnimate.frame = CGRect(x: 0, y: viewToAnimate.frame.maxY, width: viewToAnimate.frame.width, height: viewToAnimate.frame.height)
let duration = transitionDuration(using: transitionContext)
UIView.animate(withDuration: duration, delay: 0, usingSpringWithDamping: 0.80, initialSpringVelocity: 0.1, options: .curveEaseInOut, animations: {
self.backgroundView.alpha = 1.0
viewToAnimate.transform = CGAffineTransform(translationX: 0, y: -viewToAnimate.frame.height)
}) { _ in
transitionContext.completeTransition(true)
}
case .dismiss:
containerView.addSubview(fromViewController.view!)
let testView = fromViewController.view!
backgroundView.frame = CGRect(x: 0, y: 0, width: testView.frame.width, height: testView.frame.height)
backgroundView.backgroundColor = UIColor(red: 1, green: 0, blue: 0, alpha: 1)
let duration = transitionDuration(using: transitionContext)
UIView.animate(withDuration: duration, delay: 0, usingSpringWithDamping: 0.80, initialSpringVelocity: 0.1, options: .curveEaseInOut, animations: {
self.backgroundView.alpha = 0.0
testView.transform = CGAffineTransform(translationX: 0, y: testView.frame.height)
print("backgroundView Doesn't fade out")
}) { _ in
print("backgroundView disappears here")
transitionContext.completeTransition(true)
}
}
}
Your problem is that you're dealing with two separate instances of your AnimationController and hence two separate instances of backgroundView. One instance is instantiated for presentation and the other is instantiated for dismissal. Notice in your case .present: block you are calling containerView.addSubview(backgroundView) but you don't do the same in the case .dismiss: block. As a result, the backgroundView instance in the case .dismiss: block isn't even part of the view hierarchy.
What you need to do is call self.backgroundView.removeFromSuperview() in your first animation completion block. Then in your case .dismiss: you need to call containerView.addSubview(backgroundView) once again.
Try this...
switch animationType {
case .present:
// ...
containerView.addSubview(backgroundView)
// ...
UIView.animate(withDuration: duration, delay: 0, usingSpringWithDamping: 0.80, initialSpringVelocity: 0.1, options: .curveEaseInOut, animations: {
self.backgroundView.alpha = 1.0
viewToAnimate.transform = CGAffineTransform(translationX: 0, y: -viewToAnimate.frame.height)
}) { _ in
self.backgroundView.removeFromSuperview()
transitionContext.completeTransition(true)
}
case .dismiss:
// ...
containerView.addSubview(backgroundView)
containerView.addSubview(fromViewController.view!)
// ...
UIView.animate(withDuration: duration, delay: 0, usingSpringWithDamping: 0.80, initialSpringVelocity: 0.1, options: .curveEaseInOut, animations: {
self.backgroundView.alpha = 0.0
testView.transform = CGAffineTransform(translationX: 0, y: testView.frame.height)
print("backgroundView Doesn't fade out")
}) { _ in
print("backgroundView disappears here")
transitionContext.completeTransition(true)
}
}
you could try to present like this
//where DevolucionVC is you ViewController
if let controller = DevolucionVC() as? DevolucionVC {
if let window = self.window, let rootViewController = window.rootViewController {
var currentController = rootViewController
while let presentedController = currentController.presentedViewController {
currentController = presentedController
}
currentController.present(controller, animated: true, completion: nil)
}
}
you can dismiss it later by simply use
dismiss(animated: true, completion: nil)
Try This
for present replace this code
backgroundView.frame = CGRect(x: 0, y: 0, width: toViewController.view.frame.width, height: fromViewController.view.frame.height)
backgroundView.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.4)
backgroundView.alpha = 0
containerView.addSubview(backgroundView)
let transition = CATransition()
transition.duration = 0.5
transition.type = CATransitionType.moveIn
transition.subtype = CATransitionSubtype.fromTop
containerView.layer.add(transition, forKey: kCATransition)
For Dismiss Replace this code
containerView.addSubview(fromViewController.view!)
let transition = CATransition()
transition.duration = 0.5
transition.type = CATransitionType.fade
containerView.layer.add(transition, forKey: kCATransition)
backgroundView.frame = CGRect(x: 0, y: 0, width: testView.frame.width, height: testView.frame.height)
backgroundView.backgroundColor = UIColor(red: 1, green: 0, blue: 0, alpha: 1)
self.backgroundView.alpha = 0.0

Run two animations simultaneously

I'm trying to create an animation with two views and I've encountered some unexpected behaviors while performing them.
I want to animate both views position while doing a second animation which is transitionFlipFromBottom
Here's the code:
let initialFrame = CGRect(x: xpos, y: -310, width: 300, height: 300)
let firstView = UIView()
firstView.backgroundColor = .red
firstView.frame = initialFrame
let secondView = UIView()
secondView.backgroundColor = .white
secondView.frame = initialFrame
secondView.isHidden = false
self.view.addSubview(firstView)
self.view.addSubview(secondView)
// Here I try to move the views on screen while fliping them
UIView.animate(withDuration: 1, delay: 0, options: .curveEaseOut, animations: {
secondView.center = self.view.center
firstView.center = self.view.center
self.flip(firstView: firstView, secondView: secondView)
}, completion: nil)
// This function flips the views vertically while it animates the transition from the first to the second view
fileprivate func flip(firstView: UIView, secondView: UIView) {
let transitionOptions: UIViewAnimationOptions = [.transitionFlipFromBottom, .showHideTransitionViews]
UIView.transition(with: firstView, duration: 0.5, options: transitionOptions, animations: {
firstView.isHidden = true
})
UIView.transition(with: secondView, duration: 0.5, options: transitionOptions, animations: {
secondView.isHidden = false
})
}
The code above fails to execute both animations at the same time.
It only works if I place the flip function call inside the completion block, after the first animation (moving frame) finishes, as the following:
UIView.animate(withDuration: 1, delay: 0, options: .curveEaseOut, animations: {
secondView.center = self.view.center
firstView.center = self.view.center
}, completion: {(_) in
self.flip(firstView: dummyView, secondView: newGroupView)
})
I have even tried to use UIView.animateKeyframes but it still doesn't work.
Am I missing something here?
Thank you.
A couple of things:
In transition, specify .allowAnimatedContent option.
Defer the animation:
DispatchQueue.main.async {
UIView.animate(withDuration: 1, delay: 0, options: [.curveEaseOut], animations: {
secondView.center = CGPoint(x: self.view.bounds.midX, y: self.view.bounds.midY)
firstView.center = CGPoint(x: self.view.bounds.midX, y: self.view.bounds.midY)
self.flip(firstView: firstView, secondView: secondView)
}, completion: { _ in
})
}
Somewhat unrelated, you don't want:
secondView.center = self.view.center
Instead, do:
secondView.center = CGPoint(x: self.view.bounds.midX, y: self.view.bounds.midY)
You want to set secondView.center in the coordinate space of the bounds of view, not in view's superview.

Custom Segue, Multiple Animations Not Syncing

There are a lot of excellent answers on this topic, but this one has me confused.
There are two commented out lines which don't seem to work properly. I'd like this custom segue to both slide AND shrink so that the effect is for the fromVC to disappear up into nothing and the toVC to arrive from nothing from the bottom.
However, if I try to do both simultaneously it fails. Either individually work fine (a zoom or a slide), but not together.
class UnwindScaleSegue: UIStoryboardSegue {
override func perform() {
scaleDown()
}
func scaleDown() {
let toVC = self.destination
let fromVC = self.source
let screenHeight = UIScreen.main.bounds.size.height
toVC.view.transform = CGAffineTransform.init(translationX: 0.0, y: screenHeight)
//toVC.view.transform = CGAffineTransform(scaleX: 0.001, y: 0.001)
fromVC.view.superview?.insertSubview(toVC.view, at: 0)
UIView.animate(withDuration: 0.5, delay: 0, options: .curveEaseInOut, animations: {
//fromVC.view.transform = CGAffineTransform(scaleX: 0.001, y: 0.001)
fromVC.view.transform = CGAffineTransform.init(translationX: 0.0, y: -screenHeight)
toVC.view.transform = CGAffineTransform.identity
}, completion: { success in
fromVC.dismiss(animated: false, completion: nil)
})
}
}
Solved! It turns out it has to do with how the transforms are combined. I'm not certain I understand, but something having to do with the vector nature of the transformations means you have to be careful when doing multiple transforms in the same animation.
By "concatenating" them carefully it works! However, for me, if I swapped the order, it didn't work.
The key lines are starred.
class UnwindScaleSegue: UIStoryboardSegue {
override func perform() {
scaleAway()
}
func scaleAway() {
let toVC = self.destination
let fromVC = self.source
let screenHeight = UIScreen.main.bounds.size.height
var translate = CGAffineTransform(translationX: 0.0, y:screenHeight) //**
let scale = CGAffineTransform(scaleX: 0.001, y: 0.001) //**
toVC.view.transform = scale.concatenating(translate) //**
fromVC.view.superview?.insertSubview(toVC.view, at: 0)
UIView.animate(withDuration: 0.5, delay: 0, options: .curveEaseInOut, animations: {
translate = CGAffineTransform(translationX: 0.0, y: -screenHeight) //**
fromVC.view.transform = scale.concatenating(translate) //**
toVC.view.transform = CGAffineTransform.identity
}, completion: { success in
fromVC.dismiss(animated: false, completion: nil)
})
}
}

I have an error to add a root view handler in AppDelegate

I am developing an application in which the launch screen is similar to Twitter and I wanted to implement a navigation drawer with MMDrawer, but I have an error that says
"UIViewControllerHierarchyInconsistency ', reason:' adding a root view controller as a child of view controller: '"
I attached the AppDelegate code
I hope you can help me, thank you very much for reading me! regards.
//
// AppDelegate.swift
// Rodrigo
//
// Created by expo on 02/03/17.
// Copyright © 2017 Rodrigo. All rights reserved.
//
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var drawerContainer:MMDrawerController?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
//Personaliza la vista
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window! .backgroundColor = UIColor(red: 35/255, green: 34/255, blue: 34/255, alpha:1)
self.window!.makeKeyAndVisible()
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
//Recuerda al usuario
let userId = UserDefaults.standard.string(forKey: "userId")
if (userId != nil )
{
buildNavigationDrawer()
}
//Fin de recordar al usuario
else{
//rootViewController from storyboard
let navigationController = mainStoryboard.instantiateViewController(withIdentifier: "navigationController")
self.window!.rootViewController = navigationController
//logomask
navigationController.view.layer.mask = CALayer()
navigationController.view.layer.mask?.contents = UIImage(named: "logoGrande500pts")!.cgImage
navigationController.view.layer.mask?.bounds = CGRect(x:0, y:0, width: 60, height: 60)
navigationController.view.layer.mask?.anchorPoint = CGPoint (x: 0.5, y: 0.5)
navigationController.view.layer.mask?.position = CGPoint (x: navigationController.view.frame.width / 2, y: navigationController.view.frame.height / 2)
//logo mask background view
let maskBigView = UIView(frame: navigationController.view.frame)
maskBigView.backgroundColor = UIColor.white
navigationController.view.addSubview(maskBigView)
navigationController.view.bringSubview(toFront: maskBigView)
//logo mask animation
let transformAnimation = CAKeyframeAnimation (keyPath: "bounds")
transformAnimation.duration = 0.5
transformAnimation.beginTime = CACurrentMediaTime() + 1
let initalBounds = NSValue(cgRect: (navigationController.view.layer.mask?.bounds)!)
let secondBounds = NSValue(cgRect: CGRect(x: 0, y: 0, width: 50, height: 50))
let finalBounds = NSValue(cgRect: CGRect(x: 0, y: 0, width: 2000, height: 2000))
transformAnimation.values = [initalBounds, secondBounds, finalBounds]
transformAnimation.keyTimes = [0, 0.5, 1]
transformAnimation.timingFunctions = [CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut), CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)]
transformAnimation.isRemovedOnCompletion = false
transformAnimation.fillMode = kCAFillModeForwards
navigationController.view.layer.mask?.add(transformAnimation, forKey: "maskAnimation")
UIView.animate(withDuration: 0.1, delay: 1.35, options: UIViewAnimationOptions.curveEaseIn,
animations: {
maskBigView.alpha = 0.0
},
completion: {finished in
maskBigView.removeFromSuperview()
})
UIView.animate(withDuration: 0.25,
delay: 1.3,
options: UIViewAnimationOptions(),
animations: {
self.window!.rootViewController!.view.transform = CGAffineTransform(scaleX: 1.05, y: 1.05)
},
completion: {finished in
UIView.animate(withDuration: 0.3,
delay: 0.0,
options : UIViewAnimationOptions.curveEaseInOut,
animations: {
self.window!.rootViewController!.view.transform = CGAffineTransform.identity
},
completion: nil
)
})
}
// Override point for customization after application launch.
return true
}
func applicationWillResignActive(_ application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}
func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
func CAAnimationDelegate (anim: CAAnimation!, finished flag: Bool){
self.window!.rootViewController!.view.layer.mask=nil
}
func buildNavigationDrawer(){
//Personaliza la vista
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window! .backgroundColor = UIColor(red: 35/255, green: 34/255, blue: 34/255, alpha:1)
self.window!.makeKeyAndVisible()
let mainStoryBoard:UIStoryboard = UIStoryboard(name:"Main", bundle:nil)
let navigationController:PrincipalViewController = mainStoryBoard.instantiateViewController(withIdentifier: "PrincipalViewController") as! PrincipalViewController
let leftSideMenu:LeftSideViewController = mainStoryBoard.instantiateViewController(withIdentifier: "LeftSideViewController") as! LeftSideViewController
let rightSideMenu:RightSideViewController = mainStoryBoard.instantiateViewController(withIdentifier: "RightSideViewController") as! RightSideViewController
let mainPageNav = UINavigationController(rootViewController: navigationController)
window?.rootViewController = drawerContainer
self.window?.rootViewController = mainPageNav
let leftSideMenuNav = UINavigationController(rootViewController:leftSideMenu)
let rightSideMenuNav = UINavigationController(rootViewController:rightSideMenu)
drawerContainer = MMDrawerController(center: mainPageNav, leftDrawerViewController: leftSideMenuNav, rightDrawerViewController: rightSideMenuNav)
drawerContainer!.openDrawerGestureModeMask = MMOpenDrawerGestureMode.panningCenterView
drawerContainer!.closeDrawerGestureModeMask = MMCloseDrawerGestureMode.panningCenterView
/*
let navigationController = mainStoryboard.instantiateViewController(withIdentifier: "PrincipalViewController") as! PrincipalViewController
let mainPageNav = UINavigationController(rootViewController: navigationController)
self.window?.rootViewController = mainPageNav
*/
//logomask
navigationController.view.layer.mask = CALayer()
navigationController.view.layer.mask?.contents = UIImage(named: "logoGrande500pts")!.cgImage
navigationController.view.layer.mask?.bounds = CGRect(x:0, y:0, width: 60, height: 60)
navigationController.view.layer.mask?.anchorPoint = CGPoint (x: 0.5, y: 0.5)
navigationController.view.layer.mask?.position = CGPoint (x: navigationController.view.frame.width / 2, y: navigationController.view.frame.height / 2)
//logo mask background view
let maskBigView = UIView(frame: navigationController.view.frame)
maskBigView.backgroundColor = UIColor.white
navigationController.view.addSubview(maskBigView)
navigationController.view.bringSubview(toFront: maskBigView)
//logo mask animation
let transformAnimation = CAKeyframeAnimation (keyPath: "bounds")
transformAnimation.duration = 0.5
transformAnimation.beginTime = CACurrentMediaTime() + 1
let initalBounds = NSValue(cgRect: (navigationController.view.layer.mask?.bounds)!)
let secondBounds = NSValue(cgRect: CGRect(x: 0, y: 0, width: 50, height: 50))
let finalBounds = NSValue(cgRect: CGRect(x: 0, y: 0, width: 2000, height: 2000))
transformAnimation.values = [initalBounds, secondBounds, finalBounds]
transformAnimation.keyTimes = [0, 0.5, 1]
transformAnimation.timingFunctions = [CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut), CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)]
transformAnimation.isRemovedOnCompletion = false
transformAnimation.fillMode = kCAFillModeForwards
navigationController.view.layer.mask?.add(transformAnimation, forKey: "maskAnimation")
UIView.animate(withDuration: 0.1, delay: 1.35, options: UIViewAnimationOptions.curveEaseIn,
animations: {
maskBigView.alpha = 0.0
},
completion: {finished in
maskBigView.removeFromSuperview()
})
UIView.animate(withDuration: 0.25,
delay: 1.3,
options: UIViewAnimationOptions(),
animations: {
self.window!.rootViewController!.view.transform = CGAffineTransform(scaleX: 1.05, y: 1.05)
},
completion: {finished in
UIView.animate(withDuration: 0.3,
delay: 0.0,
options : UIViewAnimationOptions.curveEaseInOut,
animations: {
self.window!.rootViewController!.view.transform = CGAffineTransform.identity
},
completion: nil
)
})
}
}
The error you are getting is pretty clear.
The problem is here:
window?.rootViewController = drawerContainer
self.window?.rootViewController = mainPageNav
You are setting the rootViewController and then on the very next line, setting it again with something different.
Then, a few lines later, you do this:
drawerContainer = MMDrawerController(center: mainPageNav, leftDrawerViewController: leftSideMenuNav, rightDrawerViewController: rightSideMenuNav)
You are taking the rootViewController of your window, and trying to add it as a child to another container view controller.
Obvious fixes:
Remove this line:
self.window?.rootViewController = mainPageNav
Change the order of the rootViewController assignment and drawerContainer instantiation. Right now, you are throwing away whatever the original drawerContainer was (if it had a value at all).
drawerContainer = MMDrawerController(center: mainPageNav, leftDrawerViewController: leftSideMenuNav, rightDrawerViewController: rightSideMenuNav)
needs to come before
window?.rootViewController = drawerContainer
There are probably more problems with your code, but these 2 are issues for sure.
I enclose my code with the solution
//
// AppDelegate.swift
// Expodiseño
//
// Created by expo on 02/03/17.
// Copyright © 2017 Creategia360. All rights reserved.
//
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var drawerContainer:MMDrawerController?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
//Personaliza la vista
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window! .backgroundColor = UIColor(red: 35/255, green: 34/255, blue: 34/255, alpha:1)
self.window!.makeKeyAndVisible()
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
//Recuerda al usuario
let userId = UserDefaults.standard.string(forKey: "userId")
if (userId != nil )
{
//let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let navigationController = mainStoryboard.instantiateViewController(withIdentifier: "PrincipalViewController") as! PrincipalViewController
let mainPageNav = UINavigationController(rootViewController: navigationController)
self.window?.rootViewController = mainPageNav
//logomask
navigationController.view.layer.mask = CALayer()
navigationController.view.layer.mask?.contents = UIImage(named: "logoGrande500pts")!.cgImage
navigationController.view.layer.mask?.bounds = CGRect(x:0, y:0, width: 60, height: 60)
navigationController.view.layer.mask?.anchorPoint = CGPoint (x: 0.5, y: 0.5)
navigationController.view.layer.mask?.position = CGPoint (x: navigationController.view.frame.width / 2, y: navigationController.view.frame.height / 2)
//logo mask background view
let maskBigView = UIView(frame: navigationController.view.frame)
maskBigView.backgroundColor = UIColor.white
navigationController.view.addSubview(maskBigView)
navigationController.view.bringSubview(toFront: maskBigView)
//logo mask animation
let transformAnimation = CAKeyframeAnimation (keyPath: "bounds")
transformAnimation.duration = 0.5
transformAnimation.beginTime = CACurrentMediaTime() + 1
let initalBounds = NSValue(cgRect: (navigationController.view.layer.mask?.bounds)!)
let secondBounds = NSValue(cgRect: CGRect(x: 0, y: 0, width: 50, height: 50))
let finalBounds = NSValue(cgRect: CGRect(x: 0, y: 0, width: 2000, height: 2000))
transformAnimation.values = [initalBounds, secondBounds, finalBounds]
transformAnimation.keyTimes = [0, 0.5, 1]
transformAnimation.timingFunctions = [CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut), CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)]
transformAnimation.isRemovedOnCompletion = false
transformAnimation.fillMode = kCAFillModeForwards
navigationController.view.layer.mask?.add(transformAnimation, forKey: "maskAnimation")
UIView.animate(withDuration: 0.1, delay: 1.35, options: UIViewAnimationOptions.curveEaseIn,
animations: {
maskBigView.alpha = 0.0
},
completion: {finished in
maskBigView.removeFromSuperview()
})
UIView.animate(withDuration: 0.25,
delay: 1.3,
options: UIViewAnimationOptions(),
animations: {
self.window!.rootViewController!.view.transform = CGAffineTransform(scaleX: 1.05, y: 1.05)
},
completion: {finished in
UIView.animate(withDuration: 0.3,
delay: 0.0,
options : UIViewAnimationOptions.curveEaseInOut,
animations: {
self.window!.rootViewController!.view.transform = CGAffineTransform.identity
},
completion: nil
)
})
buildNavigationDrawer()
}
//Fin de recordar al usuario
else{
//rootViewController from storyboard
let navigationController = mainStoryboard.instantiateViewController(withIdentifier: "navigationController")
self.window!.rootViewController = navigationController
//logomask
navigationController.view.layer.mask = CALayer()
navigationController.view.layer.mask?.contents = UIImage(named: "logoGrande500pts")!.cgImage
navigationController.view.layer.mask?.bounds = CGRect(x:0, y:0, width: 60, height: 60)
navigationController.view.layer.mask?.anchorPoint = CGPoint (x: 0.5, y: 0.5)
navigationController.view.layer.mask?.position = CGPoint (x: navigationController.view.frame.width / 2, y: navigationController.view.frame.height / 2)
//logo mask background view
let maskBigView = UIView(frame: navigationController.view.frame)
maskBigView.backgroundColor = UIColor.white
navigationController.view.addSubview(maskBigView)
navigationController.view.bringSubview(toFront: maskBigView)
//logo mask animation
let transformAnimation = CAKeyframeAnimation (keyPath: "bounds")
transformAnimation.duration = 0.5
transformAnimation.beginTime = CACurrentMediaTime() + 1
let initalBounds = NSValue(cgRect: (navigationController.view.layer.mask?.bounds)!)
let secondBounds = NSValue(cgRect: CGRect(x: 0, y: 0, width: 50, height: 50))
let finalBounds = NSValue(cgRect: CGRect(x: 0, y: 0, width: 2000, height: 2000))
transformAnimation.values = [initalBounds, secondBounds, finalBounds]
transformAnimation.keyTimes = [0, 0.5, 1]
transformAnimation.timingFunctions = [CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut), CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)]
transformAnimation.isRemovedOnCompletion = false
transformAnimation.fillMode = kCAFillModeForwards
navigationController.view.layer.mask?.add(transformAnimation, forKey: "maskAnimation")
UIView.animate(withDuration: 0.1, delay: 1.35, options: UIViewAnimationOptions.curveEaseIn,
animations: {
maskBigView.alpha = 0.0
},
completion: {finished in
maskBigView.removeFromSuperview()
})
UIView.animate(withDuration: 0.25,
delay: 1.3,
options: UIViewAnimationOptions(),
animations: {
self.window!.rootViewController!.view.transform = CGAffineTransform(scaleX: 1.05, y: 1.05)
},
completion: {finished in
UIView.animate(withDuration: 0.3,
delay: 0.0,
options : UIViewAnimationOptions.curveEaseInOut,
animations: {
self.window!.rootViewController!.view.transform = CGAffineTransform.identity
},
completion: nil
)
})
}
// Override point for customization after application launch.
return true
}
func applicationWillResignActive(_ application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}
func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
func CAAnimationDelegate (anim: CAAnimation!, finished flag: Bool){
self.window!.rootViewController!.view.layer.mask=nil
}
func buildNavigationDrawer(){
let mainStoryBoard:UIStoryboard = UIStoryboard(name:"Main", bundle:nil)
let navigationController:PrincipalViewController = mainStoryBoard.instantiateViewController(withIdentifier: "PrincipalViewController") as! PrincipalViewController
let leftSideMenu:LeftSideViewController = mainStoryBoard.instantiateViewController(withIdentifier: "LeftSideViewController") as! LeftSideViewController
let rightSideMenu:RightSideViewController = mainStoryBoard.instantiateViewController(withIdentifier: "RightSideViewController") as! RightSideViewController
let mainPageNav = UINavigationController(rootViewController: navigationController)
let leftSideMenuNav = UINavigationController(rootViewController:leftSideMenu)
let rightSideMenuNav = UINavigationController(rootViewController:rightSideMenu)
// self.window!.rootViewController = navigationController
drawerContainer = MMDrawerController(center: mainPageNav, leftDrawerViewController: leftSideMenuNav, rightDrawerViewController: rightSideMenuNav)
drawerContainer!.openDrawerGestureModeMask = MMOpenDrawerGestureMode.panningCenterView
drawerContainer!.closeDrawerGestureModeMask = MMCloseDrawerGestureMode.panningCenterView
window?.rootViewController = drawerContainer
//self.window?.rootViewController = mainPageNav
}
}

Combine translation, alpha and scale animations in swift3

I'm totally newbies with iOS Swift developement and i try to combine three parameters in a single animations but i don't succeed.
I think the solution is here -Apple Dev Core Animation Programming Guide by grouping the animations but being a beginner and after a lot of Internet research i can't find what i'm looking for.
What do you think of my code and what is for you the best solution to combine performance and stability.
I want to point out that the purpose of this animation is to create an animated Splashscreen. There are other elements (UIImage) that will be to animates.
Here is my code:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
logoImg.alpha = 0
logoImg.transform = CGAffineTransform(translationX: 0, y: -200)
logoImg.transform = CGAffineTransform(scaleX: 0, y: 0)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
UIView.animate(withDuration: 1.0, delay: 1.0, usingSpringWithDamping: 0.6, initialSpringVelocity: 10, options: [.curveEaseOut], animations: {
self.logoImg.transform = CGAffineTransform(translationX: 0, y: 0)
self.logoImg.transform = CGAffineTransform(scaleX: 1, y: 1)
self.logoImg.alpha = 1
}, completion: nil)
}
Based on what I am seeing you are wanting to preset the animation and translate it back. In that case I would do this.
self.logoImg.transform = CGAffineTransform(translationX: 0, y: -200).concatenating(CGAffineTransform(scaleX: 0, y: 0))
self.logoImg.alpha = 0
UIView.animate(withDuration: 1.0, delay: 1.0, usingSpringWithDamping: 0.6, initialSpringVelocity: 10, options: [.curveEaseOut], animations: {
self.logoImg.transform = .identity
self.logoImg.alpha = 1
}, completion: nil)
I think you may not be seeing all the animation so try to start the scale at 0.5
self.logoImg.transform = CGAffineTransform(translationX: 0, y: -200).concatenating(CGAffineTransform(scaleX: 0.5, y: 0.5))
self.logoImg.alpha = 0
UIView.animate(withDuration: 1.0, delay: 1.0, usingSpringWithDamping: 0.6, initialSpringVelocity: 1, options: [.curveEaseOut], animations: {
self.logoImg.transform = .identity
self.logoImg.alpha = 1
}, completion: nil)
The key here is that the animation is animating back the original identity. Hope this helps
You can use concatenating method to combining two existing affine transforms.
UIView.animate(withDuration: 1.0, delay: 1.0, usingSpringWithDamping: 0.6, initialSpringVelocity: 10, options: [.curveEaseOut], animations: {
let translation = CGAffineTransform(translationX: 0, y: 0)
let scale = CGAffineTransform(scaleX: 1, y: 1)
self.logoImg.transform = translation.concatenating(scale)
self.logoImg.alpha = 1
}, completion: nil)
Look at Apple Document for more info. Hope it help. :)
Rotate and Translate and make it like parallax effect in tableview header:
func setupTableHeaderView() {
self.customHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: SCREEN_WIDTH, height: 200))
self.customHeaderView?.backgroundColor = .white
self.customImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: SCREEN_WIDTH, height: 200))
self.customImageView?.image = ImageNamed(name: "camera")
self.customImageView?.contentMode = .center
self.customHeaderView?.addSubview(self.customImageView!)
self.tableHeaderView = self.customHeaderView
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let yPos = scrollView.contentOffset.y
if yPos < 0 {
let scaleX = ((yPos * -1) / 200) + 1
let translateY = yPos / 2
var commonTransform = CGAffineTransform.identity
commonTransform = commonTransform.translatedBy(x: 0, y: translateY)
commonTransform = commonTransform.scaledBy(x: scaleX, y: scaleX)
self.customImageView?.transform = commonTransform
}else{
self.customImageView?.transform = CGAffineTransform.identity
}
}
NOTE : Make sure the View you apply transform to is SUBVIEW of the header view, not header view itself. In above example customImageView is subview of main headerview.
You can use this also with swift 3.0.1:
UIView.transition(with: self.imageView,
duration:0.5,
options: .transitionCrossDissolve,
animations: { self.imageView.image = newImage },
completion: nil)
Reference: https://gist.github.com/licvido/bc22343cacfa3a8ccf88

Resources