后退按钮在旋转之前不会出现在导航栏中

7
我有三个视图控制器:A -> B -> C,由导航控制器管理。 A 是一个短暂的视图控制器。它向服务器请求一些内容。如果服务器返回“一切正常”,那么AB 推入堆栈中。B必须隐藏后退按钮,因为我不希望用户手动返回到A
// B view controller
- (void)viewDidLoad 
{
    [super viewDidLoad];
    self.navigationItem.hidesBackButton = YES;
    self.title = @"B";
}

当用户点击表格单元格时,B 会将 C 推入堆栈。
// B view controller
- (void)tableView:(UITableView *)tableView 
didSelectRowAtIndexPath:(NSIndexPath *)indexPath 
{
    C *c = [[C alloc] 
        initWithStyle:UITableViewStyleGrouped
    ];
    [self.navigationController 
        pushViewController:c 
        animated:YES
    ];
    [c release];
}

.

// C view controller
- (void) viewDidLoad
{
    [super viewDidLoad];
    self.navigationItem.hidesBackButton = NO;
    self.title = @"C";
}

如果一切顺利,流程应该如下所示:
-------------    -------------    -------------
|_____A_____|    |_____B ____|    | <B|__ C___|
|           | => |           | => |           |
| loading...|    |   cells   |    |   detail  |
|           |    |           |    |           |
-------------    -------------     -----------

由于某些原因,C页面没有显示返回按钮以返回到B,直到我旋转设备。一旦旋转,所有方向都会出现返回按钮。问题似乎源自B隐藏返回按钮,C试图重新显示它,因为如果我不让B隐藏它,我就没有这个问题。那么,如何让C显示返回按钮,而不强制用户像猴子一样旋转设备呢?
更新:
  • Verizon iPhone 4上的iOS 4.2.10存在问题
  • AT&T iPhone 3GS上的iOS 5.0正常
  • AT&T iPhone 4上的iOS 4.3正常

为什么要把A留在堆栈上呢?如果您永远不想回到A,那么您可能希望将其从导航控制器中移除。 - Owen Pierce
在切换到控制器C之前,您可以尝试在控制器B中显示返回按钮。 - TheEye
@TheEye 我在B视图控制器的viewWillDisappear方法中加入了self.navigationItem.hidesBackButton = NO,但问题并未得到解决。 - JoJo
这是苹果破损控制器实现的一个典型例子,而且不公开承认错误。在Stack Overflow上查看iPhone Landscape FAQ,可以找到许多类似的问题。 - Johannes Rudolph
你可以将 A 显示为模态控制器(或其他不在导航控制器中的控制器)。 (用于登录,对吗?)然后仅使用导航控制器来管理 B 和 C。 - Jonny
显示剩余2条评论
6个回答

4

在搜索一些资料后,我在某个旧的论坛帖子上找到了这个适用于iPhone 4.2(因为您提到它适用于更高版本)的解决方案。

-(void)viewDidLoad { 
                     [super viewDidLoad];
                     self.navigationItem.hidesBackButton = YES; 
                   }

-(void)viewDidAppear:(BOOL)animated { 
                                       [super viewDidAppear:animated];
                                       self.navigationItem.hidesBackButton = NO; 
                                    }

也许这会对您有所帮助。 (查看此链接:导航控制器中的返回按钮未出现

这就是诀窍。从概念上讲,它是欺骗iOS认为它想继续隐藏返回按钮。然后一旦视图在屏幕上绘制出来,它再次偷偷地加入返回按钮。我可以看到有一瞬间按钮不在那里,然后它就被粘贴在上面了。这是为iOS 4.2必须完成的愚蠢任务。我没有在旧版iOS上进行测试,所以我不知道问题是否仅适用于4.2。 - JoJo

1

我认为你需要按照以下方式编写 C 代码

-(void)viewDidAppear:(BOOL)animated {
      [super viewDidAppear:animated];
      self.navigationItem.hidesBackButton = NO;
}

这个不起作用(在更正你的拼写错误“viViewDidAppear”之后)。 - JoJo

0

我曾经遇到过这个问题,通过给B的导航项添加了一个标题来解决它。

// B view controller
- (void)viewDidLoad 
{
    [super viewDidLoad];
    self.navigationItem.hidesBackButton = YES;
    self.navigationItem.title = @"What you want C's back button to say";
    self.title = @"B";
}

如果您不想在B中显示标题,可以将B的navigationItem的titleView设置为空视图。关于titleView属性:

如果此属性值为nil,则在接收者是顶部项时,在导航栏中心显示导航项的标题。如果将此属性设置为自定义标题,则会显示该标题而不是标题。如果leftBarButtonItem不为nil,则忽略此属性。


这个不起作用。但是,我必须旋转才能让返回按钮出现。该按钮将显示“B”,而不是“What you wan't C's back button to say”。 - JoJo
当导航项不可见且不应该存在时,它的backBarButtonItem告诉你什么?您还可以查看UINavigationBar的-(void)pushNavigationItem:(UINavigationItem *)item animated:(BOOL)animated方法。 - jbat100

0

尝试将self.navigationItem.hidesBackButton = NO;放入init方法或在pushViewController调用之前的任何位置。

当您首次请求控制器的视图时,ViewDidLoad被调用,这意味着它可能是从[self.navigationController pushViewController:c animated:YES]调用的。但请注意,导航栏不是您视图的一部分,它是由UINavigationController创建和处理的,因此它可以存在并且在viewDidLoad和viewDidAppear被调用之前绘制。如果您在那里更新导航栏,则实际上不会重新绘制。

编辑1:阅读[UIViewController navigationItem]文档后重新审视

您应该避免将导航项中的工具栏按钮项的创建与视图控制器的视图的创建相关联。可以独立于视图控制器的视图检索视图控制器的导航项。例如,在将两个视图控制器推送到导航堆栈上时,最顶层的视图控制器变为可见,但可以检索其他视图控制器的导航项以显示其返回按钮。为确保配置了导航项,可以重写此属性并添加代码以在其中加载工具栏按钮项,或者在视图控制器的初始化代码中加载这些项。

编辑2:在阅读评论后重新审视,发现我的解决方案无法工作。以下是可行的代码(iOS 5,ARC):


//
//  TestAppDelegate.m
//  NavigationTest
//
//  Created by Sulthan on 10/25/11.
//  Copyright (c) 2011 StackOverflow. All rights reserved.
//

#import "TestAppDelegate.h"

@interface TestAppDelegate ()

@property (nonatomic, strong, readwrite) UINavigationController* navigationScreen;

@property (nonatomic, strong, readwrite) UIViewController* screen1;
@property (nonatomic, strong, readwrite) UIViewController* screen2;
@property (nonatomic, strong, readwrite) UIViewController* screen3;

@end

@implementation TestAppDelegate

@synthesize window = window_;
@synthesize navigationScreen = navigationScreen_;
@synthesize screen1 = screen1_;
@synthesize screen2 = screen2_;
@synthesize screen3 = screen3_;

- (UIViewController*)createTestScreenWithLabel:(NSString*)label {
    CGRect bounds = [[UIScreen mainScreen] bounds];

    UIViewController* screen = [[UIViewController alloc] init];
    screen.view = [[UILabel alloc] initWithFrame:bounds];
    screen.view.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);

    ((UILabel*) screen.view).textAlignment = UITextAlignmentCenter;
    ((UILabel*) screen.view).text = label;

    return screen;
}

- (void)pushThirdScreen {
    if (!self.screen3) {
        self.screen3 = [self createTestScreenWithLabel:@"Screen 3"];
        self.screen3.navigationItem.hidesBackButton = NO;
    }

    [self.navigationScreen pushViewController:self.screen3 animated:YES];
}

- (void)pushSecondScreen {
    self.screen2 = [self createTestScreenWithLabel:@"Screen 2"];
    self.screen2.navigationItem.hidesBackButton = YES;

    UIBarButtonItem* button = [[UIBarButtonItem alloc] initWithTitle:@"Go"
                                                               style:UIBarButtonItemStyleBordered
                                                              target:self
                                                              action:@selector(pushThirdScreen)];

    self.screen2.navigationItem.rightBarButtonItem = button;

    [self.navigationScreen pushViewController:self.screen2 animated:YES];
}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
    CGRect bounds = [[UIScreen mainScreen] bounds];

    self.screen1 = [self createTestScreenWithLabel:@"Screen 1"];

    self.navigationScreen = [[UINavigationController alloc] initWithRootViewController:self.screen1];

    self.window = [[UIWindow alloc] initWithFrame:bounds];
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window addSubview:self.navigationScreen.view];

    [self.window makeKeyAndVisible];

    [self performSelector:@selector(pushSecondScreen) withObject:nil afterDelay:3.0];

    return YES;
}

@end

编辑3:注意到您主要在谈论iOS 4.2后重新访问。我目前无法在任何iOS 4.2上测试它,但我知道可能有一个解决方法。您可以始终在UINavigationController中隐藏导航栏,并在每个屏幕中放置单独的导航栏。您将对它们拥有绝对控制权,甚至可以在Interface Builder中编辑它们。


没起作用。我把这个代码放在了C视图控制器中:-(id)initWithStyle:(UITableViewStyle)style { self.navigationItem.hidesBackButton = NO; return [super initWithStyle:style]; } - JoJo
哦,我的天啊。[super initWithStyle:style] 必须始终是您 init 方法中调用的第一件事情。在调用它之前,您无法设置任何 navigationItem。 - Sulthan
将super调用放在开头并没有帮助。 - JoJo

0

尝试将以下内容添加到您的C类中:

-(id) init
 {
   [super init];
   self.navigationItem.hidesBackButton = NO;
 }

我不使用“init”来初始化我的C控制器,而是使用“initWithStyle”进行初始化。@Sulthan已经建议了类似的无效方法。 - JoJo
你尝试在pushViewController之前加入self.navigationItem.hidesBackButton = NO;了吗? - krammer
是的,那只是让B的返回按钮出现了。我需要C显示它的返回按钮。 - JoJo

0

不确定这是否适用于您的返回按钮情况,但我知道当我使用自定义返回按钮时,需要在推送之前将自定义返回按钮设置为分配的项目(而不是像上面的帖子中的self)。希望它也适用于您的情况-值得一试。

换句话说,请尝试:

// B view controller
- (void)tableView:(UITableView *)tableView 
didSelectRowAtIndexPath:(NSIndexPath *)indexPath 
{
    C *c = [[C alloc] 
        initWithStyle:UITableViewStyleGrouped];

    // *** set on c's navigationItem (not self) before you push
    [[c navigationItem] setHidesBackButton: NO];

    [self.navigationController 
        pushViewController:c 
        animated:YES
    ];

    [c release];
}

这个不起作用。我还尝试将B和C都设置为在推送C之前隐藏返回按钮。当C滑入时,我看到了B的返回按钮。但是C仍然没有返回按钮。 - JoJo
好的 - 值得一试。抱歉不能提供更多帮助。 - bryanmac

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接