QLPreviewController удаляет или добавляет UIBarButtonItems

Я часто встречал этот вопрос в Интернете, но, похоже, никто не знает ответа?

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

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

Мне удалось получить пользовательскую панель инструментов внизу, но это не совсем то, что я хочу.

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

EDIT (Solution): я нашел решение некоторое время назад, но не обновил этот пост, так вот как я решил проблему:

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

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

Обновить:

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


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

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

Обновить:

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

Я искал решение этой проблемы в течение нескольких месяцев и, наконец, нашел способ настроить навигационную панель QLPreviewController. Раньше я также использовал UIWebView для отображения документов, так как мне не разрешено отображать кнопку совместного доступа iOS для определенных конфиденциальных документов в моем приложении, и это то, что делает QLPreviewController. Однако я хотел иметь такие приятные функции, как оглавление, с небольшими превью и т. Д. Поэтому я искал надежный способ избавиться от этой кнопки. Как вы, ребята, я сначала изучал настройку навигационной панели QLPreviewController. Однако, как уже указывали другие, это абсолютно невозможно с iOS6. Поэтому вместо того, чтобы настраивать существующую навигационную панель, нам нужно создать собственный и разместить ее перед QL-навигационной панелью, тем самым скрывая ее.

Итак, как это сделать? Прежде всего нам нужно подclassа 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 (в Интернете есть много примеров …). Также вам нужно настроить навигационную панель и прослушать обратные вызовы кнопок. Вот и все.

Я знаю, что это немного взломанно … скрывает / заменяет кнопку действия по умолчанию QL, скрывая ее под другой навигационной панелью … но, по крайней мере, она работает надёжно для меня, и вы не получаете доступа к приватным API и т. Д.

Я тестировал свое решение на всех доступных симуляторах для iOS 6.0 – 7.0, а также на iPad 2 и 3, iPhone 4S & 5 (последний с установленной iOS 7.0 Beta 6).

Я принял ответ от Лукаса Гросса и применил его в 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) { 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) } 

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

Основываясь на ответе Лукаса Гросса и комментариях от накросса, вот что я сделал:

  1. Добавьте (подclass) QLPreviewController в качестве controllerа детского представления. Это отобразит две панели навигации: одну для главного навигационного controllerа и одну из QLPreviewController
  2. Настройте верхнее ограничение из представления контейнера в верхнее руководство по расположению (с именем containerTop в коде)
  3. Установите это ограничение на отрицательное значение, равное UINavigationBar плюс строку состояния, так что UINavigationBar QLPreviewController остается скрытым под основным 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) { 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 } 

Возможно, анимация нуждается в небольшой настройке, и это взломанно, но это лучше, чем отсутствие этой возможности. Должна быть возможность адаптировать эту страtagsю к другим сценариям без UINavigationController

Примечание. Если у вас возникли QLPreviewController при реализации представления контейнера для QLPreviewController из раскадровки, подclass QLPreviewController и реализация инициализатора:

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

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

 #import "UINavigationItem+Custome.h" #import  #import  @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 

Добавьте это как категорию и импортируйте это в свой подclass QLPreviewController и просто вызовите

 self.navigationItem.rightBarButtonItem = nil;//something you want 

Меня устраивает. Я изучаю это из http://nshipster.com/method-swizzling/ и мысли из http://codego.net/507056/

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

  • Как масштабировать UIImageView пропорционально?
  • Выполнение команд оболочки с помощью NSTask - Objective-C Cocoa
  • Как добавить userInfo в UIAlertView?
  • Сортировка NSArray строк или объектов даты
  • Как отслеживать причину SIGABRT
  • Что такое простой способ разбить NSArray с 4000+ объектами в нем на несколько массивов с 30 объектами каждый?
  • Что такое Swift-эквивалент ответаSoSelector?
  • Как проверить программно, если приложение установлено?
  • Когда происходит освобождение связанного объекта?
  • Прокрутка двумя пальцами с помощью UIScrollView
  • Как получить подстроку NSString?
  • Давайте будем гением компьютера.