如何访问UINavigationController中的先前视图元素

时间:2012-02-14 09:21:43

标签: iphone objective-c ios xcode

我有一个UINavigationController,我在其中加载不同的视图控制器。我想知道如何访问我之前视图的元素(如标签等)。 这是一个例如。

查看A. myLabel.text = @“第一次观看”;

(用户移动到查看B)

查看B. (用户输入了一条消息,我需要在视图A中显示) 类似于ViewA.myLabel.text = @“用户输入消息”

我尝试过很多东西但却找不到任何有用的东西。请帮忙..

我使用的是没有ARC且没有故事板的Xcode 4。

由于 萨姆


编辑:

我想更新View A的viewController中声明的属性,而不是直接更新标签。我的标签使用该属性进行更新。就像推动viewController一样,我们可以传递如下的值。

ViewA *myView = [[ViewA alloc] init];
myView.title = @"View B" ;
myView.tableView.tag = 3;
myView.myTextView.text = @"Some Text";
[self.navigationController pushViewController:myView animated:YES];
[myView release];

在弹出ViewB并返回ViewA时,有没有办法将这些值传递给ViewA的ViewController属性?

实际场景如下:用户获取并选择在textView中编写消息,或者他可以使用预定义的模板。如果他点击模板按钮,他将被带到预定义模板列表,在那里他可以选择任何预定义消息。现在我希望当用户点击任何预定义消息时,弹出包含预定义消息列表的视图,并且在主视图的textView中自动填充他选择的消息。实现这一目标的最佳方法是什么?

TIA 萨姆

3 个答案:

答案 0 :(得分:1)

您可以获取导航控制器的viewControllers属性并使用它,可能是这样的:

UILabel *label = ((SomeViewController *)[self.navigationController.viewControllers objectAtIndex:1]).myLabel;

但是,这不可靠。由于“上一个”视图不在屏幕上,因此系统可以卸载它以释放内存。然后label将为nil

您可以通过访问视图控制器的view属性来强制其他视图控制器重新加载其视图(如果已卸载)。

但实际上这闻起来很糟糕。当视图控制器的视图不在屏幕上时,您几乎不应该尝试访问视图控制器的视图。还记得如果视图在屏幕外,系统如何卸载视图控制器的视图?如果该视图下的某些UILabel包含重要数据的唯一副本,则该数据现已消失!

任何重要数据都需要存储在其他的某个位置而不是视图中 - 可能存储在视图控制器的属性中,或存储在模型对象中。您应该向视图控制器询问数据,或者询问包含数据的模型对象。视图控制器的视图对象几乎应该始终被视为视图控制器的私有实现细节,而不是暴露给其他类。

修改

您的问题很令人费解,因为您谈到弹出ViewB并返回ViewA,但您的代码只会创建并推送ViewA。代码中未提及ViewB

我将假设您的ViewA创建并推送ViewB。因此,您应该为ViewB提供ViewA类型的属性,如下所示:

@class ViewA;  // forward declaration to avoid circular imports

@interface ViewB

@property (weak, nonatomic) ViewA *aView;

然后,当您的ViewA创建ViewB个实例时,您需要设置aView属性:

@implementation ViewA

- (void)pushViewB {
    ViewB *bView = [[ViewB alloc] init];
    bView.aView = self;
    [self.navigationController pushViewController:bView animated:YES];
}

现在,您的ViewB可以访问创建它的ViewA,并可以设置ViewA的属性。

答案 1 :(得分:1)

您应该将AViewController设置为BViewController的委托,以便在特定事件发生后将其发回消息。使用委托还可以更好地解耦ViewControllers。

在BViewController中,定义如下协议:

BViewController.h:

@protocol BViewControllerDelegate <NSObject>
- (void)viewB:(UIViewController *)didEnterMessage:(NSString *)message;
@end

并添加委托属性:

@property (nonatomic, weak) id <BViewControllerDelegate> delegate;

当用户在BViewController中输入消息并点击弹出BViewController以显示给AViewController的按钮时,执行以下操作:

- (IBAction)messageEntered {
  if ([self.delegate respondsToSelector:@selector(viewB:didEnterMessage:)]) {
    [self.delegate viewB:self didEnterMessage:self.yourTextField.text];
  }
}

你的AViewController应该像这样实现BViewControllerDelegate协议:

AViewController.h:

@interface AViewController <BViewControllerDelegate>

当AViewController创建BViewController时,它应该在呈现之前将自己设置为其委托。可能看起来像这样:

BViewController *bvc = [[BViewController alloc] init…];
bvc.delegate = self;

最后,你的AViewController应该实现viewB:didEnterMessage:方法:

- (void)viewB:(UIViewController *)didEnterMessage:(NSString *)message {
  self.myLabel.text = message;
}

这是最干净的方法,恕我直言。

答案 2 :(得分:0)

如果您想编写一个好的代码,您应该遵循模型 - 视图 - 控制器模式。这是一个相当不错的tutrial http://www.cocoalab.com/?q=node/24在几个词中,它意味着你不应该在View中存储数据(并且视图也不应该作为控制器)。我建议你编写一个自定义类来进行这种管理(存储数据并将其从一个视图传递到另一个视图)。    如果它只是一个测试应用程序,那么你可以使用UINavigationController的viewControllers属性来访问导航堆栈中的控制器,或者只是创建一个变量来存储这些数据,例如,在View B中

- (void)textFieldDidEndEditing:(UITextField *)textField {
    stringToDisplayInFirstController = textField.text;
    NSArray * arrayOfControllers =  self.navigationController.viewControllers;
    UIViewController * viewControllerA = [arrayOfControllers objectAtIndex:[arrayOfControllers count]-1];
    viewControllerA.label.text = stringToDisplayInFirstController;

}