În acest tutorial, vom implementa o versiune minimalistă a interfeței cu stilul Facebook / Cale. Obiectivul va fi să înțelegeți cum să utilizați dispozitivul de control al vizualizării pentru a implementa fluxul personalizat în aplicația dvs..
Vizualizarea controlorilor reprezintă o parte esențială a oricărei aplicații iOS, indiferent cât de mică, mare, simplă sau complexă. Ele oferă "logica lipiciului" între modelul de date al aplicației dvs. și interfața cu utilizatorul.
În general, există două tipuri de controlori de vizualizare:
Un controler de container poate avea o componentă vizibilă proprie, dar în principiu funcționează ca o gazdă pentru controlorii de vizualizare a conținutului. Controlerele de containere servesc la "traficul" intrarilor și ieșirilor controlorilor de vizualizare a conținutului.
UINavigationController
, UITabBarController
și UIPageViewController
sunt exemple de controale de vizualizare a containerelor livrate împreună cu SDK-ul iOS. Luați în considerare modul în care cele trei sunt diferite în ceea ce privește fluxurile de aplicare pe care le generează. Controlerul de navigare este excelent pentru o aplicație tip drill-down, în care selecția pe care o face utilizatorul într-un singur ecran afectează opțiunile pe care le-a prezentat în următorul ecran. Controlerul bara de tab-uri este excelent pentru aplicații cu bucăți independente de funcționalitate, permițând o comutare convenabilă prin simpla apăsare a unui buton de file. În cele din urmă, controlerul de vizualizare a paginii prezintă o metaforă a cărții, permițând utilizatorului să se întoarcă înainte și înapoi între paginile de conținut.
Lucrul cheie pe care trebuie să-l rețineți este că un ecran real de conținut prezentat prin oricare dintre aceste controlere de vizualizare a containerelor trebuie să fie gestionat, atât din punct de vedere al datelor derivate din (model), cât și din prezentarea pe ecran (vizualizarea), care ar fi din nou funcția unui controlor de vizualizare. Acum vorbim despre controlori de vizualizare a conținutului. În unele aplicații, în special pe iPad, deoarece ecranul său mai mare permite afișarea simultană a mai multor conținuturi, este posibil ca și alte vizualizări pe ecran să fie gestionate independent. Acest lucru necesită controale multiple de vizualizare pe ecran simultan. Toate acestea implică faptul că controlorii de vizualizare dintr-o aplicație bine concepută ar trebui să fie implementată într-o manieră ierarhică, atât cu controlori de vizualizare a containerelor cât și a conținutului, care își pot juca rolurile respective.
Înainte de iOS 5, nu au existat mijloace de declarare a unei relații ierarhice (adică a părinte-copil) între două controale de vizualizare și, prin urmare, nu există un mod "potrivit" de a implementa un flux de aplicații personalizat. Unul a trebuit fie să se ocupe cu tipurile încorporate, fie să o facă într-un mod întâmplător, care în esență constau în lipirea vederilor gestionate de un controler de vizualizare în ierarhia de vizualizare a vederii gestionate de un alt controler de vizualizare. Acest lucru ar crea inconsecvențe. De exemplu, o viziune s-ar sfârși prin a fi în ierarhia vizuală a doi controlori, fără ca vreunul dintre acești controlori să recunoască celălalt, ceea ce uneori conduce la un comportament ciudat. Restrângerea a fost introdusă în iOS 5 și îmbunătățită ușor în iOS 6 și permite notiunea de control al vizualizării părintelui și copilului într-o ierarhie care să fie formalizată. În esență, controlarea corectă a controlerului de vizualizare cere ca dacă vizualizarea B este un subiectiv (copil) de vizualizare A și dacă nu este sub conducerea aceluiași controler de vizualizare, atunci controlerul de vedere al lui B trebuie să fie făcut din copilul controlerului vizual al lui A.
S-ar putea să întrebați dacă există vreun avantaj concret oferit de controlerul de vizualizare, în afară de avantajul designului ierarhic pe care l-am discutat. Raspunsul este da. Rețineți că atunci când un controler de vizualizare se afișează pe ecran sau se îndepărtează, este posibil să fie nevoie să configurați sau să rupeți resursele, să curățați, să preluați sau să salvați informații din / în sistemul de fișiere. Știm cu toții despre apelurile de apel. Prin declararea explicită a relației părinte-copil, ne asigurăm că parinterul va transmite apeluri către copiii săi ori de câte ori se pornește sau se stinge ecranul. Comenzile de rotație trebuie redirecționate de asemenea. Atunci când orientarea se schimbă, toți controlorii de vizualizare de pe ecran trebuie să cunoască, astfel încât să își poată ajusta în mod corespunzător conținutul.
Ce implică toate acestea, în termeni de cod? Vizualizați controlerele au un NSArray
proprietatea numită childViewControllers
și responsabilitățile noastre includ adăugarea și eliminarea controlorilor de vizualizare a copiilor de la și de la acest matrice în părinte, prin apelarea metodelor adecvate. Aceste metode includ addChildViewController
(numit de părinte) și removeFromParentViewController
(apelat la copil) atunci când căutăm să facem sau să rupem relația părinte-copil. Există, de asemenea, câteva mesaje de notificare trimise la controlerul vizualizare copil la începutul și la sfârșitul procesului de adăugare / eliminare. Acestea sunt willMoveToParentViewController:
și didMoveToParentViewController:
, trimis împreună cu controlerul părinte corespunzător ca argument. Argumentul este zero
, dacă copilul este înlăturat. După cum vom vedea, unul dintre aceste mesaje va fi trimis pentru noi în mod automat, în timp ce celălalt va fi responsabilitatea noastră de a trimite. Acest lucru va depinde de adăugarea sau eliminarea copilului. Vom studia secvența exactă când vom implementa lucrurile în cod. Controlorul copilului poate răspunde la aceste notificări prin implementarea metodelor corespunzătoare dacă trebuie să facă ceva în pregătirea acestor evenimente.
De asemenea, este necesar să adăugăm / eliminăm vizualizările asociate controlerului de vizualizare a copiilor în ierarhia părintelui, folosind metode precum addSubview:
sau removeFromSuperview
), inclusiv realizarea animațiilor însoțitoare. Există o metodă convenabilă (-) transitionFromViewController: toViewController: Durata: opțiuni: animații: finalizare:
care ne permite să optimizăm procesul de schimbare a controlorilor de vizualizare a copiilor pe ecran cu animații. Vom analiza exact detaliile când vom scrie codul - care este următorul!
Creați o nouă aplicație iOS în Xcode bazată pe "Cerere goală"șablon. Faceți-i un App iOS cu ARC activat VCContainmentTut.
Crearea unui nou proiectCreați o nouă clasă numită RootController
. Fa-o a UIViewController
subclasă. Asigurați-vă că toate casetele de selectare sunt deselectate. Aceasta va fi subclasa controlerului nostru de vizualizare a containerelor.
Înlocuiți codul în RootViewController.h cu următoarele.
#import@ interfață RootController: UIViewController // (1) - (id) initWithViewControllers: (NSArray *) vizualizareControllers și MenuTitles: (NSArray *) titluri; // (2) @end
Controlerul nostru pentru containere va avea o vizualizare de tabel care funcționează ca meniu și atingerea oricărei celule va înlocui controlerul de vizualizare vizibil în prezent cu cel selectat prin apăsarea utilizatorului.
Referindu-se la punctele din cod,
Să aruncăm o privire înainte pentru a vedea cum va arăta produsul nostru finit, astfel încât să aveți o imagine mentală pentru a asocia implementarea.
Acesta va ajuta să realizăm că controlerul nostru de vizualizare a containerelor este destul de similar cu un controler vizualizare tab. Fiecare element din meniu corespunde unui controler vizual independent. Diferența dintre "meniul glisant"controlerul și controlerul tab-ului sunt vizuale pentru cea mai mare parte.Fraza"meniul glisant"este un pic de nume greșit, deoarece este de fapt controlerul de vizualizare a conținutului care alunecă pentru a ascunde sau a dezvălui meniul dedesubt.
Continuând implementarea, înlocuiți tot codul în RootController.m cu următorul cod.
#define kExposedWidth 200.0 #define kMenuCellID @ "MenuCell" #import "RootController.h" @interface RootController () @property (nonatomic, puternic) meniu UITableView *; @property (nonatomic, puternic) NSArray * viewControllers; @property (nonatomic, strong) NSArray * meniuTitle; @property (nonatomic, atribuire) NSInteger indexOfVisibleController; @property (nonatomic, atribuire) BOOL esteMenuVisible; @end @implementation RootController - (id) initWithViewControllers: (NSArray *) vizualizareControllers șiMenuTitles: (NSArray *) menuTitles if (self = [super init]) NSAssert (self.menuTitles.count, "Trebuie să existe un singur titlu de meniu corespunzător fiecărui controler de vizualizare!"); // (1) NSMutableArray * tempVCs = [NSMutableArray arrayWithCapacity: viewControllers.count]; auto.menuTitles = [copyTitles copy]; pentru (UIViewController * vc în ViewControllers) // (2) if (! [vc esteMemberOfClass: [UINavigationController class]]) [tempVCs addObject: [[UINavigationController alloc] initWithRootViewController: vc]; altceva [tempVCs addObject: vc]; UIBarButtonItem * revealMenuBarButtonItem = [[UIBarButtonItem alocare] initWithTitle: @ Stilul "Meniu": UIBarButtonItemStylePlain țintă: acțiune auto: @selector (toggleMenuVisibility :); // (3) UIViewController * topVC = ((UINavigationController *) tempVCs.lastObject) .topViewController; topVC.navigationItem.leftBarButtonItems = [@ [revealMenuBarButtonItem] arrayByAddingObjectsFromArray: topVC.navigationItem.leftBarButtonItems]; self.viewControllers = [tempVCs copy]; auto.menu = [[UITableView aliniere] init]; // (4) self.menu.delegate = auto; self.menu.dataSource = auto; întoarce-te; - (void) viewDidLoad [vizualizare superDidLoad]; [self.menu registerClass: [Clasa UITableViewCell pentruCellReuseIdentifier: kMenuCellID]; self.menu.frame = self.view.bounds; [auto.view addSubview: auto.menu]; self.indexOfVisibleController = 0; UIViewController * visibleViewController = auto.viewControllers [0]; visibleViewController.view.frame = [auto offScreenFrame]; [self addChildViewController: visibleViewController]; // (5) [auto.view addSubview: visibleViewController.view]; // (6) self.isMenuVisible = DA; [self adjustContentFrameAccordingToMenuVisibility]; // (7) [auto.viewControllers [0] didMoveToParentViewController: auto]; // (8) - (void) toggleMenuVisibilitate: (id) expeditor // (9) self.isMenuVisible =! Self.isMenuVisible; [self adjustContentFrameAccordingToMenuVisibility]; - (void) adjustContentFrameAccordingToMenuVisibility // (10) UIViewController * visibleViewController = auto.viewControllers [auto.indexOfVisibleController]; Dimensiunea CGSize = visibleViewController.view.frame.size; dacă self.isMenuVisible [UIView animateWithDuration: 0.5 animații: ^ visibleViewController.view.frame = CGRectMake (kExposedWidth, 0, size.width, size.height); ]; altceva [UIView animateWithDuration: 0.5 animații: ^ visibleViewController.view.frame = CGRectMake (0, 0, size.width, size.height); ]; - (void) înlocuiVisibleViewControllerWithViewControllerAtIndex: (NSInteger) index // (11) if (index == self.indexOfVisibleController) retur; UIViewController * incomingViewController = auto.viewControllers [index]; incomingViewController.view.frame = [auto offScreenFrame]; UIViewController * outgoingViewController = auto.viewControllers [auto.indexOfVisibleController]; CGRect visibleFrame = auto.view.bounds; [outgoingViewController vaMoveToParentViewController: zero]; // (12) [auto addChildViewController: incomingViewController]; // (13) [[UIApplication sharedApplication] beginIgnoringInteractionEvents]; // (14) [tranziție de sineFromViewController: outgoingViewController // (15) toViewController: durata de intrareViewController: 0.5 opțiuni: 0 animații: ^ outgoingViewController.view.frame = [self offScreenFrame]; completare: ^ (BOOL finalizat) [Animație animațieWithDuration UIV: 0.5 animații: ^ [outgoingViewController.view removeFromSuperview]; [self.view addSubview: incomingViewController.view]; incomingViewController.view.frame = visibleFrame; [[UIApplication sharedApplication] endIgnoringInteractionEvents]; // (16); [incomingViewController didMoveToParentViewController: auto]; // (17) [ieșireaViewController removeFromParentViewController]; // (18) self.isMenuVisible = NU; auto.indexOfVisibleController = index; ]; // (19): - (NSInteger) numberOfSectionsInTableView: (UITableView *) tableView retur 1; - (NSInteger) tableView: (UITableView *) tableView numberOfRowsInSection: (NSInteger) sectiunea return self.menuTitles.count; - (UITableViewCell *) tableView: (UITableView *) tableView cellForRowAtIndexPath: (NSIndexPath *) indexPath UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier: kMenuCellID]; cell.textLabel.text = auto.menuTituri [indexPath.row]; celule retur; - (void) tableView: (UITableView *) tableView didSelectRowAtIndexPath: (NSIndexPath *) indexPath [self replaceVisibleViewControllerWithViewControllerAtIndex: indexPath.row]; - (CGRect) offScreenFrame retur CGRectMake (auto.view.bounds.size.width, 0, self.view.bounds.size.width, self.view.bounds.size.height); @Sfârșit
Acum pentru o explicație a codului. Părțile pe care le-am evidențiat pentru accent sunt deosebit de relevante pentru implementarea de izolare.
UIViewController
și NSString
tipuri. S-ar putea să vă gândiți să o faceți. Rețineți că menținem tablouri pentru fiecare dintre acestea, numite viewControllers
, și menuTitles
.viewDidLoad
, după configurarea și adăugarea vizualizării tabelului de meniuri la vizualizarea controlerului nostru, vom introduce în aplicația noastră primul controler de vizualizare din viewControllers
mulțime. Prin trimiterea mesajului addChildViewController:
mesajul către controlorul nostru rădăcină, facem prima noastră responsabilitate în ceea ce privește izolarea. Ar trebui să știți că acest lucru cauzează mesajul willMoveToParentViewController:
să fie chemat pe controlorul copilului.de sine
, instanța RootController, ca argument. În controlorul nostru pentru copii, putem implementa această metodă dacă avem nevoie.adjustContentFrameAccordingToMenuVisibility
ne permite să ajustăm cadrul controlerului de vizualizare a conținutului pentru a ne spune dacă meniul este ascuns sau nu. Dacă da, atunci se suprapune supravegherea. În caz contrar, este mutat spre dreapta prin kExposedWidth
. Am stabilit asta la 200 de puncte.replaceVisibleViewControllerWithViewControllerAtIndex
ne permite să schimbați controlorii de vizualizare și vederile corespunzătoare din ierarhie. Pentru a scoate animația, care constă în alunecarea controlerului de vizualizare înlocuit la dreapta și apoi aducerea controlerului de înlocuire din același loc, definim câteva cadre dreptunghiulare.zero
. Odată ce finalizăm acest pas, acest controler de vizualizare va înceta să primească apelări de aspect și rotație de la părinte. Acest lucru are sens deoarece nu mai este o parte activă a aplicației.didMoveToParentViewController
mesaj cu de sine ca argument.removeFromParentViewController
mesaj. Ar trebui să știi că didMoveToParentViewController:
cu zero
ca un argument primit pentru tine.-tableView: didSelectRowAtIndexPath:
metodă.S-ar putea să fi găsit secvența de apeluri legate de controlerul de izolare un pic confuz. El ajută să rezumăm.
addChildViewController:
pe părinte cu copilul ca argument. Acest lucru cauzează mesajul willMoveToParentViewController:
să fie trimis copilului împreună cu părintele ca argument.didMoveToParentViewController:
pe copil cu părintele ca argument.willMoveToParentViewController:
pe copil cu zero
ca argument.removeFromParentViewController
la copil. Cauzele mesajului didMoveToParentViewController
cu zero
ca argument care trebuie trimis copilului în numele dvs..Să testați diferitele tipuri de controale de vizualizare adăugate la controlerul nostru de bază! Creați o nouă subclasă de UIViewController
denumit ViewController
, menținând opțiunile necontrolate.
Înlocuiți codul din ViewController.m cu următorul cod.
#import ViewController () @end @implementation ViewController - (void) willMoveToParentViewController: (UIViewController *) părinte NSLog (@ "% @ ), sine, NSStringFromSelector (_cmd)); - (void) didMoveToParentViewController: (UIViewController *) părinte NSLog (@ "% @ (% p) -% @", NSStringFromClass ([self class]), NSStringFromSelector (_cmd); - (void) viewWillAppear: (BOOL) animat [super viewWillAppear: animat]; NSLog (@ "% @ (% p) -% @", NSStringFromClass ([auto clasa]), auto, NSStringFromSelector (_cmd)); - (void) vizualizareDidAppear: (BOOL) animat [super viewDidAppear: animat]; NSLog (@ "% @ (% p) -% @", NSStringFromClass ([auto clasa]), auto, NSStringFromSelector (_cmd)); - (void) willRotateToInterfaceOrientation: (UIInterfaceOrientation) laInterfaceOrientation durată: (NSTimeInterval) durata [super willRotateToInterfaceOrientation: toInterfaceOrientation duration: duration]; NSLog (@ "% @ (% p) -% @", NSStringFromClass ([auto clasa]), auto, NSStringFromSelector (_cmd)); - (void) didRotateFromInterfaceOrientation: (InterfațăOrientare) dinInterfaceOrientation [super didRotateFromInterfaceOrientation: fromInterfaceOrientation]; NSLog (@ "% @ (% p) -% @", NSStringFromClass ([auto clasa]), auto, NSStringFromSelector (_cmd)); @Sfârșit
Nu există nimic special în privința controlerului nostru de vedere, cu excepția faptului că am răsturnat diferitele apeluri telefonice, astfel încât să le putem înregistra oricând ViewController instanța devine un copil pentru controlerul nostru de bază și apare un eveniment de aspect sau de rotație.
În tot codul anterior, _cmd
se referă la selectorul corespunzător metodei în care executarea noastră este înăuntru. NSStringFromSelector ()
îl transformă într-un șir. Aceasta este o modalitate rapidă și ușoară de a obține numele metodei curente fără a fi necesar să o scrieți manual.
Să aruncăm un regulator de navigație și un controler de file în mix. De data aceasta vom folosi Storyboards.
Creați un fișier nou și sub iOS> Interfața utilizatorului, alege storyboard. Setați familia de dispozitive la iPhone, și numește-o NavStoryBoard.
De la bibliotecă de obiecte, drag and drop a Controller de navigație obiect în pânză. Trageți și fixați o element buton bar în partea stângă a barei de navigare din controlul vizualizării tabelului desemnat ca "Controler vizualizare rădăcină"Aceasta conține vizualizarea de tabelă în pânză, dați-i orice nume, am numit-o"Stânga"Scopul său este de a verifica codul pe care l-am scris pentru a face ca butonul de ascundere / dezvăluire al barei de meniu să-i înlocuiască butonul din stânga de pe bara de navigare, împingând butoanele deja prezente spre dreapta. Vizualizați controlerul și plasați-o în partea dreaptă a operatorului intitulat "Controler vizualizare rădăcină"în pânză.
Faceți clic pe unde scrie "Vizualizare tabel"în centrul celui de - al doilea controler, și în atribuie inspectorului modificați conținutul din "Prototype dinamic" la "Celule statice".
Schimbarea tipului de conținut de celule de la dinamic la staticAcest lucru va face ca trei celule statice de vizualizare a tabelului să apară în constructorul de interfață. Ștergeți numai una din aceste celule de vizualizare a tabelului și mențineți apăsat Control, faceți clic și trageți de la celula rămasă la controlerul de vizualizare de la extrema dreaptă și eliberați-o. Selectați "Apăsați" sub Selecția Segue. Toate acestea nu cauzează un pericol pentru controlerul de vizualizare dreapta atunci când apăsați pe celula singuratică din vizualizarea tabelului. Dacă doriți, puteți să renunțați la a UILabel pe celula tabelului pentru a da ceva text. Tabloul dvs. de idei ar trebui să arate similar cu fotografia de mai jos.
NavStoryBoardÎn cele din urmă, să adăugăm un controler de bara de tab-uri. La fel cum ați făcut anterior, creați un storyboard fișier și apelați-l TabStoryBoard. Trageți și fixați o butonul pentru bara de butoane articol din bibliotecă de obiecte în pânză. Acesta este preconfigurat cu două file și, dacă doriți, puteți schimba culoarea de fundal a celor două controlere de vizualizare cu file, făcând clic pe vizualizarea corespunzătoare fie unui controler de vizualizare, fie schimbând butonul "fundal"în opțiunea Atribuții Inspector. În acest fel, puteți verifica dacă selectarea controlerului de vizualizare prin fila funcționează corect.
Povestea dvs. ar trebui să arate așa.Acum este momentul să setați totul în AppDelegate.
Înlocuiți codul în AppDelegate.m cu următorul cod.
#import "AppDelegate.h" #import "RootController.h" #import "ViewController.h" @implementation Aplicație AppDelegate - (BOOL): aplicație (UIApplication *) didFinishLaunchingWithOptions: (NSDictionary *) launchOptions self.window = [[UIWindow alin] initWithFrame: [[UIScreen mainScreen] limite]]; UIStoryboard * tabStoryBoard = [UIStoryboard storyboardWithName: @ "TabStoryboard" bundle: zero]; UIStoryboard * navStoryBoard = [UTIboardboard storyboardWithName: @ "NavStoryboard" bundle: nil]; UINavigationController * navController = [navStoryBoard instantiateViewControllerWithIdentifier: @ "Nav Controller"]; UITabBarController * tabController = [tabStoryBoard instantiateViewControllerWithIdentifier: @ "Tab Controller"]; ViewController * redVC, * greenVC; redVC = [[ViewController alocare] init]; greenVC = [[ViewController alocare] init]; redVC.view.backgroundColor = [UICcolor redColor]; greenVC.view.backgroundColor = [UICcolor verdeColor]; RootController * menuController = [[RootController alloc] initWithViewControllers: @ [tabController, redVC, greenVC, navController] șiMenuTitles: @ [@ Tab "," Red ", @ Green; self.window.rootViewController = meniuController; auto.window.backgroundColor = [UICcolor whiteColor]; [auto.window makeKeyAndVisible]; reveniți DA;
Tot ce am făcut a fost crearea de instanțe ViewController
și instanțiați controlerul de navigare și tab-ul din cele două panouri storyboard. Le-am trecut într-o gamă largă RootController
de exemplu. Acesta este controlerul containerului pe care l-am implementat la început. Am făcut acest lucru împreună cu o serie de șiruri de caractere pentru a numi controlorii de vizualizare din meniu. Acum, pur și simplu, vom desemna instanța noastră de root-controler inițializată ca fereastra rootViewController
proprietate.
Construiți și rulați aplicația. Tocmai ați pus în aplicare un container de reținere! Atingeți diferitele celule de masă din meniu pentru a înlocui diapozitivul vizibil cu cel nou care alunecă din dreapta. Observați cum, pentru instanța controlerului de navigație (numită "NavC"în meniu), meniul"Stânga"a mutat un loc în dreapta și butonul din bara de meniu a preluat poziția din stânga. Puteți schimba orientarea pe peisaj și puteți verifica dacă totul pare corect.
Simulatoare de ecranÎn acest tutorial introductiv, am analizat modul în care este implementată controlul vizualizării controlerului în iOS 6. Am dezvoltat o versiune simplă a interfeței personalizate a aplicației, care a câștigat multă popularitate și este adesea văzută în aplicații extrem de utilizate precum Facebook și Path. Implementarea noastră a fost cât mai simplă posibil, astfel încât am reușit să o disecăm cu ușurință și să obținem corectitudinea. Există multe implementări sofisticate open-source ale acestui tip de controler pe care le puteți descărca și studia. Se va afișa o căutare rapidă pe Google JASidePAnels
și SWRevealViewController
, printre alții.
Iată câteva idei pentru care să lucrați.
Un lucru pe care aș dori să-l menționez aici este că în Xcode 4.5, există un nou obiect constructor de interfețe numit "Vizualizare container"care poate afișa conținutul unui controler de vizualizare și, astfel, să fie utilizat pentru a implementa izolarea direct în storyboard-ul dvs.!!