如何使用通过Interface Builder创建的nib文件加载UIView


241

我正在尝试做一些复杂的事情,但是应该可以做。因此,这对所有您的专家都是一个挑战(本论坛包括很多人:))。

我正在创建一个问卷调查“组件”,我想将其加载到NavigationContoller(my QuestionManagerViewController)上。“组件”是一个“空”组件UIViewController,可以根据需要回答的问题加载不同的视图。

我这样做的方式是:

  1. 创建Question1View对象作为UIView子类,并定义some IBOutlets
  2. 创建(使用Interface Builder)Question1View.xib (“我的问题可能在哪里”)。我将UIViewController和都设置UIView为Question1View类。
  3. 我将插座与视图的组件链接(使用IB)。
  4. 我覆盖了initWithNib我的QuestionManagerViewController,看起来像这样:

    - (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:]加载了“ Question1View”笔尖,但未设置视图出口。

我敢肯定有一种方法可以使用nib文件加载视图,而无需创建viewController类。

Answers:


224

还有一种更简单的方法来访问视图,而不是将笔尖作为数组处理。

1)创建一个自定义的View子类,其中包含您以后想要访问的所有插座。 - 我的看法

2)在要加载和处理笔尖的UIViewController中,创建一个IBOutlet属性,该属性将保存已加载的笔尖的视图,例如

在MyViewController中(UIViewController子类)

  @property (nonatomic, retain) IBOutlet UIView *myViewFromNib;

(不要忘记合成它并在您的.m文件中释放它)

3)在IB中打开笔尖(我们将其称为“ myViewNib.xib”),将文件的所有者设置为MyViewController

4)现在将文件的所有者插座myViewFromNib连接到笔尖的主视图。

5)现在在MyViewController中,编写以下行:

[[NSBundle mainBundle] loadNibNamed:@"myViewNib" owner:self options:nil];

现在,只要执行此操作,将属性命名为“ self.myViewFromNib”,即可从笔尖访问视图!


4
它适用于主控制器视图(self.view)吗?由于某些原因,我需要在标准控制器初始化方法之后从nib加载主视图。原因-复杂的子激光结构
Lukasz 2010年

@Lukasz您成功做到了多少?甚至我也在寻找像您一样的东西。
2012年

抱歉,我可能很胖,但是我一开始很困惑。如何在自定义视图子类中创建出口?我以为插座仅用于UIViewControllers?
jowie

1
如果该视图出现在多个父视图上,该怎么办?那么,您是否建议手动编辑每个xib?太糟糕了!!!
user2083364 2014年

2
仅当您要在单个视图控制器中使用自定义笔尖时,此方法才有效。如果要在不同的视图控制器上使用它,则每个视图控制器都动态创建自定义笔尖的实例,这将无法正常工作。
thgc 2014年

120

谢谢你们。我确实找到了一种实现自己想要的方式的方法。

  1. UIView使用所需的IBOutlets 创建您的。
  2. 在IB中创建xib,按自己的喜好进行设计并按以下方式链接:文件的所有者是类UIViewController(没有自定义子类,但是“真实的”子类)。文件所有者的视图连接到主视图,并且其类声明为步骤1)中的类。
  3. 将您的控件与IBOutlets 连接。
  4. DynamicViewController可以运行它的逻辑来决定哪些查看/厦门国际银行加载。一旦决定,就在loadView方法中输入如下内容:

    NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:@"QPickOneView"
                                                      owner:self
                                                    options:nil];
    
    QPickOneView* myView = [ nibViews objectAtIndex: 1];
    
    myView.question = question;

而已!

主包的loadNibNamed方法将负责初始化视图并创建连接。

现在,ViewController可以显示一个视图或另一个视图,具体取决于内存中的数据,并且“父”屏幕无需理会此逻辑。


2
我有一个非常相似的问题-我只想从xib文件加载UIView。这些步骤对我不起作用-将加载的视图添加为子视图后,该应用因“访问错误”而崩溃。
Justicle

2
对于iPhone 3.0,请使用objectAtIndex:0来获取第一个元素。完全按照Justicle的描述对我造成了崩溃。知道为什么吗?
tba

1
+1对我来说效果很好。我想添加具有一个视图控制器的多个视图(即使用一部分视图旋转的翻转视图方案),我必须在数组0(iPhone 3.0)上建立索引
Aran Mulholland 2010年

42
这是正确的答案,但是,应该修改代码,使其遍历nibViews并对每个对象进行类检查,这样您肯定会获得正确的对象。objectAtIndex:1是一个危险的假设。
M. Ryan

2
Jasconius是正确的。您应该像这样遍历
leviathan

71

我不确定要说些什么答案,但是下次我在Google中搜索时,需要在此处输入答案。关键字:“如何从笔尖加载UIView”或“如何从NSBundle加载UIView”。

这是Apress Beginning iPhone 3本书(第247页,“使用新表视图单元格”)中几乎100%的代码:

- (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,一个笔尖称为Blah,其中包含一个UIView,其类设置为Blah


类别:NSObject + LoadFromNib

#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")
    }
}

2
如果您使用,则isKindOf可以防止捆绑结构更改。
Dan Rosenstark 2010年

1
一个assert可能是一个更好的解决方案。这样,您只会隐藏问题。
Sulthan)

1
@Sulthan断言本质上是不同的。我想要Blah类型的物体位于笔尖的任何地方。我不在乎它是否被提升或降级。但是,为回应您的评论,我添加了一个断言,但是它不能代替for循环。它补充了它。
Dan Rosenstark 2012年

您可以不使用任何参数来执行loadFromNib:+(id)loadFromNib {NSArray * bundle = [[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class])owner:self options:nil]; for(捆绑中的id对象){if([[object isKindOfClass:[self class]]]){返回对象;返回nil; }
JVC

22

对于所有需要管理一个以上自定义视图实例(即Outlet Collection)的人,我以这种方式合并并自定义了@ Gonso,@ AVeryDev和@Olie答案:

  1. 创建一个自定义MyView : UIView并将其设置为所需XIB中根的“ 自定义类 ” ; UIView习俗课

  2. 创建您需要的所有出口MyView(现在执行此操作,因为IB将建议您在第3点之后将出口连接到UIViewController而不是根据需要连接到自定义视图); 定制类插座

  3. 将您设置UIViewController为自定义视图XIB的文件所有者 ” ; 在此处输入图片说明

  4. 在所需的每个实例中UIViewController添加一个新UIViews实例MyView,并将它们连接到以UIViewController创建Outlet Collection:这些视图将充当自定义视图实例的“包装”视图; 在此处输入图片说明

  5. 最后,在的viewDidLoadUIViewController添加以下行:

NSArray *bundleObjects;
MyView *currView;
NSMutableArray *myViews = [NSMutableArray arrayWithCapacity:myWrapperViews.count];
for (UIView *currWrapperView in myWrapperViews) {
    bundleObjects = [[NSBundle mainBundle] loadNibNamed:@"MyView" owner:self options:nil];
    for (id object in bundleObjects) {
        if ([object isKindOfClass:[MyView class]]){
            currView = (MyView *)object;
            break;
        }
    }

    [currView.myLabel setText:@"myText"];
    [currView.myButton setTitle:@"myTitle" forState:UIControlStateNormal];
    //...

    [currWrapperView addSubview:currView];
    [myViews addObject:currView];
}
//self.myViews = myViews; if need to access them later..

嗨...你知道如何以编程的方式做到这一点,即。根本不使用xib文件吗?
X-Coder

19

我将使用UINib实例化要重用的自定义UIView

UINib *customNib = [UINib nibWithNibName:@"MyCustomView" bundle:nil];
MyCustomViewClass *customView = [[customNib instantiateWithOwner:self options:nil] objectAtIndex:0];
[self.view addSubview:customView];

在这种情况下,需要的文件是MyCustomView.xibMyCustomViewClass.hMyCustomViewClass.m 注意,这[UINib instantiateWithOwner]将返回一个数组,因此您应该使用反映要重用的UIView的元素。在这种情况下,它是第一个元素。


1
您不是说第2行中的“ customNib”而不是“ rankingNib”吗
Danny

11

您不应将视图控制器的类设置UIView为Interface Builder中的子类。那绝对是您问题的至少一部分。将其保留为UIViewController,它的某些子类或您拥有的其他自定义类。

至于仅从xib加载视图,我假设您必须将某种类型的视图控制器(即使它不扩展UIViewController,对于您的需求而言可能太重了)设置为Interface中的File Owner。如果要使用Builder来定义界面。我也做了一些研究以确认这一点。这是因为否则将无法访问中的任何接口元素UIView,也将无法通过事件来触发代码中具有您自己的方法。

如果您将a UIViewController用作视图的文件所有者,则可以仅使用initWithNibName:bundle:加载它并获取视图控制器对象。在IB中,请确保view使用xib中的界面将插座设置为视图。如果您使用其他类型的对象作为文件的所有者,则需要使用NSBundleloadNibNamed:owner:options:方法来加载笔尖,并将文件所有者的实例传递给该方法。将根据您在IB中定义的出口正确设置其所有属性。


我正在阅读Apress的《 iPhone开发开始:探索iPhone SDK》一书,他们在第9章:导航控制器和表视图中讨论了这种方法。它似乎并不复杂。
Lyndsey Ferguson,2009年

我很想知道他们怎么说。我目前没有那本书的副本。
Marc W

10

您也可以使用UIViewController的initWithNibName代替loadNibNamed。我发现这很简单。

UIViewController *aViewController = [[UIViewController alloc] initWithNibName:@"MySubView" bundle:nil];
[self.subview addSubview:aViewController.view];
[aViewController release];  // release the VC

现在,您只需要创建MySubView.xib和MySubView.h / m。在MySubView.xib中,将File的Owner类设置为UIViewController,将view类设置为MySubView。

您可以subview使用父xib文件定位和确定大小。


8

这是一个很棒的问题(+1),答案几乎是有用的;)抱歉,伙计们,但我有点时间不解,尽管Gonso和AVeryDev都给出了很好的提示。希望这个答案对其他人有帮助。

MyVC 是持有所有这些东西的视图控制器。

MySubview 是我们要从xib加载的视图

  • 在MyVC.xib中,创建一种类型的视图,该视图的MySubView大小和形状正确,并位于所需位置。
  • 在MyVC.h中,

    IBOutlet MySubview *mySubView
    // ...
    @property (nonatomic, retain) MySubview *mySubview;
  • 在MyVC.m中,@synthesize mySubView;不要忘记在中释放它dealloc

  • 在MySubview.h中,有一个出口/属性UIView *view(可能是不必要的,但对我有用)。合成并在.m中释放它
  • 在MySubview.xib中
    • 将文件所有者类型设置为MySubview,然后将view属性链接到您的视图。
    • 布置所有位并IBOutlet根据需要连接到
  • 回到MyVC.m,有

    NSArray *xibviews = [[NSBundle mainBundle] loadNibNamed: @"MySubview" owner: mySubview options: NULL];
    MySubview *msView = [xibviews objectAtIndex: 0];
    msView.frame = mySubview.frame;
    UIView *oldView = mySubview;
    // Too simple: [self.view insertSubview: msView aboveSubview: mySubview];
    [[mySubview superview] insertSubview: msView aboveSubview: mySubview]; // allows nesting
    self.mySubview = msView;
    [oldCBView removeFromSuperview];

对我而言,棘手的一点是:其他答案中的提示从xib加载了我的视图,但并没有取代MyVC中的视图(du!)-我不得不自己替换掉。

另外,要访问mySubview的方法,view.xib文件中的属性必须设置为MySubview。否则,它将以普通格式返回UIView

如果有一种方法可以mySubview直接从自己的xib加载,那就太麻烦了,但这使我到达了我需要的位置。


1
我使用了这种方法,谢谢。一个改变我制成,以允许它支持嵌套视图是改变[ self.view insertSubview:msView aboveSubview:mySubview]; 到[ mySubview.superview insertSubview:msView aboveSubview:mySubview];
杰森

8

这应该更容易些。我最终扩展UIViewController并添加了一个loadNib:inPlaceholder:选择器。现在我可以说

self.mySubview = (MyView *)[self loadNib:@"MyView" inPlaceholder:mySubview];

这是类别的代码(它执行与Gonso所述相同的rigamarole):

@interface UIViewController (nibSubviews)

- (UIView *)viewFromNib:(NSString *)nibName;
- (UIView *)loadNib:(NSString *)nibName inPlaceholder:(UIView *)placeholder;

@end

@implementation UIViewController (nibSubviews)

- (UIView *)viewFromNib:(NSString *)nibName
{
  NSArray *xib = [[NSBundle mainBundle] loadNibNamed:nibName owner:self options:nil]; 
  for (id view in xib) { // have to iterate; index varies
    if ([view isKindOfClass:[UIView class]]) return view;
  }
  return nil;
}

- (UIView *)loadNib:(NSString *)nibName inPlaceholder:(UIView *)placeholder
{
  UIView *nibView = [self viewFromNib:nibName];
  [nibView setFrame:placeholder.frame];
  [self.view insertSubview:nibView aboveSubview:placeholder];
  [placeholder removeFromSuperview];
  return nibView;
}

@end

7

迅速

实际上,我对这个问题的解决方案是,将视图加载到我想使用该视图的CustonViewController中的viewDidLoad中:

myAccessoryView = NSBundle.mainBundle().loadNibNamed("MyAccessoryView", owner: self, options: nil)[0] as! MyAccessoryView

不要在loadView()方法中加载视图!loadView方法用于为自定义ViewController加载视图。


6

我也想做类似的事情,这就是我发现的:(SDK 3.1.3)

我有一个视图控制器A(它自己是Nav控制器所拥有的),它在按下按钮时加载VC B:

在AViewController.m中

BViewController *bController = [[BViewController alloc] initWithNibName:@"Bnib" bundle:nil];
[self.navigationController pushViewController:bController animated:YES];
[bController release];

现在VC B具有Bnib的界面,但是当按下按钮时,我想进入“编辑模式”,该UI具有与其他笔尖不同的UI,但是我不想为编辑模式使用新的VC,我希望新笔尖与我现有的B VC相关联。

因此,在BViewController.m中(在按钮按下方法中)

NSArray *nibObjects = [[NSBundle mainBundle] loadNibNamed:@"EditMode" owner:self options:nil];
UIView *theEditView = [nibObjects objectAtIndex:0];
self.editView = theEditView;
[self.view addSubview:theEditView];

然后在另一个按钮上按(退出编辑模式):

[editView removeFromSuperview];

我又回到了最初的Bnib。

这可以正常工作,但是请注意我的EditMode.nib中只有1个顶级obj,即UIView obj。将此笔尖中的文件所有者设置为BViewController还是默认的NSObject都没有关系,但请确保文件所有者中的“视图出口”未设置为任何内容。如果是这样,那么我会遇到一个exc_bad_access崩溃,并且xcode继续加载6677个堆栈帧,其中显示了一个内部UIView方法被重复调用...因此看起来像一个无限循环。(但是,View Outlet是在我最初的Bnib中设置的)

希望这可以帮助。


仔细阅读这句话,对我也有帮助。
petert 2010年

根据链接上的信息,您不应以这种方式操作视图控制器。
T. Markle

6

我做了一个我喜欢的类别:

UIView+NibInitializer.h

#import <UIKit/UIKit.h>

@interface UIView (NibInitializer)
- (instancetype)initWithNibNamed:(NSString *)nibNameOrNil;
@end

UIView+NibInitializer.m

#import "UIView+NibInitializer.h"

@implementation UIView (NibInitializer)

- (instancetype)initWithNibNamed:(NSString *)nibNameOrNil
{
    if (!nibNameOrNil) {
        nibNameOrNil = NSStringFromClass([self class]);
    }
    NSArray *viewsInNib = [[NSBundle mainBundle] loadNibNamed:nibNameOrNil
                                                        owner:self
                                                      options:nil];
    for (id view in viewsInNib) {
        if ([view isKindOfClass:[self class]]) {
            self = view;
            break;
        }
    }
    return self;
}

@end

然后,像这样调用:

MyCustomView *myCustomView = [[MyCustomView alloc] initWithNibNamed:nil];

如果笔尖的名称不是班级名称,则使用笔尖名称。

要在您的子类中覆盖它以获取其他行为,它可能看起来像这样:

- (instancetype)initWithNibNamed:(NSString *)nibNameOrNil
{
    self = [super initWithNibNamed:nibNameOrNil];
    if (self) {
        self.layer.cornerRadius = CGRectGetHeight(self.bounds) / 2.0;
    }
    return self;
}

5

对于具有可选项的Swift用户:

  1. 创建一个自定义UIView子类和一个xib文件,我们将以自己的类名命名:在我们的情况下为MemeView。在Meme View类中,请记住@IBDesignable在类声明之前使用属性将其定义为可设计的
  2. 通过UIViewIndetity Inspector面板中的自定义子类,Rember可以在xib中设置文件的所有者

    在此处输入图片说明

  3. 现在,在xib文件中,我们可以构建界面,进行约束,创建出口,执行操作等。 在此处输入图片说明

  4. 我们需要为自定义类实现一些方法以在初始化后打开xib

    class XibbedView: UIView {

    weak var nibView: UIView!
    
    override convenience init(frame: CGRect) {
        let nibName = NSStringFromClass(self.dynamicType).componentsSeparatedByString(".").last!
        self.init(nibName: nibName)
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        let nibName = NSStringFromClass(self.dynamicType).componentsSeparatedByString(".").last!
        let nib = loadNib(nibName)
        nib.frame = bounds
        nib.translatesAutoresizingMaskIntoConstraints = false
        addSubview(nib)
        nibView = nib
        setUpConstraints()
    }
    
    init(nibName: String) {
        super.init(frame: CGRectZero)
        let nibName = NSStringFromClass(self.dynamicType).componentsSeparatedByString(".").last!
        let nib = loadNib(nibName)
        nib.frame = bounds
        nib.translatesAutoresizingMaskIntoConstraints = false
        addSubview(nib)
        nibView = nib
        setUpConstraints()
    }
    
    func setUpConstraints() {
        ["V","H"].forEach { (quote) -> () in
            let format = String(format:"\(quote):|[nibView]|")
            addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(format, options: [], metrics: nil, views: ["nibView" : nibView]))
        }
    }
    
    func loadNib(name: String) -> UIView {
        let bundle = NSBundle(forClass: self.dynamicType)
        let nib = UINib(nibName: name, bundle: bundle)
        let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
    
        return view
    }

    }

  5. 在我们的自定义类中,我们还可以定义一些无法检查的属性,以从界面生成器完全控制它们

    @IBDesignable class MemeView: XibbedView {

    @IBInspectable var memeImage: UIImage = UIImage() {
        didSet {
            imageView.image = memeImage
        }
    }
    @IBInspectable var textColor: UIColor = UIColor.whiteColor() {
        didSet {
            label.textColor = textColor
        }
    }
    @IBInspectable var text: String = "" {
        didSet {
            label.text = text
        }
    }
    @IBInspectable var roundedCorners: Bool = false {
        didSet {
            if roundedCorners {
                layer.cornerRadius = 20.0
                clipsToBounds = true
            }
            else {
                layer.cornerRadius = 0.0
                clipsToBounds = false
            }
        }
    }
    
    @IBOutlet weak var label: UILabel!
    @IBOutlet weak var imageView: UIImageView!

}

几个例子:
在此处输入图片说明 在此处输入图片说明

如果我们需要添加更多信息,而视图在情节提要或另一个xib中显示,则可以执行prepareForInterfaceBuilder()此方法,只有在界面构建器中打开文件时才会执行此方法。如果您做了我写的所有事情,但没有任何效果,那么这是通过在实现中添加断点来调试sigle视图的一种方法。 这是视图层次结构。 在此处输入图片说明
查看hiearchy

希望这有助于完整的示例,可以在这里下载


全文可以从我的博客cloudintouch.it/2016/04/21/designable-xib-in-xcode-hell-yeah中看到,但我已经发布了相关部分。
Andrea

let nib = loadNib(nibName)这是一个XibbedView实例,如果调用```addSubview(nib)`,那么您要将一个XibbedView实例添加到另一个XibbedView实例上吗?
胡锦涛

我同意这一点。当您尝试从“调试视图层次结构”中查看它时,XibbedView似乎包含一个XibbedView。你可以看到。
胡锦涛

@WilliamHu如果您下载提供的示例,则可以看到MemeView.xib只是一个带有一堆子视图的UIView实例,文件持有者是一个MemeView,它是XibbedView的子类。因此,XibbedView的唯一实例就是MemeView,它加载了一个xib文件,而主视图只是一个视图
Andrea

哦,那没关系。我会测试一下。谢谢!
胡锦涛

4

我发现Aaron Hillegass(作者,讲师,可可忍者)的这篇博文很有启发性。即使您不采用他的修改方法通过指定的初始化程序加载NIB文件,您也可能至少会更好地了解正在进行的过程。我最近一直在使用这种方法取得巨大成功!


链接已死。我认为它现在位于bignerdranch.com/blog/...
joelliusp

3

先前的答案没有考虑到iPhone SDK的2.0和2.1之间发生的NIB(XIB)结构更改。用户内容现在从索引0而不是1开始。

您可以使用对所有2.1版及更高版本均有效的2.1宏(即IPHONE前的两个下划线:

 // Cited from previous example
 NSArray* nibViews =  [[NSBundle mainBundle] loadNibNamed:@"QPickOneView" owner:self options:nil];
 int startIndex;
 #ifdef __IPHONE_2_1
 startIndex = 0;
 #else
 startIndex = 1;
 #endif
 QPickOneView* myView = [ nibViews objectAtIndex: startIndex];
 myView.question = question;

对于大多数应用程序,我们都使用与此类似的技术。

巴尼


2
Barney:“上一个答案”在Stack Overflow上毫无意义,因为答案会根据投票而改变位置。最好参考“ Fred的答案”或“ Barney的答案”或“ olie的答案”。
奥利(Olie)2012年

3

我有理由做同样的事情(以编程方式从XIB文件加载视图),但是我需要完全从a的子类的子类的上下文中进行此操作UIView(即,不以任何方式涉及视图控制器)。为此,我创建了此实用程序方法:

+ (id) initWithNibName:(NSString *)nibName withSelf:(id)myself {

    NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:nibName
                                                    owner:myself options:nil];
    for (id object in bundle) {
        if ([object isKindOfClass:[myself class]]) {
            return object;
        }
    }  

    return nil;
} 

然后我从子类的initWithFrame方法中调用它,如下所示:

- (id)initWithFrame:(CGRect)frame {

    self = [Utilities initWithNibName:@"XIB1" withSelf:self];
    if (self) {
        // Initialization code.
    }
    return self;
}

出于一般兴趣发布;如果有人不这样做就发现任何问题,请告诉我。


我已经实现了您的代码,但是它在init上一直超出范围。我在实用程序类中创建了静态方法。我制作了名为MyView的h / m文件(从UIView继承而来)和名为xib的xib。在IB中,我将xib文件的所有者和视图设置为“ MyView”。在调试器中,它超出范围。我在IB中缺少什么吗?
TigerCoding '02

请看看我的新SO问题在这里:stackoverflow.com/questions/9224857/...
TigerCoding

self = [实用程序initWithNibName:@“ XIB1” withSelf:self];您尚未通过自我设定。该代码是否与此相同:self = [Utilities initWithNibName:@“ XIB1” withSelf:nil]; ?
Ken Van Hoeylandt'2

@Javy:这里的代码示例可能不适用于ARC-我尚未在启用ARC的情况下对其进行测试。
MusiGenesis'2

3

这是在Swift中执行此操作的一种方法(当前在XCode 7 beta 5中编写Swift 2.0)。

UIView在Interface Builder中设置为“自定义类” 的子类中,创建一个这样的方法(我的子类称为RecordingFooterView):

class func loadFromNib() -> RecordingFooterView? {
    let nib = UINib(nibName: "RecordingFooterView", bundle: nil)
    let nibObjects = nib.instantiateWithOwner(nil, options: nil)
    if nibObjects.count > 0 {
        let topObject = nibObjects[0]
        return topObject as? RecordingFooterView
    }
    return nil
}

然后,您可以这样称呼它:

let recordingFooterView = RecordingFooterView.loadFromNib()


2

没有任何答案可以解释如何创建独立的XIB,这是此问题的根源。没有Xcode 4选项“创建新的XIB文件”。

去做这个

1) Choose "New File..."
2) Choose the "User Interface" category under the iOS section
3) Choose the "View" item
4) You will then be prompted to choose an iPhone or iPad format

这似乎很简单,但是可以节省几分钟的时间,因为“ XIB”一词在任何地方都不会出现。


1

@AVeryDev

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

[self.view addSubview:myViewFromNib];

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

需要说明的是:视图控制器具有多个IBOutlet,其中一些连接到原始nib文件中的项目(通常),而另一些连接到已加载的nib中的项目。两个笔尖具有相同的所有者类。加载的视图将覆盖原始视图。

提示:将加载的笔尖中主视图的不透明度设置为零,这样就不会遮挡原始笔尖中的项目。


需要注意的一件好事,但是将视图添加为子视图会自动将其从其前一个父视图中删除,因此它不应泄漏。
averydev

1

花了几个小时后,我提出了以下解决方案。请按照以下步骤创建自定义UIView

1)创建从UIView继承的myCustomView类。
在此处输入图片说明

2)创建的.xib与名myCustomView
在此处输入图片说明

3)在.xib文件中更改UIView的类,在其中分配myCustomView类。
在此处输入图片说明

4)创建IBOutlets
在此处输入图片说明

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

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

注意:对于仍然遇到问题的人可以发表评论,我将为他们提供带有myCustomView的示例项目链接



0

我有一个命名Xib的约定,其中的视图与视图相同。与视图控制器相同。然后,我不必在代码中写出类名。我从同名的nib文件加载UIView。

名为MyView的类的示例。

  • 在Interface Builder中创建一个名为MyView.xib的nib文件
  • 在“界面生成器”中,添加一个UIView。将其类设置为MyView。自定义您的心脏内容,将MyView的实例变量连接到您以后可能要访问的子视图。
  • 在您的代码中,创建一个新的MyView,如下所示:

    MyView * myView = [MyView nib_viewFromNibWithOwner:owner];

这是此类别:

@implementation UIView (nib)

+ (id) nib_viewFromNib {
    return [self nib_viewFromNibWithOwner:nil];
}

+ (id) nib_viewFromNibWithOwner:(id)owner {
    NSString *className = NSStringFromClass([self class]);
    NSArray *nib = [[NSBundle mainBundle] loadNibNamed:className owner:owner options:nil];
    UIView *view = nil;
    for(UIView *v in nib) {
        if ([v isKindOfClass:[self class]]) {
            view = v;
            break;
        }
    }
    assert(view != nil && "View for class not found in nib file");
    [view nib_viewDidLoad];
    return view;
}

// override this to do custom setup
-(void)nib_viewDidLoad {

}

然后,我将按钮与正在使用的控制器中的动作连接起来,并使用自定义视图子类中的插座在标签上进行设置。


0

要在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
}

-1

我最终为此添加了一个类别到UIView:

 #import "UIViewNibLoading.h"

 @implementation UIView (UIViewNibLoading)

 + (id) loadNibNamed:(NSString *) nibName {
    return [UIView loadNibNamed:nibName fromBundle:[NSBundle mainBundle] retainingObjectWithTag:1];
 }

 + (id) loadNibNamed:(NSString *) nibName fromBundle:(NSBundle *) bundle retainingObjectWithTag:(NSUInteger) tag {
    NSArray * nib = [bundle loadNibNamed:nibName owner:nil options:nil];
    if(!nib) return nil;
    UIView * target = nil;
    for(UIView * view in nib) {
        if(view.tag == tag) {
            target = [view retain];
            break;
        }
    }
    if(target && [target respondsToSelector:@selector(viewDidLoad)]) {
        [target performSelector:@selector(viewDidLoad)];
    }
    return [target autorelease];
 }

 @end

这里的解释:viewcontroller减少了ios&mac中的视图加载


嗯 标签是什么?
2013年

知道要保留哪个顶级对象。当时有必要,但是您现在可以取出保留件以符合ARC。
gngrwzrd 2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.