UIModalTransitionStylePartialCurl con UITabBarController

Questa domanda è stata posta molto per esempio , ma qui , per quanto posso vedere, non è ancora stata data una risposta completa.

Ho un UITabBarController con un UINavigationController come root vc per una delle tabs, che a sua volta ha un MKMapView come root vc. Il comportamento che voglio è che la mappa si pieghi parzialmente verso l’alto, lasciando la barra delle tabs in posizione (simile all’app Maps).

Finora tutto ciò che sono riuscito a ottenere è che tutto il panorama si arricci, il che non è così bello.

Le soluzioni che ho visto sono impostare la proprietà hidesBottomBarWhenPushed su NO, il che avrebbe senso, tuttavia, questo non sembra funzionare (a meno che non hidesBottomBarWhenPushed facendo qualcosa di sbagliato).

Per chiarezza, il mio codice è il seguente:

 MyVC *aView = [MyVC init]; aView.modalTransitionStyle = UIModalTransitionStylePartialCurl; aView.hidesBottomBarWhenPushed = NO; 

Per la parte che presenta, ho provato le due alternative seguenti, nessuna delle quali sembra funzionare:

 [self presentModalViewController:updateStatus animated:YES]; [[self navigationController] presentModalViewController:updateStatus animated:YES]; 

Qualsiasi aiuto molto apprezzato.

Ho setacciato StackOverflow (e Internet) per una soluzione a questo problema. La domanda è stata posta molte volte, ma come si nota, la risposta non è mai stata sufficiente. Molte soluzioni offrono una soluzione accettabile se non è importante se, ad esempio, una barra degli strumenti inferiore si arriccia.

Altri hanno fornito una soluzione usando UIView animazioni di UIView / CoreAnimation piuttosto che UIModalTransitionStylePartialCurl come stile di transizione modale; nel peggiore dei casi una soluzione non è consentita nell’App Store e, nel migliore dei casi, non è esattamente lo stesso effetto ottenuto da UIModalTransitionStylePartialCurl (ad esempio la forma del ricciolo è diversa).

Nessuna di queste soluzioni ha fornito una risposta che imita la soluzione di Apple nell’app Maps (ovvero, utilizzando UIModalTransitionStylePartialCurl ma lasciando una UIToolbar non UIToolbar nella parte inferiore dello schermo).

Continuerò in questa tradizione di risposte incomplete, poiché chiedi informazioni su un UITabBarController e la mia soluzione non affronta specificamente tale caso. Tuttavia, risolve il problema che avevo, che era quello di ottenere una mezza pagina arricciata con una barra degli strumenti non curvata nella parte inferiore.

Ci deve essere un modo più elegante per farlo, ma è così che sono riuscito.

Il rootViewController del mio AppDelegate è una sottoclass di UIViewController , che chiamerò TAContainerViewController . TAContainerViewController gestisce a) il contenuto effettivo dello schermo (il “materiale da arricciare”), TAContentViewController e b) il contenuto “dietro” il TAContentViewController (ad esempio le impostazioni), che chiamerò TAUnderCurlViewController .

La mia istanza di TAContainerViewController aveva proprietà per un TAContentViewController e un TAUnderCurlViewController . L’ UIView che era il mio contenuto era una sottoview della proprietà della view di TAContentViewController ; allo stesso modo ciò che l’utente vede sotto l’arricciatura è la proprietà view di TAUnderCurlViewController .

Nel metodo init di TAContainerViewController mi assicuro di fare quanto segue:

  _underCurlVC.modalTransitionStyle = UIModalTransitionStylePartialCurl; 

E per arricciare il contenuto da rivelare sotto la pagina, ho impostato un’azione che chiama questo codice:

  [self.contentVC presentModalViewController:self.underCurlVC animated:YES];` 

dove self è TAContainerViewController , contentVC è un’istanza di TAContentViewController e underCurlVC è un’istanza di TAUnderCurlViewController .

Per chiudere la vista, semplicemente [self.contentVC dismissModalViewControllerAnimated:YES]; .

Qualche stranezza sembra verificarsi con il frame di contentVC quando la vista modale viene chiusa, quindi ripristino manualmente il frame quando la vista modale viene chiusa.

Ho pubblicato un progetto di esempio con ulteriori dettagli su Github . Speriamo che qualcuno possa prendere questo e trasformarlo in una soluzione leggermente più elegante, oppure espanderlo per funzionare con un UINavigationController o UITabBarController . Penso che il trucco sia quello di estrarre i controller di visualizzazione dalle relazioni ben definite nelle sottoclassi di Cocoa, in modo da creare sottoclassi di quelle che i controller di visualizzazione speciali potrebbero eseguire.

La risposta di Tim Arnold ha funzionato alla grande, grazie!

Una trappola a cui fare attenzione: la transizione modale della pagina di arricciatura occuperà l’intero schermo se il controller della vista del contenuto viene aggiunto come figlio del controller della vista del contenitore . Non si può semplicemente aggiungerlo come un bambino, ma nessuno dei metodi del ciclo di vita della vista verrà richiamato sul controller del contenuto (ad es. viewDidLoad , viewWillAppear ), che potrebbe essere un problema.

Fortunatamente, c’è un modo per aggirare questo. Nel controller del contenitore:

  • Aggiungi il tuo controller dei contenuti come un bambino in viewDidLoad
  • viewDidAppear come un bambino in viewDidAppear
  • Ri-aggiungilo come un bambino in viewWillDisappear .

In questo modo, il controller del contenuto riceve i suoi metodi del ciclo di vita chiamati, pur essendo in grado di eseguire una transizione modale della pagina di arricciatura senza occupare l’intero schermo.

Ecco l’intero codice di una soluzione bare-bones:

 @interface XXContainerController : UIViewController @property (strong, nonatomic) UIViewController *contentController; @property (nonatomic) BOOL curled; @end @implementation XXContainerController @synthesize contentController = _contentController; @synthesize curled = _curled; - (void)viewDidLoad { [super viewDidLoad]; self.contentController = [self.storyboard instantiateViewControllerWithIdentifier:@"SomeControllerInStoryboard"]; // Add content controller as child view controller. // This way, it will receive all the view lifecycle events [self addChildViewController:self.contentController]; self.contentController.view.frame = self.view.bounds; [self.view addSubview:self.contentController.view]; [self.contentController didMoveToParentViewController:self]; } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; // Remove the content controller as child view controller. // This way, the modal page curl transition will // not take over the whole screen. // NOTE: need to wait until content controller has appeared // (which will happen later). // Achieve this by running the code at the end of the animation loop [UIView animateWithDuration:0 animations:nil completion:^(BOOL finished) { [self.contentController removeFromParentViewController]; }]; } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; // Add the content controller as child view controller again // so it receives the view lifecycle events [self addChildViewController:self.contentController]; } - (void)setCurled:(BOOL)curled { if (curled == _curled) return; _curled = curled; // Curl up the content view and show underneath controller's view if (curled) { // Note you can specify any modal transition in storyboard // Eg page curl, flip horizontal [self.contentController performSegueWithIdentifier:@"SomeModalSegueDefinedInStoryboard" sender:self]; // Uncurl and show the content controller's view again } else { [self.contentController dismissModalViewControllerAnimated:YES]; // Have to do this, otherwise the content controller's view // gets messed up for some reason self.contentController.view.frame = self.view.bounds; } } @end