Qual é a melhor maneira de tirar proveito dos novos recursos de layout automático do iOS 6 e ainda oferecer compatibilidade com dispositivos mais antigos em versões anteriores do iOS?
Qual é a melhor maneira de tirar proveito dos novos recursos de layout automático do iOS 6 e ainda oferecer compatibilidade com dispositivos mais antigos em versões anteriores do iOS?
Respostas:
A execução automática pode ser ativada ou desativada em cada arquivo .storyboard ou .xib. Basta selecionar o arquivo em particular e modificar a propriedade "Use Autolayout" usando o Inspetor de arquivos no Xcode:
O uso de arquivos de interface habilitados para autolayout com o destino de implantação definido para uma versão do iOS anterior à 6.0 resulta em erros de compilação, por exemplo:
Erro no MainStoryboard.storyboard: 3: Layout automático nas versões do iOS anteriores à 6.0
Uma de suas opções para usar o autolayout em um projeto e ainda preservar a compatibilidade com o iOS4-5 é criar dois destinos : um para o destino de implantação iOS 6.0 e outro para uma versão anterior do iOS, por exemplo:
Você também pode criar duas versões para cada um dos seus arquivos storyboard e XIB e usar o autolayout ativado com o destino 6.0 e a outra com o destino herdado, por exemplo:
Você adiciona MainStoryBoardAutoSize às fases de criação do destino iOS6 e o outro arquivo ao destino iOS4. Você pode aprender mais sobre o uso de vários destinos aqui .
EDIT: Como a resposta do marchinram indica, se você carregar arquivos de storyboard a partir do código e não usar a configuração "Main Storyboard" no Xcode para definir o storyboard inicial, poderá usar um único destino.
Para mim, o custo da complexidade adicional de manter vários destinos e arquivos de interface parece compensar os benefícios do uso do programa automático. Exceto em alguns casos especiais, é provavelmente muito melhor usar o dimensionamento automático antigo simples (ou layoutSubViews do código) exclusivamente se a compatibilidade com iOS4-5 for necessária.
Você realmente precisa de dois alvos? Eu consegui funcionar assim, eu tenho 2 storyboard como Imre Kelényi disse, um com layouts automáticos habilitados e o outro sem, então no delegado do aplicativo eu apenas verifico qual versão eles estão usando e seleciono o storyboard certo:
#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
Ter dois alvos funciona bem, mas parece um exagero para mim
SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO
é um exagero. Basta testar uma classe somente do iOS 6 contra zero. Veja developer.apple.com/library/mac/#documentation/developertools/…
Se as diferenças de layout não forem grandes, é muito mais fácil usar Molas e Suportes para posicionar elementos.
Inspirada na idéia de um único objetivo da @ marchinram, esta é a solução que finalmente surgiu. Dois storyboards, um para molas e molas e outro para o layout automático. No resumo do destino, defino o storyboard de autolayout como padrão. Em seguida, no appDelegate, verifico se preciso carregar o storyboard de molas e molas anteriores à 6.0:
- (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];
}
Além disso, defino o destino de implantação do storyboard struts-and-springs como iOS 5.1 e o do storyboard de layout automático como Project SDK (iOS 6.0).
Eu realmente queria fazer a troca antes que o padrão no storyboard fosse carregado, em willFinishLaunchingWithOptions: mas isso resulta em um 'NSInvalidUnarchiveOperationException', motivo: 'Não foi possível instanciar a classe denominada NSLayoutConstraint', independentemente do que eu tentasse.
Tente usar o RRAutoLayout: https://github.com/RolandasRazma/RRAutoLayout É o backport do iOS6 AutoLayout para o iOS5.