UIStoryboardPopoverSegue открывает несколько окон при нажатии кнопки

Я использую UIStoryboardPopoverSegue, чтобы представить popover для iOS 5 iPad. Segue отлично работает, но похоже, что панель инструментов, содержащая кнопку, представляет собой сквозное представление для контроллера popover, поэтому, если вы продолжаете нажимать кнопку, появляется больше popovers. Поскольку я не создаю и не отслеживаю UIPopoverController сам (как это делает Раскадровка), я не могу уволить его, когда кнопка снова коснулась. Кто-нибудь еще сталкивается с этим? У меня ошибка с Apple, но они не ответили.

EDIT. Я решил это, используя ответ ниже. Вот код, который я использовал. currentPopover - это __weak ivar в моем классе контроллера, поэтому, когда контроллер будет выполнен, он автоматически опустится до нуля.

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
    if([segue isKindOfClass:[UIStoryboardPopoverSegue class]]){
        // Dismiss current popover, set new popover
        [currentPopover dismissPopoverAnimated:YES];
        currentPopover = [(UIStoryboardPopoverSegue *)segue popoverController];
    }
}

Ответы

Ответ 1

Вам нужно сохранить ссылку на свойство popoverController, переданное как часть класса UIStoryboardPopoverSegue в методе класса prepareForSegue.

Чтобы получить доступ к нему, перетащите метод в контроллере вызывающего вида следующим образом:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    // The Storyboard Segue is named popover in this case:
    if ([segue.identifier compare:@"popover"] == NSOrderedSame) {
        // segue.popoverController is only present in popover segue's
        // self.seguePopoverController is a UIPopoverController * property.
        self.seguePopoverController = segue.popoverController;
    }
}

Затем вы можете отклонить его обычным способом.

Ответ 2

В вашем решении Cory есть некоторые проблемы с изображением.

Два варианта, которые можно рассмотреть - просто удалите или измените действие кнопки, которая представляет popover.

Вариант 1, удерживая указатель на действие кнопки, и после отображения popover установите действие равным нулю. При увольнении popover reset к исходному действию.

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{

    action = [sender action];
    [sender setAction:nil];

    self.currentPopover = [(UIStoryboardPopoverSegue *)segue popoverController];
    self.currentPopover.delegate = self;
}

-(BOOL)popoverControllerShouldDismissPopover:(UIPopoverController *)popoverController
{
    [self.navigationItem.rightBarButtonItem setAction:action];

    return YES;
}

Таким образом, popover может появляться только один раз и будет отклонен как ожидалось.

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

    -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
    {

        action = [sender action];
        target = [sender target];

        [sender setTarget:self];
        [sender setAction:@selector(dismiss:)];

        self.currentPopover = [(UIStoryboardPopoverSegue *)segue popoverController];
        self.currentPopover.delegate = self;
    }

-(void)dismiss:(id)sender
{
    [self.navigationItem.rightBarButtonItem setAction:action];
    [self.navigationItem.rightBarButtonItem setTarget:target];
    ////or
//  [sender setAction:action];
//  [sender setTarget:target];
    [self.currentPopover dismissPopoverAnimated:YES];
}


    -(BOOL)popoverControllerShouldDismissPopover:(UIPopoverController *)popoverController
    {
        [self.navigationItem.rightBarButtonItem setAction:action];
        [self.navigationItem.rightBarButtonItem setTarget:target];

        return YES;
    }

Ответ 3

Просто подключите UIBarButtonItem через IBAction. Используйте set itendifier в построителе интерфейса:

-(IBAction)barButtonItemPressed:(id)sender {
    if (currentPopoverController && currentPopoverController.popoverVisible) {
        [currentPopoverController dismissPopoverAnimated:YES];
        currentPopoverController = nil;
    } else {
        [self performSegueWithIdentifier:@"aSegueIdentifier" sender:sender];
    }
}

Получить ссылку на новый UIPopoverCOntroller из seque:

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    if ([[segue identifier] isEqualToString:@"aSegueIdentifier"])
        currentPopoverController = [(UIStoryboardPopoverSegue *)segue popoverController];
}

currentPopoverController - это переменная экземпляра, определяемая в файле заголовка:

UIPopoverController *currentPopoverController;

Важно: свойство привязки сека должно быть установлено на соответствующий UIBarButtonItem!

Ответ 4

Это решение также может иметь визуальные проблемы, но это не для моего простого случая. В моем случае popover просто показывал некоторую помощь. Я собрал следующее (с ARC), которое уволит контроллеры просмотра popover, когда кнопка кнопки будет нажата второй раз (как оригинал, так и вновь созданный).

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{   
    if( [segue isKindOfClass:[UIStoryboardPopoverSegue class]] )
    {
        UIStoryboardPopoverSegue *popoverSegue      = (id)segue;
        UIPopoverController      *popoverController = popoverSegue.popoverController;

        if( m_popoverController.popoverVisible )
        {
            [m_popoverController dismissPopoverAnimated:NO];
            dispatch_async( dispatch_get_main_queue(), ^{
                [popoverController dismissPopoverAnimated:YES];
            });
            m_popoverController = nil;
        }
        else
            m_popoverController = popoverController;        
    }    
}

Я также добавил некоторую очистку в dealloc

- (void)dealloc
{
    if( m_popoverController.popoverVisible )
        [m_popoverController dismissPopoverAnimated:YES];
}

Он требует переменную-член в вашем классе

UIPopoverController *m_popoverController;

Ответ 5

Я предпочитаю использовать статическую слабую переменную, которая держит все вместе в одном месте:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender  
{
    if ([[segue identifier] isEqualToString:@"showSomething"]) {
        static __weak UIPopoverController* currentPopover = nil;
        [currentPopover dismissPopoverAnimated:NO];
        currentPopover = [(UIStoryboardPopoverSegue *)segue popoverController];
        // ...
    }
}

Нет причин добавлять отдельную дополнительную переменную (когда у вас будет несколько экземпляров контроллера вида?), и таким образом вы можете добавить дополнительную переменную для каждого блока if().

Ответ 6

14 июня 2013 года

Спасибо за редактирование. Вместо того, чтобы отклонять и воссоздавать контроллер представления - чтобы избежать проблем с производительностью и батареями и предотвратить Flash при отклонении и повторном создании контроллера представления - как насчет предотвращения появления второго экземпляра popover?

//place in view controller (tested iOS6+, iPad, iPhone)
__weak UIPopoverController *popover;
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    if([segue isKindOfClass:[UIStoryboardPopoverSegue class]] 
        && [segue.identifier isEqualToString:@"mySegue"]) //remember to change "mySegue" 
            popover = [(UIStoryboardPopoverSegue *)segue popoverController];
}
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender {
    if ([identifier isEqualToString:@"mySegue"]) //remember to change "mySegue"
        return !popover;
    else
        return YES;
}
added checks to: http://stackoverflow.com/a/10238581/1705353 

Ответ 7

Это тоже хорошо.

@interface ViewController : UIViewController <UIPopoverControllerDelegate> {
    UIPopoverController * seguePopoverController;
}

@property (strong) UIPopoverController * seguePopoverController;

@end

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender  
{
    if (self.seguePopoverController) {
        [self.seguePopoverController dismissPopoverAnimated:NO];
        self.seguePopoverController = nil;
    }

    // The Storyboard Segue is named popover in this case:
    if ([[segue identifier] isEqualToString:@"popover"]) {

        UIStoryboardPopoverSegue* popSegue = (UIStoryboardPopoverSegue*)segue;
        UIPopoverController *thePopoverController = [popSegue popoverController];
        thePopoverController.delegate = self;
        self.seguePopoverController = thePopoverController;

    }
}

- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController
{
    self.seguePopoverController = nil;
}