Sembra che sia l'intenzione di Apple di trattare entrambi gli orientamenti dell'iPad allo stesso modo, ma come molti di noi stanno scoprendo, ci sono motivi di progettazione molto legittimi per voler variare il layout dell'interfaccia utente per iPad Portrait rispetto a iPad Landscape.
Sfortunatamente, l'attuale sistema operativo non sembra fornire supporto per questa distinzione ... il che significa che siamo tornati a manipolare i vincoli di layout automatico nel codice o soluzioni alternative simili per ottenere ciò che dovremmo idealmente essere in grado di ottenere gratuitamente utilizzando l'interfaccia utente adattiva .
Non una soluzione elegante.
Non c'è un modo per sfruttare la magia che Apple ha già integrato in IB e UIKit per utilizzare una classe di dimensioni a nostra scelta per un determinato orientamento?
~
Considerando il problema in modo più generico, mi sono reso conto che le "classi di dimensioni" sono semplicemente modi per indirizzare più layout memorizzati in IB, in modo che possano essere richiamati secondo necessità in fase di esecuzione.
In effetti, una "classe di dimensioni" è in realtà solo una coppia di valori enum. Da UIInterface.h:
typedef NS_ENUM(NSInteger, UIUserInterfaceSizeClass) {
UIUserInterfaceSizeClassUnspecified = 0,
UIUserInterfaceSizeClassCompact = 1,
UIUserInterfaceSizeClassRegular = 2,
} NS_ENUM_AVAILABLE_IOS(8_0);
Quindi, indipendentemente da ciò che Apple ha deciso di denominare queste diverse varianti, fondamentalmente, sono solo una coppia di numeri interi usati come identificatori univoci, per distinguere un layout da un altro, archiviati in IB.
Ora, supponendo di creare un layout alternativo (utilizzando una classe di dimensioni inutilizzata) in IB, ad esempio per iPad Portrait ... esiste un modo per fare in modo che il dispositivo utilizzi la nostra classe di dimensioni (layout dell'interfaccia utente) secondo necessità in fase di esecuzione ?
Dopo aver provato diversi approcci (meno eleganti) al problema, ho sospettato che potesse esserci un modo per sovrascrivere la classe di dimensione predefinita a livello di programmazione. E c'è (in UIViewController.h):
// Call to modify the trait collection for child view controllers.
- (void)setOverrideTraitCollection:(UITraitCollection *)collection forChildViewController:(UIViewController *)childViewController NS_AVAILABLE_IOS(8_0);
- (UITraitCollection *)overrideTraitCollectionForChildViewController:(UIViewController *)childViewController NS_AVAILABLE_IOS(8_0);
Pertanto, se puoi impacchettare la gerarchia del controller di visualizzazione come controller di visualizzazione `` figlio '' e aggiungerlo a un controller di visualizzazione genitore di primo livello ... allora puoi ignorare condizionalmente il figlio facendogli pensare che sia una classe di dimensioni diversa da quella predefinita dal sistema operativo.
Ecco un'implementazione di esempio che esegue questa operazione, nel controller di visualizzazione "padre":
@interface RDTraitCollectionOverrideViewController : UIViewController {
BOOL _willTransitionToPortrait;
UITraitCollection *_traitCollection_CompactRegular;
UITraitCollection *_traitCollection_AnyAny;
}
@end
@implementation RDTraitCollectionOverrideViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self setUpReferenceSizeClasses];
}
- (void)setUpReferenceSizeClasses {
UITraitCollection *traitCollection_hCompact = [UITraitCollection traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassCompact];
UITraitCollection *traitCollection_vRegular = [UITraitCollection traitCollectionWithVerticalSizeClass:UIUserInterfaceSizeClassRegular];
_traitCollection_CompactRegular = [UITraitCollection traitCollectionWithTraitsFromCollections:@[traitCollection_hCompact, traitCollection_vRegular]];
UITraitCollection *traitCollection_hAny = [UITraitCollection traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassUnspecified];
UITraitCollection *traitCollection_vAny = [UITraitCollection traitCollectionWithVerticalSizeClass:UIUserInterfaceSizeClassUnspecified];
_traitCollection_AnyAny = [UITraitCollection traitCollectionWithTraitsFromCollections:@[traitCollection_hAny, traitCollection_vAny]];
}
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
_willTransitionToPortrait = self.view.frame.size.height > self.view.frame.size.width;
}
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]
_willTransitionToPortrait = size.height > size.width;
}
-(UITraitCollection *)overrideTraitCollectionForChildViewController:(UIViewController *)childViewController {
UITraitCollection *traitCollectionForOverride = _willTransitionToPortrait ? _traitCollection_CompactRegular : _traitCollection_AnyAny;
return traitCollectionForOverride;
}
@end
Come rapida demo per vedere se funzionava, ho aggiunto etichette personalizzate in modo specifico alle versioni "Regular / Regular" e "Compact / Regular" del layout del controller figlio in IB:
Ed ecco come appare in esecuzione, quando l'iPad è in entrambi gli orientamenti:
Ecco! Configurazioni di classi di dimensioni personalizzate in fase di esecuzione.
Si spera che Apple lo renderà superfluo nella prossima versione del sistema operativo. Nel frattempo, questo potrebbe essere un approccio più elegante e scalabile rispetto a modificare a livello di codice i vincoli di layout automatico o eseguire altre manipolazioni nel codice.
~
EDIT (6/4/15): tieni presente che il codice di esempio sopra è essenzialmente una prova di concetto per dimostrare la tecnica. Sentiti libero di adattarti secondo necessità per la tua specifica applicazione.
~
EDIT (7/24/15): È gratificante che la spiegazione di cui sopra sembra aiutare a demistificare il problema. Anche se non l'ho testato, il codice di mohamede1945 [sotto] sembra un'ottimizzazione utile per scopi pratici. Sentiti libero di provarlo e facci sapere cosa ne pensi. (Per motivi di completezza, lascerò il codice di esempio sopra così com'è.)