Een van de populairste nieuwe functies die in iOS 8 is geïntroduceerd, is de mogelijkheid om verschillende typen extensies te maken. In deze zelfstudie begeleid ik u bij het maken van een aangepaste widget voor de Vandaag gedeelte van het meldingscentrum. Laten we eerst eerst enkele onderwerpen over extensies doornemen en de belangrijke concepten begrijpen die ten dele widgets zijn.
Een extensie is een binair bestand voor speciale doeleinden. Het is geen complete app, het heeft een bevattende app worden verspreid. Dit kan uw bestaande app zijn, die een of meer extensies kan bevatten of een nieuw gemaakte extensie. Hoewel de extensie niet afzonderlijk wordt gedistribueerd, heeft deze wel een eigen container.
Een extensie wordt gestart en beheerd via zijn host-app. Het kan Safari zijn, bijvoorbeeld, als u een gedeelde extensie maakt, of de Today-systeem-app die zorgt voor het meldingscentrum en andere widgets. Elk systeemgebied dat ondersteuning biedt voor uitbreiding wordt een an uitbreidingspunt.
Als u een extensie wilt maken, moet u een doel toevoegen aan het project van de betreffende app. De sjablonen die door Xcode worden aangeboden, bevatten al de geschikte kaders voor elk uitbreidingspunt, zodat de app kan communiceren met en het juiste beleid van de host-app kan volgen.
Verlengingen gemaakt voor het huidige uitbreidingspunt, de zogenaamde widgets, zijn bedoeld om eenvoudige en snelle toegang tot informatie te bieden. Widgets linken naar het meldingscentrum-framework. Het is belangrijk dat u uw widget ontwerpt met een eenvoudige en gerichte gebruikersinterface, omdat te veel interactie een probleem kan zijn. Merk ook op dat u geen toegang hebt tot een toetsenbord.
Van widgets wordt verwacht dat ze goed presteren en dat hun inhoud wordt bijgewerkt. Prestaties zijn een groot aandachtspunt. Je widget moet snel klaar zijn en verstandig omgaan met middelen. Dit zal voorkomen dat je de hele ervaring vertraagt. Het systeem beëindigt bijvoorbeeld widgets die te veel geheugen gebruiken. Widgets moeten eenvoudig zijn en gericht op de inhoud die ze weergeven.
Dat is genoeg theorie voor nu. Laten we beginnen met het maken van een aangepaste widget van vandaag. De widget die we gaan maken, toont informatie over het schijfgebruik, inclusief een voortgangsbalk om snel een visuele referentie voor de gebruiker te bieden. Onderweg bespreken we ook andere belangrijke concepten van iOS 8-extensies.
Als je deze widget wilt bouwen als een uitbreiding op een bestaande app, ga je gang en open je je Xcode-project en ga je naar de tweede stap. Als je net als ik helemaal opnieuw begint, moet je eerst een bevattende app maken.
Open Xcode en in de het dossier menu selecteren Nieuw> Project ... . We zullen Objective-C gebruiken als de programmeertaal en de de Toepassing enkele weergave sjabloon om mee te beginnen.
Open de het dossier menu en kies Nieuw> Doel ... . In de Application Extension categorie, selecteert u de Vandaag verlenging sjabloon.
Je zult merken dat het project waaraan het doel zal worden toegevoegd, is het project waarmee we momenteel werken en de extensie zal worden ingesloten in de betreffende toepassing. Merk ook op dat de extensie een duidelijk bundel-ID heeft op basis van die van de betreffende applicatie, com.tutsplus.Today.Used-Space.
Klik volgende, geef je widget bijvoorbeeld een naam, Gebruikte ruimte, en klik Af hebben om het nieuwe doel te maken. Xcode heeft een nieuw schema voor je gemaakt en zal je vragen het voor je te activeren. Klik Activeren doorgaan.
Xcode heeft een nieuwe groep gemaakt voor de genoemde widget Gebruikte ruimte en er een aantal bestanden aan toegevoegd, UIViewController
subklasse en een storyboard. Dat klopt, een widget is niets meer dan een beeldcontroller en een storyboard. Als je de header van de view controller in de code-editor opent, zul je merken dat het inderdaad subclassing is UIViewController
.
Als u het extensiedoel in de lijst met doelen selecteert, opent u de Bouw fases tab en vouw de Binaire koppeling met bibliotheken In dit gedeelte ziet u dat het nieuwe doel is gekoppeld aan het Notificatie centrum kader.
We zullen nu een eenvoudige gebruikersinterface voor onze widget bouwen. Het bepalen van de grootte van de widget is belangrijk en er zijn twee manieren om het systeem te vertellen hoeveel ruimte we nodig hebben. De ene gebruikt Auto-indeling en de andere gebruikt de preferredContentSize
eigendom van de view controller.
Het concept van adaptieve lay-outs is ook van toepassing op widgets. We hebben nu niet alleen iPhones met verschillende breedten (en iPads en toekomstige apparaten), maar onthouden ook dat de widget de inhoud mogelijk moet weergeven in liggende stand. Als de gebruikersinterface kan worden beschreven met beperkingen voor Auto-indeling, is dat een duidelijk voordeel voor de ontwikkelaar. De hoogte kan later worden aangepast met setPreferredContentSize:
indien nodig.
Open MainInterface.storyboard in de Xcode-editor. U zult merken dat een label met "Hello World" al aanwezig is in de weergave van de view controller. Selecteer het en verwijder het uit de weergave omdat we het niet zullen gebruiken. Voeg een nieuw label toe aan de weergave en lijn het uit met de rechtermarge zoals hieronder wordt weergegeven.
In de Kenmerken Inspector, stel tekstkleur in op wit, tekstuitlijning op rechts en de tekst van het label op 50,0%.
kiezen Grootte om te passen inhoud van Xcode's Editor om het formaat van het label naar behoren te wijzigen als het te klein is om in de inhoud te passen.
Voeg vervolgens een toe UIProgressView
bijvoorbeeld links van het label en plaats het zoals hieronder getoond.
Met de voortgangsweergave geselecteerd, wijzigt u de Voortgangstint attribuut in de Kenmerken Inspector naar wit en de Track Tint kleur tot donkergrijs. Dit maakt het beter zichtbaar. Dit ziet er tot nu toe goed uit. Het is tijd om enkele beperkingen toe te passen.
Selecteer het percentagetiket en voeg een beperkende voorwaarde voor boven, onder en achter toe toe, zoals hieronder wordt weergegeven. Zorg ervoor dat u de markering uitschakelt Beperken tot marges checkbox.
Selecteer de voortgangsweergave en voeg een beperkende voorwaarde toe, leidend en achterliggend. Gebruik deze mogelijkheid om de hoofdruimte te wijzigen in 3 en vergeet niet om uit te vinken Beperken tot marges.
Omdat we de waarde van de belangrijkste beperking van de voortgangsweergave hebben gewijzigd, hebben we een klein probleem dat we moeten oplossen. Het frame van de voortgangsweergave weerspiegelt niet de beperkingen van de voortgangsweergave. Klik met de voortgangsweergave geselecteerd op Los problemen in de automatische lay-out op knop onderaan en kies Frames bijwerken van de Geselecteerde weergaven sectie. Hiermee wordt het frame van de voortgangsweergave bijgewerkt op basis van de beperkingen die we eerder hebben ingesteld.
Het is tijd om de widget in actie te zien. Met de Gebruikte ruimte schema geselecteerd, selecteer Rennen van de Artikel menu of druk op Command-R. Open het meldingscentrum door vanaf de bovenkant van het scherm naar beneden te vegen en tik op Bewerk knop onderaan het meldingscentrum. Je widget moet beschikbaar zijn om toe te voegen aan het gedeelte Vandaag. Voeg het toe aan het gedeelte Vandaag door op de knop Toevoegen aan de linkerkant te tikken.
Dit is hoe onze extensie eruit zou moeten zien.
Dat ziet er goed uit, maar waarom is er zoveel ruimte onder het voortgangsoverzicht en label? Ook, waarom heeft het besturingssysteem de leidende beperking van het voortgangsoverzicht niet gerespecteerd?
Beide problemen zijn standaardmarges die door het besturingssysteem zijn ingesteld. We zullen dit in de volgende stap wijzigen. Houd er echter rekening mee dat de linkermarge wenselijk is, omdat de voortgangsweergave wordt uitgelijnd met de naam van de widget.
Als u uw apparaat draait of de toepassing op een ander apparaat uitvoert, ziet u dat de widget de grootte correct aanpast. Dat is te danken aan Auto Layout.
Open TodayViewController.m in de editor van Xcode. Je zult zien dat de view controller voldoet aan de NCWidgetProviding
protocol. Dit betekent dat we het moeten implementeren widgetMarginInsetsForProposedMarginInsets:
methode en retourneer een aangepaste marge door een UIEdgeInsets
structuur. Werk de implementatie van de methode bij zoals hieronder wordt weergegeven.
- (UIEdgeInsets) widgetMarginInsetsForProposedMarginInsets: (UIEdgeInsets) margins margins.bottom = 10.0; terugkeermarges;
Voer de toepassing opnieuw uit om het resultaat te bekijken. De widget moet kleiner zijn met onderaan minder marge. U kunt deze marges aanpassen om het gewenste resultaat te krijgen.
Voordat we verdergaan, kunnen we de gebruikersinterface voltooien door twee uitgangen toe te voegen. Met het storyboard-bestand geopend, schakelt u over naar de assistent-editor en zorgt u ervoor dat deze wordt weergegeven TodayViewController.m.
Houden Controle en sleep van het label naar de interface van de view controller om een outlet voor het label te creëren. Geef de outlet een naam percentLabel
. Herhaal deze stap en maak een outlet genaamd Barview
voor de UIProgressView
aanleg.
We zullen de gebruiken NSFileManager
klasse om de beschikbare ruimte van het apparaat te berekenen. Maar hoe updaten we de widget met die gegevens??
Dit is waar een andere methode uit de NCWidgetProviding
protocol komt om de hoek kijken. Het besturingssysteem roept de widgetPerformUpdateWithCompletionHandler:
methode wanneer de widget is geladen en deze kan ook op de achtergrond worden aangeroepen. In het laatste geval, zelfs als de widget niet zichtbaar is, kan het systeem het starten en om updates vragen om een momentopname op te slaan. Deze momentopname wordt de volgende keer dat de widget verschijnt weergegeven, meestal gedurende een korte periode totdat de widget wordt weergegeven.
Het argument dat in deze methode wordt doorgegeven, is een voltooiingshandler die moet worden aangeroepen wanneer de inhoud of gegevens worden bijgewerkt. Het blok neemt een parameter van het type NCUpdateResult
om te beschrijven of we nieuwe inhoud te tonen hebben. Zo niet, dan weet het besturingssysteem dat het niet nodig is om een nieuwe snapshot op te slaan.
We moeten eerst een aantal eigenschappen maken om de vrije, gebruikte en totale grootte te behouden. We zullen ook een eigenschap toevoegen om de gebruikte ruimte op het apparaat te houden. Dit geeft ons later meer flexibiliteit. Voeg deze eigenschappen toe aan de klas-extensie in TodayViewController.m.
@property (nonatomic, assign) unsigned long long fileSystemSize; @property (nonatomic, assign) unsigned long long freeSize; @property (nonatomic, assign) unsigned long long usedSize; @property (nonatomic, assign) double usedRate;
updateSizes
Maak en implementeer vervolgens een hulpmethode, updateSizes
, om de benodigde gegevens op te halen en de gebruikte ruimte van het apparaat te berekenen.
- (void) updateSizes // Vind de attributen van NSFileManager NSDictionary * dict = [[NSFileManager defaultManager] attributesOfFileSystemForPath: NSHomeDirectory () error: nil]; // Stel de waarden in self.fileSystemSize = [[dict valueForKey: NSFileSystemSize] unsignedLongLongValue]; self.freeSize = [[dict valueForKey: NSFileSystemFreeSize] unsignedLongLongValue]; self.usedSize = self.fileSystemSize - self.freeSize;
We kunnen profiteren van NSUserDefaults
om de berekende gebruikte ruimte tussen lanceringen op te slaan. De levenscyclus van een widget is kort, dus als we deze waarde cachen, kunnen we de gebruikersinterface instellen met een beginwaarde en vervolgens de werkelijke waarde berekenen.
Dit is ook handig om te bepalen of we de widgetmomentopname moeten bijwerken of niet. Laten we twee gemaksmethoden maken om toegang te krijgen tot de database met gebruikersstandaarden.
// @implementation - (double) usedRate return [[[NSUserDefaults standardUserDefaults] valueForKey: RATE_KEY] doubleValue]; - (void) setUsedRate: (double) usedRate NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults]; [standaardwaarden setValue: [NSNumber numberWithDouble: usedRate] forKey: RATE_KEY]; [standaardsynchronisatie];
Merk op dat we een macro gebruiken RATE_KEY
dus vergeet niet om deze bovenaan toe te voegen TodayViewController.m.
// Macro voor NSUserDefaults-sleutel # define RATE_KEY @ "kUDRateUsed"
Omdat onze widget een view-controller is, is de viewDidLoad
methode is een goede plaats om de gebruikersinterface bij te werken. We maken gebruik van een hulpmethode, updateInterface
om dit te doen.
- (void) updateInterface double rate = self.usedRate; // de gecachte waarde ophalen self.percentLabel.text = [NSString stringWithFormat: @ "%. 1f %%", (koers * 100)]; self.barView.progress = rate; - (void) viewDidLoad [super viewDidLoad]; [zelf updateInterface];
Het aantal gratis bytes neigt nogal vaak te veranderen. Om te controleren of we de widget echt moeten bijwerken, controleren we de berekende gebruikte ruimte en passen we een drempelwaarde van 0,01% toe in plaats van het exacte aantal gratis bytes. Wijzig de implementatie widgetPerformUpdateWithCompletionHandler:
zoals hieronder getoond.
- (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);
We herberekenen de gebruikte ruimte en, als deze aanzienlijk verschilt van de vorige waarde, sla de waarde op en werk de interface bij. Vervolgens vertellen we het besturingssysteem dat er iets is veranderd. Zo niet, dan is er geen nieuwe snapshot nodig. Hoewel we het in dit voorbeeld niet gebruiken, is er ook een NCUpdateResultFailed
waarde om aan te geven dat er een fout is opgetreden.
Voer uw aanvraag nogmaals uit. Het moet nu de juiste waarde weergeven van hoeveel ruimte door uw apparaat wordt gebruikt.
Laten we de levenscyclus van uw nieuwe widget bekijken. Wanneer u de Vandaag paneel, kan het systeem een vorige momentopname weergeven totdat deze gereed is. De weergave is geladen en uw widget haalt een gecachte waarde op NSUserDefaults
en gebruik het om de gebruikersinterface te updaten.
volgende, widgetPerformUpdateWithCompletionHandler:
wordt aangeroepen en zal de werkelijke waarde opnieuw berekenen. Als de cache en de nieuwe waarde niet significant verschillen, doen we niets. Als de nieuwe waarde aanzienlijk anders is, cachen we deze in en werken de gebruikersinterface dienovereenkomstig bij.
Op de achtergrond kan de widget door het besturingssysteem worden gestart en hetzelfde proces wordt herhaald. Als NCUpdateResultNewData
wordt geretourneerd, een nieuwe snapshot wordt gemaakt om weer te geven voor de volgende weergave.
Hoewel we de gebruikte ruimte al laten zien, zou het interessant zijn om een precies aantal te hebben. Om te voorkomen dat de gebruikersinterface overbelast raakt, zullen we onze widget interactiever maken. Als de gebruiker op het percentagetiket tikt, breidt de widget uit en toont een nieuw label met absolute aantallen. Dit is ook een geweldige kans om te leren hoe je animatie in widgets kunt gebruiken.
Open MainInterface.storyboard en selecteer het percentage label. In de Kenmerken Inspector, onder de Uitzicht sectie, zoek de Gebruikersinteractie ingeschakeld optie en schakel het in.
Vervolgens moeten we de bodembeperking van het label verwijderen. De afstand van het label tot de onderkant van de weergave zal programmatisch veranderen, wat betekent dat de beperking ongeldig zou worden.
Selecteer het label, open de Grootte gebied in de Size Inspector, selecteer de beperking van de bodemruimte en druk op delete. U kunt ook handmatig de beperkingsgids in de weergave selecteren en verwijderen. Het label heeft nu alleen een bovenste en achterliggende spatiebeperking, zoals hieronder weergegeven.
Selecteer de weergaveregelaar door op het eerste van de drie pictogrammen bovenaan de scène te klikken. In de Grootte gebied van de Size Inspector, stel de hoogte in op 106.
Voeg een nieuw label toe aan de weergave en stel zoals eerder de kleur in wit in op wit Kenmerken Inspector. Stel bovendien het aantal regels in 3, de hoogte tot 61, en de breedte 200. Dit zou voldoende moeten zijn om drie informatielijnen te bevatten. Je wilt ook dat het uitgelijnd is met de onderste en linkermarges.
De laatste stap is om de assistent-editor te openen en een outlet te maken voor het genoemde label detailsLabel
.
De widget wordt slechts een korte moment uitgebreid. We kunnen een boolean opslaan NSUserDefaults
en laad het terwijl je de vorige staat onthoudt, maar om het simpel te houden, zal elke keer dat de widget wordt geladen deze worden gesloten. Wanneer u op het percentagelabel tikt, wordt de extra informatie weergegeven.
Laten we eerst twee macro's definiëren bovenaan TodayViewController.m om te helpen met de maten.
#define kWGeslotenHoogte 37.0 #define kWExpandedHeight 106.0
In viewDidLoad
, voeg twee regels code toe om de initiële hoogte van de widget in te stellen en het etiket met details transparant te maken. We gaan het detailslabel vervagen wanneer het percentagelabel wordt aangeboord.
- (void) viewDidLoad [super viewDidLoad]; [zelf updateInterface]; // nieuw [self setPreferredContentSize: CGSizeMake (0.0, kWClosedHeight)]; [self.detailsLabel setAlpha: 0.0];
Merk op dat we de breedte van de widget instellen op 0.0, omdat de breedte door het besturingssysteem wordt ingesteld.
In het detaillabel geven we waarden weer voor vrije, gebruikte en totale beschikbare ruimte met behulp van NSByteCountFormatter
. Voeg de volgende implementatie toe aan de view-controller.
-(void) updateDetailsLabel NSByteCountFormatter * formatter = [[NSByteCountFormatter alloc] init]; [formatter setCountStyle: NSByteCountFormatterCountStyleFile]; self.detailsLabel.text = [NSString stringWithFormat: @ "Gebruikt: \ t% @ \ nFree: \ t% @ \ nTotal: \ t% @", [formatter stringFromByteCount: self.usedSize], [formatter stringFromByteCount: self.freeSize ], [formatter stringFromByteCount: self.fileSystemSize]];
Om aanrakingen te detecteren, overschrijven we de touchesBegan: withEvent:
methode. Het idee is eenvoudig: wanneer een aanraking wordt gedetecteerd, wordt de widget uitgevouwen en wordt het gegevenslabel bijgewerkt. Merk op dat de grootte van de widget wordt bijgewerkt door te bellen setPreferredContentSize:
op de view-controller.
-(ongeldig) raaktBegan: (NSSet *) raakt aan metEvent: (UIEvent *) -gebeurtenis [self updateDetailsLabel]; [self setPreferredContentSize: CGSizeMake (0.0, kWExpandedHeight)];
Hoewel de widget boetes werkt, kunnen we de gebruikerservaring verbeteren door het label met details te vervagen terwijl de widget wordt uitgevouwen. Dit is mogelijk als we het implementeren viewWillTransitionToSize: withTransitionCoordinator:
. Deze methode wordt aangeroepen wanneer de hoogte van de widget verandert. Omdat een overgangscoördinatorobject wordt doorgegeven, kunnen we extra animaties toevoegen.
Zoals u kunt zien, veranderen we de alpha-waarde van het detaillabel, maar u kunt elk type animatie toevoegen waarvan u denkt dat het de gebruikerservaring verbetert..
-(void) viewWillTransitionToSize: (CGSize) size withTransitionCoordinator: (id) coördinator [coordinator animateAlongsideTransition: ^ (id context) [self.detailsLabel setAlpha: 1.0]; voltooiing: nul];
We zijn klaar om de applicatie nog een keer uit te voeren. Probeer het en tik op het percentage label om de nieuwe details te onthullen.
Hoewel al deze logica voor zo'n eenvoudige taak te ingewikkeld lijkt, zult u nu bekend zijn met het volledige proces om een huidige uitbreiding te maken. Houd deze principes in gedachten bij het ontwerpen en bouwen van uw widget. Vergeet niet om het eenvoudig en direct te houden, en de prestaties niet te vergeten.
Cachen hier zou helemaal niet nodig zijn met deze snelle bewerkingen, maar het is vooral belangrijk als u dure bewerkingen moet uitvoeren. Gebruik uw kennis van beeldcontrollers en controleer of deze voor verschillende schermformaten werkt. Het wordt ook aanbevolen om scrollen of complexe aanraakherkenning te vermijden.
Hoewel de extensie een afzonderlijke container zal hebben, zoals we eerder hebben gezien, is het mogelijk om het delen van gegevens tussen de extensie en de bevattende app in te schakelen. Je kan ook gebruiken NSExtensionContext
's openURL: completionHandler:
met een aangepast URL-schema om uw app vanuit de widget te starten. En als code is wat u met uw extensie wilt delen, maak dan een framework om te gebruiken in uw app en extensie.
Ik hoop dat de hier gepresenteerde kennis handig is bij het bouwen van je volgende geweldige widget van vandaag.