什么是利用 iOS 6 新的自动布局功能的最佳方式,同时仍然提供与早期版本 iOS 上的旧设备的兼容性?
6 回答
可以在每个 .storyboard 或 .xib 文件上启用或禁用自动布局。只需选择特定文件并使用 Xcode 中的文件检查器修改“使用自动布局”属性:
使用启用自动布局的接口文件并将部署目标设置为 6.0 之前的 iOS 版本会导致编译错误,例如:
MainStoryboard.storyboard:3 中的错误:6.0 之前的 iOS 版本上的自动布局
在项目中使用自动布局并仍然保持与 iOS4-5 的兼容性的选项之一是创建两个目标:一个用于部署目标 iOS 6.0,一个用于早期 iOS 版本,例如:
您也可以为每个故事板和 XIB 文件创建两个版本,并使用 6.0 目标启用的自动布局,另一个使用旧目标,例如:
然后将 MainStoryBoardAutoSize 添加到 iOS6 目标的 Build 阶段,将另一个文件添加到 iOS4 目标。您可以在此处了解有关使用多个目标的更多信息。
编辑:正如marchinram 的回答所指出的,如果您从代码中加载情节提要文件并且不使用 Xcode 中的“主情节提要”设置来设置初始情节提要,则可以使用单个目标。
对我来说,维护多个目标和接口文件所增加的复杂性的成本似乎超过了使用自动布局的好处。除了一些特殊情况,如果需要 iOS4-5 兼容性,您可能最好使用普通的旧自动调整大小(或代码中的 layoutSubViews)。
你真的需要两个目标吗?我让它像这样工作,我有 2 个故事板,就像 Imre Kelényi 说的,一个启用了自动布局,另一个没有,然后在应用程序委托中,我只需检查他们使用的版本并选择正确的故事板:
#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
有 2 个目标也可以,但对我来说似乎有点矫枉过正
如果布局差异不大,使用Springs 和 Struts来定位元素会容易很多。
受@marchinram 的一个目标想法的启发,这是我最终想出的解决方案。两个故事板,一个用于支柱和弹簧,一个用于自动布局。在目标摘要中,我将自动布局故事板设置为默认值。然后,在 appDelegate 中,我检查是否需要加载 6.0 之前的 struts-and-springs 故事板:
- (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];
}
另外,我将 struts-and-springs 故事板的部署目标设置为 iOS 5.1,将自动布局故事板的部署目标设置为 Project SDK(iOS 6.0)。
我真的很想在加载故事板的默认值之前在 willFinishLaunchingWithOptions 中进行切换:但这会导致“NSInvalidUnarchiveOperationException”,原因是:“无论我尝试什么,都无法实例化名为 NSLayoutConstraint 的类”。
尝试使用 RRAutoLayout:https ://github.com/RolandasRazma/RRAutoLayout 这是 iOS6 AutoLayout backport 到 iOS5。
我发现将 Xibs 主视图大小设置为 Freeform,然后使用 Autosizing 就可以了。无需在代码中处理视图问题。