我知道以前有人问过这个问题,但答案是矛盾的,我很困惑,所以请不要喷我。
我想在我的应用程序中拥有一个可重用的 UIView
子类。我想使用 nib 文件来描述界面。
现在假设它是一个加载指示器视图,其中包含一个活动指示器。我想在某个事件上实例化这个视图并动画到视图控制器的视图中。我可以毫无问题地以编程方式描述视图的界面,以编程方式创建元素并在 init 方法中设置它们的框架等。
我怎么能用笔尖做到这一点呢?保持界面生成器中给定的大小,而无需设置框架。
我已经设法这样做了,但我确定这是错误的(它只是一个带有选择器的视图):
- (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 获取框架。
编辑:我想在视图控制器中创建这个自定义视图,并有权控制视图的元素。我不想要一个新的视图控制器。
谢谢
编辑:我不知道这是否是最佳做法,我确定不是,但这就是我的做法:
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) {
}];
仍然需要正确的练习
这是否应该使用视图控制器和带有 nib 名称的 init 来完成?我应该在代码中的一些 UIView 初始化方法中设置笔尖吗?或者我做的还好吗?
initWithDataSource
中的原始问题中使用的代码几乎相同吗?无论如何,即使您将所有者设为“无”,这也将起作用。
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
中,您可以将控件拖动到这些插座。
不要忘记,在 TopArea.xib
中,您可能必须单击视图本身并将其拖动到某个插座,以便在必要时控制它。 (一个非常有价值的提示是,当您对表格单元格行执行此操作时,您绝对必须这样做 - 您必须将视图本身连接到代码中的相关属性。)
如果您想让 CustomView
及其 xib
独立于 File's Owner
,请按照以下步骤操作
将文件的所有者字段留空。
单击CustomView的xib文件中的实际视图并将其自定义类设置为CustomView(自定义视图类的名称)
在自定义视图的 .h 文件中添加 IBOutlet。
在自定义视图的 .xib 文件中,单击视图并进入连接检查器。在这里,您将在 .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];
-init
。相反,它将调用 initWithFrame:
和 -awakeFromNib
。如果您编写与 -init
方法相同的代码来加载视图,您将进入无限循环。
请按照以下步骤操作
创建一个名为 MyView .h/.m 的 UIView 类型的类。创建一个同名的 xib MyView.xib。现在将 File Owner 类从 xib 中的 NSObject 更改为 UIViewController。请参阅下图将文件所有者视图连接到您的视图。请参见下图 将 View 的类更改为 MyView。同3。放置控件创建IBOutlets。
这是加载视图的代码:
UIViewController *controller=[[UIViewController alloc] initWithNibName:@"MyView" bundle:nil];
MyView* view=(MyView*)controller.view;
[self.view addSubview:myview];
希望能帮助到你。
澄清:
UIViewController
用于加载您的 xib,UIViewController
拥有的视图实际上是您在 MyView xib 中分配的 MyView
。
演示我做了一个演示抓取here
在这里回答我自己关于 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()
在斯威夫特:
例如,您的自定义类的名称是 InfoView
首先,您创建文件 InfoView.xib
和 InfoView.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 Owner
设置为 UIViewController
,如下所示:
https://i.stack.imgur.com/YSsmC.png
将您的 View
重命名为 InfoView
:
https://i.stack.imgur.com/GoOtG.png
右键单击 File's Owner
并将您的 view
字段与您的 InfoView
连接起来:
https://i.stack.imgur.com/Uiu0F.png
确保类名是 InfoView
:
https://i.stack.imgur.com/5VXwM.png
在此之后,您可以毫无问题地将操作添加到自定义类中的按钮:
https://i.stack.imgur.com/nYctT.png
并在您的 MainViewController
中使用此自定义类:
func someMethod() {
var v = InfoView.instanceFromNib()
v.frame = self.view.bounds
self.view.addSubview(v)
}
好吧,您可以使用视图控制器初始化 xib 并使用 viewController.view。或者按照你的方式去做。仅将 UIView
子类作为 UIView
的控制器是一个坏主意。
如果您的自定义视图没有任何出口,那么您可以直接使用 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
(使其成为文件的所有者,以便正确连接插座)。
YourCustomController *yCustCon = [[YourCustomController alloc] initWithNibName:@"YourXibName"].
无论您想在哪里添加可以使用的视图。
[parentView addSubview:yCustCon.view];
但是,在加载 xib 时将另一个视图控制器(已经用于另一个视图)作为所有者传递并不是一个好主意,因为控制器的视图属性将被更改,并且当您想要访问原始视图时,您不会有一个参考。
编辑:如果您将新的 xib 与文件所有者设置为相同的主 UIViewController
类并将视图属性绑定到新的 xib 视图,您将面临此问题。
IE;
YourMainViewController -- 管理 -- mainView
CustomView - 需要在需要时从 xib 加载。
下面的代码稍后会引起混淆,如果你写在视图中确实加载了 YourMainViewController
。这是因为 self.view
从此时起将引用您的自定义视图
-(void)viewDidLoad:(){
UIView *childView= [[[NSBundle mainBundle] loadNibNamed:@"YourXibName" owner:self options:nil] objectAtIndex:0];
}