我为这个问题寻找了几个月的解决方案,终于找到了一种自定义 QLPreviewController 导航栏的方法。以前我也使用 UIWebView 来显示文档,因为我不允许在我的应用程序中显示某些机密文档的 iOS 共享按钮,这就是 QLPreviewController 所做的。但是,我希望拥有那些不错的功能,例如带有小预览和内容的目录。所以我寻找一种可靠的方法来摆脱这个按钮。和你们一样,我最初是在研究自定义 QLPreviewController 的导航栏。然而,正如其他人已经指出的那样,这在 iOS6 之后是绝对不可能的。因此,我们需要做的不是自定义现有导航栏,而是创建一个自己的导航栏并将其放置在 QL 导航栏的前面,从而将其隐藏。
那么如何做到这一点呢?首先,我们需要继承 QLPreviewContoller 并覆盖 viewDidAppear 方法和 viewWillLayoutSubviews,如下所示:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.qlNavigationBar = [self getNavigationBarFromView:self.view];
self.overlayNavigationBar = [[UINavigationBar alloc] initWithFrame:[self navigationBarFrameForOrientation:[[UIApplication sharedApplication] statusBarOrientation]]];
self.overlayNavigationBar.autoresizingMask = UIViewAutoresizingFlexibleWidth;
[self.view addSubview:self.overlayNavigationBar];
NSAssert(self.qlNavigationBar, @"could not find navigation bar");
if (self.qlNavigationBar) {
[self.qlNavigationBar addObserver:self forKeyPath:@"hidden" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) context:nil];
}
// Now initialize your custom navigation bar with whatever items you like...
UINavigationItem *item = [[UINavigationItem alloc] initWithTitle:@"Your title goes here"];
UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(doneButtonTapped:)];
item.leftBarButtonItem = doneButton;
item.hidesBackButton = YES;
[self.overlayNavigationBar pushNavigationItem:item animated:NO];
}
- (void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
self.overlayNavigationBar.frame = [self navigationBarFrameForOrientation:[[UIApplication sharedApplication] statusBarOrientation]];
}
qlNavigationBar是 QLPreviewController 拥有的默认导航栏,overlayNavigationBar是我们的自定义导航栏,它将隐藏默认导航栏。我们还向默认 QL 导航栏添加键值观察,以便在默认导航栏隐藏/重新出现时收到通知。在viewWillLayoutSubviews方法中,我们负责自定义导航栏框架。
接下来我们应该做的是监听 quicklook 导航栏的可见性变化:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
// Toggle visiblity of our custom navigation bar according to the ql navigationbar
self.overlayNavigationBar.hidden = self.qlNavigationBar.isHidden;
}
因此,现在我们需要实现获取 QL 导航栏所需的方法,以及始终为我们的自定义导航栏提供当前框架的方法:
- (UINavigationBar*)getNavigationBarFromView:(UIView *)view {
// Find the QL Navigationbar
for (UIView *v in view.subviews) {
if ([v isKindOfClass:[UINavigationBar class]]) {
return (UINavigationBar *)v;
} else {
UINavigationBar *navigationBar = [self getNavigationBarFromView:v];
if (navigationBar) {
return navigationBar;
}
}
}
return nil;
}
- (CGRect)navigationBarFrameForOrientation:(UIInterfaceOrientation)orientation {
// We cannot use the frame of qlNavigationBar as it changes position when hidden, also there seems to be a bug in iOS7 concerning qlNavigationBar height in landscape
return CGRectMake(0.0f, self.isIOS6 ? 20.0f : 0.0f, self.view.bounds.size.width, [self navigationBarHeight:orientation]);
}
- (CGFloat)navigationBarHeight:(UIInterfaceOrientation)orientation {
if([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
if(UIInterfaceOrientationIsLandscape(orientation)) {
return self.isIOS6 ? 32.0f : 52.0f;
} else {
return self.isIOS6 ? 44.0f : 64.0f;
}
} else {
return self.isIOS6 ? 44.0f : 64.0f;
}
}
还有什么?当然,您需要定义属性,删除dealloc中的观察者以及定义和设置 iOS6 属性(网络上有很多示例......)。您还需要自定义导航栏并收听按钮回调。而已。
我知道这有点 hacky ...通过将其隐藏在另一个导航栏下方来隐藏/替换默认的 QL 操作按钮 ...但至少它对我来说是可靠的,并且您不访问私有 API 等。
我在适用于 iOS 6.0 - 7.0 的所有可用模拟器以及 iPad 2 和 3、iPhone 4S 和 5(后者安装了 iOS 7.0 Beta 6)上测试了我的解决方案。