如何在没有协议的情况下在两个View Controller之间传递值?

时间:2012-08-12 00:05:31

标签: objective-c ios cocoa-touch

我有两个视图控制器,称之为viewAViewB

  • 所有操作均在主视图中进行 - ViewA
  • 点击一个菜单按钮,显示ViewB,一切正常,菜单出现

现在,用户触摸了一个IBAction按钮,该按钮在编程上只需要:

  1. 更改BOOL的值,将其称为myBOOLYES
  2. 解雇ViewB
  3. myBOOL变量的当前状态YES传递回ViewA
  4. 我已声明相同的BOOL,设置属性,在两个视图上合成,但在NSLog被解除ViewB并加载回ViewA时,它会恢复回到NO

    所以我知道我正在切线,我只是想知道你是否可以在两个控制器之间发送BOOL的值,如果是,请给我一个例子......作为搜索已经找到NSString的协议和委托示例,当我尝试使用BOOL时,我陷入了导入循环,但是我已经读过它可以创建一个全局{{1} ,就像它的设计一样糟糕,我现在只需要克服这个障碍。

7 个答案:

答案 0 :(得分:7)

关于这个主题的问题应该更多地关注NSNotificationCenter而不是NSUserDefaults,注意两者都是单身。

NSUserDefaults

此类的目的是 NOT 在类之间传递变量。它的目的是存储用户的默认值。 (即偏好,设置等)。

NSNotificationCenter

这个类非常方便,有许多不同的用途,其中一个用于广播 任何类接收的变量。接收类称为观察者。此模式称为Observer Pattern

注意: NSUserDefaults方法的优点是允许您在初始化其他类之前设置变量,并且可以随时检索。然而,这真的很草率(恕我直言),并认为是不好的做法。


NSNotificationCenter上的快速和脏代码示例:

// upon initializing the class that wants to observe the changes, we add it as an observer.
// So, somewhere in the A.m, upon being initialized (init, maybe?).

- (id)init {
    if (self = [super init]) {
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(calledUponNotif:)
                                                     name:@"MyObserveKey"
                                                   object:nil];
    }
    return self;
}

// the selector should look something like this:
- (void)calledUponNotif:(NSNotification *)notif {
    id sentVar = [notif object];
}

// Somewhere in the B.m
    [[NSNotificationCenter defaultCenter] postNotificationName:@"MyObserveKey"
                                                        object:varToSend];

另一个注释:调用postNotification方法后,其他类中已注册的选择器将被同步调用,因此您不必担心。

答案 1 :(得分:3)

有独立于视图的价值保持工具。您可以使用:

[[NSUserDefaults standardUserDefaults]setObject:<#(id)#> forKey:<#(NSString *)#>]

例如,您在A视图中输入字符串或数据,您可以将它们存储在上面的变量中。然后,在B视图中,您可以通过以下代码使用它们:

 [[NSUserDefaults standardUserDefaults]objectOrKey:<#(NSString *)#>]

以下是NSUserDefaults数据的示例:

ss

查看A:

- (void)textFieldDidEndEditing:(UITextField *)sender
    {
        if (sender == homepage) {
            [[NSUserDefaults standardUserDefaults]
             setURL:[NSURL URLWithString:homepage.text] forKey:Ever5secHomepagePrefKey];
            if( [homepage canResignFirstResponder] ) {
                [homepage resignFirstResponder];   
            }
        } else if (sender == userId) {
            [[NSUserDefaults standardUserDefaults]
             setObject:userId.text forKey:Ever5secUserIdPrefKey];
objectForKey:Ever5secUserIdPrefKey]);
            if( [userId canResignFirstResponder] ) {
                [userId resignFirstResponder];   
            }
        } else if (sender == password) {
            [[NSUserDefaults standardUserDefaults]
             setObject:password.text forKey:Ever5secPasswordPrefKey];
            if( [password canResignFirstResponder] ) {
                [password resignFirstResponder];   
            }
        }
    }

查看B:

userId.text = [[NSUserDefaults standardUserDefaults]
               objectForKey:Ever5secUserIdPrefKey];
password.text = [[NSUserDefaults standardUserDefaults]
                 objectForKey:Ever5secPasswordPrefKey];
homepage.text = [[[NSUserDefaults standardUserDefaults]
                  URLForKey:Ever5secHomepagePrefKey]
                 description];

答案 2 :(得分:3)

这不是一个很好的封装答案,但是没有能够使用协议或代理我不相信它会有很好的封装。

您还可以创建一个全局变量,您可以在一个视图控制器中设置该访问,并在另一个视图控制器中访问。

ViewControllerOne.h

  extern NSString *globalVariable;

  @interface ViewControllerOne

  @end

ViewControllerOne.m

 #import "ViewControllerOne.h"

 @implementation ViewControllerOne

 NSString *globalVariables = @"Some String in the variable to access in second controller";

 @end

ViewControllerTwo.m

 #import "ViewControllerTwo.h"
 #import "ViewControllerOne.h"

 @implemetation ViewControllerTwo

 - (void)viewDidLoad
 {
     NSLog("%@", globalVariables);
 }

 @end

这将打印到控制台

 ****CONSOLE****
 Some String in the variable to access in second controller

答案 3 :(得分:1)

您不需要使用NSNotificationCenter,NSUserDefaults或全局变量。

只要视图控制器是相关的(并查看OP的问题,它们看起来确实如此),您可以简单地设置视图控制器以保持对彼此的引用(使用其中一个引用)当然是软弱的,以避免&#34;保留&#34;或强大的参考&#34;,循环)。然后,每个视图控制器可以根据需要在另一个视图控制器上设置属性。示例如下......

注意:此概念对任何两个相关的视图控制器都有效。但是,以下代码假定:

  • 有问题的视图控制器通过导航控制器相关,第二个视图控制器通过push segue连接到第一个视图控制器。
  • 正在使用iOS 5.0或更高版本(因为它使用了故事板)。

FirstViewController.h

@interface FirstViewController : UIViewController

/* Hold the boolean value (or whatever value should be
   set by the second view controller) in a publicly
   visible property */
@property (nonatomic, assign) BOOL someBooleanValue;

/* Provide a method for the second view controller to 
   request the first view controller to dismiss it */
- (void)dismissSecondViewController;

@end

FirstViewController.m

#import "FirstViewController.h"
#import "SecondViewController.h"

@implementation FirstViewController

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    /* Get the reference to the second view controller and set
       the appropriate property so that the secondViewController
       now has a way of talking to the firstViewController */
    SecondViewController *vc = [segue destinationViewController];
    vc.firstViewController = self;
}

- (void)dismissSecondViewController
{
    // Hide the secondViewController and print out the boolean value
    [self.navigationController popViewControllerAnimated:YES];    
    NSLog(@"The value of self.someBooleanValue is %s", self.someBooleanValue ? "YES" : "NO");
}

@end

SecondViewController.h

#import "FirstViewController.h"

@interface SecondViewController : UIViewController

// Create a 'weak' property to hold a reference to the firstViewController 
@property (nonatomic, weak) FirstViewController *firstViewController;

@end

SecondViewController.m

@implementation SecondViewController

/* When required (in this case, when a button is pressed),
   set the property in the first view controller and ask the
   firstViewController to dismiss the secondViewController */
- (IBAction)buttonPressed:(id)sender {
    self.firstViewController.someBooleanValue = YES;
    [self.firstViewController dismissSecondViewController];
}

@end

当然,处理这种inter-viewController通信的最正确方法是使用协议/委托/数据源,以便SecondViewController不需要知道其父/所有者对象的细节。但是,有时为了证明这个概念,更快/更简单地构建这样的解决方案。然后,如果一切正常并且代码值得保留,则重构使用协议。

如果视图控制器没有 - 并且不应该彼此了解,则可能需要使用NSNotificationCenter。不要使用全局变量或NSUserDefaults来进行视图控制器之间的通信。

答案 4 :(得分:1)

有两个选项可用于在不同的视图控制器中存储和检索数据。

1)NSUserDefaults是存储数据和访问任何其他视图控制器的最佳选择。

NSUserDefaults类提供了访问float, double, integer, Boolean等常见类型的便捷方法。

默认对象必须是属性列表,即(或集合的实例组合)的实例:NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary

这是存储和检索数据的最简单方法。

如果你想阅读NSUserDefaults,我在这里分享文件。 [NsuserDefaults Document。] [1]

2)当您希望在类或其他视图控制器外部访问属性时,可以创建属性。

以这种方式创建属性。 @property (nonatomic, retain) NSArray *arrayData;然后您也可以在其他视图控制器中使用此数组值。

属性替换对象的访问器方法。

答案 5 :(得分:1)

有两个选项可用于在不同的视图控制器中存储和检索数据。

1)NSUserDefaults是存储数据和访问任何其他视图控制器的最佳选择。

NSUserDefaults类提供了访问float, double, integer, Boolean等常见类型的便捷方法。

默认对象必须是属性列表,即(或集合的实例组合)的实例:NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary

这是存储和检索数据的最简单方法。

如果你想阅读NSUserDefaults,我在这里分享文件。 NsuserDefaults Document.

2)当您希望在类或其他视图控制器外部访问属性时,可以创建属性。

以这种方式创建属性。 @property (nonatomic, retain) NSArray *arrayData;然后您也可以在其他视图控制器中使用此数组值。

属性替换对象的访问器方法。

你可以在这里看到我的答案。 Pass value from one view controller to another

答案 6 :(得分:0)

我认为以下方式使用块的强大功能的最佳方法。

在ViewB.h中

typedef void (^CompletionHandler)(BOOL myBool);
@interface ViewB : UIViewController {
    CompletionHandler completionHandler;
}
- (void)dismissHandler:(CompletionHandler)handler;

在ViewB.m

- (void)dismissHandler:(CompletionHandler)handler {
    completionHandler = handler;
}
- (IBAction)dismiss:(id)sender {
    completionHandler (YES); // your yes no logic here
}

在ViewA.m

- (IBAction)showPopup:(id)sender {
    ViewB *vc = [[ViewB alloc] init];
    [self.view addSubview:vc.view];
    [vc dismissHandler:^(BOOL myBool) {
        if (myBool) {
                //Do your work;
        }
    }];
}