unwind segue animation error - ios

I have two view controllers the first is called "Login View controller" followed by a navigation controller and then another view controller called "Registration View Controller". I found an article online detailing the proper way in performing an unwind segue animation and placed this code inside of "Login View Controller"
override func segueForUnwinding(to toViewController: UIViewController,
from fromViewController: UIViewController,
identifier: String?) -> UIStoryboardSegue {
return UIStoryboardSegue(identifier: identifier, source: fromViewController, destination: toViewController) {
let fromView = fromViewController.view
let toView = toViewController.view
if let containerView = fromView?.superview {
let initialFrame = fromView?.frame
var offscreenRect = initialFrame
offscreenRect?.origin.x -= (initialFrame?.width)!
toView?.frame = offscreenRect!
containerView.addSubview(toView!)
// Being explicit with the types NSTimeInterval and CGFloat are important
// otherwise the swift compiler will complain
let duration: TimeInterval = 1.0
let delay: TimeInterval = 0.0
let options = UIViewAnimationOptions.curveEaseInOut
let damping: CGFloat = 0.5
let velocity: CGFloat = 4.0
UIView.animate(withDuration: duration, delay: delay, usingSpringWithDamping: damping,
initialSpringVelocity: velocity, options: options, animations: {
toView?.frame = initialFrame!
}, completion: { finished in
toView?.removeFromSuperview()
if let navController = toViewController.navigationController {
navController.popToViewController(toViewController, animated: false)
}
})
}
}
What happens now when I press the back button inside of the "Registration View Controller" is the code successfully executes but then removes the "Login View Controller" from view and stays on the "Registration View Controller". After switching out "toView?.removeFromSuperview()" to "fromView?.removeFromSuperview()", it successfully stays on the "Login View Controller" but now if I press the button to take me to "Register View Controller" it just sends me back to "Login View Controller" essentially creating an infinite loop.

Related

Custom Bottom Bar transition to another view in Swift?

So, I've made a custom bottom bar with UIButton's. Now I am trying to replicate the fade in transition so when you click one of the buttons from the bottom bar will send to another view controller and show that view. I managed to do that but my navigation bar it's not a custom one and whenever I switch the views, the main view goes under the navigation bar.
I am using this code:
//MARK: Main view
func yourFoodGoalAction() -> Bool {
print("transition: YourFoodGoal")
let nextViewController = YourFoodGoal()
let toView = nextViewController.view
UIView.transition(from: view!, to: toView!, duration: 0.3, options: [.transitionCrossDissolve]) { (true) in
print("disolve")
UIView.animate(withDuration: 0.3, animations: {
self.navigationItem.title = "Your Goal"
})
}
return true
}
And to go back to the view (vice versa):
func selectedBarButtonAction() -> Bool {
print("transition: YourFoodGoal")
let nextViewController = YourFoodVC()
let toView = nextViewController.view
UIView.transition(from: view!, to: toView!, duration: 0.3, options: [.transitionCrossDissolve]) { (true) in
UIView.animate(withDuration: 0.3, animations: {
self.navigationItem.title = "Today's Meals"
})
}
return true
}
What should I do so my views will not go under the navigation bar once I change between them? I do not want to use container view as every view will be custom. Everything it's being made programmatically.
I found my answer. I was calling the modal transition wrong. Modals go full screen with the main view. I just embedded the navigation when I am presenting the new view and call the modal from there:
func todaysMealsAction() {
let yourFoodVC = YourFoodVC()
yourFoodVC.modalTransitionStyle = UIModalTransitionStyle.crossDissolve
let navBarOnModal: UINavigationController = UINavigationController(rootViewController: yourFoodVC)
self.present(navBarOnModal, animated: false, completion: nil)
}

Swift Switching To and FromTable ViewController Issue Using Custom Segue

I have run into an issue when using a custom segue. I have two tableviews that I'm an trying to switch back and forth from. When I click on a cell in tableview1 it should take me to tableview2. I have a button on tableview2 that connects to the exit of the storyboard. From there it should take me back to tableview1 but whenever I press the button, the application crashes with a BAD_ACCESS error.
Here is my custom segue class:
class TableViewSegue: UIStoryboardSegue {
override func perform() {
scale()
}
func scale () {
let toViewcontroller = self.destination
let fromViewcontroller = self.source
let containerView = fromViewcontroller.view.superview
let originalCenter = fromViewcontroller.view.center
toViewcontroller.view.transform = CGAffineTransform(scaleX: 0.05, y: 0.05)
toViewcontroller.view.center = originalCenter
containerView?.addSubview(toViewcontroller.view)
UIView.animate(withDuration: 0.5, delay: 0, options: .curveEaseInOut, animations: {
toViewcontroller.view.transform = CGAffineTransform.identity
}, completion: { success in
fromViewcontroller.present(toViewcontroller, animated: false, completion: nil) //The application crashes and highlights this line as the error.
})
}
}
I have implemented this method in my tableViewController1:
#IBAction func prepareForUnwind(segue: UIStoryboardSegue) {
}
Not sure why the tableview2 does not dismiss.
EDIT: The issue had to do with needing a navigation controller.
The problem is that you are presenting the toViewcontroller each time a segue is performed. So the app presents table2 over table1, and then tries again to present table1 over table2 on the unwind.
Modify your custom segue to check - essentially - which direction you're going:
class TableViewSegue: UIStoryboardSegue {
override func perform() {
scale()
}
func scale () {
let toViewcontroller = self.destination
let fromViewcontroller = self.source
let containerView = fromViewcontroller.view.superview
let originalCenter = fromViewcontroller.view.center
toViewcontroller.view.transform = CGAffineTransform(scaleX: 0.05, y: 0.05)
toViewcontroller.view.center = originalCenter
containerView?.addSubview(toViewcontroller.view)
let fromP = fromViewcontroller.presentingViewController
UIView.animate(withDuration: 0.5, delay: 0, options: .curveEaseInOut, animations: {
toViewcontroller.view.transform = CGAffineTransform.identity
}, completion: { success in
// if nil, we are presenting a new VC
if fromP == nil {
fromViewcontroller.present(toViewcontroller, animated: false, completion: nil)
} else {
fromViewcontroller.dismiss(animated: false, completion: nil)
}
})
}
}
Note: This is assuming:
you are not trying to push/pop within a UINavigationController ... you'd need to add some other checks to handle that.
you are only going one-level-in, that is, you are not presenting, presenting, presenting, etc. and then trying to unwind.

How to modally present a view controller without dismissing the parent/first view controller

I'm trying to animate up a UICollectionViewController over an original UIViewController with the original UIViewController blurred as background, however, every time the animation finishes, I can see clearly through the blurred view that the original view controller is dismissed, what do I do to avoid the first UIViewController being dismissed?
Code in the first view controller to present the second:
let VC = storyboard?.instantiateViewController(withIdentifier: "PopoverCollectionVC") as! PopoverCollectionVC
VC.setDataSource(with: .calcDPSItems)
VC.collectionView?.backgroundColor = UIColor.clear
VC.transitioningDelegate = self
self.present(VC, animated: true, completion: nil)
Code in the animator object for custom animation:
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
let containerView = transitionContext.containerView
let fromView = transitionContext.view(forKey: .from)!
let toView = transitionContext.view(forKey: .to)!
if presenting {
// configure blur
effectView.frame = fromView.bounds
containerView.addSubview(effectView)
// configure collection view
toView.frame = CGRect(x: 0, y: fromView.frame.height, width: fromView.frame.width, height: fromView.frame.height / 2)
containerView.addSubview(toView)
UIView.animateKeyframes(withDuration: duration, delay: 0, options: .calculationModeCubic, animations: {
UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.6) {
toView.center.y = fromView.center.y
}
UIView.addKeyframe(withRelativeStartTime: 0.1, relativeDuration: 1) {
self.effectView.effect = UIBlurEffect(style: .dark)
}
}) { _ in
transitionContext.completeTransition(true)
}
} else {
...
}
You need to specify that the modal presentation style will be over the current context :
VC.modalPresentationStyle = .overCurrentContext
Then to get the the presented view you need
// Get the from view from The ViewController because there is a bug in iOS when
// using some modalPresentationStyle values
// that causes the viewForKey to returm nil for UITransitionContextFromViewKey
// www.splinter.com.au/2015/04/17/ios8-view-controller-transiti‌​oning-bug/
let fromVC = transitionContext.viewController(forKey: .from)
let fromView = fromVC?.view
You should instead take a snapshot of the previous view, then blur than and use it as a background on the new VC.
let oldView = view_to_copy.snapshotView(afterScreenUpdates: false)
Then blur this, and add as a subview to your collectionView controller.
EDIT:
Just to add if you are using the VC inside something like UITabbar or UINavigationController you may need to snapshot that view to ensure all the UI is inside the snapshot.

UINavigationController Custom transition issue

I followed many tutorials about how to create custom transitions but most of them apply to the presentation and dismissing of the view controllers. I just want to apply custom transitions to the UINavigationController for the push/pop actions.
After reading apple documentation I created simple project to test if it works.
Here is all my code for custom Navigation Controller:
class CustomNC: UINavigationController {
override func viewDidLoad() {
super.viewDidLoad()
delegate = self
}
}
extension CustomNC: UINavigationControllerDelegate {
func navigationController(
navigationController: UINavigationController,
animationControllerForOperation operation: UINavigationControllerOperation,
fromViewController fromVC: UIViewController,
toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
print("Creating UIViewControllerAnimatedTransitioning for operation: \(operation.rawValue)")
return self
}
}
extension CustomNC: UIViewControllerAnimatedTransitioning {
func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
print("Returning duration for transition")
return 1.0
}
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
print("Animating transition")
let container = transitionContext.containerView()!
let fromView = transitionContext.viewForKey(UITransitionContextFromViewKey)!
let toView = transitionContext.viewForKey(UITransitionContextToViewKey)!
// set up from 2D transforms that we'll use in the animation
let offScreenRight = CGAffineTransformMakeTranslation(container.frame.width, 0)
let offScreenLeft = CGAffineTransformMakeTranslation(-container.frame.width, 0)
// start the toView to the right of the screen
toView.transform = offScreenRight
// add the both views to our view controller
container.addSubview(toView)
container.addSubview(fromView)
// get the duration of the animation
// DON'T just type '0.5s' -- the reason why won't make sense until the next post
// but for now it's important to just follow this approach
let duration = self.transitionDuration(transitionContext)
// perform the animation!
// for this example, just slid both fromView and toView to the left at the same time
// meaning fromView is pushed off the screen and toView slides into view
// we also use the block animation usingSpringWithDamping for a little bounce
UIView.animateWithDuration(
duration,
delay: 0.0,
usingSpringWithDamping: 0.5,
initialSpringVelocity: 0.8,
options: [],
animations: {
fromView.transform = offScreenLeft
toView.transform = CGAffineTransformIdentity
}, completion: { finished in
// tell our transitionContext object that we've finished animating
transitionContext.completeTransition(true)
})
}
}
Then I simply create two default view controllers First and Second. First has UIButton with show action to the Second. First is embedded in NavigationController and this NavigationController has Custom Class set to CustomNC.
After running I see V1 controller but when I tap UIButton application crash without any error message. In Logs I see only those lines:
Creating UIViewControllerAnimatedTransitioning for operation: 1
Returning duration for transition
(lldb)
So as I understand the delegate is visible and navigation controller is using this delegate properly. When transition starts it gets the animation duration time interval but animateTransition method is never called because I don't see "Animating transition" message in log.
Can somebody point me where I have problem?

ios swift custom transition to darken fromViewController

I want to present my view controller with a custom transition so the fromViewController will be darkened as if presenting a AlertViewController.
I have created my customTransition manager:
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
let container = transitionContext.containerView()
let fromView = transitionContext.viewForKey(UITransitionContextFromViewKey)!
let toView = transitionContext.viewForKey(UITransitionContextToViewKey)!
let fadeRect = CGRectMake(0, 0, fromView.frame.size.width, fromView.frame.size.height)
let fadeView = UIView(frame: fadeRect)
if (self.presenting == true){
fadeView.backgroundColor = UIColor.blackColor()
fadeView.alpha = 0
fromView.addSubview(fadeView)
container.insertSubview(fadeView, aboveSubview: fromView)
} else {
// adding subviews to container
}
let duration = self.transitionDuration(transitionContext)
UIView.animateWithDuration(duration, animations: { () -> Void in
if (self.presenting == true){
fadeView.alpha = 0.5
} else {
}
}) { (Bool) -> Void in
if self.presenting{
container.addSubview(toView)
}
transitionContext.completeTransition(true)
}
}
}
I assign my transitioning manager to the toViewController as it can be seen below:
var purchaseSpecialItemsViewController = self.storyboard?.instantiateViewControllerWithIdentifier("specialItemPurchaseVC") as! BRSpecialItemPurchaseViewController
purchaseSpecialItemsViewController.transitioningDelegate = self.fadedTransitionManager
self.presentViewController(purchaseSpecialItemsViewController, animated: true, completion: nil)
The View Controller I am about to display is in storyboard with a fixed size. It has a clear background and I added a tableview there that I want to be in the center of the screen.
The TableView is displayed without the problem, but I can't see the fromViewController at all. It is just black even though I set the alpha of the black background to 0 which is later animated to 0.5.
I am presenting the view controller modally, is that causing the problem? Should I use instead a push and pop operation?
As described here: http://mathewsanders.com/custom-menu-transitions-in-swift/
I needed to add the modal presentation style like this:
purchaseSpecialItemsViewController.modalPresentationStyle = UIModalPresentationStyle.OverFullScreen
and instead getting ViewControllersViews, use
let screens: (from: UIViewController, to: UIViewController) = (transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)!, transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)!)
to access the ViewControllers.

Resources