I have seen this kind of question a lot on the internet but it seems no one really knows the answer?
I am using QLPreviewController for displaying PDF documents. I f
By mixing a bit out of the existing answers/comments I was able to get this working for my use case: I needed to display files inside a UINavigationController
and keep the ability of hiding/showing the UINavigationBar
when the file content is tapped
Based on the answer from Lukas Gross and the comment from nacross here's what I ended up doing:
QLPreviewController
as a child view controller. This will show two navigation bars: one for the main navigation controller and one from the QLPreviewController
containerTop
in the code)UINavigationBar
plus the status bar, so that the QLPreviewController
's UINavigationBar
remains hidden under the main UINavigationBar
.hidden
property of the UINavigationBar
so that we can (1) hide/show our main UINavigationBar
and (2) reset the top constraintI ended up with something like this:
var qlNavigationBar: UINavigationBar?
func getQLNavigationBar(fromView view: UIView) -> UINavigationBar? {
for v in view.subviews {
if v is UINavigationBar {
return v as? UINavigationBar
} else {
if let navigationBar = self.getQLNavigationBar(fromView: v) {
return navigationBar
}
}
}
return nil
}
func setObserverForNavigationBar() {
self.qlNavigationBar = self.getQLNavigationBar(fromView: self.view)
if let qln = self.qlNavigationBar {
qln.addObserver(self, forKeyPath: "hidden", options: [NSKeyValueObservingOptions.New, NSKeyValueObservingOptions.Old], context: nil)
}
}
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer) {
self.navigationController?.setNavigationBarHidden(self.qlNavigationBar!.hidden, animated: true)
self.containerTop.constant = self.qlNavigationBar!.hidden ? self.getStatusBarHeight() * -1 : self.getFullNavigationBarHeight() * -1
UIView.animateWithDuration(0.5) {
self.view.layoutIfNeeded()
}
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated);
self.setObserverForNavigationBar()
self.containerTop.constant = self.getFullNavigationBarHeight() * -1
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated);
if let qln = self.qlNavigationBar {
qln.removeObserver(self, forKeyPath: "hidden")
}
}
func getFullNavigationBarHeight() -> CGFloat {
if let nav = self.navigationController {
return nav.navigationBar.frame.origin.y + nav.navigationBar.frame.size.height
}
return 0
}
func getStatusBarHeight() -> CGFloat {
return UIApplication.sharedApplication().statusBarFrame.size.height
}
The animations might need a little tweaking and it is hacky, but it's better than not having this possibility.
It should be possible to adapt this strategy to other scenarios without the UINavigationController
Note: If you have a crash when implementing the container view for the QLPreviewController
from a storyboard, subclass the QLPreviewController
and implement the initializer:
class MyPreviewController: QLPreviewController {
required init?(coder aDecoder: NSCoder) {
super.init(nibName: nil, bundle: nil)
}
}