ios 为 UIView 子类加载 Nib 的正确方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15405916/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Correct way to load a Nib for a UIView subclass
提问by Adam Waite
I am aware this question has been asked before but the answers are contradicting and I am confused, so please don't flame me.
我知道以前有人问过这个问题,但答案是矛盾的,我很困惑,所以请不要喷我。
I want to have a reusable UIView
subclass throughout my app. I want to describe the interface using a nib file.
我想UIView
在我的应用程序中拥有一个可重用的子类。我想用一个 nib 文件来描述界面。
Now let's say it's a loading indicator view with an activity indicator in it. I would like on some event to instantiate this view and animate in to a view controller's view. I could describe the view's interface no problem programmatically, creating the elements programmatically and setting their frame inside an init method etc.
现在假设它是一个加载指示器视图,其中包含一个活动指示器。我希望在某个事件中实例化此视图并将动画添加到视图控制器的视图中。我可以以编程方式描述视图的界面,以编程方式创建元素并在 init 方法中设置它们的框架等。
How can I do this using a nib though? Maintaining the size given in interface builder without having to set a frame.
我怎么能用笔尖做到这一点?保持界面构建器中给定的大小,而无需设置框架。
I've managed to do it like this, but I'm sure it is wrong (it's just a view with a picker in it):
我已经设法这样做了,但我确定这是错误的(它只是一个带有选择器的视图):
- (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;
}
But I'm overwriting self, and when I instantiate it:
但是我正在覆盖自我,当我实例化它时:
FSASelectView *selectView = [[FSASelectView alloc] initWithDataSource:selectViewDictionary];
selectView.delegate = self;
selectView.frame = CGRectMake(0, self.view.bottom + 50, [FSASelectView width], [FSASelectView height]);
I have to manually set the frame rather than have it picked up from IB.
我必须手动设置框架,而不是从 IB 获取它。
EDIT: I want to create this custom view in a view controller, and have access to control the view's elements. I don't want a new view controller.
编辑:我想在视图控制器中创建这个自定义视图,并有权控制视图的元素。我不想要一个新的视图控制器。
Thanks
谢谢
EDIT: I Don't know if this is best practice, I'm sure it's not, but this is how I did it:
编辑:我不知道这是否是最佳实践,我确定不是,但我是这样做的:
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) {
}];
Correct practice still wanted
仍然需要正确的练习
Should this have been done using a view controller and init with nib name? Should I have set the nib in some UIView initialisation method in the code? Or is what I have done ok?
这是否应该使用视图控制器和带有 nib 名称的 init 来完成?我应该在代码中的某些 UIView 初始化方法中设置笔尖吗?或者我所做的一切正常吗?
回答by Premsuraj
MyViewClass *myViewObject = [[[NSBundle mainBundle] loadNibNamed:@"MyViewClassNib" owner:self options:nil] objectAtIndex:0]
I'm using this to initialise the reusable custom views I have.
我正在使用它来初始化我拥有的可重用自定义视图。
Note that you can use "firstObject"at the end there, it's a little cleaner. "firstObject" is a handy method for NSArray and NSMutableArray.
请注意,您可以在最后使用“firstObject”,它更简洁一些。“firstObject”是 NSArray 和 NSMutableArray 的一个方便的方法。
Here's a typical example, of loading a xib to use as a table header. In your file YourClass.m
这是一个典型的例子,加载一个 xib 用作表头。在您的文件 YourClass.m 中
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
return [[NSBundle mainBundle] loadNibNamed:@"TopArea" owner:self options:nil].firstObject;
}
Normally, in the TopArea.xib
, you would click on File Owner and set the file owner to YourClass. Then actually in YourClass.hyou would have IBOutlet properties. In TopArea.xib
, you can drag controls to those outlets.
通常,在 中TopArea.xib
,您将单击 File Owner 并将文件所有者设置为 YourClass。然后实际上在 YourClass.h 中,您将拥有 IBOutlet 属性。在 中TopArea.xib
,您可以将控件拖动到这些出口。
Don't forget that in TopArea.xib
, you may have to click on the View itselfand drag that to some outlet, so you have control of it, if necessary. (A very worthwhile tip is that when you are doing this for table cell rows, you absolutely have to do that - you have to connect the view itself to the relevant property in your code.)
不要忘记在 中TopArea.xib
,您可能必须单击视图本身并将其拖到某个出口,以便在必要时控制它。(一个非常有价值的提示是,当您对表格单元格行执行此操作时,您绝对必须这样做 - 您必须将视图本身连接到代码中的相关属性。)
回答by Ans
If you want to keep your CustomView
and its xib
independent of File's Owner
, then follow these steps
如果你想保持你的CustomView
和它的xib
独立File's Owner
,那么按照这些步骤
- Leave the
File's Owner
field empty. - Click on actual view in
xib
file of yourCustomView
and set itsCustom Class
asCustomView
(name of your custom view class) - Add
IBOutlet
in.h
file of your custom view. - In
.xib
file of your custom view, click on view and go inConnection Inspector
. Here you will all your IBOutlets which you define in.h
file - Connect them with their respective view.
- 将该
File's Owner
字段留空。 - 单击
xib
文件中的实际视图CustomView
并将其设置Custom Class
为CustomView
(自定义视图类的名称) - 添加
IBOutlet
在.h
您的自定义视图的文件。 - 在
.xib
自定义视图的文件中,单击视图并进入Connection Inspector
。在这里,您将在.h
文件中定义所有 IBOutlets - 将它们与各自的视图联系起来。
in .m
file of your CustomView
class, override the init
method as follow
在.m
您的CustomView
班级文件中,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;
}
Now when you want to load your CustomView
, use the following line of code
[[CustomView alloc] init];
现在,当您要加载您的 时CustomView
,请使用以下代码行
[[CustomView alloc] init];
回答by iphonic
Follow the following steps
请按照以下步骤操作
- Create a class named MyView .h/.m of type
UIView
. - Create a xib of same name
MyView.xib
. - Now change the File Owner class to
UIViewController
fromNSObject
in xib. See the image below Connect the File Owner View to your View. See the image below
Change the class of your View to
MyView
. Same as 3.- Place controls create IBOutlets.
- 创建一个名为 MyView .h/.m 的类,类型为
UIView
。 - 创建一个同名的 xib
MyView.xib
。 - 现在在 xib 中将File Owner 类更改为
UIViewController
fromNSObject
。见下图 将文件所有者视图连接到您的视图。见下图
将视图的类更改为
MyView
. 与 3 相同。- 放置控件创建IBOutlets。
Here is the code to load the View:
这是加载视图的代码:
UIViewController *controller=[[UIViewController alloc] initWithNibName:@"MyView" bundle:nil];
MyView* view=(MyView*)controller.view;
[self.view addSubview:myview];
Hope it helps.
希望能帮助到你。
Clarification:
澄清:
UIViewController
is used to load your xib and the View which the UIViewController
has is actually MyView
which you have assigned in the MyView xib..
UIViewController
用于加载您的 xib 和视图,该视图UIViewController
实际上MyView
是您在 MyView xib 中分配的。
DemoI have made a demo grab here
演示我在这里做了一个演示抓取
回答by Adam Waite
Answering my own question about 2 or something years later here but...
在这里回答我自己关于 2 年或几年后的问题,但是......
It uses a protocol extension so you can do it without any extra code for all classes.
它使用协议扩展,因此您无需为所有类添加任何额外代码即可完成此操作。
/*
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()
回答by vkalit
In Swift:
在斯威夫特:
For example, name of your custom class is InfoView
例如,您的自定义类的名称是 InfoView
At first, you create files InfoView.xib
and InfoView.swift
like this:
首先,您创建文件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
}
Then set File's Owner
to UIViewController
like this:
然后设置File's Owner
成UIViewController
这样:
Rename your View
to InfoView
:
将您的重命名View
为InfoView
:
Right-click to File's Owner
and connect your view
field with your InfoView
:
右键单击File's Owner
并将您的view
字段与您的InfoView
:
Make sure that class name is InfoView
:
确保类名是InfoView
:
And after this you can add the action to button in your custom class without any problem:
在此之后,您可以毫无问题地将操作添加到自定义类中的按钮:
And usage of this custom class in your MainViewController
:
并在您的 中使用此自定义类MainViewController
:
func someMethod() {
var v = InfoView.instanceFromNib()
v.frame = self.view.bounds
self.view.addSubview(v)
}
回答by Rakesh
Well you could either initialize the xib using a view controller and use viewController.view. or do it the way you did it. Only making a UIView
subclass as the controller for UIView
is a bad idea.
好吧,您可以使用视图控制器初始化 xib 并使用 viewController.view。或者按照你的方式去做。只将UIView
子类作为控制器UIView
是一个坏主意。
If you don't have any outlets from your custom view then you can directly use a UIViewController
class to initialize it.
如果您的自定义视图中没有任何插座,那么您可以直接使用一个UIViewController
类来初始化它。
Update:In your case:
更新:在你的情况下:
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
Otherwise you have to make a custom UIViewController
(to make it as the file's owner so that the outlets are properly wired up).
否则,您必须进行自定义UIViewController
(使其成为文件的所有者,以便正确连接插座)。
YourCustomController *yCustCon = [[YourCustomController alloc] initWithNibName:@"YourXibName"].
Wherever you want to add the view you can use.
无论您想在何处添加可以使用的视图。
[parentView addSubview:yCustCon.view];
However passing the another view controller(already being used for another view) as the owner while loading the xib is not a good idea as the view property of the controller will be changed and when you want to access the original view, you won't have a reference to it.
但是,在加载 xib 时将另一个视图控制器(已用于另一个视图)作为所有者传递不是一个好主意,因为控制器的视图属性将被更改,并且当您想要访问原始视图时,您将不会有一个参考。
EDIT:You will face this problem if you have setup your new xib with file's owner as the same main UIViewController
class and tied the view property to the new xib view.
编辑:如果您将文件所有者设置为相同的主UIViewController
类并将视图属性绑定到新的 xib 视图,您将面临此问题。
i.e;
IE;
- YourMainViewController -- manages -- mainView
- CustomView -- needs to load from xib as and when required.
- YourMainViewController -- 管理 -- mainView
- CustomView——需要在需要时从xib加载。
The below code will cause confusion later on, if you write it inside view did load of YourMainViewController
. That is because self.view
from this point on will refer to your customview
下面的代码稍后会引起混淆,如果你把它写在 view did load of YourMainViewController
. 那是因为self.view
从这一点开始将引用您的自定义视图
-(void)viewDidLoad:(){
UIView *childView= [[[NSBundle mainBundle] loadNibNamed:@"YourXibName" owner:self options:nil] objectAtIndex:0];
}