10

私はこれが非常に簡単であることを知っていますが、あまりにも多くの髪を引っ張った後、私は解決策にはほど遠い.

XIB などを使用してビューを作成する方法を説明するチュートリアルを見てきました。しかし、それらのどれも、私がここにいる状況に対処していません。

ラベルとボタンがほとんどないカスタム UIView サブクラスである XIB ファイルがあります。UIView サブクラスは再利用可能です。これが、単一の View コントローラー内にアウトレットを配置できない理由です。その結果、このビューの個々のコントロール (サブビュー) をカスタム UIView 自体に格納します。すべてのView Controllerに含まれるこのカスタムビューのサブビューをView Controllerが所有するべきではないため、これは論理的です。

問題は、UI 全体を完全に初期化する方法がわからないことです。

UIView サブクラスのコードは次のとおりです。

@interface MCPTGenericView : UIView

+(id)createInstance : (bool) bPortrait;

@property (weak, nonatomic) IBOutlet UIView *topView;
@property (weak, nonatomic) IBOutlet UIView *titleView;
@property (weak, nonatomic) IBOutlet UILabel *titleLabel;
@property (weak, nonatomic) IBOutlet UIButton *logoButton;
@property (weak, nonatomic) IBOutlet UITextField *searchTextField;
@property (weak, nonatomic) IBOutlet UIButton *menuButton;
@end

後で、この UIView の横向きにも同じ XIB ファイルを使用する予定であり、同じ XIB で横向きのコントロールを持つ同じ上記のアウトレットを使用する予定です。

そして、ここに実装があります:

@implementation MCPTGenericView
//@synthesize topView, titleLabel, titleView;

+(id)createInstance : (bool) bPortrait
{
    UIView * topLevelView = nil;
    MCPTGenericView * instance = [MCPTGenericView new];
    NSArray * views = [[NSBundle mainBundle] loadNibNamed:@"MoceptGenericView" owner:instance options:nil];

    int baseTag = (bPortrait)?PORTRAIT_VIEW_TAG_OFFSET:LANDSCAPE_VIEW_TAG_OFFSET;

    // make sure customView is not nil or the wrong class!

   for (UIView * view in views)
   {

       if (view.tag == baseTag)
       {
           topLevelView = view;
           break;
       }
   }

    instance.topView = (MCPTGenericView *)[topLevelView viewWithTag:baseTag + 1];
    instance.searchTextField = (UITextField *)[topLevelView viewWithTag:baseTag + 2];
    instance.menuButton = (UIButton *)[topLevelView viewWithTag:baseTag + 3];
    instance.logoButton = (UIButton *)[topLevelView viewWithTag:baseTag + 4];
    instance.titleView = [topLevelView viewWithTag:baseTag + 5];
    instance.titleLabel = (UILabel *)[topLevelView viewWithTag:baseTag + 6];

    return instance;
}

-(id)initWithCoder:(NSCoder *)aDecoder
{
    if ((self = [super initWithCoder:aDecoder]))
    {
        [self addSubview:[[[NSBundle mainBundle] loadNibNamed:@"MCPTGenericView" owner:self options:nil] objectAtIndex:0]];
    }
    return self;
}

-(void)awakeFromNib
{
    [super awakeFromNib];
    [self addSubview: self.titleView];
    [self addSubview:self.topView];
}

- (id) init
{
    self = [super init];
    if (self)
    {
        [[NSBundle mainBundle] loadNibNamed:@"MCPTGenericView" owner:self options:nil];
        [self addSubview:self.topView];
        [self addSubview:self.titleView];

    }
    return self;
}

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self)
    {
        // Initialization code
         [[NSBundle mainBundle] loadNibNamed:@"MCPTGenericView" owner:self options:nil];
        [self addSubview:self.topView];
        [self addSubview:self.titleView];
    }
    return self;
}

/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
    // Drawing code
}
*/

@end

うまくいったもの:

ビューコントローラーからの呼び出しに成功しましたinitWithFrame:frame。そうすれば、すべてのコントロールが適切に初期化されていることがわかりました。しかし、すでに XIB を描画しているのに、なぜフレームを提供する必要があるのでしょうか? XIB の意図された用途であるため、loadNibNamed はフレーム設定とレイアウトを処理するべきではありませんか?

loadNibNamed が所有者オブジェクトを必要とする方法にも困惑しています。XIB から同じオブジェクトを取得するために、オブジェクトが既に必要なのはなぜですか? それも中途半端?

助けてください...

4

2 に答える 2

19

私を困惑させたのは、loadnibnamedxib レイアウトとアウトレット情報を失う方法でした。私はついにそれを達成する方法を見つけました。

何が機能するかの要約は次のとおりです。

1) MyCustomView がカスタム ビュー クラスであるとします。これとそのサブビューを XIB の一部として設計します。これはインターフェースビルダーを介して行うので、自明です。

2) Xcode -> File -> New -> Objective C Class で MyCustomView.h と MyCustomView.m (ボイラープレート) を追加します。

3) 次に、MyCustomView.xib 内で、File's Owner = MyCustomView (追加したばかりのクラス名) を設定します。一番上にあるビューのカスタム クラスには触れないでください。UIView のままにします。そうでなければ、再帰になってしまいます!!!

4) MyCustomView.h で、MyCustomView.xib 内のサブビューに対応するいくつかのアウトレットを作成します。

そのような:

@property (weak)  IBOutlet UILabel * label1;
@property (weak)  IBOutlet UIButton * button1;

5) MyCustomView.xib に移動します。各サブビュー (ラベル、ボタン) を選択し、右クリックして [新しい参照アウトレット] からドラッグし、ファイルの所有者までドラッグします。

これにより、ドラッグした場所からサブビューのタイプに一致するアウトレットのリストがポップアップ表示されます。ラベルからドラッグすると、label1 がポップアップ表示されます。これは、このステップまでに行ったことがすべて正しいことを示しています。

一方、いずれかのステップで失敗した場合、ポップアップは表示されません。手順、特に 3 と 4 を確認してください。

この手順を正しく実行しない場合、Xcode は次の例外を歓迎します。

setValue:forUndefinedKey: this class is not key value coding-compliant for the key

6) MyCustomView.m に、次のコードを貼り付ける/上書きします。

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

    if (self)
    {
        NSString * nibName = @"MyCustomView";
        [[[NSBundle mainBundle] loadNibNamed:nibName owner:self options:nil] firstObject];

        [self addSubview:self.labelContinentName];
    }
    return self;
}

この手順は非常に重要です。アウトレット値 (label1、button1) を nil から具体的なサブビューに設定し、最も重要なこととして、MyCustomView.xib 内で設定した内容に従ってフレームを設定します

7) ストーリーボード ファイルに、タイプ MyCustomView のビューを追加します - 他のビューと同じように:

  • View Controllerのメインビューの長方形にUIViewをドラッグします
  • 新しく追加されたビューを選択します
  • Utilities -> Identity Inspector で、カスタム クラスの値を MyCustomView に設定します。

問題なく稼働しているはずです。

于 2014-04-05T11:47:55.657 に答える
0

loadNibNamedはフレーム設定を処理せず、コンテンツのみをロードし、オブジェクトをコードで使用できるようにします。initWithFrame:ウィンドウのビュー階層に新しいオブジェクトを挿入するために呼び出す必要があります。

于 2013-12-02T08:44:11.097 に答える