Как убрать раскадровки Поповера

Я создал 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