我试着做一些复杂的事情,但应该是可能的。所以这里有一个对你们所有专家的挑战(这个论坛是由你们很多人组成的:))。

我正在创建一个问卷“组件”,我想加载在一个navigationcontroller(我的QuestionManagerViewController)。“组件”是一个“空的”UIViewController,它可以根据需要回答的问题加载不同的视图。

我的做法是:

Create Question1View object as a UIView subclass, defining some IBOutlets. Create (using Interface Builder) the Question1View.xib (HERE IS WHERE MY PROBLEM PROBABLY IS). I set both the UIViewController and the UIView to be of class Question1View. I link the outlets with the view's component (using IB). I override the initWithNib of my QuestionManagerViewController to look like this: - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { if (self = [super initWithNibName:@"Question1View" bundle:nibBundleOrNil]) { // Custom initialization } return self; }

当我运行代码时,我得到这个错误:

2009-05-14 15:05:37.152 iMobiDines[17148:20b] ***由于未捕获异常'NSInternalInconsistencyException'而终止应用程序,原因:'-[UIViewController _loadViewFromNibNamed:bundle:] loaded the "Question1View" nib but the view outlet was not set.'

我确信有一种方法可以使用nib文件加载视图,而不需要创建一个viewController类。


当前回答

花了好几个小时后,我想出了以下解决办法。 按照以下步骤创建自定义UIView。

1)创建类myCustomView继承自UIView。

2)创建名称为myCustomView的.xib。

3)在你的.xib文件中改变UIView的类,在那里分配myCustomView类。

4)创建iboutlet

5)在myCustomView * customView中加载.xib。 使用下面的示例代码。

myCustomView * myView = [[[NSBundle mainBundle] loadNibNamed:@"myCustomView" owner:self options:nil] objectAtIndex:0];
[myView.description setText:@"description"];

注:对于那些仍然面临问题的人可以评论,我将提供他们的链接 示例项目的myCustomView

其他回答

你不应该在Interface Builder中将视图控制器的类设置为UIView的子类。这肯定是你的部分问题。保留它为UIViewController,它的子类,或者其他自定义类。

As for loading only a view from a xib, I was under the assumption that you had to have some sort of view controller (even if it doesn't extend UIViewController, which may be too heavyweight for your needs) set as the File's Owner in Interface Builder if you want to use it to define your interface. I did a little research to confirm this as well. This is because otherwise there would be no way to access any of the interface elements in the UIView, nor would there be a way to have your own methods in code be triggered by events.

If you use a UIViewController as your File's Owner for your views, you can just use initWithNibName:bundle: to load it and get the view controller object back. In IB, make sure you set the view outlet to the view with your interface in the xib. If you use some other type of object as your File's Owner, you'll need to use NSBundle's loadNibNamed:owner:options: method to load the nib, passing an instance of File's Owner to the method. All its properties will be set properly according to the outlets you define in IB.

在Swift 4中以编程方式从nib/xib加载视图:

// Load a view from a Nib given a placeholder view subclass
//      Placeholder is an instance of the view to load.  Placeholder is discarded.
//      If no name is provided, the Nib name is the same as the subclass type name
//
public func loadViewFromNib<T>(placeholder placeholderView: T, name givenNibName: String? = nil) -> T {

    let nib = loadNib(givenNibName, placeholder: placeholderView)
    return instantiateView(fromNib: nib, placeholder: placeholderView)
}

// Step 1: Returns a Nib
//
public func loadNib<T>(_ givenNibName: String? = nil, placeholder placeholderView: T) -> UINib {
    //1. Load and unarchive nib file
    let nibName = givenNibName ?? String(describing: type(of: placeholderView))

    let nib = UINib(nibName: nibName, bundle: Bundle.main)
    return nib
}

// Step 2: Instantiate a view given a nib
//
public func instantiateView<T>(fromNib nib: UINib, placeholder placeholderView: T) -> T {
    //1. Get top level objects
    let topLevelObjects = nib.instantiate(withOwner: placeholderView, options: nil)

    //2. Have at least one top level object
    guard let firstObject = topLevelObjects.first else {
        fatalError("\(#function): no top level objects in nib")
    }

    //3. Return instantiated view, placeholderView is not used
    let instantiatedView = firstObject as! T
    return instantiatedView
}

花了好几个小时后,我想出了以下解决办法。 按照以下步骤创建自定义UIView。

1)创建类myCustomView继承自UIView。

2)创建名称为myCustomView的.xib。

3)在你的.xib文件中改变UIView的类,在那里分配myCustomView类。

4)创建iboutlet

5)在myCustomView * customView中加载.xib。 使用下面的示例代码。

myCustomView * myView = [[[NSBundle mainBundle] loadNibNamed:@"myCustomView" owner:self options:nil] objectAtIndex:0];
[myView.description setText:@"description"];

注:对于那些仍然面临问题的人可以评论,我将提供他们的链接 示例项目的myCustomView

@AVeryDev

6)将加载的视图附加到视图控制器的视图:

[self.view addSubview:myViewFromNib];

据推测,有必要将其从视图中删除以避免内存泄漏。

澄清一下:视图控制器有几个iboutlet,其中一些连接到原始nib文件中的项(像往常一样),一些连接到加载的nib中的项。两个nib都有相同的所有者类。加载的视图覆盖了原始视图。

提示:在加载的笔尖中设置主视图的不透明度为零,那么它将不会从原始笔尖中模糊项目。

我不确定一些答案在谈论什么,但我需要把这个答案放在这里,当我下次在谷歌中搜索时。关键词:“如何从一个nib加载一个UIView”或“如何从一个NSBundle加载一个UIView”

下面的代码几乎100%直接来自Apress Beginning iPhone 3的书籍(第247页,“使用新的表视图单元格”):

- (void)viewDidLoad {
    [super viewDidLoad];
    NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:@"Blah"
                                                 owner:self options:nil];
    Blah *blah;
    for (id object in bundle) {
        if ([object isKindOfClass:[Blah class]]) {
            blah = (Blah *)object;
            break;
        }
    }   
    assert(blah != nil && "blah can't be nil");
    [self.view addSubview: blah];
} 

这假设你有一个UIView子类叫Blah,一个nib叫Blah它包含一个UIView它的类设置为Blah。


类别:+ LoadFromNib NSObject

#import "NSObject+LoadFromNib.h"

@implementation NSObject (LoadFromNib)

+ (id)loadFromNib:(NSString *)name classToLoad:(Class)classToLoad {
    NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:name owner:self options:nil];
    for (id object in bundle) {
        if ([object isKindOfClass:classToLoad]) {
            return object;
        }
    }
    return nil;
}

@end

迅速扩展

extension UIView {
    class func loadFromNib<T>(withName nibName: String) -> T? {
        let nib  = UINib.init(nibName: nibName, bundle: nil)
        let nibObjects = nib.instantiate(withOwner: nil, options: nil)
        for object in nibObjects {
            if let result = object as? T {
                return result
            }
        }
        return nil
    }
}

还有一个正在使用的例子:

class SomeView: UIView {
    class func loadFromNib() -> SomeView? {
        return self.loadFromNib(withName: "SomeView")
    }
}