viewWillAppear的困境

时间:2012-01-29 23:28:54

标签: iphone ios nsuserdefaults

我正在使用NSUserDefaults来保持对象在UIViewController中使用的几个UITabbarController之间保持同步。为此,我正在实施以下

- (void)viewWillAppear:(BOOL)animated
{
    NSLog(@"ViewControllerX Will Appear");
    [super viewWillAppear:animated];

    NSDictionary *dict = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"sharedDictionary"];
    [customObject setDictionary:dict];
}


- (void)viewWillDisappear:(BOOL)animated
{
    NSLog(@"ViewControllerX Will Disappear");
    NSDictionary *dict = [customObject dictionary];
    [[NSUserDefaults standardUserDefaults] setObject:dict forKey:@"sharedDictionary"];
    [[NSUserDefaults standardUserDefaults] synchronize];
}

此处customObject是自定义类的实例,其属性dictionary的类型为NSDictionary。可见UIViewController可能会更改此对象。

我目前遇到的问题是当用户切换标签时,例如从ViewControllerXViewControllerY,这些方法不会按预期顺序调用。我希望在日志中看到这一点:

ViewControllerX Will Disappear
ViewControllerY Will Appear

但我看到了

ViewControllerY Will Appear
ViewControllerX Will Disappear

结果是旧字典加载到ViewControllerY中,并且只有在再次切换标签后才会出现新字典。这个问题有一个简单的方法吗?

3 个答案:

答案 0 :(得分:5)

无法保证调用这些方法的顺序,因此您不能依赖它们的任何顺序。您唯一可以保证在视图显示或消失之前调用-viewWillAppear:-viewWillDisappear:

处理此问题的另一种方法可能是将此更改为意愿/确实类型方案。因此,您可以在-viewWillDisappear:中保存对象的当前状态,并在-viewDidAppear:中恢复状态(即加载字典)。这将保证正在消失的视图在显示的视图之前保存其字典。

另一种方法是更改​​自定义词典在视图控制器之间传递的方式,并使用应用程序UITabBarViewController上的委托对象来处理将这些更改同步到用户默认值。您可以将其集成到您的应用中但是最有意义,但我将在下面提供一个基本示例以及您需要为您的应用提供的更改(如您的问题中所述):

要使用该示例,您需要进行这些更改(适应您的编码风格):

  • NSDictionary添加到名为_sharedDictionary的ivar到您的应用程序代理
  • 在应用启动时从用户默认值加载字典
  • 声明您的app delegate实现了UITabBarControllerDelegate协议
  • 当您的应用加载时,将您的应用代理人指定为主UITabBarController的代表。
  • 更改您的视图控制器以响应您可以调用sharedDictionary
  • 的新属性
  • 您可以维护代码的其余部分,在-viewWillAppear中将视图控制器的sharedDictionary属性的值设置为自定义对象,并像以前一样继续

完成这些操作后,将以下方法实现添加到您的app delegate:

-(BOOL)tabBarController:(UITabBarController*)tabBarController shouldSelectViewController:(UIViewController*)viewController
{
  // If you're using ARC, you can remove the retain/autorelease statements
  [_sharedDictionary autorelease];

  // In order to avoid a compiler warning here, you should have your view controllers
  // possibly inherit from a parent that defines the sharedDictionary property and cast
  // to that, or have your view controllers implement a protocol that defines the
  // property, and cast to that. As long as your view controllers actually implement
  // the sharedDictionary property, however, everything will work
  _sharedDictionary = [[[tabBarController selectedViewController] sharedDictionary] retain];

  // set the shared dictionary on the new view controller -- same casting rules apply
  // as stated above
  [viewController setSharedDictionary:_sharedDictionary];

  // save this to user defaults so that if the app stops, it maintains whatever state
  // you're keeping
  dispatch_async(dispatch_get_main_queue(), ^{
    [[NSUserDefaults standardUserDefaults] setObject:_sharedDictionary forKey:@"sharedDictionary"];
    [[NSUserDefaults standardUserDefaults] synchronize];
  });

  return YES;
}

答案 1 :(得分:2)

这很简单。而不是每个视图控制器都有自己的CustomObject副本,并试图通过NSUserDefaults使它们保持同步,让两个视图控制器共享同一个CustomObject实例。

此外,您还可以尝试使用Observer设计模式。您的视图控制器将扮演Observer的角色,而单个CustomObject实例将扮演Subject的角色。


有关将customObject状态保存到用户默认值/从用户默认值加载的详细信息应封装在customObject内,并从视图控制器中隐藏。您的视图控制器不需要知道 customObject如何在用户默认值中保存/加载NSDictionnary。你的customObject课程应该是这样的:

@interface CustomClass : NSObject

@property (nonatomic, strong) NSString* name;
@property (nonatomic, assign) float value;

- (void)save;
- (void)load;
// Other methods

@end


@implementation CustomClass

@synthesize name = name_;
@synthesize value = value_;

- (void)save
{
    NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys:
                          name_, @"Name",
                          [NSNumber numberWithFloat:value_], @"Value",
                          nil];
    [[NSUserDefaults standardUserDefaults] setObject:dict
                                              forKey:@"CustomObject"];
}

- (void)load
{
    NSDictionary* dict = [[NSUserDefaults standardUserDefaults]
                          objectForKey:@"CustomObject"];
    name_ = [dict objectForKey:@"Name"];
    NSNumber* valueNum = [dict objectForKey:@"Value"];
    value_ = [valueNum floatValue];
}

@end

应用程序启动时应加载customObject一次。您可以在AppDelegate的didFinishLaunchingWithOptions方法中执行此操作:

- (BOOL)application:(UIApplication *)application
        didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    [sharedCustomObject load];
}

在视图控制器“viewWillAppear中,您无需重新加载sharedCustomObject它已经保持当前状态

在您的视图控制器“viewWillDisappear中,您只需确保用户默认值中sharedCustomObject的状态备份

- (void)viewWillDisappear:(BOOL)animated
{
    [sharedCustomObject save];
}

保存后,sharedCustomObject 仍然是最新的,无需重新加载

我希望现在事情更清楚了。 : - )

答案 2 :(得分:0)

我可能不会以这种方式使用NSUserDefaults。您应该能够在给定的视图控制器中封装行为,而不是依赖特定的执行路径来操作共享状态。要么使用Jason的viewDidAppear建议,要么将ViewControllerX的字典内部副本直接传递到ViewControllerY,以避免不得不解决它。