如何创build自定义的iOS视图类并实例化它的多个副本(在IB中)?

我目前正在做一个应用程序,将有多个计时器,基本上都是一样的。

我想创build一个自定义的类,它使用定时器的所有代码以及布局/animation,所以我可以有5个相同的定时器独立运行。

我使用IB(xcode 4.2)创build了布局,并且所有定时器的代码当前只是在viewcontroller类中。

我很难包装我的大脑如何封装一切到自定义类,然后将其添加到视图控制器,任何帮助将不胜感激。

那么从概念上来说,你的计时器应该是UIView一个子类而不是NSObject

要在IB中实例化一个定时器的实例,只需将UIView拖放到视图控制器视图上,并将其类设置为定时器的类名。

在这里输入图像说明

记得在你的视图控制器中input你的计时器类。

编辑:对于IBdevise(代码实例请参阅修订历史logging)

我并不是很熟悉故事板,但是我知道你可以使用与使用故事板版本几乎相同的.xib文件在IB中构build你的界面。 您甚至应该能够将您的视图作为整体从现有的界面复制并粘贴到.xib文件。

为了testing这个,我创build了一个名为“MyCustomTimerView.xib”的新的空的.xib 。 然后我添加了一个视图,并添加了一个标签和两个button。 像这样:

在这里输入图像说明

我创build了一个新的目标-C类子类UIView名为“MyCustomTimer”。 在我的.xib我将文件的所有者类设置为MyCustomTimer 。 现在,我可以像任何其他视图/控制器一样自由地连接操作和sockets。 生成的.h文件如下所示:

 @interface MyCustomTimer : UIView @property (strong, nonatomic) IBOutlet UILabel *displayLabel; @property (strong, nonatomic) IBOutlet UIButton *startButton; @property (strong, nonatomic) IBOutlet UIButton *stopButton; - (IBAction)startButtonPush:(id)sender; - (IBAction)stopButtonPush:(id)sender; @end 

留下来的唯一障碍就是在我的UIView子类上获取这个.xib 。 使用.xib大大减less了所需的设置。 而且,因为您正在使用故事板来加载我们知道的计时器-(id)initWithCoder:是唯一将被调用的初始化程序。 所以这里是实现文件的样子:

 #import "MyCustomTimer.h" @implementation MyCustomTimer @synthesize displayLabel; @synthesize startButton; @synthesize stopButton; -(id)initWithCoder:(NSCoder *)aDecoder{ if ((self = [super initWithCoder:aDecoder])){ [self addSubview: [[[NSBundle mainBundle] loadNibNamed:@"MyCustomTimerView" owner:self options:nil] objectAtIndex:0]]; } return self; } - (IBAction)startButtonPush:(id)sender { self.displayLabel.backgroundColor = [UIColor greenColor]; } - (IBAction)stopButtonPush:(id)sender { self.displayLabel.backgroundColor = [UIColor redColor]; } @end 

名为loadNibNamed:owner:options:的确如此。 它加载Nib并将“File's Owner”属性设置为self。 我们提取数组中的第一个对象,那就是Nib的根视图。 我们将视图添加为子视图,并将其显示在屏幕上。

显然这只是在按下button时改变了标签的背景颜色,但是这个例子应该能让你顺利的完成。

注释基于评论:

值得注意的是,如果您遇到无限recursion问题,您可能错过了此解决scheme的微妙技巧。 这不是做你认为正在做的事情。 放在故事板中的视图是看不到的,而是加载另一个视图作为子视图。 它加载的视图是在笔尖中定义的视图。 笔尖中的“文件所有者”就是那个看不见的视图。 很酷的部分是,这个看不见的视图仍然是一个Objective-C类,它可以用作从nib中引入视图的视图控制器。 例如, MyCustomTimer类中的IBAction方法是您在视图控制器中比在视图中更期望的东西。

作为一个侧面说明,有人可能会争辩说,这打破了MVC ,我有点同意。 从我的angular度来看,它与自定义的UITableViewCell更紧密相关,它有时也必须是部件控制器。

还值得注意的是,这个答案是提供一个非常具体的解决scheme; 创build一个可以在故事板上布置的相同视图中多次实例化的笔尖。 例如,你可以很容易地想象一下,这些计时器中的六个都在iPad屏幕上。 如果您只需要指定视图控制器的视图,该视图将在应用程序中多次使用,那么jyavenard提供的解决scheme对于这个问题几乎肯定是一个更好的解决scheme。

Swift的例子

更新了Swift 3

这是一个基本的步骤。 通过观看这个Youtubevideo系列,我学到了很多东西。

添加自定义视图文件

以下两个文件将形成您的自定义视图:

  • .xib文件来包含布局
  • .swift文件作为UIView子类

下面添加它们的细节。

Xib文件

将.xib文件添加到项目(文件>新build>文件…>用户界面>查看)。 我打电话给我的ReusableCustomView.xib。

创build你想要自定义视图的布局。 作为一个例子,我将使用UILabelUIButton进行布局。 使用自动布局是一个不错的主意,这样无论您稍后将其设置为什么大小,都会自动resize。

在这里输入图像说明

Swift文件

将.swift文件添加到您的项目(文件>新build>文件…>源文件> Swift文件)。 它是UIView一个子类,我正在调用我的ReusableCustomView.swift。

 import UIKit class ResuableCustomView: UIView { } 

使Swift文件的所有者

回到你的.xib文件并点击文件大纲中的“文件所有者”。 在Identity Inspector中,将swift文件的名称作为自定义类名称。

在这里输入图像说明

添加自定义查看代码

用下面的代码replaceReusableCustomView.swift文件的内容:

 import UIKit class ResuableCustomView: UIView { @IBOutlet var view: UIView! @IBOutlet weak var label: UILabel! @IBAction func buttonTap(_ sender: UIButton) { label.text = "Hi" } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) Bundle.main.loadNibNamed("ReusableCustomView", owner: self, options: nil) self.addSubview(view) view.frame = self.bounds } } 

一定要把你的.xib文件的名字拼写正确。

连接奥特莱斯和行动

通过(1)控制从xib布局拖动到swift自定义视图代码,或(2)右键单击文档大纲并将加号button向上拖到文件的所有权中,来连接出口和动作。 方法2如下所示。

在这里输入图像说明

使用你自定义的视图

您的自定义视图已经完成。 你所要做的就是在主要的故事板上添加一个UIView到你想要的位置。 在Identity Inspector中将视图的类名设置为ReusableCustomView。

在这里输入图像说明

在撰写本文时,我无法将@IBDesignable添加到代码中,所以视图不会显示在IB中,但是在您运行应用程序时会显示。

在这里输入图像说明

回答视图控制器, 而不是视图

从故事板中加载xib有一个更简单的方法。 假设你的控制器是从UIViewControllerinheritance的MyClassControllertypes。

你在故事板中使用IB添加一个UIViewController ; 将类types更改为MyClassController。 删除已经自动添加到故事板中的视图。

确保你要调用的XIB叫做MyClassController.xib。

当这个类将在故事板加载期间被实例化时,xib将被自动加载。 原因是由于UIViewController默认实现,它调用用类名命名的XIB。

这不是一个真正的答案,但我认为分享这种方法是有帮助的。

Objective-C的

  1. 将CustomViewWithXib.h和CustomViewWithXib.m导入到您的项目中
  2. 创build具有相同名称的自定义视图文件(.h / .m / .xib)
  3. inheritanceCustomViewWithXib中的自定义类

迅速

  1. 将CustomViewWithXib.swift导入到您的项目中
  2. 创build具有相同名称的自定义视图文件(.swift和.xib)
  3. inheritanceCustomViewWithXib中的自定义类

可选的 :

  1. 转到你的xib文件,如果你需要连接一些元素,用你的自定义类名来设置所有者(更多细节请参阅使得Swift文件成为@Suragch答案的所有者)

这一切,现在你可以添加你的自定义视图到你的故事板,它会显示:)

CustomViewWithXib.h

  #import <UIKit/UIKit.h> /** * All classes inherit from CustomViewWithXib should have the same xib file name and class name (.h and .m) MyCustomView.h MyCustomView.m MyCustomView.xib */ // This allows seeing how your custom views will appear without building and running your app after each change. IB_DESIGNABLE @interface CustomViewWithXib : UIView @end 

CustomViewWithXib.m

 #import "CustomViewWithXib.h" @implementation CustomViewWithXib #pragma mark - init methods - (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { // load view frame XIB [self commonSetup]; } return self; } - (instancetype)initWithCoder:(NSCoder *)aDecoder { self = [super initWithCoder:aDecoder]; if (self) { // load view frame XIB [self commonSetup]; } return self; } #pragma mark - setup view - (UIView *)loadViewFromNib { NSBundle *bundle = [NSBundle bundleForClass:[self class]]; // An exception will be thrown if the xib file with this class name not found, UIView *view = [[bundle loadNibNamed:NSStringFromClass([self class]) owner:self options:nil] firstObject]; return view; } - (void)commonSetup { UIView *nibView = [self loadViewFromNib]; nibView.frame = self.bounds; // the autoresizingMask will be converted to constraints, the frame will match the parent view frame nibView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; // Adding nibView on the top of our view [self addSubview:nibView]; } @end 

CustomViewWithXib.swift

 import UIKit @IBDesignable class CustomViewWithXib: UIView { // MARK: init methods required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) commonSetup() } override init(frame: CGRect) { super.init(frame: frame) commonSetup() } // MARK: setup view private func loadViewFromNib() -> UIView { let viewBundle = NSBundle(forClass: self.dynamicType) // An exception will be thrown if the xib file with this class name not found, let view = viewBundle.loadNibNamed(String(self.dynamicType), owner: self, options: nil)[0] return view as! UIView } private func commonSetup() { let nibView = loadViewFromNib() nibView.frame = bounds // the autoresizingMask will be converted to constraints, the frame will match the parent view frame nibView.autoresizingMask = [.FlexibleWidth, .FlexibleHeight] // Adding nibView on the top of our view addSubview(nibView) } } 

你可以在这里find一些例子。

希望有所帮助。