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

时间:2013-10-07 15:21:33

标签: ios objective-c cocoa-touch ios7 xcode5

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

该代码适用于iOS 6,其状态栏不透明,顶部视图未与状态栏进行alpha混合。

然而,在iOS 7上,例如,我在我的.xib文件中创建了这个视图,这是它在ioS 6模拟器中运行的方式,这里显示的是滑出菜单:

enter image description here

在ios 7上运行相同的.xib文件,当滑出菜单打开时,滑出菜单顶部的.xib内容现在位于状态栏下,因为Apple表示它将在他们的ios 7过渡指南中:

enter image description here

我需要在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,让我做上述我正在考虑的黑客攻击?

例如,这里是一个非常丑陋的黑客,我不打扰在苹果系统状态栏下放置任何视图,在顶部留下一个黑色条,这是不好的,但它显示我正在修改代码中的右侧区域,至少:

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

如果我还将此添加到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

2 个答案:

答案 0 :(得分:1)

我也一直在努力使用PKRevealController。虽然我仍在寻找更好的解决方案,但我将分享到目前为止所提出的建议。

我的两个问题是:

  1. 状态栏样式始终相同,我希望前视图和菜单有不同的样式;
  2. 菜单视图顶部单元格(它是一个表视图控制器)显示在状态栏后面。
  3. 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知道如何操作。

答案 1 :(得分:0)

如果您不需要iOS 5-支持,则可以使用autolayout并将最顶层的视图与topLayoutGuide对齐。

iOS6 iOS7


因此,例如,如果你的左视图控制器是一个带有UITableView的UIViewController,你可以将UITableView的上边缘捕捉到topLayoutGuide。 您可以在(1) IB(故事板)或(2)代码中执行此操作。

我个人更喜欢第一种方法,只要它不需要不必要的代码。您只需打开故事板并将表格视图的顶部边缘捕捉到topLayoutGuide。在iOS 7中,您将最终得到topLayoutGuide约束,在iOS6中,topLayoutGuide约束被转换为通用的容器视图常量。

IB way

如果您使用第二种方法,则必须确保在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
}