Как уволить всплывающем окне раскадровки
Я создал диалоговое окно с UIBarButtonItem использование раскадровки Xcode (так что нет кода), как это:

представление popover работает просто отлично. Однако, я не могу получить диалоговое окно, чтобы исчезает когда я нажимаю на UIBarButtonItem Что заставило его появиться.
при первом нажатии кнопки появляется всплывающее окно. Когда кнопка нажата снова (второй раз), на ней появляется тот же поповер, поэтому теперь у меня есть два поповера (или больше если я продолжу нажимать кнопку). Согласно рекомендации по построению интерфейса приложений для iOS мне нужно, чтобы поповер появился на первом кране и исчез на втором:
убедитесь, что на экране одновременно отображается только один popover. Вы не должны отображать более одного popover (или пользовательский вид, предназначенный для просмотра и поведения как popover) одновременно. В частности, следует избегать одновременного отображения каскада или иерархии всплывающих окон, в которых один пирог вытекает из другого.
как я могу закрыть диалоговое окно, когда пользователь нажимает UIBarButtonItem во второй раз?
6 ответов:
EDIT: эти проблемы, по-видимому, исправлены с iOS 7.1 / Xcode 5.1.1. (Возможно, раньше, так как я не смог проверить все версии. Определенно после iOS 7.0, так как я тестировал этот.) При создании диалогового окна переход из
UIBarButtonItem, segue гарантирует, что нажатие на поповер снова скрывает поповер, а не показывает дубликат. Он работает прямо для новогоUIPresentationController- на основе popover сегментирует, что Xcode 6 создает для iOS 8, тоже.так как мое решение может представлять исторический интерес для тех, кто все еще поддерживает более ранние версии iOS, я оставил его ниже.
если вы сохраняете ссылку на контроллер popover segue, отклоняя его перед установкой нового значения при повторных вызовах
prepareForSegue:sender:, все, что вы избегаете, это проблема получения нескольких стековых всплывающих окон при повторных нажатиях кнопки - вы все еще не можете использовать кнопку, чтобы отклонить всплывающее окно, как рекомендует HIG (и как видно в приложениях Apple, так далее.)вы можете воспользоваться дугой обнуления слабых ссылок для простого решения, хотя:
1: переход от кнопки
начиная с iOS 5, вы не можете сделать эту работу с сегментом из
UIBarButtonItem, но вы можете на iOS 6 и более поздних версиях. (На iOS 5 вам нужно будет перейти от самого контроллера вида, а затем вызвать действие кнопкиperformSegueWithIdentifier:после проверки на popover.)2: используйте ссылку на popover в
-shouldPerformSegue...@interface ViewController @property (weak) UIPopoverController *myPopover; @end @implementation ViewController - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { // if you have multiple segues, check segue.identifier self.myPopover = [(UIStoryboardPopoverSegue *)segue popoverController]; } - (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender { if (self.myPopover) { [self.myPopover dismissPopoverAnimated:YES]; return NO; } else { return YES; } } @end3: нет никакого третьего шага!
хорошая вещь об использовании обнуления слабой ссылки здесь заключается в том, что как только контроллер popover отклоняется-будь то программно в
shouldPerformSegueWithIdentifier:, или автоматически пользователь нажав где-то еще за пределами popover -- Ивар идет кnilснова, так что мы вернулись к нашему исходному состоянию.без обнуления слабых ссылок, мы должны были бы также:
- set
myPopover = nilкогда уволить его вshouldPerformSegueWithIdentifier:и- установите себя в качестве делегата контроллера popover, чтобы поймать
popoverControllerDidDismissPopover:а такжеmyPopover = nilтам (так что мы ловим, когда поповер автоматически отклоняется).
Я нашел решение здесь https://stackoverflow.com/a/7938513/665396 В первом prepareForSegue: sender: храните в свойстве ivar/указатель на UIPopoverController и пользователь, указывающий на отклонение popover в последующих вызовах.
... @property (nonatomic, weak) UIPopoverController* storePopover; ... - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { if ([segue.identifier isEqualToString:@"My segue"]) { // setup segue here [self.storePopover dismissPopoverAnimated:YES]; self.storePopover = ((UIStoryboardPopoverSegue*)segue).popoverController; ... }
я использовал пользовательский сегмент для этого.
1
создать пользовательский сегмент для использования в раскадровке:
@implementation CustomPopoverSegue -(void)perform { // "onwer" of popover - it needs to use "strong" reference to retain UIPopoverReference ToolbarSearchViewController *source = self.sourceViewController; UIViewController *destination = self.destinationViewController; // create UIPopoverController UIPopoverController *popoverController = [[UIPopoverController alloc] initWithContentViewController:destination]; // source is delegate and owner of popover popoverController.delegate = source; popoverController.passthroughViews = [NSArray arrayWithObject:source.searchBar]; source.recentSearchesPopoverController = popoverController; // present popover [popoverController presentPopoverFromRect:source.searchBar.bounds inView:source.searchBar permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES]; } @end2
в контроллере вида, который является источником / входом segue, например, start segue с действием:
-(void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar { if(nil == self.recentSearchesPopoverController) { NSString *identifier = NSStringFromClass([CustomPopoverSegue class]); [self performSegueWithIdentifier:identifier sender:self]; } }3
ссылки назначаются segue, который создает UIPopoverController - при отклонении popover
-(void)searchBarTextDidEndEditing:(UISearchBar *)searchBar { if(self.recentSearchesPopoverController) { [self.recentSearchesPopoverController dismissPopoverAnimated:YES]; self.recentSearchesPopoverController = nil; } }С уважением, Питер
я решил это, создав пользовательский
ixPopoverBarButtonItemчто либо провоцирует переход или закрывает диалоговое окно отображается.что я делаю: я переключаю действие и цель кнопки, поэтому он либо запускает сегмент, либо удаляет текущий показ popover.
мне потребовалось много гуглить для этого решения, я не хочу брать кредиты за идею переключения действия. Ввод кода в пользовательскую кнопку был моим подходом, чтобы сохранить шаблонный код в моем представлении минимум.
в раскадровке я определяю класс BarButtonItem для моего пользовательского класса:
затем я передаю popover, созданный segue для моей пользовательской реализации кнопки в
prepareForSegue:sender:способ:- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { if ([segue.identifier isEqualToString:@"myPopoverSegue"]) { UIStoryboardPopoverSegue* popSegue = (UIStoryboardPopoverSegue*)segue; [(ixPopoverBarButtonItem *)sender showingPopover:popSegue.popoverController]; } }кстати... поскольку у меня есть несколько кнопок, запускающих всплывающие окна, мне все равно нужно сохранить ссылку на отображаемый в настоящее время всплывающий экран и отклонить его, когда я сделаю новый видимым, но это не ваш вопрос...
вот как я реализовал свой пользовательский UIBarButtonItem:
...интерфейс:
@interface ixPopoverBarButtonItem : UIBarButtonItem - (void) showingPopover: (UIPopoverController *)popoverController; @end... и осущ:
#import "ixPopoverBarButtonItem.h" @interface ixPopoverBarButtonItem () @property (strong, nonatomic) UIPopoverController *popoverController; @property (nonatomic) SEL tempAction; @property (nonatomic,assign) id tempTarget; - (void) dismissPopover; @end @implementation ixPopoverBarButtonItem @synthesize popoverController = _popoverController; @synthesize tempAction = _tempAction; @synthesize tempTarget = _tempTarget; -(void)showingPopover:(UIPopoverController *)popoverController { self.popoverController = popoverController; self.tempAction = self.action; self.tempTarget = self.target; self.action = @selector(dismissPopover); self.target = self; } -(void)dismissPopover { [self.popoverController dismissPopoverAnimated:YES]; self.action = self.tempAction; self.target = self.tempTarget; self.popoverController = nil; self.tempAction = nil; self.tempTarget = nil; } @endps: Я новичок в ARC, поэтому я не совсем уверен, что я протекаю здесь. Пожалуйста, скажи мне, так ли это...
я решил эту проблему без необходимости хранить копию
UIPopoverController. Просто обрабатывать все в раскадровке (панель инструментов, кнопки. так далее.), и
- обрабатывать видимость popover с помощью логического значения,
- убедитесь, что есть делегат, и он установлен в self
вот весь код:
ViewController.h
@interface ViewController : UIViewController <UIPopoverControllerDelegate> @endViewController.м
@interface ViewController () @property BOOL isPopoverVisible; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; self.isPopoverVisible = NO; } - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { // add validations here... self.isPopoverVisible = YES; [[(UIStoryboardPopoverSegue*)segue popoverController] setDelegate:self]; } - (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender { return !self.isPopoverVisible; } - (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController { self.isPopoverVisible = NO; } @end
Я взял ответ rickster и упаковывают его в класс, производный от для UIViewController. Это решение требует следующего:
- iOS 6 (или более поздняя версия) с ARC
- вывести контроллер вида из этого класса
- обязательно вызовите "супер" версии prepareForSegue:sender и shouldPerformSegueWithIdentifier: sender, если вы переопределяете эти методы
- использовать в имени диалоговым окном переход
хорошая вещь об этом вам не нужно делать никакого "специального" кодирования для поддержки правильной обработки всплывающих окон.
интерфейс:
@interface FLStoryboardViewController : UIViewController { __strong NSString *m_segueIdentifier; __weak UIPopoverController *m_popoverController; } - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender; - (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender; @endреализация:
@implementation FLStoryboardViewController - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { if( [segue isKindOfClass:[UIStoryboardPopoverSegue class]] ) { UIStoryboardPopoverSegue *popoverSegue = (id)segue; if( m_popoverController == nil ) { assert( popoverSegue.identifier.length > 0 ); // The Popover segue should be named for this to work fully m_segueIdentifier = popoverSegue.identifier; m_popoverController = popoverSegue.popoverController; } else { [m_popoverController dismissPopoverAnimated:YES]; m_segueIdentifier = nil; m_popoverController = nil; } } else { [super prepareForSegue:segue sender:sender]; } } - (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender { // If this is an unnamed segue go ahead and allow it if( identifier.length != 0 ) { if( [identifier compare:m_segueIdentifier] == NSOrderedSame ) { if( m_popoverController == NULL ) { m_segueIdentifier = nil; return YES; } else { [m_popoverController dismissPopoverAnimated:YES]; m_segueIdentifier = nil; m_popoverController = nil; return NO; } } } return [super shouldPerformSegueWithIdentifier:identifier sender:sender]; } @end

Comments