I den første opplæringen av denne korte serien på UIKit Dynamics, lærte vi grunnleggende om API-en ved å skape en animert menykomponent. I denne opplæringen fortsetter vi å jobbe med vårt prosjekt og implementere en annen animert komponent, en tilpasset varselsvisning.
Standardvarslingsvisningen på iOS er flott, men det er ikke veldig tilpassbart når det gjelder utseende og oppførsel. Hvis du trenger en varselvisning som er tilpassbar, må du opprette din egen løsning, og det er det vi skal gjøre i denne opplæringen. Fokuset på denne opplæringen er om oppførselen til varselvisningen og ikke så mye på funksjonaliteten. La oss se hva resultatet er at vi er ute etter
Varselvisningen blir en UIView
eksempel som vi legger til følgende undervisninger:
UILabel
Motta for visning av varselvisningens tittelUILabel
Motta for visning av varselvisningens meldingUIButton
forekomster for å la brukeren interagere med varselvisningenVi bruker UISnapBehavior
klasse for å presentere varselvisningen. Som navnet antyder, dette UIDynamicBehavior
subclass tvinger en dynamisk gjenstand til å snap til et punkt som om det ble magnetisk trukket til det.
De UISnapBehavior
Klassen definerer en ekstra eiendom, demping
, som definerer mengden av svingning når det dynamiske elementet har nådd det punktet det tiltrekkes av.
Vi bruker tyngdekraftsadferd, i kombinasjon med kollisjon og trykkadferd, for å avvise varselvisningen. Husk at vi allerede brukte disse oppføringene i den forrige opplæringen.
Varslingsvisningen vil animere inn fra toppen av skjermen. Når visningsvisningen er i ferd med å vises, vil snap-oppføringen få det til å synke og se på midten av skjermen. For å avvise varselvisningen, vil et trykkadferd raskt skyve det til bunnen av skjermen, og en tyngdekraftenes oppførsel vil da trekke den opp på toppen av skjermen og gjøre den animert på skjermen.
Vi lager en tilpasset initialiseringsmetode for varselvisningskomponenten som aksepterer varselens tittel, melding, knappetitler og overordnet visning. Vi vil ikke implementere en delegatprotokoll for varselvisningen. I stedet bruker vi blokker, noe som gir en mer elegant og moderne løsning. Blokken eller handleren vil akseptere to parametere, indeksen og tittelen på knappen som brukeren tappet på.
Vi viser også en halvtransparent visning bak varselvisningen for å forhindre at brukeren samhandler med sin overordnede visning så lenge varselvisningen er synlig. La oss begynne med å se på varselvisningens egenskaper og den tilpassede initialiseringen.
trykk Kommando-N på tastaturet for å opprette en ny fil og velg Mål-C klasse fra listen over iOS maler. Gjør det til en underklasse av NSObject og nev det AlertComponent.
Det neste trinnet er å erklære noen få private eiendommer. Åpen AlertComponent.m, legg til en klasseutvidelse øverst, og erklære følgende egenskaper:
@interface AlertComponent () @property (ikkeatomisk, sterk) UIView * alertView; @property (nonatomic, strong) UIView * backgroundView; @property (nonatomic, strong) UIView * targetView; @property (nonatomic, strong) UILabel * titleLabel; @property (nonatomic, strong) UILabel * messageLabel; @property (nonatomic, strong) UIDynamicAnimator * animator; @property (nonatomic, strong) NSString * tittel; @property (nonatomic, strong) NSString * melding; @property (nonatomic, strong) NSArray * buttonTitles; @property (nonatomic) CGRect initialAlertViewFrame; @slutt
Funksjonen til hver eiendom vil bli tydelig når vi implementerer varselkomponenten. Det er på tide å lage komponentens tilpassede initialiserer.
Som jeg allerede nevnte, skal vi bruke en tilpasset initialiserer for å gjøre arbeidet med varslingskomponenten så enkelt som mulig. Initialiseringen aksepterer fire parametere, advarsels tittel, dens melding, knappetitlene og visningen som alarmkomponenten blir lagt til, dens overordnede visning. Åpen AlertComponent.h og legg til følgende erklæring:
@interface AlertComponent: NSObject - (id) initAlertWithTitle: (NSString *) tittel ogMessage: (NSString *) melding andButtonTitles: (NSArray *) buttonTitles andTargetView: (UIView *) targetView; @slutt
I denne delen vil varselvisningen settes opp, og alle undervisninger blir lagt til den. Bakgrunnsvisningen, så vel som den dynamiske animatoren, vil også bli satt opp.
Åpen AlertComponent.m og erklære følgende private metoder i den private klassen utvidelsen:
@interface AlertComponent () ... - (void) setupBackgroundView; - (ugyldig) setupAlertView; @slutt
Metodenavnene er selvforklarende. La oss starte med å implementere setupAlertView
metode først siden det meste av varselets oppsett foregår i denne metoden.
I setupAlertView
, vi gjør tre ting:
La oss begynne med å beregne varselvisningens størrelse og posisjon som vist i kodestykket nedenfor.
- (void) setupAlertView // Angi størrelsen på varselvisningen. CGSize alertViewSize = CGSizeMake (250.0, 130.0 + 50.0 * self.buttonTitles.count); // Angi det opprinnelige opprinnelsespunktet, avhengig av retningen for varselvisningen. CGPoint initialOriginPoint = CGPointMake (self.targetView.center.x, self.targetView.frame.origin.y - alertViewSize.height);
Vi starter med å angi størrelsen på varselvisningen. For å gjøre varselvisningen dynamisk legger vi til 50,0
peker på høyden for hver knapp. Vær også oppmerksom på at den opprinnelige opprinnelsen til varselvisningen er på skjermen. Det neste trinnet er å initialisere og sette opp varselvisningen:
self.alertView = [[UIView alloc] initWithFrame: CGRectMake (initialOriginPoint.x, initialOriginPoint.y, alertViewSize.width, alertViewSize.height)]; // Bakgrunnsfarge. [self.alertView setBackgroundColor: [UIColor colorWithRed: 0.94 green: 0.94 blue: 0.94 alpha: 1.0]]; // Lag varselvisningen med avrundede hjørner. [self.alertView.layer setCornerRadius: 10.0]; // Angi en kantlinje for varselvisningen. [self.alertView.layer setBorderWidth: 1.0]; [self.alertView.layer setBorderColor: [UIColor blackColor] .CGColor]; // Tilordne startvarslingsrammen til respektive eiendom. self.initialAlertViewFrame = self.alertView.frame;
Ved hjelp av alertViewSize
og initialOriginPoint
, vi initialiserer alertView
objekt og angi bakgrunnsfargen. Vi beveger vekk siktens hjørner ved å sette den lag
's cornerRadius
til 10,0
, det er borderWidth
til 1.0
, og dets grensefarge
til svart. Vi lagrer også varselvisningens innledende ramme i sin initialAlertViewFrame
eiendom som vi trenger det senere.
Hvis Xcode forteller deg, vet den ikke om alertView
's lag
eiendom, legg deretter til følgende importoppgave øverst i implementeringsfilen:
#importere
Det er på tide å legge til etikettene. La oss starte med titteletiketten.
// Oppsett titteletiketten. self.titleLabel = [[UILabel alloc] initWithFrame: CGRectMake (0.0, 10.0, self.alertView.frame.size.width, 40,0)]; [self.titleLabel setText: self.title]; [self.titleLabel setTextAlignment: NSTextAlignmentCenter]; [self.titleLabel setFont: [UIFont fontWithName: @ "Avenir-Heavy" størrelse: 14.0]]; // Legg til titteletiketten i varselvisningen. [self.alertView addSubview: self.titleLabel];
Sette opp meldingsetiketten er ganske lik.
// Oppsett meldingsetiketten. self.messageLabel = [[UILabel alloc] initWithFrame: CGRectMake (0.0, self.titleLabel.frame.origin.y + self.titleLabel.frame.size.height, self.alertView.frame.size.width, 80,0)]; [self.messageLabel setText: self.message]; [self.messageLabel setTextAlignment: NSTextAlignmentCenter]; [self.messageLabel setFont: [UIFont fontWithName: @ "Avenir" størrelse: 14.0]]; [self.messageLabel setNumberOfLines: 3]; [self.messageLabel setLineBreakMode: NSLineBreakByWordWrapping]; // Legg til meldingsetiketten i varselsvisningen. [self.alertView addSubview: self.messageLabel];
Legg merke til at numberOfLines
eiendommen er satt til 3
og lineBreakMode
er satt til NSLineBreakByWordWrapping
.
Det siste vi trenger å sette opp er knappene for varselvisningen. Selv om antall knapper kan variere, er det enkelt å sette opp og posisjonere knappene. Vi skiller mellom knappene 5
poeng og bruk a til
loop for å initialisere dem.
CGFloat lastSubviewBottomY = self.messageLabel.frame.origin.y + self.messageLabel.frame.size.height; for (int i = 0; i<[self.buttonTitles count]; i++) UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(10.0, lastSubviewBottomY + 5.0, self.alertView.frame.size.width - 20.0, 40.0)]; [button setTitle:[self.buttonTitles objectAtIndex:i] forState:UIControlStateNormal]; [button.titleLabel setFont:[UIFont fontWithName:@"Avenir" size:13.0]]; [button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; [button setTitleColor:[UIColor yellowColor] forState:UIControlStateHighlighted]; [button setBackgroundColor:[UIColor colorWithRed:0.0 green:0.47 blue:0.39 alpha:1.0]]; [button addTarget:self action:@selector(handleButtonTap:) forControlEvents:UIControlEventTouchUpInside]; [button setTag:i + 1]; [self.alertView addSubview:button]; lastSubviewBottomY = button.frame.origin.y + button.frame.size.height;
Merk at hver knapp påkaller handleButtonTap:
metode når den er tappet. Vi kan bestemme hvilken knapp brukeren tappet ved å inspisere knappens stikkord
eiendom.
Til slutt legger du til varselvisningen til mål- eller foreldrevisningen ved å legge til følgende linje nederst i setupAlertView-metoden:
// Legg til varselvisningen til overordnet visning. [self.targetView addSubview: self.alertView];
Den andre metoden vi trenger å implementere er setupBackgroundView
. Bakgrunnsvisningen forhindrer brukeren i å samhandle med varselvisningens overordnede visning så lenge visningsvisningen vises. Vi satte i utgangspunktet sin alfa
eiendom til 0.0
, noe som betyr at den er gjennomsiktig.
- (ugyldig) setupBackgroundView self.backgroundView = [[UIView alloc] initWithFrame: self.targetView.frame]; [self.backgroundView setBackgroundColor: [UIColor grayColor]]; [self.backgroundView setAlpha: 0.0]; [self.targetView addSubview: self.backgroundView];
Med setupAlertView
og setupBackgroundView
klar til bruk, la oss implementere den tilpassede initialiseringen vi erklærte tidligere.
- (NSString *) title andMessage: (NSString *) melding andButtonTitles: (NSArray *) buttonTitles andTargetView: (UIView *) targetVise if (self = [super init]) // Tilordne parameterverdiene til lokale eiendommer. self.title = title; self.message = message; self.targetView = targetView; self.buttonTitles = buttonTitles; // Oppsett bakgrunnsvisning. [selvoppsettBackgroundView]; // Oppsett varselvisningen. [selvoppsettAlertView]; // Oppsett animatoren. self.animator = [[UIDynamicAnimator allokere] initWithReferenceView: self.targetView]; returner selv;
Vi satte tittel
, budskap
, targetView
, og buttonTitles
egenskaper, påkalle setupBackgroundView
og setupAlertView
, og initier den dynamiske animatoren, passerer inn self.targetView
som referansevisning.
For å vise varselvisningen etter at den har blitt initialisert, må vi deklarere og implementere en offentlig metode som kan bli kalt av, for eksempel, visningscontrolleren som gir varselvisningen. Åpen AlertComponent.h og legg til følgende metodedeklarasjon:
- (Void) showAlertView;
Gå tilbake til AlertComponent.m å implementere showAlertView
. Som jeg tidligere nevnte i denne opplæringen, bruker vi en ny UIDynamicBehavior
underklasse for å vise varselvisningen, UISnapBehavior
. La oss se hvordan vi bruker denne klassen i showAlertView
.
- (void) showAlertView [self.animator removeAllBehaviors]; UISnapBehavior * snapBehavior = [[UISnapBehavior alloc] initWithItem: self.alertView snapToPoint: self.targetView.center]; snapBehavior.damping = 0.8; [self.animator addBehavior: snapBehavior]; [UIView animateWithDuration: 0.75 animasjoner: ^ [self.backgroundView setAlpha: 0.5]; ];
Vi starter med å fjerne eksisterende dynamiske oppføringer fra den dynamiske animatoren for å sikre at ingen konflikter kommer opp. Husk at noen dynamiske oppføringer kun kan legges til én gang til den dynamiske animatoren, for eksempel gravitasjonsadferd. Vi legger også til andre dynamiske oppføringer for å avvise varselvisningen.
Som du kan se, er det ikke vanskelig å bruke en snapadferd. Vi spesifiserer hvilket dynamisk element atferden skal brukes til og angi hvilket punkt det dynamiske elementet skal snape. Vi stiller også oppførselen demping
eiendom som vi diskuterte tidligere. Vær også oppmerksom på at vi animerer alfa
Egenskapen til bakgrunnsvisningen.
For å teste varselvisningen må vi gjøre noen endringer i ViewController
klasse. La oss begynne med å legge til en UIButton
forekommer til visningskontrollørens visning for å vise varselvisningen. Åpen Main.storyboard og dra a UIButton
eksempel fra Objektbibliotek til visningskontrollørens visning. Plasser knappen nær bunnen av visningen og gi den en tittel på Vis varselvisning. Legg til en handling til ViewController.h som vist under.
@interface ViewController: UIViewController - (IBAction) showAlertView: (id) avsender; @slutt
Gå tilbake til storyboardet og koble visningskontrollens handling til knappen. Åpen ViewController.m og importer headerfilen til AlertComponent
klasse.
#import "AlertComponent.h"
Deretter erklære en eiendom i den private klassen utvidelsen av typen AlertComponent
og nev det alertComponent
.
@interface ViewController () @property (ikkeatomisk, sterk) MenyKomponent * menyKomponent; @property (ikkeatomisk, sterk) AlertComponent * alertComponent; - (void) showMenu: (UIGestureRecognizer *) gestureRecognizer; @slutt
Vi initialiserer deretter varselkomponenten i visningsregulatorens viewDidLoad
metode.
- (void) viewDidLoad ... // Initialiser Alert Component self.alertComponent = [[AlertComponent allok] initAlertWithTitle: @ "Custom Alert" ogMessage: @ "Du har en ny e-postmelding, men jeg vet ikke fra hvem." andButtonTitles: @ [@ "Show me", @ "I do not care", @ "For meg, egentlig?"] ogTargetView: self.view];
For å vise varselkomponenten, påkalle showAlertView:
i handlingen vi nettopp har opprettet, showAlertView:
.
- (IBAction) showAlertView: (id) avsender [self.alertComponent showAlertView];
Kjør din søknad og trykk på knappen for å vise varselvisningen. Resultatet skal se ut som det nedenfor.
Som vi så tidligere, den handleButtonTap:
Metoden påberopes når brukeren tapper en knapp i varselvisningen. Varselvisningen skal skjules når en av knappene er tappet. La oss se hvordan dette virker.
revidere AlertComponent.m og i den private klassen forlengelsen, erklærer handleButtonTap:
metode.
@interface AlertComponent () ... - (void) handleButtonTap: (UIButton *) avsender; @slutt
I denne metoden lager vi en rekke dynamiske oppføringer og legger dem til det dynamiske animasjonsobjektet. Den dynamiske oppførselen vi trenger er:
Etter å ha fjernet eksisterende oppførsel fra den dynamiske animatoren og initierer push-oppførselen som vist nedenfor.
- (void) handleButtonTap: (UIButton *) avsender // Fjern alle oppføringer fra animator. [self.animator removeAllBehaviors]; UIPushBehavior * pushBehavior = [[UIPushBehavior allok] initWithItems: @ [self.alertView] modus: UIPushBehaviorModeInstantaneous]; [pushBehavior setAngle: M_PI_2 magnitude: 20.0]; [self.animator addBehavior: pushBehavior];
De vinkel
Egenskapen til push-oppførelsen definerer retningen til push-knappen. Ved å sette vinkelen til M_PI_2
, kraften i push-oppførelsen er rettet mot bunnen av skjermen.
Det neste trinnet er å legge til tyngdekraften. Vektoren vi sender til setGravityDirection
vil resultere i en kraft mot toppen av skjermen, og trekker varselvisningen oppover.
UIGravityBehavior * gravityBehavior = [[UIGravityBehavior allokere] initWithItems: @ [self.alertView]]; [gravityBehavior setGravityDirection: CGVectorMake (0.0, -1.0)]; [self.animator addBehavior: gravityBehavior];
Det som er interessant om kollisjonens atferd er at vi definerer en grense som er utenfor skjermen.
UICollisionBehavior * collisionBehavior = [[UICollisionBehavior alloc] initWithItems: @ [self.alertView]]; [collisionBehavior addBoundaryWithIdentifier: @ "alertCollisionBoundary" fraPoint: CGPointMake (self.initialAlertViewFrame.origin.x, self.initialAlertViewFrame.origin.y - 10.0) toPoint: CGPointMake (self.initialAlertViewFrame.origin.x + self.initialAlertViewFrame.size.width, self.initialAlertViewFrame.origin.y - 10.0)]; [self.animator addBehavior: collisionBehavior];
Vi trenger også en dynamisk gjenstandsadferd for å angi kollisjonens elastisitet. Resultatet er at varselvisningen vil hoppe litt når den kolliderer med grensen på skjermen.
UIDynamicItemBehavior * itemBehavior = [[UIDynamicItemBehavior alloc] initWithItems: @ [self.alertView]]; itemBehavior.elasticity = 0.4; [self.animator addBehavior: itemBehavior];
Vi må også gjøre bakgrunnsvisningen gjennomsiktig igjen. Vi gjør dette ved å sette bakgrunnsvisningen alfa
eiendom til 0.0
i en animasjonsblokk.
[UIView animateWithDuration: 2.0 animasjoner: ^ [self.backgroundView setAlpha: 0.0]; ];
Kjør din søknad en gang til for å se resultatet.
Selv om varselvisningen responderer på brukerinteraksjon, vet vi for øyeblikket ikke hvilken knapp brukeren har tappet på. Det er det vi skal fokusere på i denne delen.
Som vi gjorde med menykomponenten, skal vi gjøre bruk av blokker for å løse dette problemet. Blokker gir en elegant løsning og kan ofte være enklere å bruke enn en delegatprotokoll.
Vi starter med å oppdatere publikum showAlertView
metode. Metoden må godta en ferdigstillingshåndterer som varselvisningen påberoper når brukeren har tappet en av knappene. I AlertComponent.h, oppdater oppklaringen av showAlertView
metode fra:
- (Void) showAlertView;
til:
- (void) showAlertViewWithSelectionHandler: (void (^) (NSInteger buttonIndex, NSString * buttonTitle)) handler;
Fullføringshandleren aksepterer to parametere, indeksen, av typen NSInteger
, og tittelen, av typen NSString
, av knappen som ble tappet av brukeren. Hvis vi ønsker å påkalle ferdigstillingsbehandleren når brukeren trykker på en knapp i varselvisningen, må vi henvise til ferdigstillingsbehandleren. Dette betyr at vi må erklære en eiendom for ferdigstillingsbehandleren. Vi gjør dette i den private klassen utvidelsen i AlertComponent.m.
@interface AlertComponent () ... @property (nonatomic, strong) void (^ selectionHandler) (NSInteger, NSString *); ... @end
Fortsatt i AlertComponent.m, oppdatere metodebeskrivelsen som vi gjorde i headerfilen for et øyeblikk og lagre ferdigstillingsbehandleren i selectionHandler
eiendom, som vi bare erklærte.
- (void) showAlertViewWithSelectionHandler: (void (^) (NSInteger, NSString *)) handler self.selectionHandler = handler; ...
Det siste stykket av puslespillet påkaller ferdigstillingshandleren i handleButtonTap:
, passerer inn i knappens merke og tittel.
- (void) handleButtonTap: (UIButton *) avsender // Ring utvalgshåndtereren. self.selectionHandler (sender.tag, sender.titleLabel.text); ...
AlertComponent er fullført. Det er på tide å teste alt. Gå tilbake til ViewController.m og oppdater showAlertView: handlingen som vist nedenfor. Som du kan se, bruker vi den nye showAlertViewWithSelectionHandler:
metode og passere i en blokk, som vil bli kalt når en knapp i varselvisningen er tappet av brukeren.
- (IBAction) showAlertView: (id) avsender [self.alertComponent showAlertViewWithSelectionHandler: ^ (NSInteger buttonIndex, NSString * buttonTitle) NSLog (@ "% ld,% @", (lang) buttonIndex, buttonTitle); ];
Det er det. Kjør din søknad en gang til og inspiser Xcodes konsoll for å se resultatet av vårt arbeid.
UIKit Dynamics ble først introdusert i iOS 7 og kan hjelpe deg med å skape realistiske animasjoner raskt. Denne korte serien har vist at bruk av UIKit Dynamics i prosjektene dine ikke er vanskelig, og du trenger ikke å være ekspert i matematikk eller fysikk.
Merk at UIKit Dynamics primært er ment for bruk i visningsbaserte applikasjoner. Hvis du leter etter en lignende løsning for spill, anbefaler jeg at du tar en titt på Apples Sprite Kit, som er rettet mot spillutvikling.