使NSView不剪贴子视图超出其范围

时间:2023-01-24 08:45:29

Is it possible to make an NSView not clip its subviews that are outside of the bounds? On iOS I would simply set clipsToBounds of my UIView no NO. But NSView doesn't have such a property. I tried experimenting with wantsLayer, masksToBounds, wantsDefaultClipping, but all of these seem to only change the clipping of the drawRect method, not the subviews.

是否可以让NSView不裁剪其在边界之外的子视图?在iOS中,我只需要设置UIView的clipsToBounds不。但NSView没有这样的属性。我尝试了wantsLayer、masksToBounds、wantsDefaultClipping,但是所有这些似乎只改变了drawRect方法的剪裁,而不是子视图。

6 个解决方案

#1


5  

I was able to solve this by overriding wantsDefaultClipping of the subviews to return NO.

我可以通过重写子视图的wantsDefaultClipping来返回NO来解决这个问题。

#2


5  

If you are using autolayout, override alignmentRectInsets to expand the clipping area, making it larger than the alignment rectangle. This example gives a space of 50 on all sides:

如果您正在使用autolayout,重写alignmentrectinset来扩展剪切区域,使其大于对齐矩形。这个例子给出了所有边的50空间:

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

#3


3  

After 5 hours of struggling I've just achieve it. Just change class of any NSView in .storyboard or .xib to NoClippingView and it will NOT clip any of it's subviews.

经过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()
        }
    }

Why do I override masksToBounds in NoClippingLayer? Because some native AppKit classes change this property of all sublayers in runtime without any warnings. For example, NSCollectionView do this for views of it's cells.

为什么我要在NoClippingLayer中重写masksToBounds ?因为一些本地AppKit类在运行时更改了所有子类的此属性,没有任何警告。例如,NSCollectionView对其单元格的视图执行此操作。

#4


0  

wantsDefaultClipping method works... both the parent & the child need to override wantsDefaultClipping.

wantsDefaultClipping方法……父母和孩子都需要重写wantsDefaultClipping。

Once you go that route, however, cleaning up after yourself is cumbersome.

然而,一旦你走了这条路,在你自己之后清理是很麻烦的。

Here's my solution for attaching text to an NSProgressBar:

下面是我将文本附加到NSProgressBar的解决方案:

Thanks to BGHUDAppKit and *

感谢BGHUDAppKit和*

@implementation BGHUDOverlayTextField
- (BOOL) wantsDefaultClipping { return NO; } // thanks *.com
@end


@interface BGHUDProgressIndicator : NSProgressIndicator {

    NSBezierPath *progressPath;
    NSString *themeKey;
   NSString *LayerKey; 
   BGHUDOverlayTextField *customTitleField;
   BOOL hiding;
}

@implementation BGHUDProgressIndicator
- (BOOL) wantsDefaultClipping { return (customTitleField == nil); } // thanks *.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];
   }
}

#5


0  

For people looking for equivalent swift code:
Add this code to a subClass of NSView:

如果您正在寻找相同的swift代码,请将此代码添加到NSView的子类:

override var wantsDefaultClipping:Bool{return false}//avoids clipping the view

覆盖var wantsDefaultClipping:Bool{return false}//避免剪切视图

You don't have to change the parent. as long as the parent has a big enough clipping area. But its convenient to also add this variable to the parent.

你不需要改变父母。只要父母有足够大的剪接面积。但是也可以方便地将这个变量添加到父变量中。

Update 1:

This isnt an easy subject, ive spent weeks researching this and my best solution can be found here: http://stylekit.org/blog/2015/12/24/The-odd-case-of-luck/

这不是一个简单的话题,我花了数周的时间研究这个问题,我的最佳解决方案可以在这里找到:http://stylekit.org/blog/2015/12/24/ the oddcase -of-luck/

Update 2:

To see the above concept in action check this article: http://stylekit.org/blog/2015/12/30/Graphic-framework-for-OSX/

要查看上面的概念,请查看本文:http://stylekit.org/blog/2015/12/30/graphic - frameworkfor osx/

#6


0  

I couldn't get any of these solutions to work. I think you may have to just change your view hierarchy so that views don't draw outside of their frame. You can create an intermediary view that doesn't do any drawing but has a larger frame to allow for a larger area.

我无法得到任何这些解。我认为你可能需要改变你的视图层次结构,这样视图就不会在框架外画了。您可以创建一个中间视图,它不做任何绘图工作,但是有一个更大的框架来允许更大的区域。

#1


5  

I was able to solve this by overriding wantsDefaultClipping of the subviews to return NO.

我可以通过重写子视图的wantsDefaultClipping来返回NO来解决这个问题。

#2


5  

If you are using autolayout, override alignmentRectInsets to expand the clipping area, making it larger than the alignment rectangle. This example gives a space of 50 on all sides:

如果您正在使用autolayout,重写alignmentrectinset来扩展剪切区域,使其大于对齐矩形。这个例子给出了所有边的50空间:

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

#3


3  

After 5 hours of struggling I've just achieve it. Just change class of any NSView in .storyboard or .xib to NoClippingView and it will NOT clip any of it's subviews.

经过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()
        }
    }

Why do I override masksToBounds in NoClippingLayer? Because some native AppKit classes change this property of all sublayers in runtime without any warnings. For example, NSCollectionView do this for views of it's cells.

为什么我要在NoClippingLayer中重写masksToBounds ?因为一些本地AppKit类在运行时更改了所有子类的此属性,没有任何警告。例如,NSCollectionView对其单元格的视图执行此操作。

#4


0  

wantsDefaultClipping method works... both the parent & the child need to override wantsDefaultClipping.

wantsDefaultClipping方法……父母和孩子都需要重写wantsDefaultClipping。

Once you go that route, however, cleaning up after yourself is cumbersome.

然而,一旦你走了这条路,在你自己之后清理是很麻烦的。

Here's my solution for attaching text to an NSProgressBar:

下面是我将文本附加到NSProgressBar的解决方案:

Thanks to BGHUDAppKit and *

感谢BGHUDAppKit和*

@implementation BGHUDOverlayTextField
- (BOOL) wantsDefaultClipping { return NO; } // thanks *.com
@end


@interface BGHUDProgressIndicator : NSProgressIndicator {

    NSBezierPath *progressPath;
    NSString *themeKey;
   NSString *LayerKey; 
   BGHUDOverlayTextField *customTitleField;
   BOOL hiding;
}

@implementation BGHUDProgressIndicator
- (BOOL) wantsDefaultClipping { return (customTitleField == nil); } // thanks *.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];
   }
}

#5


0  

For people looking for equivalent swift code:
Add this code to a subClass of NSView:

如果您正在寻找相同的swift代码,请将此代码添加到NSView的子类:

override var wantsDefaultClipping:Bool{return false}//avoids clipping the view

覆盖var wantsDefaultClipping:Bool{return false}//避免剪切视图

You don't have to change the parent. as long as the parent has a big enough clipping area. But its convenient to also add this variable to the parent.

你不需要改变父母。只要父母有足够大的剪接面积。但是也可以方便地将这个变量添加到父变量中。

Update 1:

This isnt an easy subject, ive spent weeks researching this and my best solution can be found here: http://stylekit.org/blog/2015/12/24/The-odd-case-of-luck/

这不是一个简单的话题,我花了数周的时间研究这个问题,我的最佳解决方案可以在这里找到:http://stylekit.org/blog/2015/12/24/ the oddcase -of-luck/

Update 2:

To see the above concept in action check this article: http://stylekit.org/blog/2015/12/30/Graphic-framework-for-OSX/

要查看上面的概念,请查看本文:http://stylekit.org/blog/2015/12/30/graphic - frameworkfor osx/

#6


0  

I couldn't get any of these solutions to work. I think you may have to just change your view hierarchy so that views don't draw outside of their frame. You can create an intermediary view that doesn't do any drawing but has a larger frame to allow for a larger area.

我无法得到任何这些解。我认为你可能需要改变你的视图层次结构,这样视图就不会在框架外画了。您可以创建一个中间视图,它不做任何绘图工作,但是有一个更大的框架来允许更大的区域。