ver*_*rdy 112 cocoa-touch objective-c storyboard uiviewcontroller ios5
假设我有一个包含UINavigationController
初始视图控制器的故事板.它的根视图控制器是子类UITableViewController
,它是BasicViewController
.它已IBAction
连接到导航栏的右侧导航按钮
从那里我想使用故事板作为其他视图的模板,而无需创建其他故事板.假设这些视图具有完全相同的接口,但具有类的根视图控制器,SpecificViewController1
并且SpecificViewController2
是其子类BasicViewController
.
除了IBAction
方法之外,这两个视图控制器将具有相同的功能和接口.
它将如下所示:
@interface BasicViewController : UITableViewController
@interface SpecificViewController1 : BasicViewController
@interface SpecificViewController2 : BasicViewController
Run Code Online (Sandbox Code Playgroud)
我可以这样做吗?
我可以实例化故事板BasicViewController
但是具有根视图控制器的子类SpecificViewController1
和SpecificViewController2
?
谢谢.
Coc*_*aEv 57
很好的问题 - 但不幸的是只有一个蹩脚的答案.我不相信目前可以做你提出的建议,因为UIStoryboard中没有初始化程序允许覆盖与故事板相关联的视图控制器,如初始化时故事板中的对象详细信息中所定义.初始化时,stoaryboard中的所有UI元素都链接到视图控制器中的属性.
默认情况下,它将使用storyboard定义中指定的视图控制器进行初始化.
如果您试图重新使用在故事板中创建的UI元素,它们仍然必须链接或关联到视图控制器正在使用它们的属性,以便它们能够"告诉"视图控制器有关事件的信息.
复制故事板布局并不是什么大问题,特别是如果你只需要3个视图的类似设计,但是如果你这样做,你必须确保清除所有以前的关联,否则它会在尝试时崩溃与前一个视图控制器通信.您将能够在日志输出中将它们识别为KVO错误消息.
您可以采取以下几种方法:
将UI元素存储在UIView中 - 在xib文件中,并从基类中实例化它,并将其作为子视图添加到主视图中,通常是self.view.然后,您只需使用故事板布局,基本上空白的视图控制器在故事板中保留它们的位置,但分配给它们的是正确的视图控制器子类.由于他们会从基地继承,他们会得到那个观点.
在代码中创建布局并从基本视图控制器安装它.显然,这种方法违背了使用故事板的目的,但可能是你的情况.如果您有应用程序的其他部分可以从故事板方法中受益,那么可以在适当的时候在这里和那里进行偏离.在这种情况下,如上所述,您只需使用分配了子类的银行视图控制器,并让基本视图控制器安装UI.
如果Apple提出了一种方法来做你提出的建议会很好,但是将图形元素与控制器子类预关联的问题仍然是一个问题.
祝新年快乐!好吧
Jiř*_*lka 44
我们正在寻找的代码是:
object_setClass(AnyObject!, AnyClass!)
Run Code Online (Sandbox Code Playgroud)
在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
}
}
Run Code Online (Sandbox Code Playgroud)
kga*_*dis 14
正如公认的答案所述,它看起来不像是可以用故事板.
我的解决方案是使用Nib - 就像开发人员在故事板之前使用它们一样.如果你想拥有一个可重用的,可子类化的视图控制器(甚至是一个视图),我建议使用Nibs.
SubclassMyViewController *myViewController = [[SubclassMyViewController alloc] initWithNibName:@"MyViewController" bundle:nil];
Run Code Online (Sandbox Code Playgroud)
当您将所有出口连接到"文件所有者"时,MyViewController.xib
您没有指定Nib应该加载的类,您只需指定键值对:" 此视图应连接到此实例变量名称." 调用[SubclassMyViewController alloc] initWithNibName:
初始化过程时,指定将使用哪个视图控制器来" 控制 "您在笔尖中创建的视图.
故事板可以实例化自定义视图控制器的不同子类,但它涉及一种稍微不正统的技术:覆盖alloc
视图控制器的方法.创建自定义视图控制器时,重写的alloc方法实际上返回alloc
在子类上运行的结果.
我应该在答案前加上附带条件,虽然我已经在各种场景中对其进行了测试并且没有收到任何错误,但我无法确保它能够应对更复杂的设置(但我认为没有理由说它不应该工作) .此外,我还没有使用这种方法提交任何应用程序,因此有可能被Apple的审核流程拒绝(尽管我再也看不出它为什么应该这样做).
出于演示目的,我有一个UIViewController
被调用的子类TestViewController
,它有一个UILabel IBOutlet和一个IBAction.在我的故事板中,我添加了一个视图控制器并修改了它的类TestViewController
,并将IBOutlet连接到UILabel,将IBAction连接到UIButton.我通过前面的viewController上的UIButton触发的模态segue呈现TestViewController.
为了控制实例化哪个类,我添加了一个静态变量和相关的类方法,因此获取/设置要使用的子类(我想可以采用其他方法来确定要实例化哪个子类):
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];
}
}
}
Run Code Online (Sandbox Code Playgroud)
对于我的测试,我有两个子类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";
}
Run Code Online (Sandbox Code Playgroud)
GreenTestViewController.m:
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor greenColor];
self.testLabel.text = @"Set by GreenTestVC";
}
Run Code Online (Sandbox Code Playgroud)
在某些情况下,我可能想要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"];
}
Run Code Online (Sandbox Code Playgroud)
请注意,该setClassForStoryBoard
方法检查以确保所请求的类名确实是TestViewController的子类,以避免任何混淆.上面的参考BlueTestViewController
是用来测试这个功能.
小智 6
在instantiateViewControllerWithIdentifier之后尝试这个.
- (void)setClass:(Class)c {
object_setClass(self, c);
}
Run Code Online (Sandbox Code Playgroud)
喜欢 :
SubViewController *vc = [sb instantiateViewControllerWithIdentifier:@"MainViewController"];
[vc setClass:[SubViewController class]];
Run Code Online (Sandbox Code Playgroud)
特别基于nickgzzjr和Ji?í Zahálka 的回答以及CocoaBob的第二个回答下的评论,我准备了一个简短的通用方法,正是 OP 所需要的。您只需要检查故事板名称和视图控制器故事板 ID
class func instantiate<T: BasicViewController>(as _: T.Type) -> T? {
let storyboard = UIStoryboard(name: "StoryboardName", bundle: nil)
guard let instance = storyboard.instantiateViewController(withIdentifier: "Identifier") as? BasicViewController else {
return nil
}
object_setClass(instance, T.self)
return instance as? T
}
Run Code Online (Sandbox Code Playgroud)
添加了可选项以避免强制解包(swiftlint 警告),但方法返回正确的对象。
另外:在从强制转换的对象中读取它们之前,您需要初始化仅存在于子类中的属性(如果子类具有这些属性而BasicViewController
没有)。这些属性不会被自动初始化,并且在初始化之前尝试读取它们会导致崩溃。因为它们在强制转换中起作用,所以很可能即使是弱变量也不会被设置为 nil(将包含垃圾)。
尽管它不是严格的子类,但您可以:
这是我编写的Bloc教程的一个示例,ViewController
使用WhiskeyViewController
以下方法进行子类化:
这使您可以在情节提要中创建视图控制器子类的子类。然后,您可以instantiateViewControllerWithIdentifier:
用来创建特定的子类。
这种方法有点不灵活:在情节提要中对基类控制器进行的后续修改不会传播到子类。如果您有很多子类,则使用其他解决方案中的一种可能会更好,但是这样做会很困难。
归档时间: |
|
查看次数: |
20455 次 |
最近记录: |