Как передать события прокрутки родительскому NSScrollView

Мне нужны NSTextViews фиксированного размера внутри большего окна прокрутки. IB требует, чтобы текстовые представления находились внутри своих собственных NSScrollView, даже если их минимальные/максимальные размеры фиксированы, поэтому они фактически не будут прокручиваться. Когда жесты трекпада выполняются внутри фреймов текстового представления (независимо от того, есть ли у них фокус), они захватываются прокруткой текстовых представлений, поэтому ничего не происходит.

Как мне указать прокруткам текстовых представлений передавать события прокрутки до основного прокрутки окна? (Или, возможно, мне следует спросить, как я могу указать главному прокрутке окна обрабатывать эти события самостоятельно, а не передавать их своим дочерним прокруткам.)

Структура ИБ такая:

  • window
    • window’s content view
      • big scrollview for window (desired target for scroll events)
        • box
          • swappable content view in separate xib
            • scrollview for textview
              • textview
And, yes, the window does scroll correctly when the textviews do not have focus.


person Wienke    schedule 09.06.2011    source источник


Ответы (5)


Вам не нужно создавать выход "svActive" для отслеживания вашего супер прокрутки. Просто напишите это предложение в событии scrollWheel:

[[self nextResponder] scrollWheel:event];

это передаст событие следующему ответчику в цепочке ответчиков.

person phaibin    schedule 17.10.2011
comment
Хороший совет. Да, это работает, но, по крайней мере, в этом проекте следующий ответчик не svActive, поэтому необходимо выполнить серию вызовов по цепочке, пока не будет достигнут svActive. Возможно, вызовы nextResponder настолько дешевы, что это не имеет значения, но явная отправка события в svActive делает код более понятным, поэтому я оставляю его как есть. - person Wienke; 19.10.2011

IB не требует наличия текстового представления внутри NSScrollView; это просто значение по умолчанию, потому что большую часть времени вы хотите, чтобы ваше представление прокручивалось. Выберите NSTextView и выберите «Макет» > «Отключить объекты». Обратите внимание, что после этого момента вы больше не сможете перемещать или изменять размер представления в IB. Кажется, это ошибка.

Вот пример того, как поместить два NSTextViews в один NSScrollView.

Добавьте два текстовых представления рядом друг с другом; поместите в них текст, чтобы вы могли видеть, что происходит.

Выберите представления; выберите «Макет» > «Встроить объекты в» > «Прокрутка». Это помещает их в общий NSView внутри NSScrollView.

Выберите текстовые представления; выберите «Макет» > «Отключенные объекты».

Отключите пружины и стойки (автоматическое изменение размера) для каждого текстового представления, чтобы они не изменялись при уменьшении размера прокрутки.

Обратите внимание на высоту просмотра документа (здесь это 175).

Уменьшите вид прокрутки. Это также изменяет размер представления документа (NSView).

Восстановите вид документа до его исходного размера (я установил высоту до 175).

Сделанный! Прокрутка работает так, как вы ожидаете.

person Nicholas Riley    schedule 10.06.2011
comment
Это не сработало в IB3, но я только что попробовал это в Xcode 4, и это может сработать. Кажется, вы не можете настроить его местоположение после того, как отключите его. Похоже, что сейчас он может просто глючить. - person Rob Napier; 10.06.2011
comment
Я только что попробовал это в IB 3.2.6, и у него та же проблема с запретом на изменение его местоположения, но он работает. Однако теперь я понимаю, что мои знания о NSScrollView крайне недостаточны. - person Nicholas Riley; 10.06.2011
comment
Ах; Я вижу разницу в 3.2.6. Если вы выберете текстовое представление в окне документа, то Unembed Objects будет выделено серым цветом. Если вы выберете его, нажав на сам текстовый вид, то он работает (вроде). IB действительно не нравится это в любом случае. Изменение представления во время выполнения было ответом в течение долгого времени. lists.apple.com/archives/cocoa-dev/2001/ май/msg00085.html - person Rob Napier; 10.06.2011
comment
Я заставил это полностью работать в IB (но, как вы сказали, IB это не нравится). Я обновлю свой ответ более длинным объяснением. - person Nicholas Riley; 10.06.2011
comment
@Nicholas Riley: Прекрасный пример. Сохраню для справки. К сожалению, я не могу использовать его в этом проекте, потому что текстовое представление должно находиться в отдельном байте от окна прокрутки. - person Wienke; 10.06.2011
comment
Ну, вы все еще можете сделать то же самое в отдельном xib; Я просто делал все в одном xib, чтобы все было просто. - person Nicholas Riley; 10.06.2011
comment
Xcode 4.5.2 доходит до того, что запрещает NSTextView не встраиваться в его NSScrollView. - person adib; 12.11.2012

Это действительно смущает. После нескольких недель откладывания я предпринял первую попытку заставить подкласс NSScrollView вести себя пассивно — и это оказалось несложным делом.

Вот подкласс:

ч файл:

#import <Cocoa/Cocoa.h>

@interface ScrollViewPassive : NSScrollView {

// This property is assigned a ref to windowController’s main scrollview.
NSScrollView *svActive; 

}

@property (nonatomic, retain) NSScrollView *svActive;

@end

м файл:

#import "ScrollViewPassive.h"

@implementation ScrollViewPassive

@synthesize svActive;

// Pass any gesture scrolling up to the main, active scrollview.
- (void)scrollWheel:(NSEvent *)event {
    [svActive scrollWheel:event];
}

@end

Нет необходимости делать выходы для этих пассивных прокруток; Я даю им их ссылки на основной вид прокрутки сразу после того, как их xibs назначаются в качестве содержимого для NSBox:

    [self.boxDisplayingTextViews setContentView:self.subviewCtllr1.view];
    // A textview's superview's superview is its scrollview:    
    ((ScrollViewPassive *)[[self.subviewCtllr1.textview1 superview] superview]).svActive = self.scrollviewMain;

Вот и все. Работает как шарм.

person Wienke    schedule 25.06.2011

Я считаю, что IB3 и Xcode4 борются с вами, если вы пытаетесь сделать это напрямую, но вы можете сделать это косвенно. Сначала перетащите текстовое представление из прокрутки и удалите прокрутку. Вы получите осиротевшее текстовое представление. Я не знаю, как заставить IB разрешить вам поместить это в ваше окно, но оно будет в вашем NIB. Теперь прикрепите к нему IBOutlet, а во время выполнения выполните addSubview: и настройте его фрейм, чтобы переместить его в любой вид прокрутки, в котором вы хотите его видеть.

По моему опыту, NIB — это хорошо, но каждый сложный NIB, с которым я когда-либо работал, нуждался в окончательной реорганизации кода.

person Rob Napier    schedule 10.06.2011
comment
Спасибо за совет. Здесь, однако, когда я пытаюсь addSubview/setFrame в потерянном текстовом представлении, ничего не появляется. После отладки я вижу, что размер кадра остается равным 0,0. Я попробовал это с потерянным текстовым полем, чтобы убедиться, что мой код правильный и работает нормально. (Но даже если мне удастся установить рамки для просмотра текста, перемещение их непосредственно в область прокрутки окна создаст серьезные трудности с несколькими треугольниками раскрытия блоков и анимацией слоев, включающей снимки содержимого блока.) Пользователю, возможно, придется придерживаться полосы прокрутки… - person Wienke; 10.06.2011

Основываясь на ответе @phaibin, вот как это сделать в Swift 4.2.

Сначала создайте подкласс NSScrollView и переопределите scrollWheel:

class ScrollThingy: NSScrollView{
  override func scrollWheel(with event: NSEvent) {
    self.nextResponder?.scrollWheel(with: event)
  }
}

Затем поместите класс ScrollThingy (или как вы его назовете) на NSScrollView, обернутый вокруг вашего NSTextView. После этого родитель NSScrollView получит событие прокрутки.

person Clifton Labrum    schedule 05.02.2019