自定义按钮栏,带有自定义后退导航按钮

时间:2012-10-24 20:31:21

标签: objective-c ios xcode cocoa-touch

Reusable button bars?让我参与其中,但现在我遇到了“后退按钮”要求的问题。

我需要一个布局解决方案:

  • 适用于iOS 5.0和6.0
  • 顶部有一个自定义视图,有几个按钮;此视图应该可以在每个屏幕(场景)中重复使用,而不是在Interface Builder中为每个场景手动复制按钮。
  • 在该顶部自定义视图中有一个自定义的“后退”按钮。通过我必须实现的设计,我不能只使用默认导航栏
  • 适用于UINavigationController;当用户点击“后退”按钮时,主视图控制器(带按钮栏)应该保留,但代表实际场景内容的子视图控制器应该返回到前一个场景。

目前的问题是“后退”按钮不会更改子控制器 - 它会更改父控制器,返回到具有按钮栏的场景之前的前一场景。我尝试了几种不同的方式。我不确定我做得不对,或者做不到。

一种可能性是实现我自己的“后退”功能,保留一堆子视图控制器,并在用户点击“返回”时手动更改它们。然而,与使用UINavigationController相比,这种设计很糟糕。

也许我对此采取了错误的方式。我不能接受在Interface Builder中的每个场景中复制按钮栏...但也许我应该以编程方式创建它,然后我可以轻松地从每个场景调用该代码。然后我会有“普通”视图控制器,使用UINavigationController会更容易。但在我走这条路并完全废弃到目前为止,我想看看是否还有另一种方式。


以下是我的解决方案的某些部分的概述:

我创建了一个ButtonBarController,为我想要的按钮设置了一个UIView的Storyboard,并为内容窗格设置了一个UIView。我还在后退按钮的顶部分层了一个带有应用徽标的按钮(转到应用程序的主屏幕)。

然后我为其他每个屏幕创建了一个控制器。在那些子屏幕/子视图控制器中,我首先添加一个大小正确的UIView以适合我的内容窗格,然后将添加我想要的所有其他控件。我让所有这些子视图控制器继承自另一个控制器,它负责一些常见任务 - 例如获取对按钮栏控制器的引用,以及帮助调整3.5“与4”屏幕的视图大小的代码。 / p>

我创建了一个changeToControllerWithIndex方法;当应用程序加载时,当用户单击主按钮栏中的一个按钮以更改场景时,或者在需要另一个场景更改的场景中发生任何事件时,我会调用此方法。我重载此方法以提供另外两条信息:为NSDictionary提供子视图控制器所需的任何额外信息,并告诉它这是否是顶级场景,或者我们是否需要后退按钮。

(注意:在Identity Inspector中为那些子视图控制器设置Storyboard ID非常重要。我不小心在Attribute Inspector中设置了Title)

- (void)changeToControllerWithIndex:(NSInteger)index {
    [self changeToControllerWithIndex:index withPayload:nil isRootView:YES];
}

// This is the method that will change the active view controller and the view that is shown
- (void)changeToControllerWithIndex:(NSInteger)index withPayload:(id)payload isRootView:(BOOL)isRootView
{
    if (YES) {
        self.index = index;

        // The code below will properly remove the the child view controller that is
        // currently being shown to the user and insert the new child view controller.
        UIViewController *vc = [self setupViewControllerForIndex:index withPayload:payload];

        if (isRootView) {
            NSLog(@"putting navigation controller in");
            childNavigationController = [[UINavigationController alloc] initWithRootViewController:vc];
            [childNavigationController setNavigationBarHidden:YES];
            [self addChildViewController:childNavigationController];
            [childNavigationController didMoveToParentViewController:self];

            if (self.currentViewController){
                [self.currentViewController willMoveToParentViewController:nil];

                [self transitionFromViewController:self.currentViewController toViewController:childNavigationController duration:0 options:UIViewAnimationOptionTransitionNone animations:^{
                    [self.currentViewController.view removeFromSuperview];
                } completion:^(BOOL finished) {
                    [self.currentViewController removeFromParentViewController];
                    self.currentViewController = childNavigationController;
                }];
            } else {
                [self.currentView addSubview:childNavigationController.view];
                self.currentViewController = childNavigationController;
            }

            [self.currentView addSubview:childNavigationController.view];

            //We are at the root of the navigation path, so no back button for us
            [homeButton setHidden:NO];
            [backButton setHidden:YES];
        } else {
            //Not a root view -- we're in navigation and want a back button

            [childNavigationController pushViewController:vc animated:NO];

            [homeButton setHidden:YES];
            [backButton setHidden:NO];
        }
    }   
}

然后我有一个重载方法来设置每个单独的视图控制器...有些需要比其他人更多的准备。

- (UIViewController *)setupViewControllerForIndex:(NSInteger)index {
    return [self setupViewControllerForIndex:index withPayload:nil];
}

// This is where you instantiate each child controller and setup anything you need  on them, like delegates and public properties.
- (UIViewController *)setupViewControllerForIndex:(NSInteger)index withPayload:(id)payload {
    UIViewController *vc = nil;
    if (index == CONTROLLER_HOME){
        vc = [self.storyboard instantiateViewControllerWithIdentifier:@"Home"];
    } else if (index == CONTROLLER_CATEGORIES){
        SAVECategoryViewController *child = [self.storyboard instantiateViewControllerWithIdentifier:@"Categories"];
        if (payload) {
            child.currentCategory = [(NSNumber *) [(NSDictionary *)payload objectForKey:ATTRIBUTE_CAT_ID] integerValue];
        } else {
            child.currentCategory = CATEGORY_ALL;
        }
        vc = child;
    } //etc for all the other controllers...
    payload = nil;

    return vc;
}

我提到了管理“后退”导航的难度。上面的代码确保导航控制器保持适当的“后退”历史记录,每当我们使用其中一个按钮栏按钮来更改屏幕时,它就会重新开始。当我们使用子控制器内的按钮从一个场景导航到另一个场景时,这就是我们回过头来的方式:

- (IBAction)backButtonPressed:(id)sender {
    [childNavigationController popViewControllerAnimated:YES];
    if ([[childNavigationController viewControllers] count] <= 1) {
        //Root view
        [homeButton setHidden:NO];
        [backButton setHidden:YES];
    }
}

1 个答案:

答案 0 :(得分:1)

我认为您需要实现至少一个自定义容器视图控制器 - 根视图控制器。那将是主持自定义按钮栏的那个。在按钮栏下方,您将添加一个UINavigationController来管理您的其他VC。首先看看这个:

@implementation RootVC
//...

- (void)viewDidLoad
{
    self.navVC = [[UINavigationController alloc] initWithRootViewController:someOtherVC];
    self.navVC.navigationBarHidden = YES;
    self.navVC.view.frame = ...;

    [self addChildViewController:self.navVC];
    [self.view addSubview:self.navVC.view];
    [self.navVC didMoveToParentViewController:self];
}

- (void)backButtonTouched:(UIButton *)button
{
    [self.navVC popViewControllerAnimated:YES];
}