NSScrollView внутри другого NSScrollView
У меня есть NSScrollview
вложенный в другой NSScrollview
, Как сделать так, чтобы внутренний вид обрабатывал только горизонтальную прокрутку? Вертикальная прокрутка должна сместить внешний вид.
В настоящее время я сдаю scrollWheel:
Событие к внешнему виду от внутреннего, но оно очень медленное.
2 ответа
У меня также была проблема вложенных представлений прокрутки. Внутренняя прокрутка должна прокручиваться горизонтально, а внешняя прокручиваться вертикально.
При обработке событий прокрутки с помощью волшебной мыши / трекпада важно выбрать только одно из представлений прокрутки для каждого жеста, в противном случае вы увидите странные подергивания, когда ваши пальцы не двигаются идеально ровно. Вы также должны убедиться, что касание трекпада двумя пальцами показывает оба скроллера.
При обработке устаревших событий прокрутки от могучей мыши или мыши со старомодными колесами прокрутки необходимо выбрать правильное представление прокрутки для каждого события, поскольку в событиях нет информации о фазе жеста.
Это мой подкласс для вида внутренней прокрутки, протестированный только в Mountain Lion:
@interface PGEHorizontalScrollView : NSScrollView {
BOOL currentScrollIsHorizontal;
}
@end
@implementation PGEHorizontalScrollView
-(void)scrollWheel:(NSEvent *)theEvent {
/* Ensure that both scrollbars are flashed when the user taps trackpad with two fingers */
if (theEvent.phase==NSEventPhaseMayBegin) {
[super scrollWheel:theEvent];
[[self nextResponder] scrollWheel:theEvent];
return;
}
/* Check the scroll direction only at the beginning of a gesture for modern scrolling devices */
/* Check every event for legacy scrolling devices */
if (theEvent.phase == NSEventPhaseBegan || (theEvent.phase==NSEventPhaseNone && theEvent.momentumPhase==NSEventPhaseNone)) {
currentScrollIsHorizontal = fabs(theEvent.scrollingDeltaX) > fabs(theEvent.scrollingDeltaY);
}
if ( currentScrollIsHorizontal ) {
[super scrollWheel:theEvent];
} else {
[[self nextResponder] scrollWheel:theEvent];
}
}
@end
Моя реализация не всегда перенаправляет события отмены жеста правильно, но по крайней мере в 10.8 это не вызывает проблем.
Это мой подкласс NSScrollView, который делает то, что вы просите. Так как он просто передает события, он не заботится о цепочке респондента, он должен быть таким же производительным, как если бы он не был подклассом (или, по крайней мере, близким)
ч файл
#import <Cocoa/Cocoa.h>
@interface HorizontalScrollView : NSScrollView
@end
И м
@implementation HorizontalScrollView
- (void)scrollWheel:(NSEvent *)theEvent {
NSLog(@"%@", theEvent);
if(theEvent.deltaX !=0)
[super scrollWheel:theEvent];
else
[[self nextResponder] scrollWheel:theEvent];
}
@end
Свифт 4.
Ответ - перевод превосходного ответа Якоба выше, с добавлением флага направления.
Как упоминалось в комментариях к его ответу, могут быть незначительные осложнения, если это не сделано правильно. Когда я просто переадресовывал все вызовы scrollWheel() следующему респонденту, я обнаружил проблемы с некорректным обновлением NSTrackingAreas и смещением всплывающих подсказок и стилей курсора над представлениями NSTextView.
class ECGuidedScrollView: NSScrollView
{
enum ScrollDirection {
case vertical
case horizontal
}
private var currentScrollMatches: Bool = false
private var scrollDirection: ScrollDirection
init(withDirection direction: ScrollDirection) {
scrollDirection = direction
super.init(frame: NSRect.zero)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func scrollWheel(with event: NSEvent)
{
// Ensure that both scrollbars are flashed when the user taps trackpad with two fingers
if event.phase == .mayBegin {
super.scrollWheel(with: event)
nextResponder?.scrollWheel(with: event)
return
}
/*
Check the scroll direction only at the beginning of a gesture for modern scrolling devices
Check every event for legacy scrolling devices
*/
if event.phase == .began || (event.phase == [] && event.momentumPhase == [])
{
switch scrollDirection {
case .vertical:
currentScrollMatches = fabs(event.scrollingDeltaY) > fabs(event.scrollingDeltaX)
case .horizontal:
currentScrollMatches = fabs(event.scrollingDeltaX) > fabs(event.scrollingDeltaY)
}
}
if currentScrollMatches {
super.scrollWheel(with: event)
} else {
self.nextResponder?.scrollWheel(with: event)
}
}
}