带有自定义后退导航按钮的自定义按钮栏
Posted
技术标签:
【中文标题】带有自定义后退导航按钮的自定义按钮栏【英文标题】:Custom button bar with custom back navigation button 【发布时间】:2012-10-24 20:31:20 【问题描述】:Reusable button bars? 让我参与其中,但现在我遇到了“后退按钮”要求的问题。
我需要一个布局解决方案:
适用于 ios 5.0 和 6.0 顶部有一个自定义视图,带有几个按钮;此视图应该可以在每个屏幕(场景)中重复使用,而不是在 Interface Builder 中为每个场景手动复制按钮。 在该顶部自定义视图中有一个自定义“返回”按钮。对于我必须实现的设计,我不能只使用默认导航栏 与 UINavigationController 配合得很好;当用户点击“返回”按钮时,主视图控制器(带有按钮栏)应该保留,但代表实际场景内容的子视图控制器应该返回到前一个场景。目前的问题是“返回”按钮不会改变子控制器——它会改变父控制器,返回到带有按钮栏的场景之前的上一个场景。我已经尝试了几种不同的方法。我不确定是我做的不对,还是做不到。
一种可能性是实现我自己的“返回”功能,保留一堆子视图控制器并在用户点击“返回”时手动更改它们。然而,与使用 UINavigationController 相比,这很尴尬,而且设计也很糟糕。
也许我走错了路。我不能接受在 Interface Builder 中的每个场景中复制按钮栏......但也许我应该以编程方式创建它,然后我可以轻松地从每个场景中调用该代码。然后我会有“普通”视图控制器,使用 UINavigationController 会更容易。但在我走那条路并完全放弃我目前所拥有的之前,我想看看是否还有其他方法。
以下是我的解决方案某些部分的概述:
我创建了一个 ButtonBarController,为 Storyboard 布置了我想要的按钮的 UIView 和内容窗格的 UIView。我还在后退按钮的顶部添加了一个带有应用徽标的按钮(用于转到应用的主屏幕)。
然后我为每个其他屏幕创建了一个控制器。在那些子屏幕/子视图控制器中,我会首先添加一个尺寸正确的 UIView 以适合我的内容窗格,然后添加我想要的所有其他控件。我让所有这些子视图控制器都继承自另一个控制器,该控制器负责一些常见任务——例如获取对按钮栏控制器的引用,以及帮助调整 3.5 英寸和 4 英寸屏幕视图大小的代码。
我创建了一个 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】:我认为您至少需要实现一个自定义容器视图控制器——根视图控制器。那将是托管自定义按钮栏的那个。在按钮栏下方,您将添加一个 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];
【讨论】:
以上是关于带有自定义后退导航按钮的自定义按钮栏的主要内容,如果未能解决你的问题,请参考以下文章
使用LeftBarButtonItems ios中的自定义按钮创建默认后退按钮