Как уволить всплывающем окне раскадровки



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



Xcode 5.0 Connections Inspector with Popover



представление popover работает просто отлично. Однако, я не могу получить диалоговое окно, чтобы исчезает когда я нажимаю на UIBarButtonItem Что заставило его появиться.



при первом нажатии кнопки появляется всплывающее окно. Когда кнопка нажата снова (второй раз), на ней появляется тот же поповер, поэтому теперь у меня есть два поповера (или больше если я продолжу нажимать кнопку). Согласно рекомендации по построению интерфейса приложений для iOS мне нужно, чтобы поповер появился на первом кране и исчез на втором:




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




как я могу закрыть диалоговое окно, когда пользователь нажимает UIBarButtonItem во второй раз?

707   6  

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;
    }
}
@end

3: нет никакого третьего шага!

хорошая вещь об использовании обнуления слабой ссылки здесь заключается в том, что как только контроллер 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];

}
@end

2

в контроллере вида, который является источником / входом 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 для моего пользовательского класса:

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

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

вот как я реализовал свой пользовательский 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;
}


@end

ps: Я новичок в ARC, поэтому я не совсем уверен, что я протекаю здесь. Пожалуйста, скажи мне, так ли это...

я решил эту проблему без необходимости хранить копию UIPopoverController. Просто обрабатывать все в раскадровке (панель инструментов, кнопки. так далее.), и

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

вот весь код:

ViewController.h

@interface ViewController : UIViewController <UIPopoverControllerDelegate>
@end

ViewController.м

@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

источник доступен на GitHub

Comments

    Ничего не найдено.