Как убрать раскадровки Поповера
Я создал popover из UIBarButtonItem
с помощью Xcode Storyboards (так там нет кода), как это:
![Xcode 5.0 Connections Inspector with Popover]()
Представление popover отлично работает. Однако я не могу заставить popover исчезнуть, когда я коснулся UIBarButtonItem
, который запустил его.
При нажатии кнопки (первый раз) появляется всплывающее окно. Когда кнопка снова нажата (второй раз), поверх нее появляется тот же самый popover, поэтому теперь у меня есть два popovers (или больше, если я продолжаю нажимать кнопку). В соответствии с Руководством по человеческому интерфейсу iOS мне нужно, чтобы popover появился на первом касании и исчез на втором:
Убедитесь, что на экране одновременно отображается только один popover. Вы не должны отображать более одного popover (или пользовательский вид, предназначенный для просмотра и поведения как popover) одновременно. В частности, вам следует избегать одновременного отображения каскада или иерархии popovers, в котором один popover появляется из другого.
Как я могу отклонить popover, когда пользователь второй раз удаляет UIBarButtonItem
?
Ответы
Ответ 1
РЕДАКТИРОВАТЬ: Эти проблемы, как представляется, исправлены с iOS 7.1/Xcode 5.1.1. (Возможно, раньше, так как я не смог протестировать все версии. Определенно после iOS 7.0, так как я тестировал этот.) Когда вы создаете сегмент popover из UIBarButtonItem
, segue убеждается, что нажатие на popover снова скрывается вместо того, чтобы показывать дубликат. Он работает правильно для новых сегментов pop UIPresentationController
, которые Xcode 6 создает для iOS 8.
Поскольку мое решение может представлять исторический интерес для тех, кто все еще поддерживает более ранние версии iOS, я оставил его ниже.
Если вы храните ссылку на контроллер segue popover, отбрасывая ее перед тем, как установить ее на новое значение при повторных вызовах prepareForSegue:sender:
, все, что вы избегаете, это проблема получения нескольких стекающих popovers при повторных нажатиях кнопки - вы по-прежнему не можете использовать кнопку, чтобы отклонить popover, как рекомендует HIG (и, как видно из приложений Apple, и т.д.).
Вы можете использовать ARC для обнуления слабых ссылок для простого решения:
1: Segue от кнопки
Как и в iOS 5, вы не могли бы выполнить эту работу с помощью segue с UIBarButtonItem
, но вы можете на iOS 6 и более поздних версиях. (На iOS 5 вам придется переходить из самого контроллера представления, затем после вызова проверки кнопки performSegueWithIdentifier:
вызывать вызов кнопки
).
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;
}
}
@end
3: Нет третьего шага!
Хорошая идея об использовании нулевой ссылки здесь заключается в том, что после того, как контроллер popover будет уволен - программно ли он в shouldPerformSegueWithIdentifier:
или автоматически, когда пользователь выстукивает где-то еще за пределами popover - ivar переходит на nil
снова, поэтому мы вернемся к нашему начальному состоянию.
Без обнуления слабых ссылок нам нужно также:
- установите
myPopover = nil
, отклонив его в shouldPerformSegueWithIdentifier:
и
- установите себя как делегат контроллера popover, чтобы поймать
popoverControllerDidDismissPopover:
, а также установить myPopover = nil
там (так что мы поймаем, когда popover автоматически отклоняется).
Ответ 2
Я нашел решение здесь fooobar.com/questions/77254/...
В первом файле prepareForSegue: отправитель: сохраните в ivar/property указатель на 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;
...
}
Ответ 3
Я использовал для этого пользовательский segue.
1
создать пользовательский segue для использования в раскадровке:
@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];
}
@end
2
в виде контроллера, который является источником/входом 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;
}
}
С уважением,
Питер
Ответ 4
Я решил создать пользовательский ixPopoverBarButtonItem
, который либо запускает segue, либо отклоняет отображаемое popover.
Что я делаю: я переключаю действие и цель кнопки, поэтому он либо запускает сеанс, либо удаляет отображаемое в данный момент popover.
Мне понадобилось много поисковых запросов для этого решения, я не хочу брать кредиты за идею переключения действия. Ввод кода в пользовательскую кнопку был моим подходом, чтобы сохранить код шаблона в моем представлении до минимума.
В раскадровке я определяю класс BarButtonItem своему пользовательскому классу:
![custom bar button]()
Затем я передаю 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];
}
}
Btw... так как у меня есть несколько кнопок, запускающих popovers, мне все равно нужно держать ссылку на отображаемое в данный момент popover и отклонять его, когда я делаю новый видимым, но это был не ваш вопрос...
Вот как я применил свой пользовательский UIBarButtonItem:
... интерфейс:
@interface ixPopoverBarButtonItem : UIBarButtonItem
- (void) showingPopover: (UIPopoverController *)popoverController;
@end
... и impl:
#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;
}
@end
ps: Я новичок в ARC, поэтому я не совсем уверен, что я пропадаю здесь. Скажите, пожалуйста, если я...
Ответ 5
Я решил эту проблему без необходимости хранить копию UIPopoverController
. Просто обрабатывайте все в раскадровке (Панель инструментов, BarButtons и т.д.) И
- обрабатывает видимость popover с помощью булева,
- убедитесь, что есть делегат, и он настроен на self
Вот весь код:
ViewController.h
@interface ViewController : UIViewController <UIPopoverControllerDelegate>
@end
ViewController.m
@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
Ответ 6
Я взял рикстерский ответ и упаковал его в класс, полученный из UIViewController. Для этого решения требуется следующее:
- iOS 6 (или более поздняя версия) с ARC
- Вывести контроллер вида из этого класса
- обязательно вызовите "супер" версии prepareForSegue: sender и shouldPerformSegueWithIdentifier: отправитель, если вы переопределяете эти методы.
- Использовать названный popover segue
Самое приятное в этом - вам не нужно делать какие-либо "специальные" кодировки для поддержки правильной обработки Popovers.
Интерфейс
@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
Источник, доступный в GitHub