I have an issue that I'm stuck on, but I have no idea why it even happens; If I push a detail controller on the stack, and I swipe back very quickly using the default left edge interactivePopGestureRecognizer
, my parent/root view controller's UINavigationBar
looks corrupt or something, almost like the built in iOS transition mechanism didn't have time to do it's job at resetting it after the detail view is gone. Also to clarify, everything in this 'corrupt' UINavigationBar
is still touchable and everything on my parent/root view controller works perfectly.
For people downvoting due to no source code: there is no source code! This is an Apple bug!
Is there anyway to reset this UINavigationBar
to what it should be when the parent/root view controller's viewDidAppear method gets called?
Note that this bug does not occur if I tap the top left back button instead of using the left edge interactivePopGestureRecognizer
.
Edit: I added an NSLog to check the navigationBar's subview count on viewDidAppear on the parent/root view controller, and the count is always the same, corrupt or not, so I'd like to know why the popped controller is wreaking havoc with my UINavigationBar
.
If you can help me at all, I'd greatly appreciate it! Thank you.
I've attached a screenshot of what it looks like: Note that the back chevron isn't part of my parent/root view controller, it's part of what was popped off the stack. Testing123 is the title for the parent/root view controller and not that of what was popped off the stack. The head and gear icons are part of the parent/root view controller.
Edit: I've thought something like this could fix the issue, but turns out it doesn't, and is really bad experience IMO too. This is not the kind of solution I'm looking for. I'm posting a large bounty so this can be resolved correctly!
After investigating this issue for some time with debug console, Instruments and Reveal, I have found out the following:
1) On simulator the bug can be recreated every time, if using Profile/Automation Template and adding the following script:
2) On real device (iPhone 5s, iOS 7.1) this script never causes the bug. I tried various options for flick coordinates and the delay.
3) UINavigationBar consists of:
4) When bug happens UILabel looks half transparent and in the wrong position, but the actual properties of the UILabel are correct (alpha: 1 and frame as in normal situation). Also _UINavigationBarBackIndicatorView looks doesn't correspond to actual properties - it is visible although it's alpha is 0.
From this I conclude that it's a bug of Simulator and that you can't even detect from the code that something is wrong.
So @troop231 - are you 100% sure this also happens on device?
Key Concept
Disable gesture recognizer when pushing view controller, and enable it when view appeared.
A Common Solution: Subclassing
You can subclass
UINavigationController
andUIViewController
to prevent corruption.MyNavigationController : UINavigationController
MyViewController : UIViewController
Problem: Too annoying
MyNavigationController
andMyViewController
instead ofUINavigationController
andUIViewController
.UITableViewController
,UICollectionViewController
, and more.A Better Solution: Method Swizzling
It could be done by swizzling
UINavigationController
andUIViewController
methods. Want to know about method swizzling, visit here.Example below uses JRSwizzle that makes method swizzling easy.
UINavigationController
UIViewController
Being Simple: Use Open Source
SwipeBack
SwipeBack does it automatically without any code.
With CocoaPods, just add a line below into your
Podfile
. You don't need to write any code. CocoaPods automatically import SwipeBack globally.pod 'SwipeBack'
Install pod, and it's done!
TL;DR
I made a category on
UIViewController
that hopefully fixes this issue for you. I can't actually reproduce the navigation bar corruption on a device, but I can do it on the simulator pretty frequently, and this category solves the problem for me. Hopefully it also solves it for you on the device.The Problem, and the Solution
I actually don't know exactly what causes this, but the navigation bar's subviews' layers' animations seem to either be executing twice or not fully completing or... something. Anyway, I found that you can simply add some animations to these subviews in order to force them back to where they should be (with the right opacity, color, etc). The trick is to use your view controller's
transitionCoordinator
object and hook into a couple of events – namely the event that happens when you lift your finger up and the interactive pop gesture recognizer finishes and the rest of the animation starts, and then the event that occurs when the non-interactive half of the animation finishes.You can hook into these events using a couple methods on the
transitionCoordinator
, specificallynotifyWhenInteractionEndsUsingBlock:
andanimateAlongsideTransition:completion:
. In the former, we create copies of all of the current animations of the navbar's subviews' layers, modify them slightly, and save them so we can apply them later when the non-interactive portion of the animation finishes, which is in the completion block of the latter of those two methods.Summary
Code
And here's the code for the
UIViewController
category:And then just call this method in your view controller's
viewWillAppear:
method (in your test project's case, it would be theViewController
class):