NSView
not範囲外のサブビューをクリップすることは可能ですか? iOSでは、clipsToBounds
no UIView
のNO
を設定するだけです。しかし、NSView
にはそのようなプロパティはありません。 wantsLayer
、masksToBounds
、wantsDefaultClipping
を試してみましたが、これらはすべてdrawRect
メソッドのクリッピングのみを変更し、サブビューは変更しないようです。
サブビューのwantsDefaultClipping
をオーバーライドしてNO
を返すことで、これを解決することができました。
5時間の苦労の末、私はそれを達成しました。 .storyboardまたは.xib内のNSViewのクラスをNoClippingViewに変更するだけで、そのサブビューはクリップされません。
class NoClippingLayer: CALayer {
override var masksToBounds: Bool {
set {
}
get {
return false
}
}
}
class NoClippingView: OSView {
override var wantsDefaultClipping: Bool {
return false
}
override func awakeFromNib() {
super.awakeFromNib()
wantsLayer = true
layer = NoClippingLayer()
}
}
NoClippingLayerでmasksToBoundsをオーバーライドするのはなぜですか?一部のネイティブAppKitクラスは、実行時にすべてのサブレイヤーのこのプロパティを警告なしに変更するためです。たとえば、NSCollectionViewは、そのセルのビューに対してこれを実行します。
自動レイアウトを使用している場合は、alignmentRectInsets
をオーバーライドしてクリッピング領域を拡張し、配置長方形よりも大きくします。この例では、すべての辺に50のスペースがあります。
override var alignmentRectInsets: NSEdgeInsets {
return NSEdgeInsets(top: 50.0, left: 50.0, bottom: 50.0, right: 50.0)
}
この周りの動作が変わったようです。ビューのレイヤーを境界にマスクしないように設定する必要があります。
view.wantsLayer = true
view.layer?.masksToBounds = false
これらのソリューションを機能させることができませんでした。ビューがフレームの外側に描画されないように、ビュー階層を変更する必要があるかもしれません。描画を行わないが、より大きな領域を可能にするためにより大きなフレームを持つ中間ビューを作成できます。
wantsDefaultClippingメソッドは機能します...親と子の両方がwantsDefaultClippingをオーバーライドする必要があります。
しかし、一度そのルートに行くと、自分の後で片付けをするのは面倒です。
NSProgressBarにテキストを添付するための私の解決策は次のとおりです。
BGHUDAppKitとstackoverflowのおかげで
@implementation BGHUDOverlayTextField
- (BOOL) wantsDefaultClipping { return NO; } // thanks stackoverflow.com
@end
@interface BGHUDProgressIndicator : NSProgressIndicator {
NSBezierPath *progressPath;
NSString *themeKey;
NSString *LayerKey;
BGHUDOverlayTextField *customTitleField;
BOOL hiding;
}
@implementation BGHUDProgressIndicator
- (BOOL) wantsDefaultClipping { return (customTitleField == nil); } // thanks stackoverflow.com
- (void) setCustomTitle: (NSMutableAttributedString *)iTitle
{
if ( !customTitleField )
{
NSRect r = [self bounds];
r.size.height += 10;
customTitleField = [[BGHUDOverlayTextField alloc] initWithFrame:r];
[self addSubview: customTitleField];
}
[customTitleField setAttributedStringValue:iTitle];
}
- (void)setHidden:(BOOL)flag
{
if ( customTitleField && flag && !hiding )
{
hiding = YES;
NSRect fr = [self bounds];
NSRect eraseFr = fr;
eraseFr.size = [customTitleField frame].size;
[self displayRectIgnoringOpacity:fr];
}
else if ( !flag )
hiding = NO;
[super setHidden:flag];
}
- (void) drawRect: (NSRect)fr
{
if ( customTitleField )
{
fr = [self bounds];
NSRect eraseFr = fr;
eraseFr.size = [customTitleField frame].size;
[[NSColor normalSolidFill] set];
NSRectFill( eraseFr );
if ( hiding )
{
[customTitleField setAttributedStringValue:nil];
NSRectFill( eraseFr );
return;
}
[NSGraphicsContext saveGraphicsState];
NSRectClip(fr);
}
[super drawRect:fr];
if ( customTitleField )
{
[NSGraphicsContext restoreGraphicsState];
}
}
同等のSwiftコードを探している人のために:
このコードをNSViewのサブクラスに追加します。
override var wantsDefaultClipping:Bool{return false}//avoids clipping the view
親を変更する必要はありません。親が十分な大きさのクリッピング領域を持っている限り。ただし、この変数を親にも追加すると便利です。
これは簡単なテーマではありません。これを調査するのに何週間も費やしました。私の最善の解決策はここにあります: http://stylekit.org/blog/2015/12/24/The-odd-case-of-luck/
上記の概念の実際を確認するには、次の記事を確認してください。 http://stylekit.org/blog/2015/12/30/Graphic-framework-for-OSX/