99

我知道以前有人问过这个问题,但答案是矛盾的,我很困惑,所以请不要喷我。

我想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 初始化方法中设置笔尖吗?或者我做的还好吗?

4

6 回答 6

132
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,您将单击 File Owner 并将文件所有者设置为 YourClass。然后实际上在 YourClass.h 中,您将拥有 IBOutlet 属性。在TopArea.xib中,您可以将控件拖动到这些插座。

不要忘记,在 中TopArea.xib,您可能必须单击视图本身并将其拖到某个插座,以便在必要时控制它。(一个非常有价值的提示是,当您对表格单元格行执行此操作时,您绝对必须这样做 - 您必须将视图本身连接到代码中的相关属性。)

于 2013-03-14T09:56:14.060 回答
39

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

  • 将该File's Owner字段留空。
  • 单击xib文件中的实际视图CustomView并将其设置Custom ClassCustomView(您的自定义视图类的名称)
  • 添加自定义视图IBOutlet.h文件。
  • .xib您的自定义视图文件中,单击视图并进入Connection Inspector。在这里,您将在.h文件中定义所有 IBOutlets
  • 将它们与各自的视图联系起来。

.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;
}

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

于 2015-03-14T09:29:56.133 回答
25

请按照以下步骤操作

  1. 创建一个名为 MyView .h/.m 的类UIView
  2. 创建一个同名的xib MyView.xib
  3. 现在将 File Owner 类更改为UIViewControllerxibNSObject中的 from。见下图 在此处输入图像描述
  4. 将文件所有者视图连接到您的视图。见下图 在此处输入图像描述

  5. 将视图的类更改为MyView. 与 3 相同。

  6. 放置控件创建 IBOutlets。

这是加载视图的代码:

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

希望能帮助到你。

澄清

UIViewController用于加载您的 xib 以及您在 MyView xib 中分配的UIViewController实际视图。MyView

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

于 2013-03-14T10:02:56.690 回答
11

在这里回答我自己关于 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()
于 2016-01-08T09:28:18.923 回答
8

在斯威夫特:

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

首先,您创建文件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这样:

在此处输入图像描述

将您的重命名ViewInfoView

在此处输入图像描述

右键单击File's Owner并将您的view字段与您的InfoView

在此处输入图像描述

确保类名是InfoView

在此处输入图像描述

在此之后,您可以毫无问题地将操作添加到自定义类中的按钮:

在此处输入图像描述

并在您的MainViewController

func someMethod() {
    var v = InfoView.instanceFromNib()
    v.frame = self.view.bounds
    self.view.addSubview(v)
}
于 2016-10-10T22:32:57.123 回答
2

好吧,您可以使用视图控制器初始化 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 加载。

下面的代码稍后会引起混淆,如果你把它写在 view did load of YourMainViewController. 那是因为self.view从这一点开始将引用您的自定义视图

-(void)viewDidLoad:(){
  UIView *childView= [[[NSBundle mainBundle] loadNibNamed:@"YourXibName" owner:self options:nil] objectAtIndex:0];
}
于 2013-03-14T10:50:59.760 回答