Question Comment maintenir la position de défilement dans NSScrollView lorsque vous changez d'échelle?


J'ai un NSView (myView) enveloppé dans un NSScrollView (myScrollView). En utilisant les boutons de zoom avant / arrière, l'utilisateur peut modifier l'échelle de myView. Si l'utilisateur fait actuellement défiler un endroit particulier dans myView, j'aimerais conserver cette partie de la vue à l'écran après le zoom.

J'ai le code qui ressemble à ceci:

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

Et cela semble un peu proche, mais ce n'est pas tout à fait correct et je ne peux pas comprendre ce que je fais mal. Mes deux questions sont:

1) N'y a-t-il pas quelque chose de similaire à:

   [myView adjustScaleBy: 0.5 whilePreservingLocationInScrollview:myScrollView];

2) Si ce n’est pas le cas, est-ce que quelqu'un peut voir ce que je fais mal dans mon approche du long terme, ci-dessus?

Merci!


14
2017-08-18 22:29


origine


Réponses:


Garder la même position de défilement après la mise à l'échelle n'est pas facile. Une chose que vous devez décider est ce que vous entendez par "le même" - voulez-vous que le haut, le milieu ou le bas de la zone visible avant la mise à l'échelle reste en place après la mise à l'échelle?

Ou, plus intuitivement, voulez-vous que la position qui reste en place un pourcentage dans le rectangle visible soit égale au pourcentage que vous faites défiler dans le document lorsque vous démarrez (par exemple, le centre du curseur ne se déplace pas vers le haut ou vers le bas pendant une balance, le pouce pousse ou rétrécit).

Si vous voulez le dernier effet, une façon de le faire est de récupérer les verticalScroller et horizontalScroller du NSScrollView, puis de lire leurs 'floatValue'. Ceux-ci sont normalisés de 0 à 1, où "0" signifie que vous êtes en haut du document et 1 signifie que vous êtes à la fin. La bonne chose à propos de demander à scroller est que si le document est plus court que le NSScrollView, le scroller retourne toujours une réponse sensée dans tous les cas pour «floatValue», vous n'avez donc pas à faire de cas particulier.

Après avoir redimensionné, définissez la position de défilement du NSScrollView pour qu'elle soit identique à celle avant l'échelle - mais, malheureusement, voici où je agite un peu les mains. Je ne l'ai pas fait depuis longtemps dans mon code, mais si je me souviens bien, vous ne pouvez pas simplement définir directement les valeurs de floatValue des NSScrollers - elles vont défiler, mais elles n'affecteront pas réellement NSScrollView.

Donc, vous devrez écrire des calculs pour calculer le nouveau point supérieur gauche de votre document en fonction du pourcentage que vous voulez parcourir - sur l'axe des y, par exemple, "Si le document est maintenant plus court que le contenu de scrollView, faites défiler jusqu'au point 0, sinon faites défiler le document jusqu'à un point ((hauteur de contentView - hauteur de documentView) * oldVerticalPercentage). " L'axe X est bien sûr similaire.

De plus, je suis presque certain que vous n’avez pas besoin d’un appel à l’affichage ici, et en général, vous ne devriez jamais l’appeler, jamais. (-setNeedsDisplay: au plus)

-Wil


11
2017-09-20 03:17



Je pense que tu aimes trop taper ... ;-)

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

7
2017-09-30 15:37



C'est une vieille question, mais j'espère que quelqu'un qui cherche cela trouve ma réponse utile ...

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

6
2018-06-16 05:40