如何使用单个故事板uiviewcontroller多个子类
假设我有一个包含UINavigationController
作为初始视图控制器的故事板。 它的根视图控制器是UITableViewController
子类,它是BasicViewController
。 它具有连接到导航栏的右侧导航button的IBAction
从那里我想使用故事板作为其他视图的模板,而不必创build额外的故事板。 说这些视图将具有完全相同的接口,但具有BasicViewController
子类SpecificViewController1
和SpecificViewController2
根视图控制器。
除了IBAction
方法,这两个视图控制器将具有相同的function和接口。
这将如下所示:
@interface BasicViewController : UITableViewController @interface SpecificViewController1 : BasicViewController @interface SpecificViewController2 : BasicViewController
我可以做那样的事吗?
我可以实例化BasicViewController
的故事板,但有根视图控制器子类SpecificViewController1
和SpecificViewController2
?
谢谢。
很好的问题 – 但不幸的是只有一个蹩脚的答案。 我不相信现在可以做你提出的build议,因为在UIStoryboard中没有初始化器允许覆盖与故事板中的对象细节相关联的视图控制器在初始化时故事板中定义。 初始化时,stoaryboard中的所有UI元素都链接到视图控制器中的属性。
它将默认使用故事板定义中指定的视图控制器进行初始化。
如果您试图获得在故事板中创build的UI元素的重用,则仍然必须将其链接或关联到View Controller正在使用它们的属性,以便能够“将”事件“告知”视图控制器。
如果你只需要一个类似于3个视图的devise,那么复制一个故事板布局并不是什么大事,但是如果你这样做的话,你必须确保所有以前的关联都被清除,否则它会在尝试时崩溃与前一个视图控制器进行通信。 您将能够在日志输出中将它们识别为KVO错误消息。
你可以采取几种方法:
-
将UI元素存储在一个UIView中 – 在一个xib文件中,并从您的基类实例化,并将其添加为主视图中的子视图,通常为self.view。 然后,您将简单地使用故事板布局,基本上空白的视图控制器在故事板中占据他们的位置,但具有分配给它们的正确的视图控制器子类。 既然他们会从基地inheritance,他们会得到这个观点。
-
在代码中创build布局,并从您的基本视图控制器进行安装。 很明显,这种方法打破了使用故事板的目的,但可能是你的情况。 如果您的应用程序的其他部分可以从故事板方法中受益,则可以在适当的地方偏离这里和那里。 在这种情况下,就像上面所说的那样,你只需要在你的子类中使用银行视图控制器,并让基本视图控制器安装UI。
如果苹果提出了一种方法来实现你的build议,那将是一件好事,但是将控制器子类预先链接的graphics元素仍然是一个问题。
有一个伟大的新年! 好的
我们正在寻找的代码是:
object_setClass(AnyObject!, AnyClass!)
在Storyboard – >添加UIViewController给它一个ParentVC类的名字。
class ParentVC: UIViewController { var type: Int? override func awakeFromNib() { if type = 0 { object_setClass(self, ChildVC1.self) } if type = 1 { object_setClass(self, ChildVC2.self) } } override func viewDidLoad() { } } class ChildVC1: ParentVC { override func viewDidLoad() { super.viewDidLoad() println(type) // Console prints out 0 } } class ChildVC2: ParentVC { override func viewDidLoad() { super.viewDidLoad() println(type) // Console prints out 1 } }
正如接受的答案所述,看起来不像故事板可能。
我的解决scheme是使用Nib的 – 就像开发者在故事板之前使用它们一样。 如果你想有一个可重用的子类视图控制器(甚至一个视图),我的build议是使用Nibs。
SubclassMyViewController *myViewController = [[SubclassMyViewController alloc] initWithNibName:@"MyViewController" bundle:nil];
当你把所有的sockets连接到MyViewController.xib
的“文件所有者”时,你并没有指定Nib应该加载的类,只是指定了键值对:“ 这个视图应该连接到这个实例variables的名字 “。 调用[SubclassMyViewController alloc] initWithNibName:
初始化过程指定将使用哪个视图控制器来“ 控制 ”您在nib中创build的视图。
故事板可以实例化一个自定义视图控制器的不同子类,尽pipe它涉及一个非正统的技术:重写视图控制器的alloc
方法。 当创build自定义视图控制器时,重写的alloc方法实际上会返回在子类上运行alloc
的结果。
我应该在答案的前面加上一个前提条件,虽然我已经在各种情况下进行了testing,并没有收到任何错误,但是我不能保证它能够处理更复杂的设置(但我没有理由不应该这样做) 。 此外,我还没有提交任何使用这种方法的应用程序,所以有外部的机会,可能会被苹果的审查过程拒绝(虽然我没有看到为什么它应该)。
为了演示的目的,我有一个名为TestViewController
的UIViewController
的子类,它有一个UILabel IBOutlet和一个IBAction。 在我的故事板中,我添加了一个视图控制器,并将其类修改为TestViewController
,并将IBOutlet连接到UILabel,将IBAction连接到UIButton。 我通过在前面的viewController上由UIButton触发的模式segue来呈现TestViewController。
为了控制实例化哪个类,我添加了一个静态variables和关联的类方法,以便获取/设置要使用的子类(我想可以采用其他方法来确定要实例化哪个子类):
TestViewController.m:
#import "TestViewController.h" @interface TestViewController () @end @implementation TestViewController static NSString *_classForStoryboard; +(NSString *)classForStoryboard { return [_classForStoryboard copy]; } +(void)setClassForStoryBoard:(NSString *)classString { if ([NSClassFromString(classString) isSubclassOfClass:[self class]]) { _classForStoryboard = [classString copy]; } else { NSLog(@"Warning: %@ is not a subclass of %@, reverting to base class", classString, NSStringFromClass([self class])); _classForStoryboard = nil; } } +(instancetype)alloc { if (_classForStoryboard == nil) { return [super alloc]; } else { if (NSClassFromString(_classForStoryboard) != [self class]) { TestViewController *subclassedVC = [NSClassFromString(_classForStoryboard) alloc]; return subclassedVC; } else { return [super alloc]; } } }
对于我的testing,我有两个TestViewController
子类: RedTestViewController
和GreenTestViewController
。 每个子类都有附加的属性,每个子类都会覆盖viewDidLoad
来更改视图的背景颜色,并更新UILabel IBOutlet的文本:
RedTestViewController.m:
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. self.view.backgroundColor = [UIColor redColor]; self.testLabel.text = @"Set by RedTestVC"; }
GreenTestViewController.m:
- (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor greenColor]; self.testLabel.text = @"Set by GreenTestVC"; }
在某些情况下,我可能想要实例化TestViewController
本身,在其他场合RedTestViewController
或GreenTestViewController
。 在前面的视图控制器中,我是这样随机的:
NSInteger vcIndex = arc4random_uniform(4); if (vcIndex == 0) { NSLog(@"Chose TestVC"); [TestViewController setClassForStoryBoard:@"TestViewController"]; } else if (vcIndex == 1) { NSLog(@"Chose RedVC"); [TestViewController setClassForStoryBoard:@"RedTestViewController"]; } else if (vcIndex == 2) { NSLog(@"Chose BlueVC"); [TestViewController setClassForStoryBoard:@"BlueTestViewController"]; } else { NSLog(@"Chose GreenVC"); [TestViewController setClassForStoryBoard:@"GreenTestViewController"]; }
请注意, setClassForStoryBoard
方法将检查以确保所请求的类名确实是TestViewController的一个子类,以避免混淆。 上面提到的BlueTestViewController
的参考就是为了testing这个function。
虽然它不是严格的子类,但可以:
- 选项 -拖动“文档大纲”中的基类视图控制器以进行复制
- 将新的视图控制器副本移动到故事板上的一个单独的位置
- 将Class更改为Identity Inspector中的子类视图控制器
下面是我写的Bloc教程的一个例子,用WhiskeyViewController
ViewController
进行子类WhiskeyViewController
:
这使您可以在故事板中创build视图控制器子类的子类。 然后你可以使用instantiateViewControllerWithIdentifier:
来创build特定的子类。
这种方法有点不灵活:故事板以后对基类控制器的修改不会传播给子类。 如果你有很多的子类,你可能会更好的与其他解决scheme之一,但这将是一个捏。
在instantiateViewControllerWithIdentifier之后试试这个。
- (void)setClass:(Class)c { object_setClass(self, c); }
喜欢 :
SubViewController *vc = [sb instantiateViewControllerWithIdentifier:@"MainViewController"]; [vc setClass:[SubViewController class]];
Objc_setclass方法不会创buildchildvc的一个实例。 但是在popupchildvc的同时,正在调用childvc的deinit。 由于没有为childvc单独分配的内存,应用程序崩溃。 Basecontroller有一个实例,而vc没有。
可能最灵活的方法是使用可重用的视图。
(在独立的XIB文件或Container view
创buildContainer view
,并将其添加到故事板中的每个子类视图控制器场景)
如果您不太依赖故事板,则可以为控制器创build一个单独的.xib文件。
将适当的文件所有者和出口设置为MainViewController
并覆盖主VC中的init(nibName:bundle:)
,以便其子代可以访问相同的Nib及其出口。
你的代码应该是这样的:
class MainViewController: UIViewController { @IBOutlet weak var button: UIButton! override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { super.init(nibName: "MainViewController", bundle: nil) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewDidLoad() { super.viewDidLoad() button.tintColor = .red } }
而你的Child VC将能够重用其父母的nib:
class ChildViewController: MainViewController { override func viewDidLoad() { super.viewDidLoad() button.tintColor = .blue } }