0

NSViewそれ自体に境界線があるカスタムサブクラスがあります。境界線はこのビュー内に描画されます。自動レイアウトでこの境界を尊重することは可能ですか?

たとえば、サブビューをカスタム ビューに配置し、次のような制約を設定すると:

@"H:|-(myViewSubView)-|" (not @"H:|-(myViewBorderWidth)-(myViewSubView)-(myViewBorderWidth)-|")
@"V:|-(myViewSubView)-|"

レイアウトは次のようにする必要があります。

Horizontal: |-(myViewBorderWidth)-|myViewSubview|-(myViewBorderWidth)-|
  Vertical: |-(myViewBorderWidth)-|myViewSubview|-(myViewBorderWidth)-| 

-boundsビューのメソッドを上書きして、境界線のない境界四角形を返そうとしましたが、役に立ちません。

4

4 に答える 4

2

アップデート

あなたの質問が(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、アウトレットを作成しました。そうすれば、コンテンツ ビューとして使用する別のトップレベル ビューを作成し、それをBorderViewcontentViewアウトレットに接続できます。

このように実装するには、とそのBorderViewの間の 4 つの制約のそれぞれにインスタンス変数が必要です。BorderViewcontentView

@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 リポジトリに完全な作業例を入れました。

于 2013-04-06T18:25:40.313 に答える
0

私が見つけた1つの解決策は、メソッドをオーバーロードし、addConstraint:追加する前に制約を変更することです:

- (void)addConstraint:(NSLayoutConstraint *)constraint
{
    if(constraint.firstItem == self || constraint.secondItem == self) {
        if(constraint.firstAttribute == NSLayoutAttributeLeading) {
            constraint.constant += self.leftBorderWidth;
        } else if (constraint.firstAttribute == NSLayoutAttributeTrailing) {
            constraint.constant += self.rightBorderWidth;
        } else if (constraint.firstAttribute == NSLayoutAttributeTop) {
            constraint.constant += self.topBorderWidth;
        } else if (constraint.firstAttribute == NSLayoutAttributeBottom) {
            constraint.constant += self.bottomBorderWidth;
        }
    }

    [super addConstraint:constraint];
}

そして、xxxBorderWidthセッターでこの制約も処理します。

于 2013-04-06T15:14:37.353 に答える
0

境界線のサイズを含むように固有のサイズを設定しようとしましたか?

- (NSSize)intrinsicContentSize
{
    return NSMakeSize(width+bordersize, height+bordersize);
}

次に、コンテンツの圧縮抵抗の優先度を両方向に必須に設定します。

[self setContentCompressionResistancePriority:NSLayoutPriorityRequired forOrientation:NSLayoutConstraintHorizontal];
[self setContentCompressionResistancePriority:NSLayoutPriorityRequired forOrientation:NSLayoutConstraintVertical];
于 2013-04-06T16:03:54.183 に答える