How to make UIPopoverController keep same position after rotating?

前端 未结 13 1356
粉色の甜心
粉色の甜心 2021-01-30 18:21

I can\'t keep popover the same position on the screen after rotation. Is there any good way to do that, because just setting some frame to popover works terrible after rotating.

13条回答
  •  渐次进展
    2021-01-30 18:38

    I had a same problem. Instead of performing -presentPopoverFromRect each time by keeping track of the source rectangle / view from which it is presented, I subclassed UIPopoverController. After doing it, all you have to do is set either the UIBarButtonItem / UIView from where the popover has to be displayed. You can even opt for displaying the popover from custom frame which can be passed in as a NSString value.

    CSPopoverController.h:

    #import 
    
    // 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
    

    Usage:

    If it is a UIBarButtonItem from where you are presenting it:

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

    If it is a UIView from where you are presenting the popover:

    CSPopoverController *popOver = [[CSPopoverController alloc] initWithContentViewController:navigation];
    self.iPadPopoverController = popOver;
    [newDateVC setIPadPopoverController:self.iPadPopoverController];
    [popOver setPopoverDisplaySource:inButton];
    [popOver setPopoverDisplayView:inView];
    

提交回复
热议问题