如何修改 PKRevealController 滑出菜单以处理 iOS 7?

Posted

技术标签:

【中文标题】如何修改 PKRevealController 滑出菜单以处理 iOS 7?【英文标题】:How can I modify PKRevealController slide-out menus to deal with iOS 7? 【发布时间】:2013-10-07 15:21:32 【问题描述】:

我有一个使用PKRevealController 的应用程序,它实现了一个类似于ios 上流行的Facebook 和GMAIL 应用程序中的滑出菜单。该应用程序是在 XCode 5 中构建的,并在 iOS 6 和 iOS 7 上运行。我需要弄清楚如何让它在这两个地方都正常工作,所以一个简单的 .XIB hack 让它在 iOS 7 中看起来不错,但让它看起来在 iOS 6 中更糟糕的是不行。

该代码适用于 iOS 6,其中状态栏是不透明的,顶视图未与状态栏混合使用 alpha。

但是,例如,在 iOS 7 上,我在我的 .xib 文件中创建了这个视图,下面是它在 ios 6 模拟器中运行时的显示方式,此处显示的是打开滑出菜单:

在 ios 7 上运行的相同 .xib 文件,当滑出菜单打开时,滑出菜单的顶部 .xib 内容现在位于状态栏下方,正如 Apple 在其 ios 7 过渡指南中所说的那样:

我需要在 PKRevealController 中修改的类可能是正在创建和呈现包含视图的呈现视图控制器,我认为包含的视图称为 PKRevealControllerContainerView。我想我可能需要创建 像这样的某种视图层次结构:

   [  Outermost View container 
        [ some kind of blob to occupy the header area ]
        [ the client view I want to appear the way it did in iOS 6]
   ]

我一直在阅读,可能有更简单的方法,但我不太了解它们,例如向我的 info.plist 添加属性的方法,例如View controller-based status bar appearance = YES。我试过了,没有达到预期的效果。

我该如何解决这个问题?我已经阅读了 Apple 发布的Fine Guide,它没有提供代码,只有像 status bar 上的这个页面这样的一般指导。

很容易复制这个问题,只需克隆 git repo https://github.com/pkluz/PKRevealController,构建并运行。

弹出视图的代码如下所示:

- (void)addLeftViewControllerToHierarchy

    if (self.leftViewController != nil && ![self.childViewControllers containsObject:self.leftViewController])
    
        [self addChildViewController:self.leftViewController];
        self.leftViewContainer.viewController = self.leftViewController;

        if (self.leftViewContainer == nil)
        
            self.leftViewContainer = [[PKRevealControllerContainerView alloc] initForController:self.leftViewController shadow:NO];
            self.leftViewContainer.autoresizingMask = [self autoresizingMaskForLeftViewContainer];
        

        self.leftViewContainer.frame = [self leftViewFrame];
        [self.view insertSubview:self.leftViewContainer belowSubview:self.frontViewContainer];
        [self.leftViewController didMoveToParentViewController:self];
    

上面是由PKRevealController.m调用的,像这样:

- (void)showLeftViewControllerAnimated:(BOOL)animated
                            completion:(PKDefaultCompletionHandler)completion

    __weak PKRevealController *weakSelf = self;

    void (^showLeftViewBlock)(BOOL finished) = ^(BOOL finished)
    
        [weakSelf removeRightViewControllerFromHierarchy];
        [weakSelf addLeftViewControllerToHierarchy];  // HELLO LEFT Slide-out menu.

        ....

有比我的想法更好的方法吗? Apple 是否提供了一些方法来简化此操作,或者尝试在单个代码库中支持 iOS 6 和 iOS 7 是否让我做上述我正在考虑的黑客行为?

例如,这里是一个非常丑陋的 hack,我不费心在苹果系统状态栏下方放置任何视图,在顶部留下一个黑条,这不好,但它表明我正在修改代码中的正确区域,至少:

- (void)addLeftViewControllerToHierarchy

    CGRect   lvFrame;

    if (self.leftViewController != nil && ![self.childViewControllers containsObject:self.leftViewController])
    
        [self addChildViewController:self.leftViewController];
        self.leftViewContainer.viewController = self.leftViewController;

        if (self.leftViewContainer == nil)
        
            self.leftViewContainer = [[PKRevealControllerContainerView alloc] initForController:self.leftViewController shadow:NO];
            self.leftViewContainer.autoresizingMask = [self autoresizingMaskForLeftViewContainer];
        

        lvFrame = [self leftViewFrame];
        lvFrame.origin.y += 20; // ugly hack demo code only! don't really do it this badly!
        lvFrame.size.height -= 20;
        self.leftViewContainer.frame = lvFrame;
        [self.view insertSubview:self.leftViewContainer belowSubview:self.frontViewContainer];
        [self.leftViewController didMoveToParentViewController:self];
    

上面的hack就差不多够了,如果我也把这个加到UIViewController+PKRevealController.m:

-(UIStatusBarStyle)preferredStatusBarStyle
    return UIStatusBarStyleBlackOpaque;

上述代码在添加时会导致以下提示/警告:

Category is implementing a method that will also be implemented by its primary class.

我将上述说明包括在内,以展示我的尝试,我欢迎了解真正的专家如何做到这一点。

我自己修改后的 PKRevealController 代码副本,包括上面的 hack,以略微改进的形式,可在此处找到:https://github.com/wpostma/PKRevealController

【问题讨论】:

【参考方案1】:

我也一直在为 PKRevealController 苦苦挣扎。虽然我仍在寻找更好的解决方案,但我会分享我到现在为止的想法。

我的两个问题是:

    状态栏样式始终相同,我希望前视图和菜单的样式不同; 菜单视图顶部单元格(它是一个表格视图控制器)显示在状态栏后面。

1。动态状态栏样式

首先我有我自己的 PKRevealController 子类,我有一个自定义初始化程序和一些自定义方法来将新视图控制器加载到前视图导航视图控制器中。但这暂时无关紧要。

我使用这个子类来实现preferredStatusBarStyle,如下所示,以便显示控制器可以为每个状态提供正确的样式:

- (UIStatusBarStyle)preferredStatusBarStyle 
    switch (self.state) 
        case PKRevealControllerFocusesLeftViewController:
            return [self.leftViewController preferredStatusBarStyle];
            break;
        case PKRevealControllerFocusesRightViewController:
            return [self.rightViewController preferredStatusBarStyle];
            break;
        case PKRevealControllerFocusesFrontViewController:
            return [self.frontViewController preferredStatusBarStyle];
            break;
        case PKRevealControllerFocusesLeftViewControllerInPresentationMode:
            return [self.leftViewController preferredStatusBarStyle];
            break;
        case PKRevealControllerFocusesRightViewControllerInPresentationMode:
            return [self.rightViewController preferredStatusBarStyle];
            break;

        default:
            return UIStatusBarStyleDefault;
            break;
    

但是,仅此一项是行不通的。还是要说状态栏样式需要改用setNeedsStatusBarAppearanceUpdate。正如 Apple 所说,这应该从动画循环内部调用,您可以在 PKRevealController 的 setFrontViewFrameLinearly 方法中找到它。这是我修改后的样子:

- (void)setFrontViewFrameLinearly:(CGRect)frame
                         animated:(BOOL)animated
                         duration:(CGFloat)duration
                          options:(UIViewAnimationOptions)options
                       completion:(PKDefaultCompletionHandler)completion

    [UIView animateWithDuration:duration delay:0.0f options:options animations:^
    
        self.frontViewContainer.frame = frame;
        if ([[[UIDevice currentDevice] systemVersion] compare:@"7.0" options:NSNumericSearch] != NSOrderedAscending) 
            [self setNeedsStatusBarAppearanceUpdate];
        
    
    completion:^(BOOL finished)
    
        safelyExecuteCompletionBlockOnMainThread(completion, finished);
    ];

如果你在这一点上尝试一下,样式将会混淆。您可以很快得出结论,在调用 preferredStatusBarStyle 时,显示控制器状态仍未更改。为此,请转到设置状态的每个方法,例如enterPresentationModeForRightViewControllerAnimated 并在调用帧的任何更改之前设置状态(将触发动画循环)。我在 5 个不同的地方做过。

2。带插图的左/右菜单

对于这个我不得不说我使用了一种解决方法:我刚刚在表格视图(tableHeaderView 属性)上设置了一个标题视图。

把这个放在你的 UITableViewController 的viewDidLoad 中:

UIView *headerView = [[UIView alloc] initWithFrame:CGRectMake(0.f, 0.f, self.tableView.frame.size.width, 20.f)];
headerView.backgroundColor = [UIColor whiteColor];

self.tableView.tableHeaderView = headerView;

不要忘记添加一些条件,这样它就不会在 iOS 6 中执行。使用this other answer 知道如何做。

【讨论】:

【参考方案2】:

如果您不需要 iOS 5 支持,您可以使用自动布局并将最上面的视图对齐到 topLayoutGuide

因此,例如,如果您的左视图控制器是一个带有 UITableView 的 UIViewController,您可以将 UITableView 的顶部边缘捕捉到 topLayoutGuide。 您可以在 (1) IB(故事板)或 (2) 从代码中完成。 我个人更喜欢第一种方法,因为它不需要不必要的代码。您只需打开故事板并将表格视图的顶部边缘捕捉到 topLayoutGuide。在 iOS 7 中,您最终会得到 topLayoutGuide 约束,在 iOS6 中,topLayoutGuide 约束被转换为通用的容器视图常量。 如果您使用第二种方法,则必须确保在 iOS6 中不使用 topLayoutGuide,如下所示:

// assume you'r in your UIViewController subclass
if (![self respondsToSelector:@selector(topLayoutGuide)]) 

    // topLayoutGuide is not supported, probably iOS6
    // add constraints to snap tableview's top edge to superview's top edge

else

    // cool, topLayoutGuide is supported, probably iOS7
    // add constraints to snap tableview's top edge to topLayoutGuide

【讨论】:

以上是关于如何修改 PKRevealController 滑出菜单以处理 iOS 7?的主要内容,如果未能解决你的问题,请参考以下文章

使用 PKRevealController 时在前视图上推送新视图

在无限平滑修改的猫头鹰滑块上添加暂停

php facetwp修改滑块标签

php facetwp修改滑块标签

wpf slider拖动滑块气泡提示

如何锁定滑块并防止使用鼠标将值更新到 dat.GUI 菜单