Qual è il modo migliore per sfruttare le nuove funzionalità di layout automatico di iOS 6 pur garantendo la compatibilità con i dispositivi più vecchi su versioni precedenti di iOS?
Qual è il modo migliore per sfruttare le nuove funzionalità di layout automatico di iOS 6 pur garantendo la compatibilità con i dispositivi più vecchi su versioni precedenti di iOS?
Risposte:
Il layout automatico può essere abilitato o disabilitato su ogni file .storyboard o .xib. Basta selezionare il file specifico e modificare la proprietà "Usa autolayout" utilizzando la finestra di ispezione File in Xcode:
L'uso di file di interfaccia abilitati per il completamento automatico con la destinazione di distribuzione impostata su una versione iOS precedente alla 6.0 provoca errori di compilazione, ad esempio:
Errore in MainStoryboard.storyboard: 3: Layout automatico su versioni iOS precedenti alla 6.0
Una delle opzioni per utilizzare il layout automatico in un progetto e preservare la compatibilità con iOS4-5 è quella di creare due target : uno per destinazione di distribuzione iOS 6.0 e uno per una versione precedente di iOS, ad esempio:
Puoi anche creare due versioni per ciascuno dei tuoi storyboard e file XIB e utilizzare il autolayout abilitato con la destinazione 6.0 e l'altra con la destinazione legacy, ad esempio:
Quindi aggiungi MainStoryBoardAutoSize alle fasi di costruzione della destinazione iOS6 e l'altro file alla destinazione iOS4. Puoi saperne di più sull'uso di più target qui .
EDIT: Come sottolinea la risposta di marchinram , se carichi i file dello storyboard dal codice e non usi l'impostazione "Main Storyboard" in Xcode per impostare lo storyboard iniziale, puoi utilizzare un singolo target.
Per me, il costo della complessità aggiunta di mantenere più target e file di interfaccia sembra superare i vantaggi dell'utilizzo del layout automatico. Fatta eccezione per alcuni casi speciali, è probabilmente molto meglio utilizzare il vecchio dimensionamento automatico semplice (o layoutSubVista dal codice) esclusivamente se è richiesta la compatibilità con iOS4-5.
Hai davvero bisogno di due obiettivi? Ho funzionato in questo modo, ho 2 storyboard come ha detto Imre Kelényi, uno con layout automatici abilitati e l'altro senza, quindi nel delegato dell'app controllo solo quale versione stanno usando e seleziono lo storyboard giusto:
#import "AppDelegate.h"
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:(v) options:NSNumericSearch] != NSOrderedAscending)
@interface AppDelegate ()
@property (strong, nonatomic) UIViewController *initialViewController;
@end
@implementation AppDelegate
@synthesize window = _window;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UIStoryboard *mainStoryboard = nil;
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"6.0")) {
mainStoryboard = [UIStoryboard storyboardWithName:@"iPhone_iOS6" bundle:nil];
} else {
mainStoryboard = [UIStoryboard storyboardWithName:@"iPhone_iOS5" bundle:nil];
}
self.initialViewController = [mainStoryboard instantiateInitialViewController];
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.rootViewController = self.initialViewController;
[self.window makeKeyAndVisible];
return YES;
}
@end
Avere 2 bersagli funziona bene ma mi sembra eccessivo
SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO
è eccessivo. Basta testare una classe solo per iOS 6 contro zero. Vedi developer.apple.com/library/mac/#documentation/developertools/…
Se le differenze di layout non sono grandi, è molto più semplice utilizzare Molle e puntoni per posizionare gli elementi.
Ispirata all'unica idea target di @ marchinram, questa è la soluzione che ho finalmente trovato. Due storyboard, uno per montanti e molle e uno per autolayout. Nel riepilogo di destinazione, ho impostato lo storyboard di autolayout come predefinito. Quindi, in AppDelegate, controllo se devo caricare lo storyboard struts-and-springs pre-6.0 dopo tutto:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
Class cls = NSClassFromString (@"NSLayoutConstraint");
if (cls == nil) {
NSString *mainStoryboardName = nil;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
mainStoryboardName = @"MainStoryboard_iPad_StrutsAndSprings";
} else {
mainStoryboardName = @"MainStoryboard_iPhone_StrutsAndSprings";
}
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:mainStoryboardName bundle:nil];
UIViewController *initialViewController = [mainStoryboard instantiateInitialViewController];
self.window.rootViewController = initialViewController;
[self.window makeKeyAndVisible];
}
Inoltre, ho impostato il target di distribuzione dello storyboard struts-and-springs su iOS 5.1 e quello dello storyboard di autolayout su Project SDK (iOS 6.0).
Volevo davvero fare il passaggio prima che venisse caricato il valore predefinito nello storyboard, in willFinishLaunchingWithOptions: ma questo si traduce in un "NSInvalidUnarchiveOperationException", motivo: "Impossibile creare un'istanza della classe denominata NSLayoutConstraint", indipendentemente da ciò che ho provato.
Prova a utilizzare RRAutoLayout: https://github.com/RolandasRazma/RRAutoLayout È il backport AutoLayout di iOS6 su iOS5.