16

私はインターネットでこの種の質問をたくさん見ましたが、誰も答えを本当に知らないようですか?

PDFドキュメントを表示するためにQLPreviewControllerを使用しています。最初は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]];
4

5 に答える 5

27

私はこの問題の解決策を何ヶ月も探し、ついにQLPreviewControllerのナビゲーションバーをカスタマイズする方法を見つけました。以前は、アプリ内の特定の機密ドキュメントのiOS共有ボタンを表示することが許可されていないため、UIWebViewを使用してドキュメントを表示していました。これは、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プロパティを定義して設定する必要があります(Webには多くの例があります...)。また、ナビゲーションバーをカスタマイズして、ボタンのコールバックをリッスンする必要があります。それでおしまい。

これは少しハッキーだと思います...デフォルトのQLアクションボタンを別のナビゲーションバーの下に非表示にすることで非表示/置換します...しかし、少なくとも私にとっては信頼性が高く、プライベートAPIなどにアクセスしません。

私は、iOS 6.0〜7.0、およびiPad 2&3、iPhone 4S&5(iOS 7.0 Beta 6がインストールされている後者)で利用可能なすべてのシミュレーターでソリューションをテストしました。

于 2013-09-06T13:00:34.420 に答える
17

アップデート:

これはiOS6では機能しなくなりました。クイックルックはXPCを使用する別のプロセスで実行されます。詳細については、こちらをご覧ください。QLPreviewControllerをカスタマイズする方法はありません。次の答えは、iOS6以前に興味のある人には当てはまります。


先日ここでほぼ同じ質問に答えました。質問は、それほど難しくない印刷ボタンの削除に関するものでした。注意すべき点の1つQLPreviewControllerは、カスタマイズするためのものではないということです。QLPreviewControllerカスタマイズ可能なサブクラスを作成しました。ここにGithubに配置しました。アクションボタンなどの機能も簡単に削除できるように設計されています。ボタンをカスタムボタンに交換するのに、それほど手間はかかりません。

注意すべき最大の点は、新しいドキュメントが表示されるたびに、アクションボタンがナビゲーションバーに再度追加されることです。私のコードでこれに気付くはずです。アクションボタンを削除するときはいつでもRBFilePreviewer、カスタムボタンを再度追加する必要があります。カスタムボタンを追加するにはUIBarButtonItem、4つのボタンを含むカスタムビューを保持するを作成する必要があります。次に、作成したカスタムとして右バーボタンアイテムを設定しUIBarButtonItemます。

アップデート:

RBFilePreviewerカスタムの右バーボタンアイテムをすぐに設定できるように更新しました。ただ呼び出すだけ-setRightBarButtonItem:RBFilePreviewer、それはうまくいきます。

于 2011-08-08T18:46:22.697 に答える
3

私はLukasGrossからの応答を受け取り、それを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)
}
于 2015-07-22T15:20:20.793 に答える
1

私はこの答えがこれに少し遅れていることを理解しています。しかし、私は本当にこれに対する解決策を見つけます。

#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/から学びます。

がんばれ。

于 2014-11-27T08:01:18.017 に答える
1

既存の回答/コメントを少し混ぜることで、これをユースケースで機能させることができました。ファイルを内部に表示し、ファイルのコンテンツがタップされUINavigationControllerたときに非表示/表示する機能を維持する必要がありました。UINavigationBar

Lukas Grossからの回答とnacrossからのコメントに基づいて、私がやったことは次のとおりです。

  1. QLPreviewController子ビューコントローラとして(のサブクラス)を追加します。これにより、2つのナビゲーションバーが表示されます。1つはメインナビゲーションコントローラー用で、もう1つはQLPreviewController
  2. コンテナビューからトップレイアウトガイド(containerTopコードで指定)へのトップコンストレイントを設定します
  3. この制約を負の値に設定しUINavigationBarます。これは、ステータスバーにプラスを加えたものに等しく、QLPreviewController'UINavigationBarがメインの下に隠れたままになるようにしUINavigationBarます。
  4. KVOを使用して、のhiddenプロパティを監視し、 UINavigationBar(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)
    }

}
于 2016-05-13T15:02:42.927 に答える