Как сохранить положение прокрутки в 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) Если нет, может ли кто-нибудь увидеть, что я делаю неправильно в моем "длинном пути", выше?
Спасибо!
Ответы
Ответ 1
Сохранение одной и той же позиции прокрутки после масштабирования непросто. Одна вещь, которую вам нужно решить, - это то, что вы подразумеваете под "тем же" - хотите ли вы видеть верхнюю, среднюю или нижнюю часть видимой области до того, как масштабирование останется на месте после масштабирования?
Или, более интуитивно, вы хотите, чтобы позиция оставалась на месте в процентах от видимого прямоугольника, равного проценту, который вы прокручиваете вниз по документу при запуске (например, так, что центр большого пальца скроллера не перемещаться вверх или вниз во время масштаба, большой палец просто увеличивается или сжимается).
Если вам нужен последний эффект, один из способов сделать это - получить NSScrollView verticalScroller и horizontalScroller, а затем прочитать их "floatValue" . Они нормированы от 0 до 1, где "0" означает, что вы находитесь в верхней части документа, а 1 означает, что вы в конце. Самое приятное в том, что просить скроллер для этого состоит в том, что если документ короче, чем NSScrollView, скроллер все равно возвращает здравый ответ во всех случаях для "floatValue" , поэтому вам не нужно делать специальный случай.
После изменения размера установите положение прокрутки NSScrollView в том же проценте, что и до масштаба, но, к сожалению, здесь, где я немного машу руками. Я не делал этого некоторое время в своем коде, но, как я помню, вы не можете просто установить "floatValue" NSScrollers напрямую, они будут LOOK прокручиваться, но они фактически не влияют на NSScrollView.
Итак, вам нужно будет написать математику, чтобы вычислить новую верхнюю левую точку в вашем документе на основе процента, который вы хотите пройти через нее - на оси y, например, это будет выглядеть "Если документ теперь короче, чем scrollView contentView, прокрутите до пункта 0, в противном случае прокрутите до точки, которая ((высота содержимогоView - высота документаView) * oldVerticalPercentage) вниз по документу." Ось X, конечно, схожа.
Кроме того, я почти уверен, что вам не нужен звонок для -display здесь, и вообще никогда не следует называть его когда-либо. (-setNeedsDisplay: самое большее.)
-Wil
Ответ 2
Мне кажется, что вам нравится набирать слишком много...; -)
// 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);
Ответ 3
Это старый вопрос, но я надеюсь, что тот, кто ищет это, найдет мой ответ полезным...
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];
}