NSScrollView 具有未剪辑的内容视图?

发布于 2024-11-03 02:17:50 字数 1163 浏览 1 评论 0 原文

有没有办法设置我的滚动视图不剪辑其内容? (这是一个 NSTextView

我有一个 NSScrollView 的子类,并且希望它的内容被剪切到它的边界。 我尝试覆盖:

- (BOOL) wantsDefaultClipping{
    return NO;
}

MyScrollViewMytextView 中没有任何效果。 在 iOS 中,我会简单地执行: myuitextView.clipsToBounds=NO; 我如何在 Cocoa 中执行此操作?

编辑

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

在此处查看图片

编辑2

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

normal NSScrollView with NSTextView

你看到这个词了吗 * ****编辑***** 第一行就被剪掉了? 我希望文本不要以这种方式被剪切,而是希望它完全显示,并且我将放置一个半透明图像,这样当它在框架之外时它看起来会逐渐消失。

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

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

任何建议表示赞赏。

Is there a way I can set my scrollview not to clip its contents? (Which is a NSTextView)

I have a subclass of NSScrollView and want its content not to be clipped to its bounds.
I have tried overriding:

- (BOOL) wantsDefaultClipping{
    return NO;
}

in MyScrollView and in MytextView without any effect.
In the iOS I would simply would do: myuitextView.clipsToBounds=NO; how can I do this in Cocoa?

EDIT

This is an example of what I want to achieve but in the mac
The scrollview is white, the scroller will never go outside its bounds but the text does since I did myuitextView.clipsToBounds=NO

See picture here

EDIT2

I wouldn't mind clip my view like @Josh suggested. But the real behaviour I would like to have can be explained with this picture:

normal NSScrollView with NSTextView

Do you see the word *****EDIT***** that has being cut in the very first line?
I want the text not to be cut this way, rather I want it to completely appear and I will put a semitransparent image so it looks like it fades off when it's outside the frame.

Q: Why don't I simply put a semitransparent NSImageView on it so it looks like what I want?
A: Because 1.Scroller will be faded as well. Even if I correctly place the semitransparent NSImageView so the scroller looks fine, the cursor/caret will be able to go underneath the semitransparent NSImageView again it does not look good.

I would like to be able to control the area is clipped by NSClipView. I think that would solve my problem. Is there any alternative I have? maybe I can control the caret position or scrolling position through NSTextView so caret will never go near the top/bottom frame limits? or any work-around?

Any advice is appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

又怨 2024-11-10 02:17:50

现在已经是 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 中内容视图的顶部或通过代码,这将导致它位于标题栏下方。然后,告诉它自动更新它的插入

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
}

希望这有帮助!

Now that it's 2016 and we're using vibrant titlebars with full size content views, I'll add my thoughts to how someone might accomplish this. Hopefully, this will help anyone who came here looking for help on this, as it helped me.

This answers the question in regards to scrolling under the titlebar, but you could easily modify this technique to scroll under other things using the insets and caret position.

To get a scroll view (with or without an NSTextView inside of it) to scroll behind a titlebar, you can use:

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

This effectively overlays the titlebar of the NSWindow onto the window's contentView.

To constrain something to the top of the window without knowing the height of the titlebar:

// 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

This allows you to constrain the top of an element to the bottom of the titlebar (and or toolbar + any accessory views it may have). This was shown at WWDC in 2015: https://developer.apple.com/videos/play/wwdc2014/220/

To get the scrollview to scroll under the titlebar but show its scrollbars inside the unobscured part of the window, pin it to the top of the content view in IB or via code, which will cause it to be under the titlebar. Then, tell it to automatically update it's insets:

scrollView.automaticallyAdjustsContentInsets = true

Finally, you can subclass your window and handle the cursor/caret position. There is a presumed bug (or developer error on my part) that doesn't make the scrollview always scroll to the cursor/caret when it goes above or below the content insets of the scrollview.

To fix this, you must manually find the caret position and scroll to see it when the selection changes. Forgive my awful code, but it seems to get the job done. This code belongs in an NSWindow subclass, so self is referring to the window.

// 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
}

Hope this helps!

只是偏爱你 2024-11-10 02:17:50

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

I would simply try to observe the document view's frame and match the scroll view's frame when the document resizes.

心欲静而疯不止 2024-11-10 02:17:50

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

This is a little hairy. AFAIK, NSViews can't draw outside their own frame. At any rate I've never seen it done, and I was somewhat surprised when I realized that UIView allows it by default. But what you probably want to do here is not manipulate clipping rectangles (doing any such thing inside NSScrollView will probably not do what you want or expect), but instead try to cover up the vertically-truncated text lines with either layers or views that are the same color as the background. Perhaps you could subclass NSClipView and override viewBoundsChanged: and/or viewFrameChanged: in order to notice when the text view is being shifted, and adjust your "shades" accordingly.

暗地喜欢 2024-11-10 02:17:50

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

但是,您可以使用 -[NSBezierPath setClip:]

- (void)drawRect:(NSRect)dirtyRect {

    [NSGraphicsContext saveGraphicsState];
    [[NSBezierPath bezierPathWithRect:[[self documentView] frame]] setClip];

    //...

    [NSGraphicsContext restoreGraphicsState];
}

可能(因为您要求)在 NSClipView 子类中使用此代码,但没有太多相关信息,而且我认为您可能很难使其与其滚动正确交互看法。如果是我,我会首先尝试子类化 NSScrollView

You might consider using a translucent layer to achieve this appearance, without actually drawing outside your view. I'm not certain of the rules on iOS, but on the Mac, a view drawing outside its bounds can cause interference with surrounding drawing.

However, you can set the clipping region to be whatever you like inside your scroll view subclass's drawRect: using -[NSBezierPath setClip:]:

- (void)drawRect:(NSRect)dirtyRect {

    [NSGraphicsContext saveGraphicsState];
    [[NSBezierPath bezierPathWithRect:[[self documentView] frame]] setClip];

    //...

    [NSGraphicsContext restoreGraphicsState];
}

It might be possible (since you asked) to use this code in an NSClipView subclass, but there's not much info about that, and I think you may have a hard time making it interact properly with its scroll view. If it were me, I'd try subclassing NSScrollView first.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文