【问题标题】:Make NSView NOT clip subviews outside of its bounds使 NSView NOT 剪辑子视图超出其范围
【发布时间】:2013-07-21 12:13:40
【问题描述】:

是否可以制作一个NSView not 剪辑其超出范围的子视图?在 iOS 上,我只需将 clipsToBounds 设置为 UIView 不设置 NO。但是NSView 没有这样的属性。我尝试使用wantsLayermasksToBoundswantsDefaultClipping 进行试验,但所有这些似乎只改变了drawRect 方法的剪辑,而不是子视图。

【问题讨论】:

    标签: objective-c cocoa calayer nsview clip


    【解决方案1】:

    对此的行为似乎已经改变。您只需要将视图的图层设置为不遮罩到边界。

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

    【讨论】:

      【解决方案2】:

      经过 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 中的 maskToBounds?因为一些原生 AppKit 类会在运行时更改所有子层的此属性,而不会发出任何警告。例如,NSCollectionView 对其单元格的视图执行此操作。

      【讨论】:

      • 如果您不使用 .xib 或想避免该步骤,只需将 open override func makeBackingLayer() -> CALayer { return NoClippingLayer() } 添加到 NoClippingView
      【解决方案3】:

      我能够通过覆盖子视图的wantsDefaultClipping 以返回NO 来解决此问题。

      【讨论】:

      • NSButton 中使用NSTextField 对此进行了测试,但没有成功。
      • 这是 2013 年公认的答案,但 wantsDefaultClipping 不再正常工作。下面(2018 年)与 CALayers 的后续答案现在应该是公认的这样做的方式。
      【解决方案4】:

      如果您使用自动布局,请覆盖alignmentRectInsets 以扩大剪切区域,使其大于对齐矩形。此示例在所有边上给出了 50 的空间:

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

      【讨论】:

      • 这通过增加视图的框架来扩展剪切区域。如果您希望框架保持不变,但增加剪辑区域(我认为我们在这里尝试这样做),您将调整所有约束以进行补偿。
      【解决方案5】:

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

      【讨论】:

        【解决方案6】:

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

        【讨论】:

          猜你喜欢
          • 2012-08-17
          • 2013-07-07
          • 2018-07-30
          • 2014-01-30
          • 2011-07-02
          • 2015-02-07
          • 2019-04-06
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多