2011-08-05 58 views
16

我在互联网上看到了很多这样的问题,但似乎没有人真的知道答案?QLPreviewController删除或添加UIBarButtonItems

我正在使用QLPreviewController来显示PDF文档。我首先使用了一个UIWebView,但我建议使用QLPreviewController来代替性能较高的文档。

我想要的是4个自定义的UIBarButtonItem的在右上角(所以在哪里打印按钮)。

我设法得到一个自定义工具栏在底部,但那不是我想要的。

考虑到无法在打印按钮的位置添加自定义按钮,我仍然想要删除打印按钮并使用自定义工具栏。

EDIT(解决方案): 我找到了解决办法前一段时间,但没有更新这个帖子所以这里是我如何解决了这个问题:

我加人手动按钮:

// 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

RBFilePreviewer现在支持您正在查找的功能,无需修改。 – rbrown

+0

我的回答是否足以被接受和赏金? – rbrown

+0

这不是真的,我正在寻找,我现在有QLPreviewController的其他问题:http://stackoverflow.com/questions/7038438/quicklook-not-showing-offline-files但我会除了你的答案因为它是最好的(也是唯一的),它对我有一点帮助。 – Justin

回答

17

更新:

这不再是工作的iOS 6快速查找运行在另一p使用XPC的过程。有关更多详情,请参阅here。我没有预见到任何方式来定制QLPreviewController。以下的答案仍然是有兴趣的人预的iOS 6


我回答几乎相同的问题here的一天。这个问题涉及到删除打印按钮,这不是太难。关于QLPreviewController有一点需要注意的是,它不是要定制的。我已经构建了可以自定义的QLPreviewController的子类。我已经把它放在Github上here。它旨在轻松删除操作按钮,以及其他功能。用自定义的按钮替换按钮不需要太多的努力。

要注意的最大问题是,无论何时显示新文档,操作按钮都会重新添加到导航栏中。你应该在我的代码中注意到这一点。随时RBFilePreviewer删除操作按钮,你只需要重新添加您的自定义按钮。要添加自定义按钮,您应该创建一个UIBarButtonItem,该按钮包含一个具有四个按钮的自定义视图。然后将正确的条形按钮项目设置为您创建的自定义UIBarButtonItem

更新:

我已经更新RBFilePreviewer允许您设置右出的现成的自定义权栏按钮项目。只需拨-setRightBarButtonItem:RBFilePreviewer它只是工作。

+0

出色的工作!但是,是否可以更改整个导航项目tinColor(或整个背景颜色)?我无法做到这一点:( – Centurion

+0

我刚刚添加了设置导航栏和工具栏的色调的功能,你只需要拉最新的版本即可。你必须做不同的操作,具体取决于你是否按下导航堆栈,或者是否以模态方式显示。我的添加使这两种情况完全相同。 – rbrown

+0

谢谢,我看看这个。到目前为止,我可以通过继承QLPreviewController,覆盖viewWillAppear方法,遍历self.view和搜索UINavigationBar视图对象,然后设置其tintColor来实现该功能:) – Centurion

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方法我们照顾我们的自定义导航栏框架。

我们应该做的下一件事就是监听快速查找导航栏的可见性变化:

- (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属性(网上有很多示例...)。您还需要自定义导航栏并听取按钮回调。而已。

我知道这有点不好意思......隐藏/替换默认的QL动作按钮,隐藏在另一个导航栏下......但至少它对我来说工作可靠,而且您不会访问私有API等。

我在iOS 6.0 - 7.0以及iPad 2上的所有可用模拟器上测试了我的解决方案,并在iOS 2上安装了iOS 7.0 Beta 6。

+1

这需要选择作为答案,应该是更高!这很冒险,但由于我们不能直接定制QLPreviewController的导航栏,所以它的最佳答案和**它的作品**。我不喜欢上面的答案,这是说,“嘿下载我的破坏的代码不适用于iOS 6或7”。 – BFeher

+0

适合我...很棒的工作。 –

+0

它不适用于iOS 8 GM种子。请提供其他一些建议。 – Meet

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/

想法这个祝你好运,伙计。

3

我从卢卡斯总响应和斯威夫特它应用在iOS 8,并用此解决方案,为我工作上来:

注:我有嵌入在一个UINavigationController的QLPreviewController!

代码:

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) 
} 
1

通过混合位淘汰现有的答案/意见,我能得到这个工作对我用例:我需要在UINavigationController中显示文件,并在文件内容被点击时保持隐藏/显示UINavigationBar的能力

基于the answer from Lukas Grossnacross这里的评论是我落得这样做:

  1. 添加(子类)QLPreviewController为子视图控制器。这将显示两个导航栏:一个是主导航控制器和一个从QLPreviewController
  2. 从容器视图建立顶部约束到顶部布局指南(在代号为containerTop
  3. 设置此约束至负值,等于UINavigationBar加上状态栏,这样QLPreviewControllerUINavigationBar仍然隐藏在主要的UINavigationBar下。
  4. 使用志愿,监控UINavigationBarhidden属性,以便我们可以(1)隐藏/显示我们的主要UINavigationBar和(2)重设顶部约束

我结束了这样的事情:

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 
} 

这些动画可能需要稍微调整一下,这很不方便,但它比没有这种可能性要好。 它应该能够适应这一战略,其他情况下没有UINavigationController

注意:如果你从故事板实现了QLPreviewController容器视图时,没有出车祸,子类QLPreviewController并实现初始化:

class MyPreviewController: QLPreviewController { 

    required init?(coder aDecoder: NSCoder) { 
     super.init(nibName: nil, bundle: nil) 
    } 

}