Как сохранить положение прокрутки в NSScrollView при изменении масштаба?


У меня есть NSView (myView), завернутый в nsscrollview (myScrollView). Используя кнопки увеличения/уменьшения масштаба, пользователь может изменять масштаб myView. Если пользователь в данный момент прокручивается до определенного места в myView, я хотел бы сохранить эту часть представления на экране после масштабирования.

У меня есть код, который выглядит так:

    // preserve current position in scrollview
    NSRect oldVisibleRect = [[myScrollView contentView] documentVisibleRect];
    NSPoint oldCenter = NSPointFromCGPoint(CGPointMake(oldVisibleRect.origin.x + (oldVisibleRect.size.width  / 2.0),
                                                       oldVisibleRect.origin.y + (oldVisibleRect.size.height / 2.0)));

    // adjust my zoom
    ++displayZoom;
    [self scaleUnitSquareToSize:NSSizeFromCGSize(CGSizeMake(0.5, 0.5))];
    [self calculateBounds];  // make sure my frame & bounds are at least as big as the visible content view
    [self display];

    // Adjust scroll view to keep the same position.
    NSRect newVisibleRect = [[myScrollView contentView] documentVisibleRect];
    NSPoint newOffset = NSPointFromCGPoint(CGPointMake((oldCenter.x * 0.5) - (newVisibleRect.size.width  / 2.0),
                                                       (oldCenter.y * 0.5) - (newVisibleRect.size.height / 2.0)));
    if (newOffset.x < 0)
        newOffset.x = 0;
    if (newOffset.y < 0)
        newOffset.y = 0;

    [[myScrollView contentView] scrollToPoint: newOffset];
    [myScrollView reflectScrolledClipView: [myScrollView contentView]];

И кажется, что это близко, но это не совсем правильно, и я не могу понять, что я делаю неправильно. Мои два вопроса:

1) Разве нет встроенного чего-то вроде:

   [myView adjustScaleBy: 0.5 whilePreservingLocationInScrollview:myScrollView];
2) Если нет, Может ли кто-нибудь увидеть, что я делаю неправильно в моем подходе "долгий путь вокруг", выше?

Спасибо!

3 14

3 ответа:

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

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

Если вы хотите получить последний эффект, один из способов сделать это-получить verticalScroller и horizontalScroller NSScrollView, а затем прочитать их 'floatValue'. они нормализованы от 0 до 1, где' 0 ' означает, что вы находитесь в верхней части документа, а 1 означает, что вы находитесь в конце. Хорошая вещь в том, чтобы попросить скроллер об этом, заключается в том, что если документ короче, чем NSScrollView, скроллер все равно возвращает нормальный ответ во всех случаях для "floatValue", так что вам не нужно делать это в особом случае.

После изменения размера установите положение прокрутки NSScrollView в том же проценте, что и до масштабирования, но, к сожалению, здесь я немного размахиваю руками. Я уже давно не делал этого в своем коде, но, насколько я помню, вы не можете просто установить значение floatValue NSScrollers напрямую - они будут выглядеть прокрученными, но на самом деле не повлияют на NSScrollView.

Итак, вам придется написать математику, чтобы вычислить новый верхний левый угол. точку в документе, на основании процента вы хотите быть через нее - на оси Y, например, это будет выглядеть как, "если документ теперь короче, чем представления ScrollView странице просмотр содержимого выделите точку 0, в противном случае перейдите к пункту то ((высоту странице просмотр содержимого - высоту документа просмотр) * oldVerticalPercentage) документа.- Ось Х, конечно, похожа.

Кроме того, я почти уверен, что вы не нуждаетесь в вызове для отображения здесь, и вообще не должны никогда звонить ему, никогда. (- setNeedsDisplay: самое большее.)

- Wil

Мне кажется, ты слишком любишь печатать...; -)

// instead of this:
NSPoint oldCenter = NSPointFromCGPoint(CGPointMake(oldVisibleRect.origin.x +
    (oldVisibleRect.size.width  / 2.0),

// use this:
NSPoint oldCenter = NSMakePoint(NSMidX(oldVisibleRect), NSMaxY(oldVisibleRect));

// likewise instead of this:
[self scaleUnitSquareToSize:NSSizeFromCGSize(CGSizeMake(0.5, 0.5))];

// use this:
[self scaleUnitSquareToSize:NSMakeSize(0.5, 0.5)];

// and instead of this
NSPoint newOffset = NSPointFromCGPoint(CGPointMake(
    (oldCenter.x * 0.5) - (newVisibleRect.size.width  / 2.0),
    (oldCenter.y * 0.5) - (newVisibleRect.size.height / 2.0)));

// use this:
NSPoint newOffset NSMakePoint(
    (oldCenter.x - NSWidth(newVisibleRect)) / 2.f,
    (oldCenter.y - NSHeight(newVisibleRect)) / 2.f);

Это старый вопрос, но я надеюсь, что кто-то, ищущий его, найдет мой ответ полезным...

float zoomFactor = 1.3;

-(void)zoomIn
{
    NSRect visible = [scrollView documentVisibleRect];
    NSRect newrect = NSInsetRect(visible, NSWidth(visible)*(1 - 1/zoomFactor)/2.0, NSHeight(visible)*(1 - 1/zoomFactor)/2.0);
    NSRect frame = [scrollView.documentView frame];

    [scrollView.documentView scaleUnitSquareToSize:NSMakeSize(zoomFactor, zoomFactor)];
    [scrollView.documentView setFrame:NSMakeRect(0, 0, frame.size.width * zoomFactor, frame.size.height * zoomFactor)];

    [[scrollView documentView] scrollPoint:newrect.origin];
}

-(void)zoomOut
{
    NSRect visible = [scrollView documentVisibleRect];
    NSRect newrect = NSOffsetRect(visible, -NSWidth(visible)*(zoomFactor - 1)/2.0, -NSHeight(visible)*(zoomFactor - 1)/2.0);

    NSRect frame = [scrollView.documentView frame];

    [scrollView.documentView scaleUnitSquareToSize:NSMakeSize(1/zoomFactor, 1/zoomFactor)];
    [scrollView.documentView setFrame:NSMakeRect(0, 0, frame.size.width / zoomFactor, frame.size.height / zoomFactor)];

    [[scrollView documentView] scrollPoint:newrect.origin];
}