Теперь, когда наступил 2016 год, и мы используем яркие заголовки с полноразмерными представлениями контента, я добавлю свои мысли о том, как кто-то может это сделать. Надеюсь, это поможет всем, кто пришел сюда за помощью, так же, как и мне.
Это отвечает на вопрос о прокрутке под заголовком, но вы можете легко изменить эту технику для прокрутки под другими вещами, используя вставки и положение каретки.
Чтобы получить представление прокрутки (с или без NSTextView внутри него) для прокрутки за заголовком, вы можете использовать:
// For transparent title.
window.titlebarAppearsTransparent = true
window.styleMask = window.styleMask | NSFullSizeContentViewWindowMask
window.appearance = NSAppearance(named: NSAppearanceNameVibrantLight)
Это эффективно накладывает заголовок окна NSWindow на представление содержимого окна.
Чтобы ограничить что-либо в верхней части окна, не зная высоты заголовка:
// 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
Это позволяет ограничить верх элемента элементом нижней части строки заголовка (и / или панели инструментов + любые дополнительные виды, которые у него могут быть). Это было показано на WWDC в 2015 году: 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: /6206250/nspopover-nizhe-karetki-v-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
}
Надеюсь, это поможет!