21

回転後、ポップオーバーを画面上の同じ位置に保つことができません。いくつかのフレームをポップオーバーに設定するだけで、回転後にひどく機能するため、それを行う良い方法はありますか? popover.frame = CGRectMake(someFrame);回転後のポップオーバーは、画面の中央にある場合にのみ正常に見えます。

4

13 に答える 13

30

Apple には、まさにこの問題に関する Q&A があります。詳細は次のとおりです。

テクニカル Q&A QA1694 方向変更中のポップオーバー コントローラの処理

基本的に、この手法では、View Controller のdidRotateFromInterfaceOrientationメソッドで次のようにポップを再度表示することを説明しています。

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
    [aPopover presentPopoverFromRect:targetRect.frame inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}

詳細については、上記の記事とUIPopoverController Class Referenceを参照してください。

ポップオーバーが表示されているときにユーザーがデバイスを回転すると、ポップオーバー コントローラーはポップオーバーを非表示にし、回転の最後に再び表示します。ポップオーバー コントローラーは、ポップオーバーを適切に配置しようとしますが、場合によっては、再度表示するか、完全に非表示にする必要があります。たとえば、バー ボタン アイテムから表示される場合、ポップオーバー コントローラーは、バー ボタン アイテムの位置の変更を考慮して、ポップオーバーの位置 (場合によってはサイズ) を自動的に調整します。ただし、回転中にバー ボタン項目を削除した場合、またはビュー内のターゲット四角形からポップオーバーを表示した場合、ポップオーバー コントローラーはポップオーバーの再配置を試みません。そのような場合は、ポップオーバーを手動で非表示にするか、適切な新しい位置から再度表示する必要があります。

于 2013-04-17T15:50:29.270 に答える
17

iOS 8.0.2 以降、 willRotateToInterfaceOrientationは効果がありません。mhrrt が述べたように、delegate メソッドを使用する必要があります。

- (void)popoverController:(UIPopoverController *)popoverController willRepositionPopoverToRect:(inout CGRect *)rect inView:(inout UIView *__autoreleasing *)view

たとえば、押されたボタンのすぐ下にポップオーバーを表示する場合は、次のコードを使用します。

- (void)popoverController:(UIPopoverController *)popoverController willRepositionPopoverToRect:(inout CGRect *)rect inView:(inout UIView *__autoreleasing *)view
{
   CGRect rectInView = [self.theButton convertRect:self.theButton.frame toView:self.view];
   *rect = CGRectMake(CGRectGetMidX(rectInView), CGRectGetMaxY(rectInView), 1, 1);
   *view = self.view;
}
于 2014-10-09T13:56:17.483 に答える
6

iOS 7 では- (void)popoverController:(UIPopoverController *)popoverController willRepositionPopoverToRect:(inout CGRect *)rect inView:(inout UIView *__autoreleasing *)view、インターフェイスの向きの変更時に UIPopoverController のビューを再配置するために使用できます。

UIPopoverControllerDelegate ドキュメントを参照してください。

于 2014-02-03T12:41:26.637 に答える
5

didRotateFromInterfaceOrientation:これは、ポップオーバーの表示に使用したビュー コントローラーのメソッドで行うことができます。

メソッドを使用setPopoverContentSize:animated:して、ポップオーバーのサイズを設定します。

于 2012-01-30T14:21:13.027 に答える
4

UIPopoverControllerios8で導入された UIPopoverPresentationController を支持して、ios9 で廃止されました。( から に移動するときも、この遷移を経験UIActionSheetしましたUIAlertController。) 2 つの選択肢があります (obj-C の例):

A.UIViewController以下のメソッドを実装します (UIKit は、表示されたビュー コントローラーのビューのサイズを変更する前にこのメソッドを呼び出します)。

- (void)viewWillTransitionToSize:(CGSize)size
           withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
        [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
        [coordinator animateAlongsideTransition:nil
                                     completion:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
                                         // Fix up popover placement if necessary, *after* the transition.
                                         // Be careful here if a subclass also overrides this method.
                                         if (self.presentedViewController) {
                                             UIPopoverPresentationController *presentationController =
                                                     [self.presentedViewController popoverPresentationController];
                                             UIView *selectedView = /** YOUR VIEW */;
                                             presentationController.sourceView = selectedView.superview;
                                             presentationController.sourceRect = selectedView.frame;
                                         }
                                     }];
    }

B. または、プレゼンテーションを構成するときにUIPopoverPresentationController、デリゲートも設定します。たとえば、プレゼンティング VC はデリゲートとして自身を実装UIPopoverPresentationControllerDelegateして割り当てることができます。次に、デリゲート メソッドを実装します。

- (void)popoverPresentationController:(UIPopoverPresentationController *)popoverPresentationController
          willRepositionPopoverToRect:(inout CGRect *)rect
                               inView:(inout UIView * _Nonnull *)view {
    UIView *selectedView = /** YOUR VIEW */;
    // Update where the arrow pops out of in the view you selected.
    *view = selectedView;
    *rect = selectedView.bounds;
}
于 2017-01-10T04:13:13.317 に答える
1

私はこれで解決する同様の問題を抱えています

[myPop presentPopoverFromRect:myfield.frame inView:myscrollview permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];

myfieldポップオーバーを表示するフレームと、ポップオーバーをサブビューとして追加するコンテナー ビューはどこにありますmyscrollviewか (私の場合は、inView:self.viewI use を配置するのではなく、私のスクロールビューinView:myscrollviewです)。

于 2012-05-19T06:16:03.623 に答える
0

私は同じ問題を抱えていました。-presentPopoverFromRectソース矩形/表示元のビューを追跡することによって毎回実行する代わりに、サブクラス化しUIPopoverControllerました。それを行った後、ポップオーバーを表示する必要がある UIBarButtonItem / UIView のいずれかを設定するだけです。NSString 値として渡すことができるカスタム フレームからポップオーバーを表示することもできます。

CSPopoverController.h :

#import <UIKit/UIKit.h>

// The original popover controller would not re-orientate itself when the orientation change occurs. To tackle that issue, this subclass is created
@interface CSPopoverController : UIPopoverController

@property (nonatomic, strong) NSString *popoverDisplaySourceFrame;  // Mutually Exclusive. If you want to set custom rect as source, make sure that popOverDisplaySource is nil
@property (nonatomic, strong) id popoverDisplaySource;              // Mutually exclusive. If UIBarButtonItem is set to it, popoverDisplaySourceFrame is neglected.
@property (nonatomic, strong) UIView *popoverDisplayView;

@property (nonatomic, assign, getter = shouldAutomaticallyReorientate) BOOL automaticallyReorientate;

-(void)reorientatePopover;

@end

CSPopoverController.m :

#import "CSPopoverController.h"

@implementation CSPopoverController
@synthesize popoverDisplaySourceFrame = popoverDisplaySourceFrame_;
-(NSString*)popoverDisplaySourceFrame
{
    if (nil==popoverDisplaySourceFrame_)
    {
        if (nil!=self.popoverDisplaySource)
        {
            if ([self.popoverDisplaySource isKindOfClass:[UIView class]])
            {
                UIView *viewSource = (UIView*)self.popoverDisplaySource;
                [self setPopoverDisplaySourceFrame:NSStringFromCGRect(viewSource.frame)];
            }
        }
    }
    return popoverDisplaySourceFrame_;
}
-(void)setPopoverDisplaySourceFrame:(NSString *)inPopoverDisplaySourceFrame
{
    if (inPopoverDisplaySourceFrame!=popoverDisplaySourceFrame_)
    {
        popoverDisplaySourceFrame_ = inPopoverDisplaySourceFrame;
        [self reorientatePopover];
    }
}
@synthesize popoverDisplaySource = popoverDisplaySource_;
-(void)setPopoverDisplaySource:(id)inPopoverDisplaySource
{
    if (inPopoverDisplaySource!=popoverDisplaySource_)
    {
        [self unlistenForFrameChangeInView:popoverDisplaySource_];
        popoverDisplaySource_ = inPopoverDisplaySource;
        [self reorientatePopover];

        if ([popoverDisplaySource_ isKindOfClass:[UIView class]])
        {
            UIView *viewSource = (UIView*)popoverDisplaySource_;
            [self setPopoverDisplaySourceFrame:NSStringFromCGRect(viewSource.frame)];
        }
        if (self.shouldAutomaticallyReorientate)
        {
            [self listenForFrameChangeInView:popoverDisplaySource_];
        }
    }
}
@synthesize popoverDisplayView = popoverDisplayView_;
-(void)setPopoverDisplayView:(UIView *)inPopoverDisplayView
{
    if (inPopoverDisplayView!=popoverDisplayView_)
    {
        popoverDisplayView_ = inPopoverDisplayView;
        [self reorientatePopover];
    }
}
@synthesize automaticallyReorientate = automaticallyReorientate_;
-(void)setAutomaticallyReorientate:(BOOL)inAutomaticallyReorientate
{
    if (inAutomaticallyReorientate!=automaticallyReorientate_)
    {
        automaticallyReorientate_ = inAutomaticallyReorientate;
        if (automaticallyReorientate_)
        {
            [self listenForAutorotation];
            [self listenForFrameChangeInView:self.popoverDisplaySource];
        }
        else
        {
            [self unlistenForAutorotation];
            [self unlistenForFrameChangeInView:self.popoverDisplaySource];
        }
    }
}

-(void)listenForAutorotation
{
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(orientationChanged:)
                                                 name:UIDeviceOrientationDidChangeNotification
                                               object:nil];
}

-(void)unlistenForAutorotation
{
    [[NSNotificationCenter defaultCenter] removeObserver:self
                                                    name:UIDeviceOrientationDidChangeNotification
                                                  object:nil];
}

-(void)listenForFrameChangeInView:(id)inView
{
    // Let's listen for changes in the view's frame and adjust the popover even if the frame is updated
    if ([inView isKindOfClass:[UIView class]])
    {
        UIView *viewToObserve = (UIView*)inView;
        [viewToObserve addObserver:self
                        forKeyPath:@"frame"
                           options:NSKeyValueObservingOptionNew
                           context:nil];
    }
}

-(void)unlistenForFrameChangeInView:(id)inView
{
    if ([inView isKindOfClass:[UIView class]])
    {
        UIView *viewToObserve = (UIView*)inView;
        [viewToObserve removeObserver:self
                           forKeyPath:@"frame"];
    }
}

// TODO: Dealloc is not called, check why? !!!
- (void)dealloc
{
    [self unlistenForFrameChangeInView:self.popoverDisplaySource];
    [self unlistenForAutorotation];
    DEBUGLog(@"dealloc called for CSPopoverController %@", self);
}

#pragma mark - Designated initializers
-(id)initWithContentViewController:(UIViewController *)viewController
{
    self = [super initWithContentViewController:viewController];
    if (self)
    {
        [self popoverCommonInitializations];
    }
    return self;
}

-(void)popoverCommonInitializations
{
    [self setAutomaticallyReorientate:YES];
}

#pragma mark - Frame
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if (object==self.popoverDisplaySource)
    {
        [self setPopoverDisplaySourceFrame:nil];
        [self reorientatePopover];
    }
}

#pragma mark - Orientation
-(void)orientationChanged:(NSNotification *)inNotification
{
    [self reorientatePopover];
}

-(void)reorientatePopover
{
    [NSObject cancelPreviousPerformRequestsWithTarget:self
                                             selector:@selector(performReorientatePopover)
                                               object:nil];
//    if ([self isPopoverVisible])
    {
        [self performSelector:@selector(performReorientatePopover)
                   withObject:nil
                   afterDelay:0.0];
    }
}

-(void)performReorientatePopover
{
    if (self.popoverDisplaySourceFrame && self.popoverDisplayView)
    {
        [self presentPopoverFromRect:CGRectFromString(self.popoverDisplaySourceFrame)
                              inView:self.popoverDisplayView
            permittedArrowDirections:UIPopoverArrowDirectionAny
                            animated:YES];
    }
    else if (self.popoverDisplaySource && [self.popoverDisplaySource isKindOfClass:[UIBarButtonItem class]])
    {
        UIBarButtonItem *barButton = (UIBarButtonItem*)self.popoverDisplaySource;
        [self presentPopoverFromBarButtonItem:barButton
                     permittedArrowDirections:UIPopoverArrowDirectionAny
                                     animated:YES];
    }
}

@end

使用法:

それが提示している場所からの UIBarButtonItem である場合:

CSPopoverController *popOverCont = [[CSPopoverController alloc]initWithContentViewController:navCont];
self.popOver = popOverCont;
[popOverCont setPopoverDisplaySource:self.settingsButtonItem];

ポップオーバーを表示している場所からの UIView の場合:

CSPopoverController *popOver = [[CSPopoverController alloc] initWithContentViewController:navigation];
self.iPadPopoverController = popOver;
[newDateVC setIPadPopoverController:self.iPadPopoverController];
[popOver setPopoverDisplaySource:inButton];
[popOver setPopoverDisplayView:inView];
于 2013-10-21T12:29:41.083 に答える
0

iOS > 8 の場合、John Strickers の回答は役に立ちましたが、やりたいことはできませんでした。

これが私のために働いた解決策です。(完全なサンプル プロジェクトをダウンロードする場合は、ここにあります: https://github.com/appteur/uipopoverExample )

表示したいポップオーバーを保持するプロパティを作成し、sourceRect を追跡するプロパティと、ポップオーバーの矢印が指すボタンのビュー用に別のプロパティを追加しました。

@property (nonatomic, weak) UIView *activePopoverBtn;
@property (nonatomic, strong) PopoverViewController *popoverVC;
@property (nonatomic, assign) CGRect sourceRect; 

ポップオーバーをトリガーしたボタンは UIToolbar にあります。タップすると、ポップオーバーを作成して起動する次のメソッドが実行されます。

-(void) buttonAction:(id)sender event:(UIEvent*)event
{
    NSLog(@"ButtonAction");

    // when the button is tapped we want to display a popover, so setup all the variables needed and present it here

    // get a reference to which button's view was tapped (this is to get 
    // the frame to update the arrow to later on rotation)
    // since UIBarButtonItems don't have a 'frame' property I found this way is easy
    UIView *buttonView          = [[event.allTouches anyObject] view];

    // set our tracker properties for when the orientation changes (handled in the viewWillTransitionToSize method above)
    self.activePopoverBtn       = buttonView;
    self.sourceRect             = buttonView.frame;

    // get our size, make it adapt based on our view bounds
    CGSize viewSize             = self.view.bounds.size;
    CGSize contentSize          = CGSizeMake(viewSize.width, viewSize.height - 100.0);

    // set our popover view controller property
    self.popoverVC = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:@"PopoverVC"];

    // configure using a convenience method (if you have multiple popovers this makes it faster with less code)
    [self setupPopover:self.popoverVC
        withSourceView:buttonView.superview // this will be the toolbar
            sourceRect:self.sourceRect
           contentSize:contentSize];

    [self presentViewController:self.popoverVC animated:YES completion:nil];

}

'setupPopover:withSourceView:sourceRect:contentSize メソッドは、複数のポップオーバーを表示する予定があり、それらを同じように構成したい場合に popoverPresentationController プロパティを設定する便利なメソッドです。その実装は以下です。

// convenience method in case you want to display multiple popovers
-(void) setupPopover:(UIViewController*)popover withSourceView:(UIView*)sourceView sourceRect:(CGRect)sourceRect contentSize:(CGSize)contentSize
{
    NSLog(@"\npopoverPresentationController: %@\n", popover.popoverPresentationController);

    popover.modalPresentationStyle = UIModalPresentationPopover;
    popover.popoverPresentationController.delegate = self;
    popover.popoverPresentationController.sourceView                = sourceView;
    popover.popoverPresentationController.sourceRect                = sourceRect;
    popover.preferredContentSize                                    = contentSize;
    popover.popoverPresentationController.permittedArrowDirections  = UIPopoverArrowDirectionDown;
    popover.popoverPresentationController.backgroundColor           = [UIColor whiteColor];
}

iOS 8 以降の場合、viewWillTransitionToSize:withTransitionCoordinator get は、デバイスが回転するときにビュー コントローラーで呼び出されます。

以下に示すように、このメソッドを提示ビューコントローラークラスに実装しました。

// called when rotating a device
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{
    NSLog(@"viewWillTransitionToSize [%@]", NSStringFromCGSize(size));

    // resizes popover to new size and arrow location on orientation change
    [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext>  _Nonnull context)
    {
        if (self.popoverVC)
        {
            // get the new frame of our button (this is our new source rect)
            CGRect viewframe = self.activePopoverBtn ? self.activePopoverBtn.frame : CGRectZero;

            // update our popover view controller's sourceRect so the arrow will be pointed in the right place
            self.popoverVC.popoverPresentationController.sourceRect = viewframe;

            // update the preferred content size if we want to adapt the size of the popover to fit the new bounds
            self.popoverVC.preferredContentSize = CGSizeMake(self.view.bounds.size.width -20, self.view.bounds.size.height - 100);
        }

    } completion:^(id<UIViewControllerTransitionCoordinatorContext>  _Nonnull context) {
        // anything you want to do when the transition completes
    }];
}
于 2015-11-20T00:22:36.773 に答える