QLPreviewController удалить или добавить UIBarButtonItems - PullRequest
16 голосов
/ 05 августа 2011

Я часто сталкивался с подобными вопросами в Интернете, но, похоже, никто не знает ответа?

Я использую QLPreviewController для отображения PDF-документов.Сначала я использовал UIWebView, но мне рекомендовали вместо этого использовать QLPreviewController из соображений производительности с большими документами.

Мне нужно 4 пользовательских UIBarButtonItem в верхнем правом углу (так, где кнопка печати).1006 * Мне удалось получить пользовательскую панель инструментов внизу, но это не совсем то, что я хочу.

Учитывая, что невозможно добавить пользовательскую кнопку вместо кнопки печати, я все еще хочу удалитьвместо кнопки печати и используйте настраиваемую панель инструментов.

РЕДАКТИРОВАТЬ (решение): Я нашел решение некоторое время назад, но не обновил этот пост, так вот как я решилпроблема:

Я добавляю все кнопки вручную:

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

Ответы [ 5 ]

27 голосов
/ 06 сентября 2013

Я искал решение этой проблемы в течение нескольких месяцев и, наконец, нашел способ настроить панель навигации 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 * overlayNavigationэто наш пользовательский, который будет скрывать по умолчанию.Мы также добавляем наблюдение значения ключа в панель навигации 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 и 3, iPhone 4S & 5 (последняя с установленной iOS 7.0 Beta 6).

17 голосов
/ 08 августа 2011

Обновление:

Это больше не работает в iOS 6. Quick Look запускается в другом процессе с использованием XPC.Смотрите здесь для более подробной информации.Я не предполагаю никакого способа настройки QLPreviewController.Следующий ответ остается для всех, кто интересуется до iOS 6.


Я ответил на почти идентичный вопрос на днях здесь .Вопрос касался удаления кнопки печати, что не так уж сложно.О QLPreviewController следует отметить одну вещь: она не предназначена для настройки.Я создал подкласс QLPreviewController, который можно настроить.Я положил это здесь на Github.Он разработан, чтобы легко удалить кнопку действия, а также другие функции.Это не займет много усилий, чтобы заменить кнопку на пользовательскую.

Самое важное, на что следует обратить внимание, это то, что кнопка действия повторно добавляется на панель навигации каждый раз, когда отображается новый документ.Вы должны заметить это в моем коде.В любое время RBFilePreviewer удаляет кнопку действия, вам просто нужно заново добавить пользовательские кнопки.Чтобы добавить свои пользовательские кнопки, вы должны создать UIBarButtonItem, который содержит пользовательский вид с четырьмя кнопками в нем.Затем установите правый элемент панели кнопок как пользовательский UIBarButtonItem, который вы создали.

Обновление:

Я обновил RBFilePreviewer, чтобы позволить вам настраивать пользовательский элемент правой панели прямо из коробки.Просто позвоните -setRightBarButtonItem: на RBFilePreviewer, и это просто работает.

3 голосов
/ 22 июля 2015

Я взял ответ от Лукаса Гросса и применил его в Swift на iOS 8 и придумал это решение, которое работает для меня:

ПРИМЕЧАНИЕ. У меня есть 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)
}
1 голос
/ 13 мая 2016

Смешав немного из существующих ответов / комментариев, я смог заставить это работать для моего случая использования: мне нужно было отображать файлы внутри UINavigationController и сохранять возможность скрывать / показывать UINavigationBar, когдасодержимое файла прослушивается

На основании ответа от Лукаса Гросса и комментария от nacross вот что я закончил делать:

  1. Добавить (подкласс) QLPreviewController в качестве дочернего контроллера представления.Это покажет две панели навигации: одну для основного контроллера навигации и одну из 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)
    }

}
1 голос
/ 27 ноября 2014

Я понимаю, что этот ответ немного опоздал на это. Но я действительно нахожу решение для этого.

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

Удачи, ребята.

...