现在是 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
}
希望这会有所帮助!