如何在运行时更改UIStoryboard中的UIViewController类名称

Pos*_*ism 3 iphone storyboard uiviewcontroller ios

我在我的故事板中设置了这个UIViewController,包含了我需要的所有插座,视图和约束.完善.让我们调用这个WatchStateController,它将作为一个抽象的父类.

然后我有了WatchStateController的这个子类,叫做WatchStateTimeController,它将具有我需要的特定应用程序状态的功能.

因为我试图在UIStoryboard中使用1视图控制器,我在将WatchStateTimeController实例化为类型WatchStateTimeController时遇到了一些问题 - 它实例化为WatchStateController.

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

WatchStateTimeController *timeController = (WatchStateTimeController *)[mainStoryboard instantiateViewControllerWithIdentifier:@"WatchStateController"];
Run Code Online (Sandbox Code Playgroud)

这是因为故事板的Identity Inspector中的"Class"字段设置为"WatchStateController".所以问题是,我如何仅在运行时更改Identity Inspector中设置的类名?

身份检查员

注意:忽略我为什么要这样做并专注于如何做到这一点.如果你真的必须知道原因,你可以阅读战略设计模式.

小智 6

我用来强制故事板与策略模式兼容的一个稍微脏的解决方法:我在基础(抽象)视图控制器中编写一个自定义分配器,它在故事板机制结束之前返回所需的具体视图控制器子类的实例.

为此,您必须告诉基类要实例化的子类.

所以,在基本控制器中:

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);
}
Run Code Online (Sandbox Code Playgroud)

这也为子类的ivars实例化了足够的内存.

故事板中已知的视图控制器类型"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:^{}];
Run Code Online (Sandbox Code Playgroud)

具体的视图控制器被实例化并显示没有故障.


Mik*_*ock 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
Run Code Online (Sandbox Code Playgroud)

要在呈现视图控制器时进行设置,请分配适当的帮助程序对象(而不是尝试更改视图控制器本身的子类):

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

与子类化视图控制器相比,我看到这种方法的优点:

  • 它更符合SOLID原则,特别是单一责任原则,开放/封闭原则等.
  • 如果您打算编写测试,那么小的,专注的帮助程序类,可能具有很少或根本不具有UI依赖性,这取决于它们需要做什么,使得单元测试更容易
  • 它更紧密地遵循iOS中已经存在的设计模式和结构模式(使用委托,让故事板/ xib以正常方式实例化视图控制器)
  • 从视图控制器中删除逻辑.使用iOS,可以很容易地获得具有太多逻辑的大型视图控制器; 我认为我们应该一直在寻找机会来改善这一点