我在网上看到过很多这样的问题,但似乎没有人真正知道答案?
我正在使用QLPreviewController来显示PDF文档。我首先使用了一个UIWebView,但我建议使用QLPreviewController,而不是出于性能原因而使用更大的文档。
我想要的是右上角的4个自定义UIBarButtonItem(所以打印按钮在哪里)。
我设法在底部设置了一个自定义工具栏,但这不是我想要的。
考虑到无法在打印按钮的位置添加自定义按钮,我仍然想要删除打印按钮并使用自定义工具栏。
编辑(解决方案): 我刚才找到了解决方案,但没有更新这篇文章,所以这就是我如何解决问题:
我手动添加按钮:
// Create a toolbar to have the buttons at the right side of the navigationBar
UIToolbar* toolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, 180, 44.01)];
[toolbar setTranslucent:YES];
// Create the array to hold the buttons, which then gets added to the toolbar
NSMutableArray* buttons = [[NSMutableArray alloc] initWithCapacity:4];
// Create button 1
button1 = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemSearch target:self action:@selector(button1Pressed)];
[buttons addObject:button1];
// Create button 2
button2 = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCompose target:self action:@selector(button2Pressed)];
[buttons addObject:button2];
// Create button 3
button3 = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemBookmarks target:self action:@selector(button3Pressed)];
[buttons addObject:button3];
// Create a action button
openButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAction target:self action:@selector(openWith)];
[buttons addObject:openButton];
// insert the buttons in the toolbar
[toolbar setItems:buttons animated:NO];
// and put the toolbar in the navigation bar
[[self navigationItem] setRightBarButtonItem:[[UIBarButtonItem alloc] initWithCustomView:toolbar]];
答案 0 :(得分:27)
我搜索了这个问题的解决方案好几个月,终于找到了一种方法来自定义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等。< / p>
我在iOS 6.0 - 7.0以及iPad 2和iPad上的所有可用模拟器上测试了我的解决方案。 3,iPhone 4S&amp; 5(后者安装了iOS 7.0 Beta 6)。
答案 1 :(得分:17)
<强>更新强>
这在iOS 6中不再有效。快速查看在使用XPC的另一个进程中运行。有关详细信息,请参阅here。我没有预见到任何定制QLPreviewController的方法。以下答案适用于对iOS 6之前感兴趣的任何人。
前几天我回答了一个几乎相同的问题here。问题在于移除打印按钮,这不是太难。有关QLPreviewController
的一点需要注意的是,它并不是要定制的。我已经构建了一个可以自定义的QLPreviewController
子类。我把它here放在Github上。它的设计也可以轻松删除动作按钮等功能。用自定义按钮替换按钮不会花费太多精力。
最值得注意的是,只要显示新文档,操作按钮就会重新添加到导航栏。你应该在我的代码中注意到这一点。无论何时RBFilePreviewer
删除操作按钮,您只需重新添加自定义按钮即可。要添加自定义按钮,您应创建一个UIBarButtonItem
,其中包含一个自定义视图,其中包含四个按钮。然后将右侧栏按钮项设置为您创建的自定义UIBarButtonItem
。
<强>更新强>
我已更新RBFilePreviewer
,允许您开箱即用设置自定义右侧栏按钮项。只需在-setRightBarButtonItem:
上致电RBFilePreviewer
即可。
答案 2 :(得分:3)
我接受了 Lukas Gross 的回复,并在iOS 8上将其应用于Swift,并提出了适合我的解决方案:
注意:我将QLPreviewController嵌入到UINavigationController中!
<强>代码:强>
var QLNavigationBar: UINavigationBar?
var overlayNavigationBar: 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 as! UIView)) {
return navigationBar
}
}
}
return nil
}
func handleNavigationBar() {
self.QLNavigationBar = self.getQLNavigationBar(fromView: self.navigationController!.view)
self.overlayNavigationBar = UINavigationBar(frame: CGRectMake(0, 0, self.view.bounds.size.width, 64.0))
self.overlayNavigationBar?.autoresizingMask = UIViewAutoresizing.FlexibleWidth
if let qln = self.QLNavigationBar {
qln.addObserver(self, forKeyPath: "hidden", options: (NSKeyValueObservingOptions.New | NSKeyValueObservingOptions.Old), context: nil)
qln.superview?.addSubview(self.overlayNavigationBar!)
}
var item = UINavigationItem(title: self.navigationItem.title!)
var doneBtn = UIBarButtonItem(barButtonSystemItem: .Done, target: self, action: "doneBtnPressed")
item.leftBarButtonItem = doneBtn
item.hidesBackButton = true
self.overlayNavigationBar?.pushNavigationItem(item, animated: false)
self.overlayNavigationBar?.tintColor = .whiteColor()
self.overlayNavigationBar?.barTintColor = .blackColor()
self.overlayNavigationBar?.titleTextAttributes = [
NSForegroundColorAttributeName : UIColor.whiteColor() ]
}
并应用此代码:
override func observeValueForKeyPath(keyPath: String, ofObject object: AnyObject, change: [NSObject : AnyObject], context: UnsafeMutablePointer<Void>) {
if self.QLNavigationBar!.hidden {
self.overlayNavigationBar?.hidden = self.QLNavigationBar!.hidden
}
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(0.5 * Double(NSEC_PER_SEC))), dispatch_get_main_queue(), {
self.QLNavigationBar?.superview?.sendSubviewToBack(self.QLNavigationBar!)
if !self.QLNavigationBar!.hidden {
self.overlayNavigationBar?.hidden = self.QLNavigationBar!.hidden
}
})
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.handleNavigationBar()
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
self.overlayNavigationBar?.frame = CGRectMake(0, 0, self.view.bounds.size.width, 64.0)
}
答案 3 :(得分:1)
通过混合现有答案/评论中的一点我能够使用这个用例:我需要在UINavigationController
内显示文件并保持隐藏/显示{{1}的能力轻触文件内容时
基于the answer from Lukas Gross以及来自 nacross 的评论,我最终在做什么:
UINavigationBar
QLPreviewController
)QLPreviewController
加上状态栏,以便containerTop
&#39; s UINavigationBar
保持隐藏在主{{1}下}}。QLPreviewController
的{{1}}属性,以便我们可以(1)隐藏/显示我们的主UINavigationBar
和(2)重置顶部约束我最终得到了类似的东西:
UINavigationBar
动画可能需要稍微调整一下,这很麻烦,但它比不具备这种可能性更好。
应该可以在没有hidden
注意:如果从故事板实现UINavigationBar
的容器视图时遇到崩溃,请继承UINavigationBar
并实现初始化程序:
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<Void>) {
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
}
答案 4 :(得分:0)
我知道这个答案有点晚了。 但我确实找到了解决方案。
#import "UINavigationItem+Custome.h"
#import <QuickLook/QuickLook.h>
#import <objc/runtime.h>
@implementation UINavigationItem (Custome)
void MethodSwizzle(Class c, SEL origSEL, SEL overrideSEL);
- (void) override_setRightBarButtonItem:(UIBarButtonItem *)item animated:(BOOL)animated{
if (item && [item.target isKindOfClass:[QLPreviewController class]] && item.action == @selector(actionButtonTapped:)){
QLPreviewController* qlpc = (QLPreviewController*)item.target;
[self override_setRightBarButtonItem:qlpc.navigationItem.rightBarButtonItem animated: animated];
}else{
[self override_setRightBarButtonItem:item animated: animated];
}
}
+ (void)load {
MethodSwizzle(self, @selector(setRightBarButtonItem:animated:), @selector(override_setRightBarButtonItem:animated:));
}
void MethodSwizzle(Class c, SEL origSEL, SEL overrideSEL) {
Method origMethod = class_getInstanceMethod(c, origSEL);
Method overrideMethod = class_getInstanceMethod(c, overrideSEL);
if (class_addMethod(c, origSEL, method_getImplementation(overrideMethod), method_getTypeEncoding(overrideMethod))) {
class_replaceMethod(c, overrideSEL, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
}else{
method_exchangeImplementations(origMethod, overrideMethod);
}
}
@end
将其添加为类别并将其导入到QLPreviewController的子类中,然后调用
self.navigationItem.rightBarButtonItem = nil;//something you want
它对我有用。 我从http://nshipster.com/method-swizzling/了解到这一点 和来自http://codego.net/507056/
的想法祝你好运,伙计们。