73

UIBarButtonItem次のように、Xcode ストーリーボードを使用して (コードがないため)ポップオーバーを作成しました。

Xcode 5.0 接続インスペクターとポップオーバー

ポップオーバーの表示は問題なく機能します。ただし、ポップオーバーを表示させたをタップしてUIBarButtonItem、ポップオーバーを非表示にすることはできません。

ボタンを押すと (初めて)、ポップオーバーが表示されます。ボタンをもう一度押すと (2 回目)、同じポップオーバーがその上に表示されるので、2 つのポップオーバー (ボタンを押し続けるとそれ以上) が表示されます。iOS ヒューマン インターフェイス ガイドラインによると、最初のタップでポップオーバーを表示し、2 番目のタップで非表示にする必要があります。

一度に 1 つのポップオーバーのみが画面に表示されるようにします。同時に複数のポップオーバー (またはポップオーバーのような外観と動作をするように設計されたカスタム ビュー) を表示しないでください。特に、あるポップオーバーが別のポップオーバーから現れるカスケードまたはポップオーバーの階層を同時に表示することは避けるべきです。

ユーザーがもう一度タップしたときにポップオーバーを閉じるにはどうすればよいUIBarButtonItemですか?

4

6 に答える 6

114

編集:これらの問題は、iOS 7.1 / Xcode 5.1.1 で修正されたようです。(すべてのバージョンをテストできなかったので、おそらくそれより前のバージョンです。iOS 7.0 をテストしたので、間違いなくそれ以降です。UIBarButtonItem)重複を表示するよりも。UIPresentationControllerXcode 6 が iOS 8 用に作成する新しいベースのポップオーバー セグエでも正しく機能します。

私の解決策は、以前の iOS バージョンをまだサポートしている人々にとって歴史的に興味深いかもしれないので、以下に残しました。


セグエのポップオーバー コントローラへの参照を保存し、 の繰り返し呼び出しで新しい値に設定する前にそれをprepareForSegue:sender:閉じると、回避できるのは、ボタンを繰り返し押すと複数のスタッキング ポップオーバーが発生するという問題だけです。 HIG が推奨する (および Apple のアプリなどで見られる) ポップオーバーを閉じるボタン

ただし、簡単な解決策として、弱い参照をゼロにする ARC を利用できます。

1: ボタンからのセグエ

iOS 5 の時点では、 からのセグエでこれを機能させることはできませんでしたUIBarButtonItemが、iOS 6 以降では可能です。performSegueWithIdentifier:(iOS 5 では、View Controller 自体からセグエし、ポップオーバーを確認した後にボタンのアクションを呼び出す必要があります。)

2: ポップオーバーへの参照を使用する-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: ステップ 3 はありません。

ここでゼロ化の弱い参照を使用することの良い点は、ポップオーバー コントローラーが閉じられると (プログラムでshouldPerformSegueWithIdentifier:、またはユーザーがポップオーバーの外側の別の場所をタップして自動的に)、ivar がnil再び移動することです。初期状態。

弱い参照をゼロにしないと、次のことも行う必要があります。

  • myPopover = nilで却下するときに設定しshouldPerformSegueWithIdentifier:
  • popoverControllerDidDismissPopover:キャッチしてそこに設定するために、自分自身をポップオーバーコントローラーのデリゲートとして設定myPopover = nilします(ポップオーバーが自動的に閉じられたときにキャッチします)。
于 2012-04-20T00:10:09.133 に答える
13

ここで解決策を見つけましたhttps://stackoverflow.com/a/7938513/665396 最初に prepareForSegue:sender: ivar/property に UIPopoverController へのポインターを格納し、そのポインターを使用して、その後の呼び出しでポップオーバーを閉じます。

...
@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;
...
}
于 2011-11-28T19:51:13.220 に答える
2

これにはカスタムセグエを使用しました。

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

セグエのソース/入力であるビューコントローラーで、アクションでセグエを開始します。

-(void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar
{
    if(nil == self.recentSearchesPopoverController)
    {
        NSString *identifier = NSStringFromClass([CustomPopoverSegue class]);
        [self performSegueWithIdentifier:identifier sender:self];
    } 
}

3

参照は UIPopoverController を作成するセグエによって割り当てられます - ポップオーバーを閉じるとき

-(void)searchBarTextDidEndEditing:(UISearchBar *)searchBar
{
    if(self.recentSearchesPopoverController)
    {
        [self.recentSearchesPopoverController dismissPopoverAnimated:YES];
        self.recentSearchesPopoverController = nil;
    }    
}

よろしく、ピーター

于 2012-03-11T14:17:03.780 に答える
2

ixPopoverBarButtonItemセグエをトリガーするか、表示されているポップオーバーを閉じるカスタムを作成して解決しました。

私がすること: ボタンのアクションとターゲットを切り替えて、セグエをトリガーするか、現在表示されているポップオーバーを破棄します。

このソリューションには多くのグーグル検索が必要でした。アクションを切り替えるというアイデアの功績を認めたくありません。コードをカスタム ボタンに配置することは、ボイラープレート コードを最小限に抑えるための私のアプローチでした。

ストーリーボードで、BarButtonItem のクラスをカスタム クラスに定義します。

カスタムバーボタン

次に、セグエによって作成されたポップオーバーを、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 を初めて使用するので、ここで漏れているかどうかは完全にはわかりません。私がそうであるかどうか教えてください...

于 2012-04-05T17:02:41.280 に答える
2

のコピーを保持する必要なく、この問題を解決しましたUIPopoverController。ストーリーボード (ツールバー、BarButtons など) ですべてを処理するだけで、

  • ブール値でポップオーバーの可視性を処理し、
  • デリゲートがあり、それが自分自身に設定されていることを確認してください

すべてのコードは次のとおりです。

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
于 2013-11-19T14:03:55.543 に答える
1

私はricksterの答えを受け取り、それをUIViewControllerから派生したクラスにパッケージ化しました。このソリューションには、次のものが必要です。

  • ARCを搭載したiOS6(またはそれ以降)
  • このクラスからViewControllerを派生させます
  • これらのメソッドをオーバーライドする場合は、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で入手可能なソース

于 2012-11-03T13:35:42.060 に答える