2009-08-18 10 views
14

Tengo un NSView (myView) envuelto en un NSScrollView (myScrollView). Usando botones de acercamiento/alejamiento, el usuario puede alterar la escala de myView. Si el usuario se desplaza actualmente a un lugar específico en myView, me gustaría mantener esa parte de la vista en pantalla después de que se haya realizado el zoom.¿Cómo mantener la posición de desplazamiento en NSScrollView cuando se cambia de escala?

Tengo código que se parece a esto:

// 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]]; 

y parece una especie de cerca, pero no es del todo bien y no puedo averiguar lo que estoy haciendo mal. Mis dos preguntas son:

1) ¿No hay una incorporado en algo en la línea de:

[myView adjustScaleBy: 0.5 whilePreservingLocationInScrollview:myScrollView]; 

2) Si no es así, ¿alguien puede ver lo que estoy haciendo mal en mi "largo camino alrededor de "acercarse, arriba?

Gracias!

Respuesta

11

Mantener la misma posición de desplazamiento después del escalado no es fácil. Una cosa que debe decidir es lo que quiere decir con "lo mismo": ¿desea la parte superior, media o inferior del área visible antes de escalar para permanecer en su lugar después de escalar?

O, más intuitivamente, ¿desea que la posición que permanece en su lugar un porcentaje abajo del rect visible igual al porcentaje que se desplaza hacia abajo del documento cuando comienza (por ejemplo, el centro del dedo pulgar no t se mueve hacia arriba o hacia abajo durante una escala, el pulgar simplemente crece o se encoge).

Si desea el último efecto, una manera de hacerlo es obtener el desplazamiento vertical y el desplazamiento horizontal NSScrollView, y luego leer su 'floatValue'. Estos están normalizados de 0 a 1, donde '0' significa que está en la parte superior del documento y 1 significa que está al final. Lo bueno de pedirle esto al desplazador es que, si el documento es más corto que NSScrollView, el desplazador aún devuelve una respuesta correcta en todos los casos para 'floatValue', por lo que no es necesario que tenga un caso especial.

Después de cambiar el tamaño, configure la posición de desplazamiento del NSScrollView para que sea el mismo porcentaje que tenía antes de la escala, pero, por desgracia, aquí es donde agito un poco las manos. No he hecho esto desde hace tiempo en mi código, pero, como recuerdo, no puedes simplemente configurar directamente los valores floatValue de los NSScrollers, sino que mirarán desplazarse, pero en realidad no afectarán a NSScrollView.

Por lo tanto, tendrá que escribir algunos cálculos matemáticos para calcular el nuevo punto superior izquierdo en su documento en función del porcentaje que desea utilizar: en el eje y, por ejemplo, se verá así: "Si el documento ahora es más corto que el contentView de scrollView, desplácese hasta el punto 0, de lo contrario desplácese hasta un punto que sea ((height of contentView - height of documentView) * oldVerticalPercentage) abajo del documento." El eje X es por supuesto similar.

Además, estoy casi seguro de que no necesita una llamada para -display aquí, y en general nunca debería llamarlo, nunca. (-setNeedsDisplay:. Como máximo)

-Wil

+0

El efecto que quiero es: supongo que estoy mirando en el medio de la pantalla. Al acercar o alejar (zoom), mantén esa cosa en el medio de la pantalla. Gracias! – Olie

+0

¡Excelente respuesta! En lugar de configurar floatValue en los scrollers después de haber desplazado mi vista al origen calculado, llamé a reflectScrolledClipView: en mi vista de desplazamiento. Esto parece tener las barras de desplazamiento en la posición correcta. No estoy seguro de si esto es correcto ya que esta es la primera aplicación de Mac que he creado, pero funciona para mí. – afarnham

7

Me piensa que usted tiene gusto de escribir demasiado ... ;-)

// 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); 
+0

Sí, me enteré de eso poco después de la publicación. Vengo del mundo del iPhone, y adivinar los nombres NS para las cosas todavía es un poco lento para mí. ¡Gracias! :) – Olie

6

Ésta es una vieja pregunta, pero espero que alguien que busca este encuentra mi respuesta útil ...

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]; 
} 
+0

Lo encontré bastante útil. Esto funcionó, después de probar varias otras cosas que no lo hicieron. ¡Gracias por publicarlo! – jbillfinger

+1

¡Esto funciona genial! El único problema es cuando se cambia el tamaño de la ventana (y, por lo tanto, de la vista de desplazamiento): el contenido se desplaza de nuevo a la esquina inferior izquierda. –

+0

@NicolasMiari También estoy teniendo problemas para que el contenido se desplaza hacia abajo a la izquierda, ¿ha encontrado una solución? –

Cuestiones relacionadas