2013-06-29 2 views
3

У меня есть этот UIViewController, установленный в моей раскадровке, со всеми точками, представлениями и ограничениями, которые мне нужны. Отлично. Назовем этот WatchStateController, он будет служить абстрактным родительским классом.Как изменить имя класса UIViewController в UIStoryboard во время выполнения

У меня есть этот подкласс WatchStateController, называемый WatchStateTimeController, который будет иметь функциональность, необходимую для конкретного состояния приложения.

Поскольку я пытаюсь использовать контроллер вида 1 в UIStoryboard, у меня возникают некоторые проблемы с созданием WatchStateTimeController в качестве типа WatchStateTimeController - он создается как WatchStateController.

UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil]; 

WatchStateTimeController *timeController = (WatchStateTimeController *)[mainStoryboard instantiateViewControllerWithIdentifier:@"WatchStateController"]; 

Это происходит потому, что поле «Класс» в раскадровке идентичности инспектора установлен в положение «WatchStateController». Итак, вопрос в том, как просто изменить это имя класса, установленное в Identity Inspector во время выполнения?

Identity Inspector

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

+4

Рассмотрите возможность изменения вашей интерпретации шаблона проектирования стратегии, чтобы лучше соответствовать структуре приложения, которое вам дано с помощью раскадровки: вместо использования наследования для изменения поведения вашего контроллера представления используйте агрегацию. Другими словами, есть свойство на WatchStateController, которое ссылается на другой объект некоторого базового класса или типа протокола, который может обеспечить желаемое поведение как своего рода делегат. –

+0

Спасибо. Я очень хорошо знаком с агрегацией и композицией - я использую ее в 95% случаев. К сожалению, использование этого здесь не имеет смысла, это приведет к повторной работе, копированию и вставке того же кода, чего я хочу избежать. Избыточность - это враг. Если это ограничение при использовании раскадровки, вы правы, вам нужно будет найти вариант шаблона. Вопрос в том, совместим ли стиль состояния/стратегии при использовании раскадровки? Или я должен просто сдаться и заманить код кучей инструкций IF/ELSE (что схема стратегии действительно предназначена для предотвращения) – PostCodeism

+0

Возможно, я прочитал это неправильно или быстро, но изображение, которое вы включили в раскадровку, показывающую watchstatecontroller, и вы хотите изменить его ... не должно быть WatchStateTimeController, поскольку вы подклассифицируете его, и это представление, которое вы хотите от подкласса? возможно, я мог бы использовать более простое описание. Например, у вас есть класс для просмотра a и вы подклассифицировали класс a для класса b для представления b, но ваша проблема - это просмотр b, выполняющийся с классом a? – rezand

ответ

5

Вот пример шаблона стратегии, используя вспомогательный объект, как я описал в комментариях:

@class WatchStateController; 

@protocol WatchStateStrategy <NSObject> 
- (void)doSomeBehaviorPolymorphically:(WatchStateController *)controller; 
@end 

@interface WatchStateController 
// or call this a delegate or whatever makes sense. 
@property (nonatomic) id <WatchStateStrategy> strategy; 
@end 

@implementation WatchStateController 
- (void)someAction:(id)sender 
{ 
    [self.strategy doSomeBehaviorPolymorphically:self]; 
} 
@end 

@interface WatchStateTimeStrategy <WatchStateStrategy> 
@end 

@implementation WatchStateTimeStrategy 
- (void)doSomeBehaviorPolymorphically:(WatchStateController *)controller 
{ 
    // here's one variation of the behavior 
} 
@end 

@interface WatchStateAnotherStrategy <WatchStateStrategy> 
@end 

@implementation WatchStateAnotherStrategy 
- (void)doSomeBehaviorPolymorphically:(WatchStateController *)controller 
{ 
    // here's another variation of the behavior 
} 
@end 

И установить это, когда вы представляете ваш контроллер представления, назначить соответствующий объект помощника (вместо в попытке изменить подкласс самого контроллера представления):

WatchStateController *viewController = [storyboard instantiateViewControllerWithIdentifier:@"WatchStateController"]; 
if (useTimeStrategy) { 
    viewController.strategy = [WatchStateTimeStrategy new]; 
} else { 
    viewController.strategy = [WatchStateAnotherStrategy new]; 
} 

преимущества я вижу такого подхода по сравнению с подклассов контроллера вид:

  • Это более тесно совмещается с SOLID principles, особенно одного принципа ответственности, открытого/закрытого принципа и т.д.
  • Малый, целенаправленный вспомогательные классы, возможно, имея мало или нет зависимостей интерфейса в зависимости от того, что им нужно сделать, чтобы сделать проще модульное тестирование, если вы планируете писать тесты
  • Это более точно соответствует шаблонам проектирования и структурным шаблонам, уже установленным в iOS (с использованием делегатов, и позволяет раскадровкам/xibs создавать экземпляры контроллеров представления обычным способом)
  • Удаляет логику из вида контроллер. С iOS очень легко получить контроллеры большого размера со слишком большой логикой; Я думаю, что мы всегда должны искать возможности для улучшения этого
+0

Это отвлекает от исходного вопроса, но мне нравится, где это происходит. Мне нравится обсуждать шаблоны программ и способы их реализации в Objective-C. Иногда нам нужно изменить наше мышление на Xcode. Но мое понимание шаблона стратегии аналогично тому, как описано в этой книге «Шаблоны проектирования Pro Objective-C для iOS». Класс CONTEXT, который «имеет класс« БЕТОН », который« является «классом АБСТРАКТА». Ограниченное пространство здесь, см. Образец главы 19: http://books.google.ca/books?id=rLZcD4hkDy4C&printsec=frontcover#v=onepage&q=Strategy%20Pattern&f=false – PostCodeism

+0

Использование протокола, аналогичного приведенному вами примеру, может быть работоспособным решение, и похоже на решение, которое я имел до этого, которое становилось запутанным для управления. Но это не шаблон стратегии. У вас все еще есть проблема управления свернутыми инструкциями IF/ELSE для поддержания логики. Решение этой проблемы является конкретной причиной того, что шаблон стратегии существует. Прочтите образец, который я предоставил в ссылке, и подумайте о том, как мы можем реализовать это в рамках ограничений UIViewControllers и UIStoriesboards. Если это невозможно, отлично. Мне нужно будет повторно реализовать решение протокола :) – PostCodeism

+1

Как бы требовалось решение @ esker для if/else для логики? Указанный протокол только инкапсулирует динамическое поведение. Можно также иметь абстрактный класс, обеспечивающий некоторые общие реализации, и конкретные стратегии наследуют его (хотя в этом случае почему бы не поместить общий impl в ваш WatchStateController и использовать протокол для остальных). Что касается последнего бита if/else для инициализации стратегии, я бы использовал фабричные методы для инкапсуляции параметра '-instantiateViewControllerWithIdentifier:' & 'vc.strategy' или установил его в -'performSegueWithIdentifier: sender:' если использовать segues для логика переключения. – qix

5

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

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

Таким образом, в базовом контроллере:

Class _concreteSubclass = nil; 
+ (void) setConcreteSubclassToInstantiate:(Class)c { 
    _concreteSubclass = c; 
} 

+ (id)allocWithZone: (NSZone *)zone { 
    Class c = _concreteSubclass ?: [self class]; 
    void *object = calloc(class_getInstanceSize(c), 1); 
    *(Class *)object = c; 
    return (id)CFBridgingRelease(object); 
} 

Это конкретизирует достаточно памяти для Ивар подкласса тоже.

Тип контроллера вида «MyViewController», известный раскадровке, является просто «BaseViewController»; но тогда, когда вы спросите раскадровку для конкретизации контроллер представления, вы делаете что-то вроде этого:

[BaseViewController setConcreteSubclassToInstantiate:[SomeSubclassOfBaseViewController class]]; 
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:@"Main" bundle: nil]; 
SomeSubclassOfBaseViewController *vc = (SomeSubclassOfBaseViewController *)[mainStoryboard instantiateViewControllerWithIdentifier:@"MyViewController"]; 
[self presentViewController:vc animated:NO completion:^{}]; 

Конкретный контроллер представления конкретизируется и отображается без задоринки.

+0

Это похоже на то, что я пытался сделать. Я проверю это. Благодаря :) – PostCodeism

Смежные вопросы