アップデート
あなたの質問が(iOS)NSView
ではなく (OS X)について話していることに気付きました。UIView
このアイデアはまだ適用できるはずですが、私のコードを変更せずにプロジェクトにドロップすることはできません。ごめん。
オリジナル
ビュー階層を変更することを検討してください。カスタムの枠付きビューが と呼ばれるとしましょうBorderView
。現在、サブビューを直接追加し、 とそのサブビューBorderView
の間に制約を作成しています。BorderView
代わりに、そのプロパティBorderView
で公開する単一のサブビューを指定します。contentView
サブビューをcontentView
に直接追加するのではなく、 に追加しますBorderView
。次に、缶は必要に応じてBorderView
レイアウトします。contentView
これが仕組みUITableViewCell
です。
次に例を示します。
@interface BorderView : UIView
@property (nonatomic, strong) IBOutlet UIView *contentView;
@property (nonatomic) UIEdgeInsets borderSize;
@end
xib を使用している場合、サブビューをcontentView
に直接追加するのではなく、 に追加する必要があることを IB が認識していないという問題がありBorderView
ます。(それは についてこれを知っていUITableViewCell
ます。) それを回避するためにcontentView
、アウトレットを作成しました。そうすれば、コンテンツ ビューとして使用する別のトップレベル ビューを作成し、それをBorderView
のcontentView
アウトレットに接続できます。
このように実装するには、とそのBorderView
の間の 4 つの制約のそれぞれにインスタンス変数が必要です。BorderView
contentView
@implementation BorderView {
NSLayoutConstraint *topConstraint;
NSLayoutConstraint *leftConstraint;
NSLayoutConstraint *bottomConstraint;
NSLayoutConstraint *rightConstraint;
UIView *_contentView;
}
contentView
アクセサーは、オンデマンドでコンテンツ ビューを作成できます。
#pragma mark - Public API
- (UIView *)contentView {
if (!_contentView) {
[self createContentView];
}
return _contentView;
}
また、セッターは、既存のコンテンツ ビューがあれば、それを置き換えることができます。
- (void)setContentView:(UIView *)contentView {
if (_contentView) {
[self destroyContentView];
}
_contentView = contentView;
[self addSubview:contentView];
}
borderSize
セッターは、制約が更新され、境界線が再描画されるように調整する必要があります。
- (void)setBorderSize:(UIEdgeInsets)borderSize {
if (!UIEdgeInsetsEqualToEdgeInsets(borderSize, _borderSize)) {
_borderSize = borderSize;
[self setNeedsUpdateConstraints];
[self setNeedsDisplay];
}
}
で境界線を描く必要がありますdrawRect:
。赤で塗りつぶします:
- (void)drawRect:(CGRect)rect {
CGRect bounds = self.bounds;
UIBezierPath *path = [UIBezierPath bezierPathWithRect:bounds];
[path appendPath:[UIBezierPath bezierPathWithRect:UIEdgeInsetsInsetRect(bounds, self.borderSize)]];
path.usesEvenOddFillRule = YES;
[path addClip];
[[UIColor redColor] setFill];
UIRectFill(bounds);
}
コンテンツ ビューの作成は簡単です。
- (void)createContentView {
_contentView = [[UIView alloc] init];
[self addSubview:_contentView];
}
それを破壊することはもう少し複雑です:
- (void)destroyContentView {
[_contentView removeFromSuperview];
_contentView = nil;
[self removeConstraint:topConstraint];
topConstraint = nil;
[self removeConstraint:leftConstraint];
leftConstraint = nil;
[self removeConstraint:bottomConstraint];
bottomConstraint = nil;
[self removeConstraint:rightConstraint];
rightConstraint = nil;
}
updateConstraints
誰かが を呼び出した場合、システムはレイアウトと描画を行う前に自動的に を呼び出します。setNeedsUpdateConstraints
これは で行いましたsetBorderSize:
。ではupdateConstraints
、必要に応じて拘束を作成し、 に基づいて定数を更新しborderSize
ます。また、自動サイズ変更マスクを制約に変換しないようにシステムに指示します。これは、満たされない制約が作成される傾向があるためです。
- (void)updateConstraints {
self.translatesAutoresizingMaskIntoConstraints = NO;
self.contentView.translatesAutoresizingMaskIntoConstraints = NO;
[super updateConstraints];
if (!topConstraint) {
[self createContentViewConstraints];
}
topConstraint.constant = _borderSize.top;
leftConstraint.constant = _borderSize.left;
bottomConstraint.constant = -_borderSize.bottom;
rightConstraint.constant = -_borderSize.right;
}
4 つの制約はすべて同じ方法で作成されるため、ヘルパー メソッドを使用します。
- (void)createContentViewConstraints {
topConstraint = [self constrainContentViewAttribute:NSLayoutAttributeTop];
leftConstraint = [self constrainContentViewAttribute:NSLayoutAttributeLeft];
bottomConstraint = [self constrainContentViewAttribute:NSLayoutAttributeBottom];
rightConstraint = [self constrainContentViewAttribute:NSLayoutAttributeRight];
}
- (NSLayoutConstraint *)constrainContentViewAttribute:(NSLayoutAttribute)attribute {
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:_contentView attribute:attribute relatedBy:NSLayoutRelationEqual toItem:self attribute:attribute multiplier:1 constant:0];
[self addConstraint:constraint];
return constraint;
}
@end
私はこの git リポジトリに完全な作業例を入れました。