Una delle nuove funzionalità più popolari introdotte in iOS 8 è la possibilità di creare diversi tipi di estensioni. In questo tutorial, ti guiderò attraverso il processo di creazione di un widget personalizzato per Oggi sezione del centro di notifica. Ma prima, esaminiamo brevemente alcuni argomenti relativi alle estensioni e comprendiamo i concetti importanti che sottendono i widget.
Un'estensione è un binario per scopi speciali. Non è un'app completa, ha bisogno di a contenente app da distribuire. Questa potrebbe essere la tua app esistente, che può includere una o più estensioni o una nuova. Sebbene l'estensione non sia distribuita separatamente, ha un proprio contenitore.
Un'estensione è lanciata e controllata tramite la sua host. Potrebbe essere Safari, ad esempio, se stai creando un'estensione di condivisione o l'app di sistema Oggi che si occupa del centro notifiche e di altri widget. Ogni area di sistema che supporta l'estensione è chiamata a punto di estensione.
Per creare un'estensione, è necessario aggiungere una destinazione al progetto dell'app contenente. I modelli forniti da Xcode includono già i framework appropriati per ciascun punto di estensione, consentendo all'app di interagire e seguire le politiche corrette dell'app host.
Le estensioni create per il punto di estensione di oggi, i cosiddetti widget, hanno lo scopo di fornire un accesso semplice e rapido alle informazioni. I widget si collegano al framework del Centro di notifica. È importante progettare il widget con un'interfaccia utente semplice e mirata, perché troppa interazione può essere un problema. Nota anche che non hai accesso a una tastiera.
I widget dovrebbero funzionare bene e mantenere aggiornato il loro contenuto. Le prestazioni sono un punto importante da considerare. Il tuo widget deve essere pronto in fretta e utilizzare le risorse con saggezza. Ciò eviterà di rallentare l'intera esperienza. Il sistema termina i widget che utilizzano troppa memoria, per esempio. I widget devono essere semplici e focalizzati sul contenuto che stanno visualizzando.
Questa è una teoria sufficiente per ora. Iniziamo a creare un widget oggi personalizzato. Il widget che stiamo per creare mostrerà informazioni sull'utilizzo del disco, inclusa una barra di avanzamento per fornire un rapido riferimento visivo per l'utente. Lungo la strada, copriremo anche altri concetti importanti delle estensioni di iOS 8.
Se vuoi creare questo widget come estensione di un'app esistente, vai avanti e apri il tuo progetto Xcode e passa al secondo passaggio. Se stai partendo da zero come me, devi prima creare un'app contenente.
Apri Xcode e nel File selezionare il menu Nuovo> Progetto ... . Useremo Objective-C come linguaggio di programmazione e il Applicazione vista singola modello per iniziare.
Apri il File menu e scegliere Nuovo> Target ... . Nel Estensione dell'applicazione categoria, selezionare il Oggi Extension modello.
Noterai che il Progetto a cui verrà aggiunto il target è il progetto con cui stiamo attualmente lavorando e l'estensione verrà incorporata nell'applicazione contenente. Si noti inoltre che l'estensione ha un identificatore di bundle distinto basato su una delle applicazioni contenenti, com.tutsplus.Today.Used-Space.
Clic Il prossimo, ad esempio, dai un nome al widget, Spazio utilizzato, e fare clic finire per creare il nuovo obiettivo. Xcode ha creato un nuovo schema per te e ti chiederà di attivarlo per te. Clic Attivare continuare.
Xcode ha creato un nuovo gruppo per il widget chiamato Spazio utilizzato e ha aggiunto un numero di file ad esso, a UIViewController
sottoclasse e uno storyboard. Esatto, un widget non è altro che un controller di visualizzazione e uno storyboard. Se apri l'intestazione del controller della vista nell'editor del codice, noterai che si tratta in effetti di una sottoclasse UIViewController
.
Se si seleziona il target dell'estensione dall'elenco di destinazioni, aprire il Costruisci fasi scheda e espandere il Collega binario con le librerie sezione, vedrai che il nuovo target è collegato al Centro di notifica struttura.
Ora creeremo un'interfaccia utente di base per il nostro widget. Determinare la dimensione del widget è importante e ci sono due modi per dire al sistema la quantità di spazio di cui abbiamo bisogno. Uno sta usando Auto Layout e l'altro sta usando il preferredContentSize
proprietà del controller della vista.
Il concetto di layout adattivi è applicabile anche ai widget. Non solo ora abbiamo iPhone con varie larghezze (e iPad e dispositivi futuri), ma ricordiamo anche che il widget potrebbe dover mostrare il suo contenuto in orientamento orizzontale. Se l'interfaccia utente può essere descritta con i vincoli di Layout automatico, questo è un chiaro vantaggio per lo sviluppatore. L'altezza può essere regolata successivamente con setPreferredContentSize:
se necessario.
Aperto MainInterface.storyboard nell'editor Xcode. Noterai che un'etichetta che mostra "Hello World" è già presente nella vista del controller della vista. Selezionalo ed eliminalo dalla vista poiché non lo useremo. Aggiungi una nuova etichetta alla vista e allinearla al margine destro come mostrato di seguito.
Nel Ispettore degli attributi, imposta il colore del testo su bianco, l'allineamento del testo a destra e il testo dell'etichetta su 50,0%.
Selezionare Dimensione per soddisfare il contenuto da Xcode's editore menu per ridimensionare correttamente l'etichetta se è troppo piccola per adattarla al suo contenuto.
Quindi, aggiungere un UIProgressView
istanza a sinistra dell'etichetta e posizionarla come mostrato di seguito.
Con la vista di avanzamento selezionata, modificare il Progress Tint attributo nel Ispettore degli attributi al bianco e al Traccia Tinta colore a grigio scuro. Questo renderà più visibile. Questo è bello finora. È tempo di applicare alcuni vincoli.
Seleziona l'etichetta percentuale e aggiungi un vincolo superiore, inferiore e finale come mostrato di seguito. Assicurati di deselezionare il Vincola ai margini casella di controllo.
Seleziona la vista di avanzamento e aggiungi un vincolo in cima, in cima e in coda. Usa questa opportunità per cambiare lo spazio iniziale in 3 e non dimenticare di deselezionare Vincola ai margini.
Poiché abbiamo modificato il valore del vincolo principale della vista di avanzamento, abbiamo un piccolo problema che dobbiamo risolvere. La cornice della vista di avanzamento non riflette i vincoli della vista di avanzamento. Con la vista di avanzamento selezionata, fare clic su Risolvi problemi di layout automatico pulsante in fondo e scegliere Aggiorna cornici dal Viste selezionate sezione. Ciò aggiornerà il frame della vista di avanzamento in base ai vincoli che abbiamo impostato in precedenza.
È tempo di vedere il widget in azione. Con il Spazio utilizzato schema selezionato, selezionare Correre dal Prodotto menu o hit Comando-R. Rivela il centro notifiche scorrendo dalla parte superiore dello schermo verso il basso e tocca il modificare pulsante nella parte inferiore del centro di notifica. Il tuo widget dovrebbe essere disponibile per essere aggiunto alla sezione Oggi. Aggiungilo alla sezione Oggi toccando il pulsante Aggiungi alla sua sinistra.
Questo è come dovrebbe essere la nostra estensione.
Sembra buono, ma perché c'è così tanto spazio sotto la visualizzazione e l'etichetta del progresso? Inoltre, perché il sistema operativo non ha rispettato il limite principale della visualizzazione del progresso?
Entrambi i problemi sono i margini standard impostati dal sistema operativo. Lo cambieremo nel prossimo passaggio. Notare, tuttavia, che il margine sinistro è desiderabile poiché allinea la vista di avanzamento con il nome del widget.
Se si ruota il dispositivo o si esegue l'applicazione su un dispositivo diverso, si noterà che il widget lo regola correttamente. Questo grazie al layout automatico.
Aperto TodayViewController.m nell'editor di Xcode. Noterai che il controller di visualizzazione è conforme a NCWidgetProviding
protocollo. Ciò significa che dobbiamo implementare il widgetMarginInsetsForProposedMarginInsets:
metodo e restituire un margine personalizzato restituendo un UIEdgeInsets
struttura. Aggiorna l'implementazione del metodo come mostrato di seguito.
- (UIEdgeInsets) widgetMarginInsetsForProposedMarginInsets: (UIEdgeInsets) margini margins.bottom = 10.0; margini di ritorno;
Esegui nuovamente l'applicazione per vedere il risultato. Il widget dovrebbe essere più piccolo con meno margine in basso. Puoi personalizzare questi margini per ottenere il risultato che cerchi.
Prima di proseguire, completiamo l'interfaccia utente aggiungendo due punti vendita. Con il file storyboard aperto, passa all'editor degli assistenti e assicurati che venga visualizzato TodayViewController.m.
tenere Controllo e trascinare dall'etichetta all'interfaccia del controller della vista per creare una presa per l'etichetta. Dai un nome alla presa percentLabel
. Ripeti questo passaggio e crea una presa chiamata Barview
per il UIProgressView
esempio.
Useremo il NSFileManager
classe per calcolare lo spazio disponibile del dispositivo. Ma come aggiorniamo il widget con quei dati?
Questo è dove un altro metodo dal NCWidgetProviding
il protocollo entra in gioco Il sistema operativo richiama il widgetPerformUpdateWithCompletionHandler:
metodo quando il widget è caricato e può anche essere chiamato in background. In quest'ultimo caso, anche se il widget non è visibile, il sistema potrebbe avviarlo e chiedere aggiornamenti per salvare un'istantanea. Questa istantanea verrà visualizzata alla successiva visualizzazione del widget, in genere per un breve periodo di tempo fino alla visualizzazione del widget.
L'argomento passato in questo metodo è un gestore di completamento che deve essere chiamato quando il contenuto oi dati vengono aggiornati. Il blocco accetta un parametro di tipo NCUpdateResult
per descrivere se abbiamo nuovi contenuti da mostrare. In caso contrario, il sistema operativo saprà che non è necessario salvare una nuova istantanea.
Per prima cosa è necessario creare alcune proprietà per contenere le dimensioni libere, utilizzate e totali. Aggiungeremo anche una proprietà per contenere lo spazio utilizzato sul dispositivo. Questo ci consente una maggiore flessibilità in seguito. Aggiungi queste proprietà all'estensione di classe in TodayViewController.m.
@property (nonatomic, assign) unsigned long long fileSystemSize; @property (nonatomic, assegna) unsigned long long freeSize; @property (nonatomic, assign) unsigned long long usedSize; @property (nonatomic, assign) double usedRate;
updateSizes
Quindi, crea e implementa un metodo di supporto, updateSizes
, per recuperare i dati necessari e calcolare lo spazio utilizzato dal dispositivo.
- (void) updateSizes // Recupera gli attributi da NSFileManager NSDictionary * dict = [[NSFileManager defaultManager] attributesOfFileSystemForPath: NSHomeDirectory () error: nil]; // Imposta i valori self.fileSystemSize = [[dict valueForKey: NSFileSystemSize] unsignedLongLongValue]; self.freeSize = [[dict valueForKey: NSFileSystemFreeSize] unsignedLongLongValue]; self.usedSize = self.fileSystemSize - self.freeSize;
Possiamo approfittare di NSUserDefaults
per salvare lo spazio utilizzato calcolato tra i lanci. Il ciclo di vita di un widget è breve, quindi se memorizziamo questo valore in cache, possiamo impostare l'interfaccia utente con un valore iniziale e quindi calcolare il valore effettivo.
Questo è anche utile per determinare se è necessario aggiornare l'istantanea del widget o no. Creiamo due metodi di convenienza per accedere al database dei valori predefiniti dell'utente.
// @implementation - (double) usedRate return [[[NSUserDefaults standardUserDefaults] valueForKey: RATE_KEY] doubleValue]; - (void) setUsedRate: (double) usedRate NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults]; [default setValue: [NSNumber numberWithDouble: usedRate] forKey: RATE_KEY]; [default si sincronizza];
Si noti che usiamo una macro RATE_KEY
quindi non dimenticare di aggiungere questo in cima TodayViewController.m.
// Macro per la chiave NSUserDefaults #define RATE_KEY @ "kUDRateUsed"
Perché il nostro widget è un controller di visualizzazione, il viewDidLoad
metodo è un buon posto per aggiornare l'interfaccia utente. Usiamo un metodo di supporto, updateInterface
fare così.
- (void) updateInterface double rate = self.usedRate; // recupera il valore memorizzato nella cache self.percentLabel.text = [NSString stringWithFormat: @ "%. 1f %%", (rate * 100)]; self.barView.progress = rate; - (void) viewDidLoad [super viewDidLoad]; [self updateInterface];
Il numero di byte liberi tende a cambiare abbastanza frequentemente. Per verificare se è davvero necessario aggiornare il widget, controlliamo lo spazio utilizzato calcolato e applichiamo una soglia dello 0,01% anziché il numero esatto di byte liberi. Cambia l'implementazione widgetPerformUpdateWithCompletionHandler:
come mostrato di seguito.
- (void) widgetPerformUpdateWithCompletionHandler: (void (^) (NCUpdateResult)) completionHandler [self updateSizes]; double newRate = (double) self.usedSize / (double) self.fileSystemSize; if (newRate - self.usedRate < 0.0001) completionHandler(NCUpdateResultNoData); else [self setUsedRate:newRate]; [self updateInterface]; completionHandler(NCUpdateResultNewData);
Ricalcolo lo spazio utilizzato e, se è significativamente diverso dal valore precedente, salva il valore e aggiorna l'interfaccia. Diamo quindi al sistema operativo che qualcosa è cambiato. In caso contrario, non c'è bisogno di una nuova istantanea. Anche se non lo usiamo in questo esempio, c'è anche a NCUpdateResultFailed
valore per indicare che si è verificato un errore.
Esegui la tua applicazione ancora una volta. Dovrebbe ora visualizzare il valore corretto di quanto spazio viene utilizzato dal dispositivo.
Rivediamo il ciclo di vita del tuo nuovo widget. Quando apri il Oggi pannello, il sistema potrebbe visualizzare uno snapshot precedente fino a quando non è pronto. La vista è caricata e il tuo widget recupererà un valore memorizzato nella cache NSUserDefaults
e usarlo per aggiornare l'interfaccia utente.
Il prossimo, widgetPerformUpdateWithCompletionHandler:
viene chiamato e ricalcolerà il valore effettivo. Se il valore memorizzato nella cache e il nuovo valore non sono significativamente differenti, allora non facciamo nulla. Se il nuovo valore è sostanzialmente diverso, lo memorizziamo nella cache e aggiorniamo di conseguenza l'interfaccia utente.
Mentre è in background, il widget può essere lanciato dal sistema operativo e lo stesso processo viene ripetuto. Se NCUpdateResultNewData
viene restituito, viene creata una nuova istantanea da visualizzare per l'aspetto successivo.
Anche se stiamo già mostrando lo spazio utilizzato, sarebbe interessante avere un numero preciso. Per evitare di ingombrare l'interfaccia utente, renderemo il nostro widget più interattivo. Se l'utente tocca l'etichetta percentuale, il widget si espande, mostrando una nuova etichetta con numeri assoluti. Questa è anche una grande opportunità per imparare come usare l'animazione nei widget.
Aperto MainInterface.storyboard e selezionare l'etichetta percentuale. Nel Ispettore degli attributi, sotto il vista sezione, trova il Interazione utente abilitata opzione e abilitarlo.
Successivamente, dobbiamo rimuovere il vincolo inferiore dell'etichetta. La distanza dell'etichetta nella parte inferiore della vista cambierà a livello di codice, il che significa che il vincolo non sarà più valido.
Seleziona l'etichetta, apri il Taglia area nel Ispettore di taglia, seleziona il vincolo dello spazio in basso e premi cancella. È anche possibile selezionare manualmente la guida ai vincoli nella vista ed eliminarla. L'etichetta ora ha solo un vincolo di spazio superiore e finale come mostrato di seguito.
Seleziona il controller di visualizzazione facendo clic sulla prima delle tre icone nella parte superiore della scena. Nel Taglia area del Ispettore di taglia, imposta l'altezza a 106.
Aggiungi una nuova etichetta alla vista e, come abbiamo fatto prima, imposta il suo colore al bianco nel Ispettore degli attributi. Inoltre, imposta il numero di linee su 3, l'altezza a 61, e la larghezza 200. Questo dovrebbe essere sufficiente per accogliere tre linee di informazione. Lo vuoi anche allineato ai margini inferiore e sinistro.
L'ultimo passaggio è aprire l'editor dell'assistente e creare uno sbocco per l'etichetta denominata detailsLabel
.
Il widget verrà espanso solo per un breve momento. Potremmo salvare un booleano dentro NSUserDefaults
e caricarlo ricordando lo stato precedente, ma, per mantenerlo semplice, ogni volta che il widget viene caricato verrà chiuso. Quando si tocca l'etichetta della percentuale, vengono visualizzate le informazioni aggiuntive.
Definiamo innanzitutto due macro nella parte superiore di TodayViewController.m per aiutare con le taglie.
#define kWClosedHeight 37.0 #define kWExpandedHeight 106.0
Nel viewDidLoad
, aggiungi due righe di codice per impostare l'altezza iniziale del widget e rendere trasparente l'etichetta dei dettagli. Verrà applicata una sfumatura nell'etichetta dei dettagli quando viene toccata l'etichetta percentuale.
- (void) viewDidLoad [super viewDidLoad]; [self updateInterface]; // new [self setPreferredContentSize: CGSizeMake (0.0, kWClosedHeight)]; [self.detailsLabel setAlpha: 0.0];
Si noti che impostiamo la larghezza del widget su 0.0, perché la larghezza verrà impostata dal sistema operativo.
Nell'etichetta di dettaglio, mostriamo i valori per lo spazio libero, usato e totale disponibile con l'aiuto di NSByteCountFormatter
. Aggiungere la seguente implementazione al controller della vista.
-(void) updateDetailsLabel NSByteCountFormatter * formatter = [[NSByteCountFormatter alloc] init]; [formatter setCountStyle: NSByteCountFormatterCountStyleFile]; self.detailsLabel.text = [NSString stringWithFormat: @ "Utilizzato: \ t% @ \ nFree: \ t% @ \ nTotale: \ t% @", [formatter stringFromByteCount: self.usedSize], [formatter stringFromByteCount: self.freeSize ], [formatter stringFromByteCount: self.fileSystemSize]];
Per rilevare i tocchi, ignoriamo il touchesBegan: withEvent:
metodo. L'idea è semplice, ogni volta che viene rilevato un tocco, il widget viene espanso e l'etichetta dei dettagli viene aggiornata. Si noti che la dimensione del widget viene aggiornata chiamando setPreferredContentSize:
sul controller della vista.
-(vuoto) touchBegan: (NSSet *) tocca conEvent: (UIEvent *) event [self updateDetailsLabel]; [self setPreferredContentSize: CGSizeMake (0.0, kWExpandedHeight)];
Anche se il widget funziona con ammende, possiamo migliorare l'esperienza dell'utente svanendo l'etichetta dei dettagli mentre il widget si espande. Questo è possibile se implementiamo viewWillTransitionToSize: withTransitionCoordinator:
. Questo metodo viene chiamato quando l'altezza del widget cambia. Poiché viene passato un oggetto coordinatore di transizione, possiamo includere animazioni aggiuntive.
Come puoi vedere, cambiamo il valore alfa dell'etichetta dei dettagli, ma puoi aggiungere qualsiasi tipo di animazione che ritieni possa migliorare l'esperienza utente.
-(vuoto) viewWillTransitionToSize: dimensione (CGSize) con Transordinationordinator: (id) coordinator [coordinator animateAlongsideTransition: ^ (id context) [self.detailsLabel setAlpha: 1.0]; completamento: nil];
Siamo pronti a eseguire l'applicazione ancora una volta. Fare un tentativo e toccare l'etichetta percentuale per rivelare i nuovi dettagli.
Mentre tutta questa logica potrebbe sembrare eccessivamente complessa per un compito così semplice, ora avrete familiarità con il processo completo per creare un'estensione di oggi. Tieni a mente questi principi quando progetti e costruisci il tuo widget. Ricorda di mantenerlo semplice e diretto e non dimenticare le prestazioni.
Il caching qui non sarebbe affatto necessario con queste operazioni veloci, ma è particolarmente importante se si ha a che fare con un'elaborazione costosa. Usa la tua conoscenza dei controller di visualizzazione e verifica che funzioni per varie dimensioni dello schermo. Si consiglia inoltre di evitare le visualizzazioni a scorrimento o il riconoscimento tattile complesso.
Sebbene l'estensione abbia un contenitore separato, come abbiamo visto in precedenza, è possibile abilitare la condivisione dei dati tra l'estensione e l'app in questione. Puoi anche usare NSExtensionContext
'S openURL: completionHandler:
con uno schema URL personalizzato per avviare l'app dal widget. E se il codice è ciò che devi condividere con la tua estensione, vai avanti e crea una struttura da utilizzare nella tua app e estensione.
Spero che la conoscenza presentata qui sia utile quando costruisci il tuo prossimo grande widget di oggi.