Синхронизированная прокрутка между двумя экземплярами NSScrollView - PullRequest
4 голосов
/ 12 ноября 2011

У меня есть два экземпляра NSScrollView, которые представляют один и тот же контент. Второе представление прокрутки, однако, имеет уменьшенную версию представления документа, представленного в первом представлении прокрутки. И ширина, и высота могут быть индивидуально масштабированы, а исходные ограничения ширины и высоты могут быть потеряны, но это не имеет значения.

У меня работает синхронизированная прокрутка, даже принимая во внимание, что второе представление прокрутки должно настроить свое поведение прокрутки на основе масштабирования. Есть одна маленькая загвоздка, из которой я вырывал свои волосы:

  • Поскольку оба вида успешно прокручиваются по меньшему виду, необходимо медленно догонять более крупный вид, чтобы они оба "достигли" конца своего документа одновременно. Сейчас этого не происходит, и в результате меньший вид находится в конце документа перед большим.

Код для синхронизированной прокрутки основан на примере из документации Apple под названием «Синхронизация представлений прокрутки». Я адаптировал synchronizedViewContentBoundsDidChange: к следующему коду:

- (void) synchronizedViewContentBoundsDidChange: (NSNotification *) notification {

    // get the changed content view from the notification
    NSClipView *changedContentView = [notification object];

    // get the origin of the NSClipView of the scroll view that
    // we're watching
    NSPoint changedBoundsOrigin = [changedContentView documentVisibleRect].origin;;

    // get our current origin
    NSPoint curOffset = [[self contentView] bounds].origin;
    NSPoint newOffset = curOffset;

    // scrolling is synchronized in the horizontal plane
    // so only modify the x component of the offset
    // "scale" variable will correct for difference in size between views
    NSSize ownSize = [[self documentView] frame].size;
    NSSize otherSize = [[[self synchronizedScrollView] documentView] frame].size;
    float scale = otherSize.width / ownSize.width;
    newOffset.x = floor(changedBoundsOrigin.x / scale);

    // if our synced position is different from our current
    // position, reposition our content view
    if (!NSEqualPoints(curOffset, changedBoundsOrigin)) {
        // note that a scroll view watching this one will
        // get notified here
        [[self contentView] scrollToPoint:newOffset];
        // we have to tell the NSScrollView to update its
        // scrollers
        [self reflectScrolledClipView:[self contentView]];
    }

}

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

РЕДАКТИРОВАТЬ: Некоторое пояснение, поскольку это приводило в замешательство, когда я сам читал его обратно: меньший вид должен замедляться, когда прокрутка первого вида достигает конца. Это, вероятно, будет означать переоценку этого коэффициента масштабирования ... но как?

РЕДАКТИРОВАТЬ 2: я изменил метод на основе предложения Алекса:

    NSScroller *myScroll = [self horizontalScroller];
NSScroller *otherScroll = [[self synchronizedScrollView] horizontalScroller];

//[otherScroll setFloatValue: [myScroll floatValue]];

NSLog(@"My scroller value: %f", [myScroll floatValue]);
NSLog(@"Other scroller value: %f", [otherScroll floatValue]);

// Get the changed content view from the notification.
NSClipView *changedContentView = [notification object];

// Get the origin of the NSClipView of the scroll view that we're watching.
NSPoint changedBoundsOrigin = [changedContentView documentVisibleRect].origin;;

// Get our current origin.
NSPoint curOffset = [[self contentView] bounds].origin;
NSPoint newOffset = curOffset;

// Scrolling is synchronized in the horizontal plane so only modify the x component of the offset.
NSSize ownSize = [[self documentView] frame].size;
newOffset.x = floor(ownSize.width * [otherScroll floatValue]);

// If our synced position is different from our current position, reposition our content view.
if (!NSEqualPoints(curOffset, changedBoundsOrigin)) {
    // Note that a scroll view watching this one will get notified here.
    [[self contentView] scrollToPoint: newOffset];
    // We have to tell the NSScrollView to update its scrollers.
    [self reflectScrolledClipView:[self contentView]];
}

При использовании этого метода меньший вид «обгоняется» большим, когда оба скроллера достигают значения 0,7, что нехорошо. Затем увеличенное изображение прокручивается за его конец документа.

Ответы [ 2 ]

1 голос
/ 18 ноября 2011

Я наконец понял это. Ответ от Алекса был хорошим намеком, но не полным решением, так как установка значения плавающего значения скроллера ничего не дает. Это значение требует перевода в конкретные координаты, в которые представление прокрутки должно прокручивать свое содержимое.

Однако из-за различий в размерах вида прокручиваемого документа вы не можете просто использовать это значение, поскольку уменьшенный вид в какой-то момент будет заменен «нормальным» видом. Это заставит обычный вид прокручивать его конец документа.

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

Код:

// Scrolling is synchronized in the horizontal plane so only modify the x component of the offset.
    NSSize ownSize = [[self documentView] frame].size;
    newOffset.x = MAX(floor(ownSize.width * [otherScroll floatValue] - [self frame].size.width),0);

Ожидание достигается путем вычитания ширины вида прокрутки из ширины, умноженной на значение скроллера. Когда уменьшенная версия по-прежнему пересекает ширину пикселей первого просмотра с прокруткой, это вычисление приведет к отрицательному смещению. Использование MAX предотвратит странные эффекты, и исходный вид будет спокойно ждать, пока значение не станет положительным, а затем начнет собственную прокрутку. Это решение также работает, когда пользователь изменяет размер окна приложения.

1 голос
/ 16 ноября 2011

Я думаю, вы могли бы подойти к этому неправильно. Я думаю, что вы должны получать процент от того, как далеко вниз прокручивается каждая прокрутка по отношению к себе, и применять это к другому представлению. Один из примеров того, как это можно сделать, - использовать NSScroller's -floatValue:

NSScroller *myScroll = [self verticalScroller];
NSScroller *otherScroll = [otherScrollView verticalScroller];

[myScroll setFloatValue:otherScroll.floatValue];
...