Как вы обрабатываете порядок инициализации представления в UIViewController при построении представления вручную? - PullRequest
4 голосов
/ 20 января 2011

У меня есть UIViewController, который материализует его представление в loadView (то есть без кончика). В соответствии с документацией (и с помощью подтверждения в коде) loadView и, следовательно, viewDidLoad не будут вызываться до тех пор, пока к представлению UIViewController не будет произведен первый доступ.

У меня есть другой класс, который создает экземпляр UIViewController и вызывает несколько методов перед тем, как на него ссылается само представление. Многие из этих методов изменяют представление / подпредставления в UIViewController. К сожалению, представление / подпредставления не создаются в данный момент. (Я легко могу добавить ссылку на представление перед вызовом другого метода, но это требует усилий и понимания невидимого контракта для пользователя моего UIViewController).

Как мне обработать методы в UIViewController, которые изменяют представление / подпредставления? В каждом из моих методов UIViewController я могу проверить, загружено ли представление, но вы не должны вызывать loadView напрямую. Я мог бы поставить "self.view;" в методе, чтобы предположительно убедиться, что представление загружено, но это кажется довольно хакерским (и, конечно, вызывает предупреждение лязг).

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

Я чувствую, что упускаю что-то довольно простое, но я не смог найти это. Как правильно решить эту проблему состояния / инициализации?

Ответы [ 2 ]

5 голосов
/ 20 января 2011

В основном ответом на подобные вещи является не показывать компоненты ваших представлений через контроллер представлений, поскольку другие объекты, вероятно, не должны получать к ним доступ. Итак, если ваш контроллер представления управляет видом, который отображает имя человека, например, вместо того, чтобы делать что-то вроде этого:

PersonNameController* pnc = [[PersonNameController alloc] initWithNibName:nil bundle:nil];
[[pnc nameLabel] setText:@"Jason"];
[[self navigationController] pushViewController:pnc animated:YES];

Вы должны отделить биты данных / модели от битов представления и выставить свойство 'name' на вашем контроллере представления. Тогда вы можете получить этот простой пример кода, который всегда работает должным образом (он требует немного больше кода, но он избавляет вас от таких головных болей и, если быть совершенно честным, это то, чем занимается MVC):

PersonNameController* pnc = [[PersonNameController alloc] initWithNibName:nil bundle:nil];
[pnc setName:@"Jason"];
[[self navigationController] pushViewController:pnc animated:YES];

И реализация такого контроллера:

@interface PersonNameController : UIViewController {
 @private
  NSString* name_;
}

// other people use this to pass model data to the controller
@property(nonatomic,copy) NSString* name;   

@end

@interface PersonNameController()

// for us only
@property(nonatomic,assign) UILabel* nameLabel;

@end

@implementation PersonNameController

// properties
@synthesize nameLabel;
@synthesize name = name_;

- (void)setName:(NSString*)value
{
  if( [value isEqualToString:name_] ) return;
  [name_ autorelease];
  name_ = [value copy];
  [[self nameLabel] setText:name_?:@""];
}

// view lifecycle
- (void)loadView
{
  // ob fake frame for now
  UIView* view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)];
  UILabel* label = [[UILabel alloc] initWithFrame:CGRectMake( 10, 10, 300, 37)];
  [view addSubview:label];
  [self setView:view];
  [self setNameLabel:label];

  // Set the name on the label if one's set already
  [label setText:name_?:@""];

  // clean up
  [view release];
  [label release];
}

// take care of unloads
- (void)viewDidUnload
{
  // no need to release, but set nil so it's not used while the view
  // is unloaded
  nameLabel = nil;
  [super viewDidUnload];
}

- (void)dealloc
{
  // nameLabel is assign (owned by our view) so we don't release here
  // but set to nil just to be good citizens
  nameLabel = nil;
  [name_ release];
  [super dealloc];
}

@end

Краткое примечание: это было просто напечатано в этом маленьком окошке, и я очень устал, поэтому он не проверяется на общую правильность синтаксиса и т. Д. Он предназначен в качестве быстрого примера, а не что-то вырезать наклеить.

Кроме того, если у вас есть несколько свойств, которые необходимо обновлять каждый раз, вы можете создать один метод утилиты, например -udpateLabels, или что-то, что обновит их все. Затем вы вызываете это из каждого сеттера и из viewDidLoad или loadView. Таким образом, вы будете иметь все в одном месте (за счет некоторой производительности). Если после профилирования вы тратите слишком много времени на этот метод, вы можете использовать его соответствующим образом.

2 голосов
/ 20 января 2011

Как правило, по соображениям проектирования я считаю, что вы не должны позволять другому классу напрямую изменять представление UIViewController (именно поэтому оно называется UIViewController, поскольку именно он контролирует представление ). UIViewController вместо этого изменяет свой вид.

Если представление не загружено в то время, у вас могут быть только ивары, которые сохраняют определенное состояние, например:

BOOL viewShrinked;

и используйте этот тип переменных для восстановления этого состояния позже, когда представление загружается или перезагружается.

- (void) loadView{
    if(viewShrinked){
       ....
    }else{
        ...
    }
}

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

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