【问题标题】:NSScrollView with unclipped content view?带有未剪辑内容视图的 NSScrollView?
【发布时间】:2011-04-25 08:37:39
【问题描述】:

有没有办法让我的滚动视图不剪裁其内容? (这是NSTextView

我有一个 NSScrollView 的子类,并希望将其内容 not 裁剪到其边界。 我尝试过覆盖:

- (BOOL) wantsDefaultClipping{
    return NO;
}

MyScrollViewMytextView 中没有任何影响。 在 iOS 中我会简单地这样做:myuitextView.clipsToBounds=NO; 如何在 Cocoa 中做到这一点?

编辑

这是我想要实现但在 mac 中的示例 滚动视图是白色的,滚动条永远不会超出它的范围,但是自从我做了myuitextView.clipsToBounds=NO

See picture here

EDIT2

我不介意像@Josh 建议的那样剪辑我的观点。但是我想要的真实行为可以用这张图来解释:

你看到第一行被删掉的*****EDIT*****这个词了吗? 我希望文本不要以这种方式被剪切,而是希望它完全出现,我会放置一个半透明的图像,这样当它在框架外时它看起来会消失。

问:为什么我不简单地在上面放一个半透明的 NSImageView 让它看起来像我想要的那样? A:因为 1.Scroller 也会被淡化。即使我正确放置了半透明的 NSImageView 以使滚动条看起来很好,光标/插入符号将能够再次进入半透明的 NSImageView 下方,它看起来并不好。

我希望能够控制被 NSClipView 裁剪的区域。我认为这会解决我的问题。我有其他选择吗?也许我可以通过 NSTextView 控制插入符号位置或滚动位置,因此插入符号永远不会接近顶部/底部框架限制?或任何解决方法?

感谢任何建议。

【问题讨论】:

    标签: iphone objective-c cocoa macos


    【解决方案1】:

    现在是 2016 年,我们正在使用具有完整尺寸内容视图的充满活力的标题栏,我将添加我的想法,看看有人如何实现这一点。希望这会帮助任何来这里寻求帮助的人,因为它帮助了我。

    这回答了有关在标题栏下滚动的问题,但您可以轻松地修改此技术以使用插入和插入符号位置在其他内容下滚动。

    要让滚动视图(其中包含或不包含 NSTextView)滚动到标题栏后面,您可以使用:

        // For transparent title.
        window.titlebarAppearsTransparent = true
        window.styleMask = window.styleMask | NSFullSizeContentViewWindowMask
        window.appearance = NSAppearance(named: NSAppearanceNameVibrantLight)
    

    这有效地将 NSWindow 的标题栏覆盖到窗口的 contentView 上。

    在不知道标题栏高度的情况下将某些内容限制在窗口顶部:

    // Make a constraint for SOMEVIEW to the top layout guide of the window:
    let topEdgeConstraint = NSLayoutConstraint(
    item: SOMEVIEW, attribute: NSLayoutAttribute.Top,
    relatedBy: NSLayoutRelation.Equal,
    toItem: window.contentLayoutGuide,
    attribute: NSLayoutAttribute.Top, multiplier: 1.0, constant: 0.0)
    
    // Turn the constraint on automatically:    
    topEdgeConstraint.active = true
    

    这允许您将元素的顶部限制在标题栏(和/或工具栏+它可能具有的任何附件视图)的底部。这在 2015 年的 WWDC 上展示过:https://developer.apple.com/videos/play/wwdc2014/220/

    要让滚动视图在标题栏下滚动但在窗口的未遮挡部分显示其滚动条,请将其固定到 IB 中的内容视图的顶部或通过代码,这将导致位于标题栏下方。然后,告诉它自动更新它的 insets

    scrollView.automaticallyAdjustsContentInsets = true
    

    最后,您可以对窗口进行子类化并处理光标/插入符号的位置。有一个假定的错误(或我的开发人员错误)不会使滚动视图在滚动视图的内容插入上方或下方时始终滚动到光标/插入符号。

    要解决此问题,您必须手动找到插入符号位置并在选择更改时滚动以查看它。原谅我糟糕的代码,但它似乎完成了工作。这段代码属于 NSWindow 子类,所以self 指的是窗口。

    // MARK: NSTextViewDelegate
    func textViewDidChangeSelection(notification: NSNotification) {
        scrollIfCaretIsObscured()
        textView.needsDisplay = true // Prevents a selection rendering glitch from sticking around
    }
    
    // MARK: My Scrolling Functions
    
    func scrollIfCaretIsObscured() {
        let rect = caretRectInWindow()
        let y: CGFloat = caretYPositionInWindow() - rect.height
        // Todo: Make this consider the text view's ruler height, if present:
        let tbHeight: CGFloat
        if textView.rulerVisible {
            // Ruler is shown:
            tbHeight = (try! titlebarHeight()) + textViewRulerHeight
        } else {
            // Ruler is hidden
            tbHeight = try! titlebarHeight()
        }
        if y <= tbHeight {
            scrollToCursor()
        }
    }
    
    func caretYPositionInWindow() -> CGFloat {
        let caretRectInWin: NSRect = caretRectInWindow()
        let caretYPosInWin: CGFloat = self.contentView!.frame.height - caretRectInWin.origin.y
        return caretYPosInWin
    }
    
    func caretRectInWindow() -> CGRect {
        // My own version of something based off of an old, outdated
        // answer on stack overflow.
        // Credit: http://stackoverflow.com/questions/6948914/nspopover-below-caret-in-nstextview
        let caretRect: NSRect = textView.firstRectForCharacterRange(textView.selectedRange(), actualRange: nil)
        let caretRectInWin: NSRect = self.convertRectFromScreen(caretRect)
        return caretRectInWin
    }
    
    /// Scrolls to the current caret position inside the text view.
    /// - Parameter textView: The specified text view to work with.
    func scrollToCursor() {
        let caretRectInScreenCoords = textView.firstRectForCharacterRange(textView.selectedRange(), actualRange: nil)
        let caretRectInWindowCoords = self.convertRectFromScreen(caretRectInScreenCoords)
        let caretRectInTextView = textView.convertRect(caretRectInWindowCoords, fromView: nil)
        textView.scrollRectToVisible(caretRectInTextView)
    }
    
    enum WindowErrors: ErrorType {
        case CannotFindTitlebarHeight
    }
    
    /// Calculates the combined height of the titlebar and toolbar.
    /// Don't try this at home.
    func titlebarHeight() throws -> CGFloat {
        // Try the official way first:
        if self.titlebarAccessoryViewControllers.count > 0 {
            let textViewInspectorBar = self.titlebarAccessoryViewControllers[0].view
            if let titlebarAccessoryClipView = textViewInspectorBar.superview {
                if let view = titlebarAccessoryClipView.superview {
                    if let titleBarView = view.superview {
                        let titleBarHeight: CGFloat = titleBarView.frame.height
                        return titleBarHeight
                    }
                }
            }
        }
        throw WindowErrors.CannotFindTitlebarHeight
    }
    

    希望这会有所帮助!

    【讨论】:

      【解决方案2】:

      我只是尝试观察文档视图的frame,并在文档调整大小时匹配滚动视图的frame

      【讨论】:

      • 即使文档没有调整大小,我也想实现这一点......例如,当用户键入很多内容时,滚动视图不应剪切其内容。不适合滚动视图框架的文本应该出现在滚动视图边界之外。
      【解决方案3】:

      这有点毛茸茸的。 AFAIK,NSViews 不能在自己的框架之外绘制。无论如何,我从未见过它完成,当我意识到 UIView 默认允许它时,我有些惊讶。但是你在这里可能想要做的不是操作剪切矩形(在 NSScrollView 中做任何这样的事情可能不会做你想要或期望的),而是尝试用图层或视图覆盖垂直截断的文本行与背景颜色相同。也许你可以继承 NSClipView 并覆盖 viewBoundsChanged: 和/或 viewFrameChanged: 以便注意到文本视图何时被移动,并相应地调整你的“阴影”。

      【讨论】:

      • 但是如果我这样做,插入符号可能仍然位于“阴影”下方,例如在开始打字时等。我做对了吗?这是我不想避免的。我目前的方法是:将“阴影”放在滚动视图和子类 NSScroller 的顶部以调整插槽框架大小,这样我的垂直滚动条就不会被“阴影”隐藏。它仍然有同样的插入符号问题。 ;(
      • 没错,插入符号可能会消失,但在正常情况下也总是有可能。您始终可以滚动以使插入符号消失,但在您的情况下,它会更快消失,因为您覆盖了几个像素。
      • NSView 可以肯定在其框架之外绘制;您所要做的就是更改其drawRect: 中的剪辑矩形;它可以在其窗口的内容视图内的任何地方绘制。这不是一个好主意(尽管你必须做一些事情来做一些事情,比如自定义对焦环)。 (幸运的是)窗口有一个“更难”的剪辑,可能是因为无论你告诉它什么,系统都不会为在活动窗口之外重绘而烦恼。
      【解决方案4】:

      您可以考虑使用半透明图层来实现这种外观,而无需实际绘制到您的视图之外。我不确定 iOS 上的规则,但在 Mac 上,超出其边界的视图绘制可能会干扰周围的绘制。

      但是,您可以使用-[NSBezierPath setClip:] 在滚动视图子类的drawRect: 中将剪切区域设置为您喜欢的任何内容:

      - (void)drawRect:(NSRect)dirtyRect {
      
          [NSGraphicsContext saveGraphicsState];
          [[NSBezierPath bezierPathWithRect:[[self documentView] frame]] setClip];
      
          //...
      
          [NSGraphicsContext restoreGraphicsState];
      }
      

      可能(因为你问过)在NSClipView 子类中使用此代码,但没有太多关于此的信息,我认为你可能很难让它与其滚动视图正确交互。如果是我,我会先尝试子类化NSScrollView

      【讨论】:

      • 你知道我把这段代码放在 NSClipView 的子类中会发生什么吗?我更新了我的问题,因为我认为它不是很清楚。
      • @nacho4d:不,我不知道。关于NSClipView 的信息不多,如果您将其子类化,您可能很难让它与其滚动视图正确交互。如果是我,我想我会尝试继承 NSScrollView,但你当然可以尝试 NSScrollView
      猜你喜欢
      • 2013-09-12
      • 2015-04-22
      • 2019-12-12
      • 1970-01-01
      • 1970-01-01
      • 2019-06-07
      • 2018-01-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多