2009-08-18 11 views
14

Ho un NSView (myView) avvolto in un NSScrollView (myScrollView). Usando i pulsanti di zoom in/out, l'utente può modificare la scala di myView. Se l'utente è attualmente spostato su un punto particolare in myView, vorrei mantenere quella parte della vista sullo schermo dopo che lo zoom è avvenuto.Come mantenere la posizione di scorrimento in NSScrollView quando si modifica la scala?

Ho codice che assomiglia a questo:

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

e sembra una sorta di stretta, ma non è giusto e non riesco a capire cosa sto facendo male. Le mie due domande sono:

1) Non v'è un built-in qualcosa sulla falsariga di:

[myView adjustScaleBy: 0.5 whilePreservingLocationInScrollview:myScrollView]; 

2) In caso contrario, qualcuno può vedere quello che sto facendo di sbagliato nel mio "modo lungo intorno "approccio, sopra?

Grazie!

risposta

11

Mantenere la stessa posizione di scorrimento dopo il ridimensionamento non è facile. Una cosa che devi decidere è cosa intendi per "lo stesso": vuoi la parte superiore, centrale o inferiore dell'area visibile prima di ridimensionarla per rimanere in posizione dopo il ridimensionamento?

Oppure, più intuitivamente, vuoi che la posizione rimanga una percentuale in basso del rect visibile uguale alla percentuale di scorrimento del documento quando inizi (ad esempio, così il centro del pollice dello scroller non lo fa? t muoversi su o giù durante una scala, il pollice cresce o si restringe).

Se si desidera il secondo effetto, un modo per farlo è ottenere il VerticalScroller e il HorizontalScroller di NSScrollView, quindi leggere i loro valori di 'floatValue'. Questi sono normalizzati da 0 a 1, dove '0' significa che sei nella parte superiore del documento e 1 significa che sei alla fine. La cosa bella di chiedere allo scroller questo è che se il documento è più corto di NSScrollView, lo scroller restituisce comunque una risposta sana in tutti i casi per "floatValue", quindi non è necessario il caso speciale.

Dopo il ridimensionamento, impostare la posizione di scorrimento di NSScrollView in modo che corrisponda alla percentuale precedente alla scala, ma, purtroppo, ecco dove agita leggermente le mani. Non l'ho fatto da un po 'nel mio codice, ma poiché ricordo che non puoi semplicemente impostare il valore di floatValue di NSScrollers, LOOK scorrerà, ma in realtà non influenzeranno NSScrollView.

Quindi, dovrai scrivere alcuni calcoli matematici per calcolare il nuovo punto in alto a sinistra nel documento in base alla percentuale che vuoi attraversare: sull'asse delle y, ad esempio, assomiglierà, "Se il documento è più corto di ContentView di scrollView, scorri fino al punto 0, altrimenti scorri fino a un punto che ((altezza di contentView - altezza di documentView) * oldVerticalPercentage) scendi il documento." L'asse X è ovviamente simile.

Inoltre, sono quasi sicuro che non hai bisogno di una chiamata per visualizzare qui, e in generale non dovrebbe mai chiamarlo, mai. (-setNeedsDisplay:. Al massimo)

-Wil

+0

L'effetto che voglio è: supponiamo che stia guardando il centro dello schermo. Mentre eseguo lo zoom in avanti (o -out), mantieni quella cosa al centro dello schermo. Grazie! – Olie

+0

Ottima risposta! Invece di impostare il floatValue sugli scroller dopo aver spostato la mia vista sull'origine calcolata, ho chiamato reflectScrolledClipView: sulla mia vista scroll. Questo sembra avere le barre di scorrimento nella giusta posizione. Non sono sicuro se questo è corretto in quanto questa è la prima app per Mac che ho creato, ma funziona per me. – afarnham

7

Me pensa che ti piace di digitare troppo ... ;-)

// 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ì, ho saputo di quelli poco dopo la pubblicazione. Vengo dal mondo di iPhone, e indovinare i nomi di NS per le cose è ancora un po 'lento per me. Grazie! :) – Olie

6

Questa è una vecchia questione, ma spero che qualcuno alla ricerca di questo trova la mia risposta 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]; 
} 
+0

L'ho trovato abbastanza utile. Questo ha funzionato, dopo aver provato diverse altre cose che non lo facevano. Grazie per averlo pubblicato! – jbillfinger

+1

Funziona alla grande! L'unico problema è quando ridimensionate la finestra (e quindi la vista di scorrimento): il contenuto viene di nuovo spostato in basso a sinistra. –

+0

@NicolasMiari Ho anche problemi con il contenuto che scorre in basso a sinistra, hai trovato una soluzione? –

Problemi correlati