首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >viewWillAppear的灾难

viewWillAppear的灾难
EN

Stack Overflow用户
提问于 2012-01-30 07:28:54
回答 3查看 1.3K关注 0票数 1

我使用NSUserDefaults在一个UIViewController中使用的几个UITabbarController中保持一个对象的同步。为了做到这一点,我实现了以下代码

代码语言:javascript
复制
- (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是一个自定义类的实例,它有一个NSDictionary类型的属性dictionary。此对象可能会被可见的UIViewController更改。

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

代码语言:javascript
复制
ViewControllerX Will Disappear
ViewControllerY Will Appear

但我却看到

代码语言:javascript
复制
ViewControllerY Will Appear
ViewControllerX Will Disappear

结果是旧字典被加载到ViewControllerY中,只有在再次切换选项卡之后,新字典才会出现。有没有解决这个问题的简单方法?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2012-01-30 07:42:08

不能保证这些方法的调用顺序,所以你不能依赖它们的任何顺序。您得到的唯一保证是在视图出现或消失之前分别调用-viewWillAppear:-viewWillDisappear:

处理这种情况的另一种方法可能是将其更改为will/did类型的场景。因此,您可以在-viewWillDisappear:中保存对象的当前状态,然后在-viewDidAppear:中恢复该状态(即加载字典)。这将保证将要离开的视图在出现的视图之前保存其字典。

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

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

当你的应用代理的launches

  • declare实现了你的应用加载的protocol

  • when时,
  • 将名为_sharedDictionaryNSDictionary添加到你的应用代理中,
  • 从用户默认加载字典。将您的应用程序委托指派为主UITabBarController.
  • change的委托您的视图控制器响应一个可以调用sharedDictionary
  • you的新属性可以维护代码的其余部分,在-viewWillAppear中,您可以将视图控制器的sharedDictionary属性的值设置为您的自定义对象,然后继续执行之前的

操作

完成这些操作后,将以下方法实现添加到应用程序委托中:

代码语言:javascript
复制
-(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;
}
票数 5
EN

Stack Overflow用户

发布于 2012-01-30 07:44:59

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

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

有关将customObject状态保存到用户默认设置或从用户默认设置加载的详细信息应该封装在customObject中,并且对视图控制器隐藏。您的视图控制器不需要知道customObject如何将NSDictionnary保存到用户默认设置或从用户默认设置加载用户默认设置。您的customObject的类应该如下所示:

代码语言:javascript
复制
@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方法中执行此操作:

代码语言:javascript
复制
- (BOOL)application:(UIApplication *)application
        didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    [sharedCustomObject load];
}

在视图控制器的viewWillAppear中,不需要让sharedCustomObject自己重新加载。它已经保存了当前状态的

在视图控制器的viewWillDisappear中,您只需要确保sharedCustomObject的状态是用户默认设置中的-backed

代码语言:javascript
复制
- (void)viewWillDisappear:(BOOL)animated
{
    [sharedCustomObject save];
}

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

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

票数 2
EN

Stack Overflow用户

发布于 2012-01-30 07:44:39

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

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/9057748

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档