加载UIView子类的Nib的正确方法

我知道这个问题之前已经被问过了,但答案是矛盾的,我很困惑,所以请不要激怒我。

我想在我的应用程序中有一个可重用的UIView子类。 我想描述使用nib文件的接口。

现在我们假设它是一个带有活动指标的加载指标视图。 我想在一些事件实例化这个视图,并animation到视图控制器的视图。 我可以用编程的方式来描述视图的接口,通过编程方式创build元素并在init方法中设置它们的框架等。

我怎样才能使用笔尖呢? 保持界面生成器中给定的大小而不必设置框架。

我已经设法做到这一点,但我相信这是错误的(这只是一个视图与select器):

  - (id)initWithDataSource:(NSDictionary *)dataSource { self = [super init]; if (self){ self = [[[NSBundle mainBundle] loadNibNamed:[NSString stringWithFormat:@"%@", [self class]] owner:self options:nil] objectAtIndex:0]; self.pickerViewData = dataSource; [self configurePickerView]; } return self; } 

但是我覆盖自我,当我实例化它:

 FSASelectView *selectView = [[FSASelectView alloc] initWithDataSource:selectViewDictionary]; selectView.delegate = self; selectView.frame = CGRectMake(0, self.view.bottom + 50, [FSASelectView width], [FSASelectView height]); 

我必须手动设置框架,而不是从IB中拾取。

编辑:我想在视图控制器中创build此自定义视图,并有权控制视图的元素。 我不想要一个新的视图控制器。

谢谢

编辑:我不知道这是否是最佳做法,我敢肯定这不是,但这是我做到的:

 FSASelectView *selectView = [[[NSBundle mainBundle] loadNibNamed:[NSString stringWithFormat:@"%@",[FSASelectView class]] owner:self options:nil] objectAtIndex:0]; selectView.delegate = self; [selectView configurePickerViewWithData:ds]; selectView.frame = CGRectMake(0, self.view.bottom + 50, selectView.width, selectView.height); selectView.alpha = 0.9; [self.view addSubview:selectView]; [UIView animateWithDuration: 0.25 delay: 0 options:UIViewAnimationOptionAllowUserInteraction |UIViewAnimationOptionCurveEaseInOut animations:^{ selectView.frame = CGRectMake(0, self.view.bottom - selectView.height, selectView.width, selectView.height); selectView.alpha = 1; } completion:^(BOOL finished) { }]; 

仍然需要正确的做法

如果这已经完成使用视图控制器和初始化与笔尖名​​称? 我应该在代码中设置一些UIView初始化方法中的nib吗? 还是我做得好?

 MyViewClass *myViewObject = [[[NSBundle mainBundle] loadNibNamed:@"MyViewClassNib" owner:self options:nil] objectAtIndex:0] 

我正在使用它来初始化我有的可重用的自定义视图。


请注意,您可以在末尾使用“firstObject” ,它会更清洁一些。 “firstObject”是NSArray和NSMutableArray的一个方便的方法。

下面是一个典型的例子,加载一个xib用作表头。 在你的文件YourClass.m

 - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { return [[NSBundle mainBundle] loadNibNamed:@"TopArea" owner:self options:nil].firstObject; } 

通常,在TopArea.xib ,您可以单击文件所有者并将文件所有者设置为YourClass 。 然后,实际上在YourClass.h中,您将拥有IBOutlet属性。 在TopArea.xib ,您可以将控件拖放到这些sockets。

不要忘记,在TopArea.xib ,你可能需要点击视图本身,然后拖到某个sockets,所以你可以控制它,如果有必要的话。 (一个非常有价值的提示是,当你这样做表格单元行时,你必须这样做 – 你必须将视图本身连接到代码中的相关属性。)

如果你想保持你的CustomView和它的xib独立于File's Owner ,然后按照下列步骤

  • File's Owner字段留空。
  • 点击xib文件中的实际视图,并将其Custom Class设置为CustomView (您的自定义视图类的名称)
  • 在自定义视图的.h文件中添加IBOutlet
  • 在自定义视图的.xib文件中,单击视图并进入Connection Inspector 。 在这里你将所有你在.h文件中定义的IBOutlets
  • 连接他们各自的看法。

CustomView类的.m文件中,如下所示覆盖init方法

 -(CustomView *) init{ CustomView *result = nil; NSArray* elements = [[NSBundle mainBundle] loadNibNamed: NSStringFromClass([self class]) owner:self options: nil]; for (id anObject in elements) { if ([anObject isKindOfClass:[self class]]) { result = anObject; break; } } return result; } 

现在当你想加载你的CustomView ,使用下面这行代码[[CustomView alloc] init];

遵循以下步骤

  1. 创build一个types为UIView MyView .h / .m类。
  2. 创build一个名字相同的MyView.xib
  3. 现在将文件所有者类从xib中的NSObject更改为UIViewController 。 看到下面的图片 在这里输入图像说明
  4. 将文件所有者视图连接到您的视图。 看到下面的图片 在这里输入图像说明

  5. 将视图的类更改为MyView 。 和3一样。

  6. 地方控制创buildIBOutlets。

这里是加载视图的代码:

 UIViewController *controller=[[UIViewController alloc] initWithNibName:@"MyView" bundle:nil]; MyView* view=(MyView*)controller.view; [self.view addSubview:myview]; 

希望能帮助到你。

澄清

UIViewController是用来加载你的xib和UIViewController拥有的视图,实际上是你在MyView xib中分配的MyView。

演示我在这里做了一个演示

回答我自己的问题大约2年后,但是…

它使用协议扩展,所以你可以做到这一点,没有任何额外的代码为所有类。

 /* Prerequisites ------------- - In IB set the view's class to the type hook up any IBOutlets - In IB ensure the file's owner is blank */ public protocol CreatedFromNib { static func createFromNib() -> Self? static func nibName() -> String? } extension UIView: CreatedFromNib { } public extension CreatedFromNib where Self: UIView { public static func createFromNib() -> Self? { guard let nibName = nibName() else { return nil } guard let view = NSBundle.mainBundle().loadNibNamed(nibName, owner: nil, options: nil).last as? Self else { return nil } return view } public static func nibName() -> String? { guard let n = NSStringFromClass(Self.self).componentsSeparatedByString(".").last else { return nil } return n } } // Usage: let myView = MyView().createFromNib() 

在Swift中:

例如,您的自定义类的名称是InfoView

起初,你创build文件InfoView.xibInfoView.swift是这样的:

 import Foundation import UIKit class InfoView: UIView { class func instanceFromNib() -> UIView { return UINib(nibName: "InfoView", bundle: nil).instantiateWithOwner(nil, options: nil)[0] as! UIView } 

然后像这样设置File's OwnerUIViewController

在这里输入图像说明

View重命名为InfoView

在这里输入图像说明

用鼠标右键单击File's Owner ,并将您的view字段连接到您的InfoView

在这里输入图像说明

确保类名是InfoView

在这里输入图像说明

在此之后,您可以添加行动到您的自定义类中的button,没有任何问题:

在这里输入图像说明

在您的MainViewController使用这个自定义类:

 func someMethod() { var v = InfoView.instanceFromNib() v.frame = self.view.bounds self.view.addSubview(v) } 

那么你可以使用视图控制器初始化xib并使用viewController.view。 或者像你这样做。 只有制作一个UIView子类作为UIView的控制器是一个坏主意。

如果您的自定义视图中没有任何sockets,则可以直接使用UIViewController类来初始化它。

更新:在你的情况下:

 UIViewController *genericViewCon = [[UIViewController alloc] initWithNibName:@"CustomView"]; //Assuming you have a reference for the activity indicator in your custom view class CustomView *myView = (CustomView *)genericViewCon.view; [parentView addSubview:myView]; //And when necessary [myView.activityIndicator startAnimating]; //or stop 

否则,你必须做一个自定义的UIViewController (使其成为文件的所有者,以便sockets正确连线)。

 YourCustomController *yCustCon = [[YourCustomController alloc] initWithNibName:@"YourXibName"]. 

无论你想添加视图,你可以使用。

 [parentView addSubview:yCustCon.view]; 

但是,在加载xib时,将另一个视图控制器(已经用于其他视图)作为所有者传递并不是一个好主意,因为控制器的视图属性将被更改,并且当您要访问原始视图时,将不会有一个参考。

编辑:你将面临这个问题,如果你已经与文件的所有者设置新的xib作为相同的主UIViewController类,并将视图属性绑定到新的xib视图。

即;

  • YourMainViewController – pipe理 – mainView
  • CustomView – 需要时从xib加载。

下面的代码稍后会引起混淆,如果你把它写在视图里面的话YourMainViewController负载。 这是因为self.view从这一点将引用您的customview

 -(void)viewDidLoad:(){ UIView *childView= [[[NSBundle mainBundle] loadNibNamed:@"YourXibName" owner:self options:nil] objectAtIndex:0]; }