viewWillAppear горе - PullRequest
       1

viewWillAppear горе

1 голос
/ 30 января 2012

Я использую 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.

Проблема, с которой я сталкиваюсь в настоящее время, заключается в том, что когда пользователь переключает вкладки, скажем, с ViewControllerX на ViewControllerY, эти методы не вызывают в ожидаемом порядке.Я ожидаю увидеть это в журнале:

ViewControllerX Will Disappear
ViewControllerY Will Appear

но вместо этого я вижу

ViewControllerY Will Appear
ViewControllerX Will Disappear

В результате старый словарь загружается в ViewControllerY и только после переключения вкладокснова появляется новый словарь.Есть ли простой способ обойти эту проблему?

Ответы [ 3 ]

5 голосов
/ 30 января 2012

Нет гарантии, в каком порядке будут вызываться эти методы, поэтому вы не можете рассчитывать на какой-либо заказ с ними.Единственная гарантия, которую вы получите, - это то, что -viewWillAppear: и -viewWillDisappear: будут вызваны до того, как представление появится или исчезнет соответственно.

Еще один способ справиться с этим, возможно, изменить это на сценарий типа завещание / сделал.Таким образом, вы сохраняете текущее состояние вашего объекта в -viewWillDisappear: и восстанавливаете состояние (т.е. загружаете свой словарь) в -viewDidAppear:.Это гарантирует, что уходящее представление сохраняет свой словарь до того, как оно появится.

Другой подход - изменить способ передачи пользовательского словаря между контроллерами представления и использовать объект делегата в приложении.UITabBarViewController для синхронизации этих изменений с пользовательскими настройками по умолчанию.Однако вы можете интегрировать это в свое приложение, однако это имеет смысл, но ниже я приведу базовый пример вместе с изменениями, которые необходимо внести в свое приложение (как описано в вашем вопросе):

Комуиспользуйте пример, вам нужно внести следующие изменения (адаптироваться к вашему стилю кодирования):

  • добавить NSDictionary к ivar с именем _sharedDictionary к вашему делегату приложения
  • загрузитьсловарь пользователя по умолчанию при запуске приложения
  • объявляет, что ваш делегат приложения реализует протокол UITabBarControllerDelegate
  • при загрузке вашего приложения, назначьте делегат приложения в качестве делегата вашего основного UITabBarController.
  • измените ваши контроллеры представления, чтобы они реагировали на новое свойство, которое вы можете вызвать sharedDictionary
  • , вы можете сохранить оставшуюся часть кода, где в -viewWillAppear вы устанавливаете значение представленияКонтроллер sharedDictionary свойство вашего пользовательского объекта и просто продолжайте, как вы делали раньше

После того, как вы сделали эти вещи, добавьте следующую реализацию метода в ваше приложениеделегат:

-(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;
}
2 голосов
/ 30 января 2012

Все просто. Вместо того, чтобы каждый контроллер представления имел свою собственную копию 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 должен быть загружен один раз при запуске приложения. Единственное место, где вы можете это сделать, - это метод didFinishLaunchingWithOptions вашего AppDelegate:

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

В ваших контроллерах вида viewWillAppear вам не нужно заставлять sharedCustomObject перезагрузить себя. Уже будет храниться текущее состояние .

В контроллерах вида 'viewWillDisappear вам нужно только убедиться, что состояние sharedCustomObject равно , резервное копирование по умолчанию пользователя.

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

После сохранения sharedCustomObject все еще актуален и не требует перезагрузки .

Надеюсь, теперь все стало понятнее. : -)

0 голосов
/ 30 января 2012

Я бы не стал использовать NSUserDefaults таким образом, наверное. Вы должны иметь возможность инкапсулировать поведение в данном контроллере представления и не полагаться на определенный путь выполнения для управления общим состоянием. Либо воспользуйтесь предложением Джейсона viewDidAppear, либо передайте внутреннюю копию словаря ViewControllerX прямо в ViewControllerY, чтобы избежать необходимости обходить его.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...