使 NSView 不将子视图剪切到其边界之外

问题描述 投票:0回答:7

是否可以制作

NSView
not 剪辑超出边界的子视图?在 iOS 上,我只需将我的
clipsToBounds
中的
UIView
设置为不
NO
。但
NSView
不具备这样的性质。我尝试尝试使用
wantsLayer
masksToBounds
wantsDefaultClipping
,但所有这些似乎只改变
drawRect
方法的剪辑,而不是子视图。

objective-c cocoa calayer nsview clip
7个回答
19
投票

围绕此的行为似乎已经改变。您只需将视图的图层设置为不遮盖边界即可。

view.wantsLayer = true
view.layer?.masksToBounds = false

15
投票

经过5个小时的努力,我终于实现了。只需将 .storyboard 或 .xib 中任何 NSView 的类更改为 NoClippingView,它就不会剪辑任何子视图。

class NoClippingLayer: CALayer {
    override var masksToBounds: Bool {
        set {

        }
        get {
            return false
        }
    }
}
    
class NoClippingView: NSView {
    override var wantsDefaultClipping: Bool {
        return false
    }

    override func awakeFromNib() {
        super.awakeFromNib()
        wantsLayer = true
        layer = NoClippingLayer()
    }
}

为什么我要覆盖NoClippingLayer中的masksToBounds?因为某些原生 AppKit 类会在运行时更改所有子层的此属性,而不会发出任何警告。例如,NSCollectionView 对其单元格的视图执行此操作。


7
投票

我能够通过覆盖子视图的

wantsDefaultClipping
来返回
NO
来解决这个问题。


5
投票

如果您使用自动布局,请覆盖

alignmentRectInsets
以扩展剪切区域,使其大于对齐矩形。此示例给出了所有边均为 50 的空间:

override var alignmentRectInsets: NSEdgeInsets {
    return NSEdgeInsets(top: 50.0, left: 50.0, bottom: 50.0, right: 50.0)
}

0
投票

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];
   }
}

0
投票

我无法让这些解决方案发挥作用。我认为您可能必须更改视图层次结构,以便视图不会在框架之外绘制。您可以创建一个不执行任何绘图但具有更大框架以允许更大区域的中间视图。


0
投票

macOS 14

NSView
有一个新属性:clipsToBounds,类似于
iOS
UIView 上的属性。

斯威夫特

view.clipsToBounds = true

目标-C

[view setClipsToBounds:YES];
© www.soinside.com 2019 - 2024. All rights reserved.