Search code examples
uitableviewuinavigationcontrolleruinavigationbartransitionuistatusbar

UINavigationBar below StatusBar after Hotspot/Call when using custom transition


I've a strange issue with the NavigationBar behind the Statusbar.

It only occurs when the default statusbar changes to an "active" statusbar like the one that appears during an active call or a wifi hotspot.

Before the "active" statusbar appears, it looks like this (which is perfectly fine):

enter image description here

When I enable the wifi hotspot it's still fine:

enter image description here

But when I disable the wifi hotspot or end a call the statusbar size shrinks back to its previous size but the ViewController (in this case a UITableViewController) doesn't move up again. It looks like it has a top margin of the statusbars' size. In addition the statusbar is transparent and I can see the background of the view controller below the actual table view controller.

enter image description here

Any ideas on this issue?

Update: I figured out that it's because of a custom modal transition that I've implemented.

It should be a dissolve animation. That's the code:

class DissolveTransition: NSObject, UIViewControllerAnimatedTransitioning, UIViewControllerTransitioningDelegate {
// vars
private var duration: NSTimeInterval = 0.3
private var presenting  = true


// MARK: - UIViewControllerAnimatedTransitioning

func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
    return self.duration
}


func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
    let destination = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)

    if (destination?.isBeingPresented() == true) {
        self.animatePresentation(transitionContext)
    }
    else {
        self.animateDismissal(transitionContext)
    }
}


private func animatePresentation(transitionContext: UIViewControllerContextTransitioning) {
    self.animateDissolve(transitionContext)
}

private func animateDismissal(transitionContext: UIViewControllerContextTransitioning) {
    self.presenting = false
    self.animateDissolve(transitionContext)
}

private func animateDissolve(transitionContext: UIViewControllerContextTransitioning) {
    let source = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)!
    let destination = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)!
    let container = transitionContext.containerView()!

    destination.beginAppearanceTransition(true, animated: true)

    let snapshotFromView = source.view.snapshotViewAfterScreenUpdates(true)
    // 1. adding real view at the bottom of the view hierarchy
    if (self.presenting) {
        container.addSubview(destination.view)   
    }

    // 2. adding snapshot of previous view to view hierarchy
    container.addSubview(snapshotFromView)

    // 3. removing (fade) prev snapshot view and show real VC
    UIView.animateWithDuration(self.duration, animations: {
        snapshotFromView.alpha = 0.0
        }, completion: { (finished) in
            if (finished) {
                snapshotFromView.removeFromSuperview()
                container.bringSubviewToFront(destination.view)
            }
            transitionContext.completeTransition(!transitionContext.transitionWasCancelled())
            destination.endAppearanceTransition()
    })
}


// MARK: - UIViewControllerTransitioningDelegate

func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    return self
}

func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    return self
}

}


Solution

  • I found out that it was because of my custom modal transition that presented this view.

    There is an odd bug in iOS that views inside the screen are not resized after the statusBar is changed. This also appears in many well-known Apps.

    I fixed it by resizing the views when the statusbar-size changes. Use the following code in your AppDelegate:

        func application(application: UIApplication, willChangeStatusBarFrame newStatusBarFrame: CGRect) {
    
        if (newStatusBarFrame.size.height < 40) {
            if let window = self.window, subviews = self.window?.subviews {
                for view in subviews {
                    UIView.animateWithDuration(0.3, animations: {
                        view.frame = window.bounds
                    })
                }
            }
        }
    }