2011-08-05 88 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) 
    } 

}