5

I am trying to animate the root-view-controller-change in my app. After I swap the view controllers, I load the data necessary for the 2nd controller right away. While the data is loading, I show a loader(MBProgressHUD). This is my function for swapping the view controllers:

class ViewUtils {

    class func animateRootViewController(duration: NSTimeInterval, changeToViewController: UIViewController) {
        let window = UIApplication.sharedApplication().delegate?.window?
        if window == nil {
            return
        }
        UIView.transitionWithView(window!,
            duration: duration,
            options: UIViewAnimationOptions.TransitionFlipFromLeft | UIViewAnimationOptions.AllowAnimatedContent,
            animations: {
                window!.rootViewController = changeToViewController
            },
            completion: nil
        )
    }
}

All good with this but one thing - it totally breaks the loader. I am attaching an imagine of what's happening: 2nd view controller This is the 2nd view controller while rotating. Once the rotation is complete, the loader appears just fine, both the spinner and the text tween to the correct position in the rounded rectangle.

I really don't understand why this happens, would somebody explain it to me, please? Is there a way to prevent it?

The code of the 2nd view controller where I show the loader:

override func viewDidLoad() {
    super.viewDidLoad()

    hud = HUD(containingView: view)
    hud.show()

    createBackground()
}

And my hud class:

class HUD {

    private var hudBG: UIView!
    private var view: UIView!
    private(set) var isShown = false

    init(containingView: UIView) {
        view = containingView
    }

    required init(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func show() {
        if !isShown {
            if(hudBG == nil) {
                hudBG = UIView(frame: CGRectMake(0, 0, view.bounds.width, view.bounds.height))
                hudBG.backgroundColor = UIColor(white: 0, alpha: 0.4)
            }
            view.addSubview(hudBG)
            let hud = MBProgressHUD.showHUDAddedTo(view, animated: true)
            hud.mode = MBProgressHUDModeIndeterminate
            hud.labelText = "Cargando"

            hudBG.alpha = 0

            UIView.animateWithDuration(0.3, animations: { () -> Void in
                self.hudBG.alpha = 1
            })
            isShown = true
        }
    }

    func hide() {
        if isShown {
            UIView.animateWithDuration(0.3, animations: {
                () -> Void in
                self.hudBG.alpha = 0
            }, completion: {
                (b) -> Void in
                self.hudBG.removeFromSuperview()
            })
            MBProgressHUD.hideHUDForView(view, animated: true)
            isShown = false
        }
    }
}

Thanks a lot for any ideas!

Fygo
  • 4,555
  • 6
  • 33
  • 47

2 Answers2

2

You are adding the hud to a view that is not properly initialized yet. If you are loading the view controller from a xib or storyboard, the view and it's subviews have the size as they were loaded from interface.

You have to add the hud after the views have been resized to their final size.

If you move

hud = HUD(containingView: view)
hud.show()

to viewDidLayoutSubviews, it should work fine.

pteofil
  • 4,133
  • 17
  • 27
  • Why do you think the view is not properly initialized in viewDidLoad? It already has the correct bounds and correct frame (e.g. (0.0,0.0,320.0,480.0) for an iP4/S). It's the view controller's rotation that makes it go crazy - without the transition there is no problem. – Fygo Apr 21 '15 at 16:07
  • How do you know the view has the correct frame? Did you put a log in viewDidLoad? – pteofil Apr 21 '15 at 17:46
  • It may also matter what view we're talking about. What is this "view", that you add the hud to, the controller main view or one of it's subviews? If it's the main view I think you should use it with self.view. – pteofil Apr 21 '15 at 17:52
  • In viewDidLoad the view always has the correct frame. I still tried your advice to put it viewDidLayoutSubviews but it behaves exactly in the same way. "view" is the main view of the controller. There is no need for self.view as it is in the same scope and not inside of a closure. – Fygo Apr 21 '15 at 18:37
  • According to some user comments to the answer here: http://stackoverflow.com/questions/7703806/rootviewcontroller-switch-transition-animation the problem comes from the fact that you change the rootViewController. I would advise you to create another RootViewController for you app that you don’t change at all. And to add your current rootViewControlelr as a child view controller to the new RootViewController created. When you want to make the transition, animate transition between child view controllers of RootViewController – pteofil Apr 22 '15 at 08:36
  • Absolutely unbelievable, the last link did work! (I haven't checked the first just yet). It doesn't really explain why it happens so I am still not sure (please note that's why I am not marking it as the correct answer) but considering the attention you put into this question I think the 100 points are well-deserved. Thanks again! – Fygo Apr 27 '15 at 15:07
0

I noticed a similar problem when moving an app from iOS 7 to iOS 8. During animations, especially when scaling was involved, the view positions got distorted.

I am pretty sure it's a bug. The simplest workaround is to animate only screenshots or view snapshots, not actual views - it's more work and you can't have views animating when the main animation is in progress but in general it's a more stable solution.

Sulthan
  • 128,090
  • 22
  • 218
  • 270