ストーリーボードでuiviewxibを再利用する


81

私は通常、インターフェイスビルダーでuiviewを作成および設計するのが好きです。ストーリーボードの複数のビューコントローラーで再利用できる単一のビューをxibに作成する必要がある場合があります。

回答:


132

ストーリーボードでxibを再利用してレンダリングします。

Swift2.2およびXcode7.3.1でテスト済み

1 ---- 'DesignableXibView'という名前の新しいUIViewを作成します

  • ファイル>新規>ファイル>ソース> CocoaTouchクラス> UIView

2 ---- 'DesignableXibView'という名前の一致するxibファイルを作成します

  • ファイル>新規>ファイル>ユーザーインターフェース>表示

3 ---- xibののファイル所有者を設定します

  1. xibを選択します
  2. ファイルの所有者を選択
  3. IdentityInspectorでカスタムクラスを「DesignableXibView」に設定します。

Xibの所有者をカスタムクラスに設定する

  • 注:xibでビューのカスタムクラスを設定しないでください。ファイル所有者のみ!

4 ---- DesignableXibViewの実装

//  DesignableXibView.swift

import UIKit

@IBDesignable

class DesignableXibView: UIView {

    var contentView : UIView?

    override init(frame: CGRect) {
        super.init(frame: frame)
        xibSetup()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        xibSetup()
    }

    func xibSetup() {
        contentView = loadViewFromNib()

        // use bounds not frame or it'll be offset
        contentView!.frame = bounds

        // Make the view stretch with containing view
        contentView!.autoresizingMask = [UIViewAutoresizing.FlexibleWidth, UIViewAutoresizing.FlexibleHeight]

        // Adding custom subview on top of our view (over any custom drawing > see note below)
        addSubview(contentView!)
    }

    func loadViewFromNib() -> UIView! {

        let bundle = NSBundle(forClass: self.dynamicType)
        let nib = UINib(nibName: String(self.dynamicType), bundle: bundle)
        let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView

        return view
    }

}

5 ----ストーリーボードで再利用可能なビューをテストします

  1. ストーリーボードを開く
  2. ビューを追加する
  3. そのビューのカスタムクラスを設定します
  4. ちょっと待って...ブーム!!

ストーリーボードビューコントローラ内でのXibレンダリング


1
ありがとう!Obj-Cの回答(VFLありとなし)で行ったように、Swiftで制約を使用して例を追加することをお勧めします。
Evan R

3
私は自分の質問に答えました。ビューではなくファイル所有者を設定するように言っているこれらのSO記事を読む前に、アウトレットをセットアップしたようです。すべてのアウトレットを切断し、ファイルの所有者が正しいことを確認してから、すべてのアウトレットを再度追加した後、これは機能しました。
SuperDuperTango 2016年

4
ファイル所有者の設定とカスタムクラスの設定の違いは何ですか?
Paul Brewczynski 2017年

9
InterfaceBuilderにもプレビューが表示されません。Xcode 9
Jaap Weijland 2017年

3
コードは実行時に機能しますが、私のXcode 9では、モジュール行の下に「設計可能なビルドに失敗しました」と表示されます
ManuQiao 2018年

76

新着!ストーリーボードに直接レンダリングする機能を備えた更新された回答(そして迅速に!)

Xcode6.3.1で動作します

'ReuseableView'という名前の新しいUIViewを作成します

  • ファイル>新規>ファイル>ソース> CocoaTouchクラス> UIView

'ReuseableView'という名前の一致するxibファイルを作成します

  • ファイル>新規>ファイル>ユーザーインターフェース>表示

xibののファイル所有者を設定します

  1. xibを選択します
  2. ファイルの所有者を選択
  3. IdentityInspectorでカスタムクラスを「ReusableView」に設定します。 ここに画像の説明を入力してください

    • 注:xibでビューのカスタムクラスを設定しないでください。ファイル所有者のみ!

ReuseableView.xibのビューからReuseableView.hインターフェイスへのアウトレットを作成します

  1. アシスタントエディターを開く
  2. コントロール+ビューからインターフェースにドラッグ

ここに画像の説明を入力してください

initWithCoder実装を追加してビューをロードし、サブビューとして追加します。

- (id)initWithCoder:(NSCoder *)aDecoder{
    self = [super initWithCoder:aDecoder];
    if (self) {

        // 1. load the interface
        [[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:self options:nil];
        // 2. add as subview
        [self addSubview:self.view];
        // 3. allow for autolayout
        self.view.translatesAutoresizingMaskIntoConstraints = NO;
        // 4. add constraints to span entire view
        [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[view]|" options:0 metrics:nil views:@{@"view":self.view}]];
        [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[view]|" options:0 metrics:nil views:@{@"view":self.view}]];

    }
    return self;
}

ここに画像の説明を入力してください

ストーリーボードで再利用可能なビューをテストします

  1. ストーリーボードを開く
  2. ビューを追加する
  3. そのビューのカスタムクラスを設定します

ここに画像の説明を入力してください

実行して観察してください! ここに画像の説明を入力してください


@Garfbargleこれに加えて...ストーリーボードにxibのサブ要素を表示することは可能ですか?最後のステップで、追加されたビューは灰色の四角のように表示されますが、それを修正する方法はありますか?THX!
Andres C

2
@ andres.cianioはい。ただし、まったく異なるアプローチを取る必要があります。プログラムでビューを作成し、IBDesignableディレクティブを使用する必要があります。これは特に、視覚的にビューを作成したい人のためのものでした。xibでビューを視覚的に作成し、ストーリーボードにレンダリングする方法を知りません。まだ可能でなくても、これがすぐに可能になるとしても、私は驚かないでしょう。
Garfbargle 2016

@Garfbargleありがとう...これは私の命を救いましたが、ビューを挿入するときにSBでレンダリングするのは美しいでしょう;)
Andres C

2
素晴らしい説明です!しかし、私はあなたにヒントがあります:UINib(nibName: nibName, bundle: nil).instantiateWithOwner(nil, options: nil)NSBundle-Versionよりも速いものを使用してください。
blackjacx 2016年

@Garfbargleこれは、IBでデザインし、ストーリーボードにレンダリングできるという要件を満たしているようです。基本的に、XIBに対して行ったことを正確@IBDesignable on the class.に実行init(frame:)し、実装する必要がありますが、それ以外はかなりうまく機能します。 supereasyapps.com/blog/2014/12/15/...
wyu

53

受け入れられた答えへのSwift3&4アップデート

1.「DesignableXibView」という名前の新しいUIViewを作成します

  • ファイル>新規>ファイル>ソース> CocoaTouchクラス> UIView

2.「DesignableXibView」という名前の一致するxibファイルを作成します

  • ファイル>新規>ファイル>ユーザーインターフェース>表示

3.xibののファイル所有者を設定します

「DesignableXibView.xib」>「ファイルの所有者」を選択し、IDインスペクターで「カスタムクラス」を「DesignableXibView」に設定します。

  • 注:xibでビューのカスタムクラスを設定しないでください。ファイル所有者のみ!

4.DesignableXibViewの実装

    import UIKit

@IBDesignable

class DesignableXibView: UIView {

    var contentView : UIView!

    override init(frame: CGRect) {
        super.init(frame: frame)
        xibSetup()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        xibSetup()
    }

    func xibSetup() {
        contentView = loadViewFromNib()

        // use bounds not frame or it'll be offset
        contentView.frame = bounds

        // Make the view stretch with containing view
        contentView.autoresizingMask = [UIViewAutoresizing.flexibleWidth, UIViewAutoresizing.flexibleHeight]

        // Adding custom subview on top of our view 
        addSubview(contentView)
    }

    func loadViewFromNib() -> UIView! {

        let bundle = Bundle(for: type(of: self))
        let nib = UINib(nibName: String(describing: type(of: self)), bundle: bundle)
        let view = nib.instantiate(withOwner: self, options: nil).first as! UIView

        return view
    }
} 

5ストーリーボードで再利用可能なビューをテストします

  • ストーリーボードを開く

  • ビューを追加する

  • そのビューのカスタムクラスを設定します


2
Xcode8 / Swift3で動作させることができません。アプリの実行時には正常に動作しますが、ストーリーボードには表示されません。
shallowThought

私にとってはうまくいきます。xibで変更を表示しようとすると、表示されません。他のコントローラー内にビューを追加してから、そのビューのクラスをに設定しDesignableXibViewます。プロジェクトをビルドして、変更を表示します。
harsh_v 2016年

1
私のためにも働いた。Garfbargleの投稿のステップ5で説明されているように、DesignableXibViewを現在のストーリーボードに追加する必要がありました
Tinkerbell

実行時に「NIBをバンドルでロードできませんでした」というメッセージが表示されますか?
ジョニー2017

1
@JonnyString(describing: type(of: self))これは安全に行うべきだと思います
harsh_v 2017

11

誰かがそれを翻訳するのに問題がある場合、swift2のinitWithCoder関数:

required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        UINib(nibName: String(self.dynamicType), bundle: NSBundle.mainBundle()).instantiateWithOwner(self, options: nil)
        self.addSubview(view)
        self.view.translatesAutoresizingMaskIntoConstraints = false
        self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[view]|", options: NSLayoutFormatOptions.AlignAllCenterY , metrics: nil, views: ["view": self.view]))
        self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[view]|", options: NSLayoutFormatOptions.AlignAllCenterX , metrics: nil, views: ["view": self.view]))
    }

ありがとう!VFLを使用したくない場合は、VFLを使用しない例を追加することをお勧めします。
Evan R

3

受け入れられた回答(@Garfbargleによる)をObjective-Cに適合させようとしている人のために

に変換SwiftするだけObjective-Cだけでは、それを機能させるのに十分ではありません。ストーリーボードでライブレンダリングを許可するのに苦労しました。

コード全体を翻訳した後、デバイス(またはシミュレーター)で実行するとビューは適切に読み込まれますが、ストーリーボードでのライブレンダリングは機能しません。これは[NSBundle mainBundle]、Interface BuilderがmainBundleにアクセスできないのに対し、私が使用したためです。代わりに使用する必要があるのは[NSBundle bundleForClass:self.classForCoder]です。ブーム、ライブレンダリングが機能するようになりました!

注:自動レイアウトで問題が発生した場合Safe Area Layout Guidesは、Xibで無効にしてみてください。

あなたの便宜のために、私は私のコード全体をここに残します、それであなたはただコピー/貼り付けする必要があります(すべてのプロセスのために、元の答えに従ってください):

BottomBarView.h

#import <UIKit/UIKit.h>

IB_DESIGNABLE
@interface BottomBarView : UIView

@end

BottomBarView.m

#import "BottomBarView.h"

@interface BottomBarView() {
    UIView *contentView;
}

@end


@implementation BottomBarView

-(id) initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        [self xibSetup];
    }
    return self;
}

-(id) initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    if (self) {
        [self xibSetup];
    }
    return self;
}

-(void) xibSetup {
    contentView = [self loadViewFromNib];

    contentView.frame = self.bounds;

    contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

    [self addSubview:contentView];
}

-(UIView*) loadViewFromNib {
    NSBundle *bundle = [NSBundle bundleForClass:self.classForCoder]; //this is the important line for view to render in IB
    UINib *nib = [UINib nibWithNibName:NSStringFromClass([self class]) bundle:bundle];
    UIView *view = [nib instantiateWithOwner:self options:nil][0];

    return view;
}

@end

いくつかの問題が発生した場合は教えてくださいが、箱から出してすぐに機能するはずです:)


0

誰かが興味を持っている場合は、@ Garfbargleのステップ4のコードのXamarin.iOSバージョンを次に示します。

public partial class CustomView : UIView
    {
        public ErrorView(IntPtr handle) : base(handle)
        {

        }

        [Export("awakeFromNib")]
        public override void AwakeFromNib()
        {
            var nibObjects = NSBundle.MainBundle.LoadNib("CustomView", this, null);
            var view = (UIView)Runtime.GetNSObject(nibObjects.ValueAt(0));
            view.Frame = Bounds;
            view.AutoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight;

            AddSubview(rootView);
        }
    }

0

これがあなたがずっと望んでいた答えです。CustomViewクラスを作成し、そのマスターインスタンスをすべてのサブビューとアウトレットを備えたxibに含めることができます。次に、そのクラスをストーリーボードまたは他のxib内の任意のインスタンスに適用できます。

ファイルの所有者をいじったり、アウトレットをプロキシに接続したり、特殊な方法でxibを変更したり、カスタムビューのインスタンスをそれ自体のサブビューとして追加したりする必要はありません。

これを行うだけです:

  1. BFWControlsフレームワークをインポートする
  2. あなたのスーパークラスを変更UIViewするNibView(またはからUITableViewCellNibTableViewCell

それでおしまい!

IBDesignableと連携して、ストーリーボードのデザイン時にカスタムビュー(xibからのサブビューを含む)をレンダリングすることもできます。

あなたはここでそれについてもっと読むことができます:https//medium.com/build-an-app-like-lego/embed-a-xib-in-a-storyboard-953edf274155

そして、ここでオープンソースのBFWControlsフレームワークを入手できます:https//github.com/BareFeetWare/BFWControls

トム👣

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.