カスタムUIViewサブクラスを作成しましたが、UIViewサブクラスのコードでUIをレイアウトしたくありません。そのためにxibを使用したいと思います。だから私がしたことは次のとおりです。
UIViewをサブクラス化するクラス「ShareView」を作成しました。ファイルの所有者を「ShareView」に設定してXIBファイルを作成しました。次に、「ShareView.h」で宣言したいくつかのアウトレットをリンクします。
次に、ShareViewをサブビューとして追加するViewController、MainViewControllerがあります。このコードで:
NSArray *arr = [[NSBundle mainBundle] loadNibNamed:@"ShareView" owner:nil options:nil];
UIView *fv = [[arr objectAtIndex:0] retain];
fv.frame = CGRectMake(0, 0, 320, 407);
[self.view addSubview:fv];
しかし、ShareViewで宣言したアウトレットでNSUnknownKeyExceptionエラーが発生します。
私がこれをすべて行った理由は、個別のXIBファイルに独自のロジックを持つUIViewが必要だからです。 ViewControllersは全画面を管理するためにのみ使用される、つまり画面の一部ではないことをいくつかの場所で読みました...では、何が間違っているのでしょうか。 ShareViewのロジックを別のクラスに入れたいので、MainControllerクラスがShareViewのロジックで肥大化することはありません(これはこの問題を解決するための手段だと思いますか?)
ThomasM、
カスタムビュー内での動作のカプセル化についても同様のアイデアがありました(たとえば、最小/最大/現在の値のコンパニオンラベルが付いたスライダーで、値が変更されたイベントも内部でコントロールによって処理されます)。
現在のベストプラクティスでは、Eimantasが回答で説明しているように、Interface Builder(ShareView.xib
)でShareViewを設計します。次に、ShareViewをMainViewController.xib
のビュー階層に埋め込みます。
IOS開発者のブログに カスタムビューのペン先を他のペン先の中に埋め込む 方法を書きました。重要なのは、カスタムビューの-awakeAfterUsingCoder:
をオーバーライドし、MainViewController.xibからロードされたオブジェクトを「埋め込み」ペン先(ShareView.xib)からロードされたオブジェクトに置き換えます。
これらの線に沿った何か:
// ShareView.m
- (id) awakeAfterUsingCoder:(NSCoder*)aDecoder {
BOOL theThingThatGotLoadedWasJustAPlaceholder = ([[self subviews] count] == 0);
if (theThingThatGotLoadedWasJustAPlaceholder) {
// load the embedded view from its Nib
ShareView* theRealThing = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([ShareView class]) owner:nil options:nil] objectAtIndex:0];
// pass properties through
theRealThing.frame = self.frame;
theRealThing.autoresizingMask = self.autoresizingMask;
[self release];
self = [theRealThing retain];
}
return self;
}
ロードされたxibの所有者をnil
として定義しました。 xib自体のファイル所有者はアウトレットが接続されており、ShareView
のインスタンスとして定義されているため、不明なキーに関する例外が発生します(nil
にはShareView
に対して定義したアウトレットプロパティがありません)。
Xibのローダーを所有者として定義する必要があります(つまり、xibのロードを担当するView Controller)。次に、別のUIViewオブジェクトをxibに追加し、それをShareView
のインスタンスとして定義します。次に、xibをロードするとき。
ShareView *shareView = [[[[NSBundle mainBundle] loadNibNamed:@"ShareView" owner:self options:nil] objectAtIndex:0] retain];
ビューコントローラのインターフェイスでshareView
をIBOutlet
として定義することもできます(ファイル所有者からのアウトレットをxib自体のビューに接続します)。次に、xibをロードするときに、xibのロードプロセスによってビューがインスタンス変数に直接再接続されるため、shareView
インスタンス変数を再割り当てする必要はありません。
答えに付け加えたいと思います。しかし、人々がこの答えを改善してくれることを願っています。
まず第一に、それは機能します。
XIB:
結果:
特にtableViewCellについては、UIViewを長い間サブクラス化したいと思います。
これが私がやった方法です。
それは成功していますが、私の意見ではまだ「厄介」な部分があります。
まず、通常の.h、.m、およびxibファイルを作成しました。作成したサブクラスがUIViewControllerのサブクラスでない場合、Appleには、xibを自動的に作成するためのチェックボックスがないことに注意してください。とにかく作成してください。
#import <UIKit/UIKit.h>
#import "Business.h"
@interface BGUIBusinessCellForDisplay : UITableViewCell
+ (NSString *) reuseIdentifier;
- (BGUIBusinessCellForDisplay *) initWithBiz: (Business *) biz;
@end
本当に単純なUITableViewCellで、後者をbizで初期化したい。
UITableViewCellに対して実行する必要があるreuseidentifierを配置します
//#import "Business.h"
@interface BGUIBusinessCellForDisplay ()
@property (weak, nonatomic) IBOutlet UILabel *Title;
@property (weak, nonatomic) IBOutlet UIImageView *Image;
@property (weak, nonatomic) IBOutlet UILabel *Address;
@property (weak, nonatomic) IBOutlet UILabel *DistanceLabel;
@property (weak, nonatomic) IBOutlet UILabel *PinNumber;
@property (strong, nonatomic) IBOutlet BGUIBusinessCellForDisplay *view;
@property (weak, nonatomic) IBOutlet UIImageView *ArrowDirection;
@property (weak, nonatomic) Business * biz;
@end
@implementation BGUIBusinessCellForDisplay
- (NSString *) reuseIdentifier {
return [[self class] reuseIdentifier];
};
+ (NSString *) reuseIdentifier {
return NSStringFromClass([self class]);
};
次に、ほとんどの初期化コードを削除し、代わりにこれを配置しました。
- (BGUIBusinessCellForDisplay *) initWithBiz: (Business *) biz
{
if (self.biz == nil) //First time set up
{
self = [super init]; //If use dequeueReusableCellWithIdentifier then I shouldn't change the address self points to right
NSString * className = NSStringFromClass([self class]);
//PO (className);
[[NSBundle mainBundle] loadNibNamed:className owner:self options:nil];
[self addSubview:self.view]; //What is this for? self.view is of type BGCRBusinessForDisplay2. That view should be self, not one of it's subview Things don't work without it though
}
if (biz==nil)
{
return self;
}
self.biz = biz;
self.Title.text = biz.Title; //Let's set this one thing first
self.Address.text=biz.ShortenedAddress;
//if([self.distance isNotEmpty]){
self.DistanceLabel.text=[NSString stringWithFormat:@"%dm",[biz.Distance intValue]];
self.PinNumber.text =biz.StringPinLineAndNumber;
それは本当に厄介であることに注意してください。
まず、initは2つの方法で使用できます。
だから私はしました:
if (self.biz == nil) //First time set up
{
self = [super init]; //If use dequeueReusableCellWithIdentifier then I shouldn't change the address self points to right
NSString * className = NSStringFromClass([self class]);
//PO (className);
[[NSBundle mainBundle] loadNibNamed:className owner:self options:nil];
[self addSubview:self.view]; //What is this for? self.view is of type BGCRBusinessForDisplay2. That view should be self, not one of it's subview Things don't work without it though
}
私がしたもう1つの厄介なことは、[self addSubview:self.view]を実行するときです。
事は私が自分自身になりたいということですtheビュー。 self.viewではありません。それにもかかわらず、どういうわけかそれは機能します。そうです、私が改善するのを手伝ってください、しかしそれは本質的にUIViewのあなた自身のサブクラスを実装する方法です。
IB_DESIGNABLEを使用して、xibで設計されたカスタムUIViewを作成し、InterfaceBuilderを作成して新しいXcode6の他のxibファイルまたはストーリーボード内に表示することもできます。 xibで、ファイル所有者をカスタムクラスに設定しますが、再通貨の読み込みの問題を回避するためにUIViewクラスを設定しないでください。デフォルトのUIViewクラスをそのままにしておくと、このUIViewをカスタムクラスビューのサブビューとして追加します。すべてのアウトレットをファイル所有者に接続し、カスタムクラスで以下のコードのようにxibをロードします。ここで私のビデオチュートリアルを確認できます: https://www.youtube.com/watch?v=L97MdpaF3Xg
IB_DESIGNABLE
@interface CustomControl : UIView
@end
@implementation CustomControl
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder])
{
[self load];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame])
{
[self load];
}
return self;
}
- (void)load
{
UIView *view = [[[NSBundle bundleForClass:[self class]] loadNibNamed:@"CustomControl" owner:self options:nil] firstObject];
[self addSubview:view];
view.frame = self.bounds;
}
@end
自動レイアウトを使用している場合は、次のように変更することをお勧めします。view.frame = self.bounds;
to:
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[view]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(view)]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[view]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(view)]];
Auto-LayoutでYangのパターンを使用するには、-awakeWithCoder:メソッドのどこかに以下を追加する必要があります。
theRealThing.translatesAutoresizingMaskIntoConstraints = NO;
-translatesAutoResizingMaskIntoConstraintsをオフにしないと、レイアウトが正しくなくなるだけでなく、コンソールで多くの意味のないデバッグが発生する可能性があります。
編集:自動レイアウトはまだ苦痛になる可能性があります。特定の制約は尊重されませんが、他の制約は尊重されます(たとえば、下部への固定は機能しませんが、上部への固定は機能します)。理由は正確にはわかりませんが、プレースホルダーからtheRealThingに手動で制約を渡すことで、これを回避できます。
このパターンは、通常の.xibの場合と同じようにストーリーボードでも機能することにも注意してください(つまり、.xibでUI要素を作成し、手順に従ってStoryBoard Viewコントローラーにドロップできます)。