Pagine

2012-12-14

I segreti degli NSSplitView - III

Nella prima puntata abbiamo visto come comunicare allo SplitView le dimensioni massime e minime delle proprie viste, mentre nella seconda puntata abbiamo implementato un metodo per associare il cambio delle dimensioni solo alla vista centrale (nell'ipotesi di tre viste).
In questa breve ultima puntata vediamo qualche finezza, che può far pensare all'utente che ci siamo sbattuti molto per farlo lavorare bene.

La vista che scompare sul serio

Abbiamo visto che con il metodo del delegate splitView:canCollapseSubview possiamo lasciare all'utente la possibilità di far scomparire la vista sulla destra, pur avendo il vincolo sulla sua dimensione minima. Se però non vogliamo lasciare questa libertà, potremmo pensare che non implementando questo metodo (oppure fornendo in ritorno sempre false), scopriremo presto che passando con il mouse sul bordo destro a vista collassata riusciamo ancora a prendere la vista invisibile e trascinarla, scoprendo gli altarini: cioè, se non facciamo nulla, si apre uno spazio vuoto: ricordiamo infatti che per evitare il caos abbiamo deciso di togliere letteralmente la vista da dentro lo SplitView.
Dobbiamo quindi convincere l'utente a non fare quella operazione e l'unico modo è... di impedirglielo.
Infatti, con il metodo SplitView:shouldHideDividerAtIndex: possiamo far scomparire definitivamente il divisore e senza quello l'utente non potrà più fare nulla di dannoso. È sufficiente verificare che il divisore sia quello incriminato e poi ritornare un risultato true:
- (BOOL)splitView:(NSSplitView *)splitView shouldHideDividerAtIndex:(NSInteger)dividerIndex
{
 NSInteger dividerToHide = 1;
 return (dividerIndex == dividerToHide);
}
Dato che parliamo della vista di destra, il divisore da far scomparire è quello indicato con il numero 1 (i divisori partono dal numero 0).

Azione finale

A questo punto abbiamo fatto in modo che l'utente non possa chiudere la vista e nemmeno riaprirla se è chiusa: ma allora... cosa può fare? Gli veniamo in soccorso e stabiliamo che l'apertura e la chiusura può essere fatta da un controllo esterno: un pulsante nella Toolbar della finestra è un buon candidato.
Per prima cosa colleghiamo il pulsante ad una IBAction del tipo:
- (IBAction)toggleInspectorPane:(id)sender
{
    if (collapsed) [self uncollapseView];  
    else [self collapseView];
}
In pratica, se la vista non c'è, la mettiamo noi, mentre se c'è la togliamo! Ed ora dedichiamoci al compito semplice ma piuttosto noioso di inserire o togliere la vista e di modificare la dimensione della sotto-vista dello SplitView.
Il nocciolo sta nel giocare con i frame, cambiando le dimensioni ed il punto di partenza di ogni view (ricordiamo che per default in Cocoa le view hanno la coordinata Y=0 nell'angolo in basso, mentre le X sono come ce le aspettiamo: crescono verso destra).
Supponendo che theSplit sia un IBOutlet legato allo NSSplitView che ci interessa:
- (void)collapseEditorView
{
 NSView *centerView = [[_theSplit subviews] objectAtIndex:1];
 NSRect centerFrame = [centerView frame];
 NSRect leftFrame = [[[_theSplit subviews] objectAtIndex:0] frame];
 NSView *rightView = [[_theSplit subviews] objectAtIndex:2];
 NSArray *viste = [rightView subviews];
  
 centerFrame.size.width = [_theSplit frame].size.width - leftFrame.size.width - [self.theSplit dividerThickness];
 centerFrame.origin = NSMakePoint(leftFrame.size.width + [self.theSplit dividerThickness], 0);
    [centerView setFrame:centerFrame];
    NSRect newCollapseRect = NSMakeRect([_theSplit frame].size.width, 0, 0, [_theSplit frame].size.height);
    [rightView setFrame:newCollapseRect];
    if ([viste count] != 0)
 {
  [(NSView*)[viste objectAtIndex:0] removeFromSuperview];
 }
 _editorViewController = nil;
 [_theSplit display];
}
In pratica andiamo ad impostare il frame della vista centrale in modo che copra tutto il restante splitView, togliendo dalla larghezza totale di questi la vista sinistra e lo spessore del divisore (righe 9-11). Per la vista destra, invece, impostiamo un frame con larghezza 0 e con origine nell'estremo destro dello splitView (righe 12-13).
Le ultime righe servono solamente a togliere la vista di mezzo, impostare il corrispondente NSViewController a nil e a rinfrescare lo splitView.

Le cose sono molto simili per il metodo che fa apparire la vista destra:
- (void)uncollapseEditorView
{
 NSView *leftView = [[_theSplit subviews] objectAtIndex:0];
 NSView *centerView = [[_theSplit subviews] objectAtIndex:1];
 NSView *rightView = [[_theSplit subviews] objectAtIndex:2];
 NSRect leftFrame = [leftView frame];
 NSRect centerFrame = [centerView frame];
 NSRect editorFrame;
 NSRect splitFrame = [_theSplit frame];
 CGFloat dividerThickness = [_theSplit dividerThickness];
 
 _editorViewController = [[editorViewCtrl alloc] initWithNibName:@"editorView" bundle:nil];
 editorFrame = [[_editorViewController view] frame];
 if ((splitFrame.size.width - editorFrame.size.width) < (leftFrame.size.width + 250))
 {
  NSRect windowFrame = [self.window frame];
  windowFrame.size.width += editorFrame.size.width;
  [self.window setFrame:windowFrame];
  splitFrame = [_theSplit frame];
  leftFrame = [leftView frame];
 }
 [rightView setHidden:NO];
 
 centerFrame.size.width = splitFrame.size.width - leftFrame.size.width - editorFrame.size.width - 2*dividerThickness;
 centerFrame.origin.x = leftFrame.size.width + dividerThickness;
 editorFrame.origin = NSMakePoint(leftFrame.size.width + centerFrame.size.width + 2*dividerThickness, 0);
 
 [centerView setFrame:centerFrame];
 [rightView setFrame:editorFrame];
}
Prima carichiamo il viewController con il nib che contiene la vista da inserire (riga 12), da cui otteniamo lo spazio necessario da riservare (13). L'idea è di rubare lo spazio necessario alla vista centrale, ma... dobbiamo fare attenzione che questa non scenda sotto la dimensione minima che abbiamo impostato (la vista lo farebbe, poiché i limiti non entrano in gioco da modifiche via codice, ma siamo noi a non volerlo!). Per cui, se la vista centrale dovesse diventare troppo piccola, la lasciamo come è e allarghiamo al suo posto la finestra, splitView compreso (in IB avremo fatto in modo che le sue dimensioni siano legate a quelle della finestra): questo è ciò che avviene nelle righe 14-20. Facciamo comparire la vista (22) e posizioniamo in modo corretto dimensioni e origine della vista centrale (24-25); poi imponiamo che la posizione della terza vista sia proprio dopo il divisore, come ci si attende (26).
Non ci resta che assegnare i nuovi frame a chi di dovere... e siamo a posto!
Volendo, potremmo animare il movimento del divider, in modo che la vista compaia scivolando da destra e scompaia chiudendosi dolcemente; ma questo è un compito abbastanza semplice con le possibilità di animazione di Cocoa!

Nessun commento:

Posta un commento