Interface Builderで作成されたnibファイルを使用してUIViewをロードする方法


241

私は少し複雑なことをしようとしていますが、可能なことはあるはずです。だから、ここにあなたのすべての専門家のための挑戦があります(このフォーラムはあなたたちの多くのパックです:))。

NavigationContoller(my QuestionManagerViewController)にロードしたいアンケート「コンポーネント」を作成しています。「コンポーネント」は「空」ですUIViewControllerであり、回答する必要がある質問に応じて異なるビューをロードできます。

私がそれをしている方法は:

  1. Question1ViewオブジェクトをUIViewサブクラスとして作成し、いくつかを定義しますIBOutlets
  2. (Interface Builderを使用して)Question1View.xib (ここに問題がある可能性があります)を作成します。私は両方のセットUIViewControllerUIViewクラスQuestion1Viewのものであると。
  3. コンセントをビューのコンポーネントにリンクします(IBを使用)。
  4. 私は次initWithNibQuestionManagerViewControllerように私のをオーバーライドします:

    - (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クラスを作成する必要なく、nibファイルを使用してビューをロードする方法があると確信しています。

回答:


224

nibを配列として扱う代わりに、ビューにアクセスする簡単な方法もあります。

1)後でアクセスしたいアウトレットがあるカスタムビューサブクラスを作成します。 - 私の見解

2)nibをロードして処理するUIViewControllerで、ロードされたnibのビューを保持するIBOutletプロパティを作成します。たとえば、

MyViewController(UIViewControllerサブクラス)

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

(合成して.mファイルで解放することを忘れないでください)

3) IBでnib( 'myViewNib.xib'と呼びます)を開き、ファイルの所有者をMyViewControllerに設定します

4)ファイルのオーナーアウトレットmyViewFromNibをnibのメインビューに接続します。

5) MyViewControllerで、次の行を記述します。

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

これを行うとすぐに、プロパティ「self.myViewFromNib」を呼び出すと、nibからビューにアクセスできるようになります。


4
メインコントローラービュー(self.view)で機能しますか?いくつかの理由で、標準のコントローラーのinitメソッドの後にnibからメインビューをロードする必要があります。理由-複雑なサブレーザー処理構造
Lukasz

@Lukaszあなたはどれだけ成功することができますか?私もあなたのようなものを探しています。
Ajay Sharma

申し訳ありませんが、私は分厚いかもしれませんが、最初の点で混乱しています。カスタムビューサブクラスでアウトレットを作成するにはどうすればよいですか?アウトレットはUIViewControllers専用であると思いましたか?
jowie

1
このビューが複数の親ビューに表示される場合はどうですか?それで、それぞれのxibを手動で編集することを提案しますか?ひどすぎる!
user2083364 2014

2
これは、単一のビューコントローラーでカスタムnibを使用する場合にのみ機能します。異なるビューコントローラーで使用する場合、それぞれが動的にカスタムnibのインスタンスを作成しますが、これは機能しません。
thgc 2014

120

皆さん、ありがとうございました。私は自分がやりたいことをする方法を見つけました。

  1. 必要なSを使用UIViewして作成しますIBOutlet
  2. IBでxibを作成し、好みに合わせてデザインし、次のようにリンクしますUIViewController。ファイル所有者のビューはメインビューに接続され、そのクラスは手順1)のビューとして宣言されます。
  3. コントロールをに接続しますIBOutlet
  4. DynamicViewController負荷へ/ XIBを見るかを決めるために、そのロジックを実行することができます。それが決定したら、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 2009年

2
iPhone 3.0の場合、objectAtIndex:0を使用して最初の要素を取得します。これはJusticleで説明されているとおりにクラッシュします。なぜだと思いますか?
tba

1
+1それは私には完璧に働きました。1つのビューコントローラーを持つ複数のビューを追加したい(ビューのセクションが回転するフリップビューシナリオを使用している)配列にインデックスを作成する必要があった(iPhone 3.0)
Aran Mulholland

42
これは正解ですが、コードを変更してnibViewsをループし、各オブジェクトのクラスチェックを実行して、確実に正しいオブジェクトを取得できるようにする必要があります。objectAtIndex:1は危険な仮定です。
M.ライアン

2
Jasconiusは正しいです。次のことを行う必要があり、このようなnibViews配列をループ:gist.github.com/769539
リヴァイアサン

71

一部の回答が何について話しているのかはわかりませんが、次回Googleで検索するときに備えて、この回答をここに入力する必要があります。キーワード:「nibから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と呼ばれるペン先があると仮定します。BlahUIViewBlah


カテゴリ: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おそらくよりよい解決策になります。この方法では、問題を隠すだけです。
スルタン

1
@Sulthanアサートは本質的に異なります。Nibのどこにでも Blahタイプのオブジェクトが欲しい。それが昇格されたり降格されたりしてもかまいません。ただし、あなたのコメントに応じて、アサートを追加しましたが、ループの代わりではありませんfor。それを補完します。
Dan Rosenstark、2012

パラメータなしでloadFromNibを実行できます。+(id)loadFromNib {NSArray * bundle = [[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class])owner:self options:nil]; for(id object in bundle){if([object isKindOfClass:[self class]]){return object; }} return nil; }
JVC

22

カスタムビューの複数のインスタンス、つまりOutlet Collectionを管理する必要があるすべての人のために、@ Gonso、@ AVeryDev、@ Olieの回答を次のようにマージしてカスタマイズしました。

  1. カスタムMyView : UIViewを作成し、目的のXIBのルートの「カスタムクラス」として設定します。 UIViewカスタムクラス

  2. で必要なすべてのアウトレットを作成しますMyView(ポイント3以降、IBがアウトレットUIViewControllerをにカスタムビューではなくに接続することを提案するため、ここで実行してください)。 カスタムクラスアウトレット

  3. をカスタムビューXIB UIViewControllerファイルの所有者」として設定します。 ここに画像の説明を入力してください

  4. UIViewController新しいを追加UIViewsのインスタンスごとにMyViewしたい、とそれらを接続するUIViewController作成アウトレットコレクション:これらのビューは、カスタムビューのインスタンスのための「ラッパー」のビューとして機能します。 ここに画像の説明を入力してください

  5. 最後に、中にviewDidLoadあなたのUIViewController次の行を追加します。

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 2013

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
「rankingNib」の代わりに2行目の「customNib」を意味するのではないですか
Danny

11

UIViewInterface Controllerでは、ビューコントローラーのクラスをのサブクラスに設定しないでください。それは間違いなくあなたの問題の少なくとも一部です。UIViewControllerそれ、そのサブクラス、または他のカスタムクラスのいずれかのままにしておきます。

xibからビューのみをロードすることについては、インターフェイスのファイルの所有者として設定された(それが拡張されない場合でも、ニーズに対して重すぎる可能性がある)ある種のビューコントローラーが必要であると想定されてましたUIViewControllerインターフェースを定義するために使用する場合はビルダー。私もこれを確認するために少し調査をしました。これは、そうしないと、のインターフェイス要素にアクセスする方法がなくUIView、コード内の独自のメソッドをイベントによってトリガーする方法もないためです。

UIViewControllerビューのファイルの所有者としてaを使用する場合は、を使用してinitWithNibName:bundle:それをロードし、View Controllerオブジェクトを取得することができます。IBでは、viewアウトレットをxibのインターフェースでビューに設定していることを確認してください。他のタイプのオブジェクトをファイルの所有者として使用する場合、NSBundleloadNibNamed:owner:options:メソッドを使用してnibをロードし、ファイルの所有者のインスタンスをメソッドに渡す必要があります。すべてのプロパティは、IBで定義したアウトレットに従って適切に設定されます。


私はApressの本「Beginning iPhone Development:Exploring the iPhone SDK」を読んでおり、彼らはこの方法について第9章:ナビゲーションコントローラーとテーブルビューで説明しました。複雑すぎないようです。
Lyndsey Ferguson、

彼らがそれがどのように行われたかを知りたいと思います。現在、その本のコピーはありません。
マルクW

10

loadNibNamedの代わりにUIViewControllerのinitWithNibNameを使用することもできます。もっと簡単だと思います。

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で、ファイルの所有者クラスをUIViewControllerに設定し、ビュークラスを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、ビュープロパティをビューにリンクします。
    • すべてのビットをレイアウトし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のビューを置き換えませんでした(そうです!)-自分でそれを交換する必要がありました。

また、mySubviewのメソッドにアクセスするにviewは、.xibファイルのプロパティをに設定する必要がありますMySubview。そうでなければ、それは普通の古いものとして戻ってきますUIView

mySubview独自のxibから直接ロードする方法がある場合、それは素晴らしいことですが、これにより、必要な場所に移動できました。


1
ありがとう、この方法を使いました。ネストされたビューをサポートできるようにするために行った変更の1つは、[ self.view insertSubview:msView aboveSubview:mySubview] を変更することでした。to [ mySubview.superview insertSubview:msView aboveSubview:mySubview];
Jason

8

これはもっと簡単なはずです。セレクターを拡張UIViewControllerして追加してしまいましたloadNib:inPlaceholder:。今私は言うことができます

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

これがカテゴリのコードです(Gonsoによって記述されたのと同じリガマロールを実行します):

@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)

ボタンを押すとVC BをロードするビューコントローラーA(それ自体はNavコントローラーが所有)があります。

AViewController.m

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

VC BにはBnibからのインターフェースがありますが、ボタンが押されたときに、別のnibとは別のUIを持つ「編集モード」に移動したいのですが、編集モード用の新しいVCは必要ありません。新しいnibを既存の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つ(UIViewオブジェクト)しかないことに注意してください。このnibのファイルの所有者がBViewControllerとして設定されているか、デフォルトのNSObjectとして設定されているかは関係ありませんが、ファイルの所有者のビューアウトレットが何にも設定されていないことを確認してください。そうである場合、exc_bad_accessのクラッシュが発生し、xcodeは、繰り返し呼び出される内部UIViewメソッドを示す6677スタックフレームの読み込みに進みます。そのため、無限ループのように見えます。(ただし、ビューアウトレットは私のオリジナルのBnibに設定されています)

お役に立てれば。


行の間を読んで、これも私を助けました。
petert 2010

linkの情報によると、この方法でView Controllerを操作しないでください
T.マークル

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];

nibにクラス名以外の名前が付けられている場合は、nib名を使用します。

追加の動作のためにサブクラスでそれをオーバーライドするには、次のようになります。

- (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パネルのカスタムサブクラスで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()、このメソッドは、インターフェイスビルダーでファイルを開いているときにのみ実行されます。私が書いたすべてを実行しても何も機能しない場合、これは実装にブレークポイントを追加することで単一ビューをデバッグする方法です。 これがビューの階層です。 ここに画像の説明を入力してください
階層を表示

これが完全なサンプルをここからダウンロードできることを願っています


完全な記事は私のブログcloudintouch.it/2016/04/21/designable-xib-in-xcode-hell-yeahから見ることができますが、関連する部分はすでに投稿しました。
Andrea

let nib = loadNib(nibName)これはXibbedViewインスタンスです。```addSubview(nib) `` `を呼び出すと、1つのXibbedViewインスタンスを別のXibbedViewインスタンスに追加しますか?
William Hu

私はこの点に同意します。「Debug View Hierarchy」から表示しようとすると、XibbedViewにXibbedViewが含まれているようです。見れば分かります。
William Hu

@WilliamHu提供された例をダウンロードすると、MemeView.xibは一連のサブビューを持つ単なるUIViewインスタンスであり、ファイルホルダーはXibbedViewのサブクラスであるMemeViewです。したがって、XibbedViewの唯一のインスタンスは、メインビューが単なるビューであるxibファイルをロードするMemeViewのみです
Andrea

あ、ならいいですよ。私はそれをテストします。ありがとうございました!
William Hu

4

アーロン・ヒレガス(著者、インストラクター、ココア忍者)によるこのブログ投稿は非常に啓発的であることがわかりました。指定されたイニシャライザを介してNIBファイルをロードする彼の修正されたアプローチを採用しなくても、少なくとも進行中のプロセスをよりよく理解できるでしょう。私は最近この方法を使用して大きな成功を収めています!


リンクが死んでいる。現在、bignerdranch.com
blog /

3

以前の回答では、iPhone SDKの2.0と2.1の間で発生したNIB(XIB)構造の変更は考慮されていません。ユーザーコンテンツは、1ではなくインデックス0から始まります。

すべてのバージョン2.1以降に有効な2.1マクロを使用できます(IPHONEの前の2つのアンダースコア:)。

 // 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
バーニー:回答は投票に基づいて位置をシフトするため、「前の回答」はスタックオーバーフローでは意味がありません。「フレッドの答え」または「バーニーの答え」または「オリエの答え」を参照する方がよい。
Olie

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のスコープから外れ続けます。Utilitiesクラスで静的メソッドを作成しました。MyView(UIViewからサブクラス化)と呼ばれるh / mファイルと、同じ名前のxibを作成しました。IBでは、xibファイルの所有者とビューを「MyView」に設定しました。デバッガーではスコープ外です。IBに何か不足していますか?
TigerCoding

ここに私の新しいSOの質問を見てください:stackoverflow.com/questions/9224857/...
TigerCoding

self = [ユーティリティinitWithNibName:@ "XIB1" withSelf:self]; それがまだ設定されていないとき、あなたは自分自身を渡している。そのコードは次のコードと同じではありません:self = [Utilities initWithNibName:@ "XIB1" withSelf:nil]; ?
Ken Van Hoeylandt、2012

@Javy:ここにある私のコードサンプルはARCでは動作しない可能性があります-ARCを有効にしてテストしていません。
MusiGenesis

3

これをSwiftで行う方法です(現在、XCode 7ベータ5でSwift 2.0を作成しています)。

UIViewInterface 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

あずきっく

6)ロードしたビューをビューコントローラのビューにアタッチするには:

[self.view addSubview:myViewFromNib];

おそらく、メモリリークを回避するために、ビューから削除する必要があります。

明確にするために:ビューコントローラーにはいくつかのIBOutletがあり、その一部は(通常のように)元のnibファイルのアイテムに接続され、一部はロードされたnibのアイテムに接続されています。両方のペン先のオーナークラスは同じです。ロードされたビューは元のビューをオーバーレイします。

ヒント:ロードされたペン先のメインビューの不透明度をゼロに設定すると、元のペン先のアイテムが不明瞭になりません。


注意が必要ですが、サブビューとしてビューを追加すると、以前の親からビューが自動的に削除されるため、リークは発生しません。
averydev 2014年

1

何時間も費やした後、私は次の解決策を作り出しました。次の手順に従って、カスタムを作成しますUIView

1)UIViewから継承したmyCustomViewクラスを作成します。
ここに画像の説明を入力してください

2)myCustomViewという名前で.xibを作成します。
ここに画像の説明を入力してください

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を提供します


1

最短バージョン:

RSFavoritePlaceholderView *favoritePlaceholderView = [[[NSBundle mainBundle] loadNibNamed:@"RSFavoritePlaceholderView" owner:self options:nil] firstObject];

0

ビューと同じビューを持つxibに名前を付けるという規則があります。ビューコントローラーの場合と同じです。その後、コードでクラス名を書き出す必要はありません。同じ名前のnibファイルからUIViewをロードします。

MyViewというクラスの例。

  • Interface BuilderでMyView.xibというnibファイルを作成します。
  • Interface Builderで、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でのビューの読み込みが少ない


Hrm。タグとは何ですか?
n13

保持する最上位オブジェクトを知る。当時はそれが必要でしたが、おそらくARCに準拠するために保持を取り出すことができます。
gngrwzrd 2013
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.