Cocoa Subclassing: Creazione di un HUD
scritto da Francesco Germinara
Introduzione
Il lavoro di seguito presentato vuole essere un
semplice inizio per la realizzazione di controlli di interfaccia utenti
(Pulsanti, Slider, ecc.) di tipo “PRO”, ossia con una interfaccia grafica
diversa da quella standard Acqua, simile a quella visibile in Aperture o DVD
Player ecc.
Contemporaneamente, vuole essere una guida
iniziale alla realizzazione di tecniche di subclassing dei controlli e delle
classi standard cocoa.
Chiunque lo desideri può contribuire a questo
progetto inviandomi, il proprio materiale o implementando nuovi controlli che
saranno aggiunti alle classi base.
Se altri programmatori cocoa si vogliono cimentare
nella realizzazione di una palette per IB con i controlli Hud presenti in
questo lavoro (ed in quelli che spero seguiranno...), sarebbe una bella cosa J ...
Definizioni
HUD = Heads Up Display è un pannello di controllo,
traslucido, che implementa uno o piu’ controlli utente che rimane aperto
durante l’esecuzione del programma per consentire all’utente di modificare
determinati parametri.
Un esempio di un pannello HUD è il seguente, (preso da Aperture della Apple)

Le parti componenti di un HUD sono
Una finestra con:
quattro angoli arrotondati
una zona grigio chiaro per il titolo
una zona grigio scuro (quasi nera)
per il resto
un titolo con scritta in bianco
un pulsante di chiusura
semitrasparente
uno o piu’ controlli utente con
attribuiti visivi
“compatibili e gradevoli con il
resto della finestra”
Come ho
pensato di procedere
Per prima cosa ho deciso di creare una versione di
finestra “NSWindow” personalizzata e quindi ho subclassato la classe NSWindow
creando la FGHUDWindow
Lo scopo fondamentale della nuova classe, è quello
di creare una finestra senza sfondo e senza bordo e di renderla visibile sopra
tutte le altre.
Inoltre, tale classe, si occupa di creare un
Pulsante (NSButton) per la chiusura della finestra stessa.
Successivamente ho bisogno di creare una classe
NSView personale da appiccicare alla FGHUDWindow
e quindi ho
subclassato una NSView standard creando la FGHUDView
Lo scopo fondamentale di questa vista è quello di
disegnare lo sfondo cosi’ come richiesto dalle caratteristiche dell’HUD in
precedenza analizzate, quindi angoli arrotondati, due colori ed il titolo della
finestra e fornire due metodi per impostare il testo del titolo ed un eventuale
testo di tooltip.
//Imposto l'eventuale testo di tool tip
-(void) setToolTipText:(NSString *) aString;
//Imposto il titolo
-(void) setTitle:(NSString
*) aString;
Nota: per poter disegnare la finestra con i rettangoli arrotondati ho
creato una “Categoria” , ossia una classe che espande le funzionalità di una
altra classe già esistente, che si chiama FGNSBezierPathAddition
Tale categoria implementa i metodi bezierPathWithRoundedRectBottom
e bezierPathWithRoundedRectTop usati per disegnare lo sfondo della vista
In tale categoria sono inoltre presenti i metodi
customHorizontalFillWithCallbacks
linearGradientFillWithStartColor
bilinearGradientFillWithOuterColor
che ho preso in prestito da internet e di cui non conosco l’autore, che
comunque ringrazio.
Questi metodi mi serviranno per disegnare gli oggetti usando un riempimento
lineare o bilineare invece di un solo
colore piatto, in modo da dare un’immagine più gradevole al controllo.
Una volta che ho la finestra e la vista, ho un pannello HUD vuoto, quindi
procedo con il subclassare i vari controlli che voglio implementare nella mia
finestra.
Per iniziare ho subclassato un NSSlider.
Per subclassare un controllo standard, occorre in realtà subclassare due
diversi elementi, il controllo e la cella del controllo, quindi nel caso
specifico ho dovuto subclassare NSSlider (il controllo) e NSSliderCell (la
cella).
Le mie nuove classi personalizzate quindi si chiamano FGHudSlider e FGHudSliderCell
Subclassing della NSSlider -> FGHudSlider
L’unico motivo per cui
occorre subclassare tale controllo è per dire che deve usare una Cella
personalizzata (nel mio caso, la FGHudSliderCell).
Per fare cio’ occorre “sovrascrivere” il metodo
+ (Class)cellClass in modo che esso restituisca una
cella di tipo FGHudSliderCell.
Subclassing della NSSliderCell -> FGHudSliderCell
La nuova classe si occupa invece di disegnare il controllo, di conseguenza
viene realizzato uno slider orizzontale che usa i colori base del mio Hud e per
il quale è possibile richiamare i seguenti metodi per modificarne in qualche
modo il comportamento o il disegno del controllo stesso.
setUseGradient
setUseCustomKnob
setUseFillColorSel
setFillColorSel
Ho subclassato il controllo NSButton e di conseguenza NSButtonCell, per
poter gestire un pulsante adatto al mio pannello Hud ed ho realizzato le classi
FGHudButton e FGHudButtonCell
Subclassing della NSButton -> FGHudButton
Lo scopo principale è ovviamente quello di “sovrascrivere” il metodo
cellClass, in modo da fare restituire una Cella di tipo FGHudButtonCell
Ho inoltre creato i seguenti
metodi per gestire diversi tipi di pulsanti
setCellButtonType
setImageButtonWithName
setTitle
Per concludere questa prima parte, riepilogo le attività e gli oggetti
creati

MODO DI UTILIZZO
Insieme a questo documento, trovare un esempio completo di
progetto in XCODE 2.1 /2.2 nel quale sono presenti sia le classi citate sia due
ulteriori classi (una classe delegata per l’oggetto Finestra Principale) e un
oggetto Test per verificare che i messaggi dei vari controlli (clic,
spostamento del know dello slider ecc. siano correttamente ricevuti).
In sintesi per proceder occorre da Interface builder
importare i files .h (header) degli oggetti personalizzati FGHudxxxx, quindi
tramite Layout Manager specificare il tipo corretto di classe da utilizzare per
subclassare il controllo o la finestra o la vista.
Esempio:
Clicco sulla NSWindow, layout manager mi dice che è una
NSWidow, invece la cambio in FGHudWindow, quindi metto una vista, NSView e
sempre tramite layout manager dico che è una FGHudView, quindi metto le varie
viste una per ogni controllo e con layout manager dico che tipo di controllo
subclassano (es. FGHudSlider e FGHunButton).
Creo una classe delegata per la finestra principale e creo
gli outlet per i vari elementi personalizzati che voglio gestire da
codice. Questo è un esempio della mia
classe delegata che trovare nel progetto
//Impostazione dati di personalizzazione
della finestra HUD
//Nota: per utilizzare le classi
// 1) In IB - importo il files .h delle
classi FGHudxxxx
// 2) - tramite layout manager, cambio le
classi agli oggetti standard (subclass)
// 3) - creo oggetto delegato della window
(questo)
// 4) - collego la FGHudWindow e la FGHudView
agli oultet di questa classe
// 5) - imposto i dati via codice nella
awakeFromNib di questa classe
#import <Cocoa/Cocoa.h>
#import "FGHUDWindow.h"
#import "FGHUDView.h"
#import "FGHudButton.h"
@interface theMainWindowDelegate : NSObject
{
IBOutlet FGHUDView *theFGHudView;
IBOutlet FGHUDWindow *theFGHudWindow;
IBOutlet FGHudButton *btnTest1;
IBOutlet FGHudButton *btnTest2;
IBOutlet FGHudButton *btnTest3;
IBOutlet FGHudButton *btnTest4;
}
@end
E questa è l’implementazione
#import
"theMainWindowDelegate.h"
@implementation
theMainWindowDelegate
- (void)
awakeFromNib
{
[theFGHudView setToolTipText:@"Sono una
FGHudView..."];
[theFGHudView setTitle:@"Dimostrazione
Hud"];
[btnTest1
setImageButtonWithName:@"sliderMax.tiff"];
[btnTest1 setCellButtonType:1];
[btnTest2 setCellButtonType:2];
[btnTest3 setCellButtonType:3];
[btnTest4 setCellButtonType:4];
[btnTest4 setTitle:@"Test"];
}
@end
A presto e spero che l’argomento sia di interesse e che la
spiegazione sufficientemente chiara.
F.Germinara
Il link del progetto è il seguente http://www.germinara.it/FGBseHUDPart1.zip