在 iOS 6 中启用自动布局,同时保持向后兼容 iOS 5

Posted

技术标签:

【中文标题】在 iOS 6 中启用自动布局,同时保持向后兼容 iOS 5【英文标题】:Enabling auto layout in iOS 6 while remaining backwards compatible with iOS 5 【发布时间】:2012-09-06 21:05:06 【问题描述】:

在利用 ios 6 新的自动布局功能的同时,还能在早期版本的 iOS 上提供与旧设备的兼容性的最佳方式是什么?

【问题讨论】:

+1。你在哪里能弄清楚这一点?有什么线索吗? @Jennis 还没有。 iOS 6 将于明天(2012 年 9 月 19 日)正式发布。希望这包括有关该主题的一些额外文档。 我还没有找到任何东西。有求知欲的人想知道! 我认为这是不可能的。很像故事板在 iOS4 上是不可能的。 除了提供两个 nib 文件之外,我看不出这怎么可能。但我同意你的看法。 【参考方案1】:

受@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 的类。

【讨论】:

【参考方案2】:

我发现将 Xibs 主视图大小设置为 Freeform,然后使用 Autosizing 会很有效。无需在代码中处理视图问题。

【讨论】:

【参考方案3】:

尝试使用RRAutoLayout:https://github.com/RolandasRazma/RRAutoLayout 它是 iOS6 AutoLayout 向后移植到 iOS5。

【讨论】:

【参考方案4】:

如果布局差异不大,使用Springs and Struts定位元素会容易很多。

【讨论】:

【参考方案5】:

可以在每个 .storyboard 或 .xib 文件上启用或禁用自动布局。只需选择特定文件并使用 Xcode 中的文件检查器修改“使用自动布局”属性:

使用启用自动布局的接口文件并将部署目标设置为 6.0 之前的 iOS 版本会导致编译错误,例如:

MainStoryboard.storyboard:3 中的错误:iOS 6.0 之前版本的自动布局

在项目中使用自动布局并仍然保持与 iOS4-5 的兼容性的一个选项是创建两个 targets:一个用于部署目标 iOS 6.0,一个用于早期 iOS 版本,例如:

您也可以为每个故事板和 XIB 文件创建两个版本,并使用 6.0 目标启用的自动布局,另一个使用旧目标,例如:

然后您将 MainStoryBoardAutoSize 添加到 iOS6 目标的构建阶段,并将另一个文件添加到 iOS4 目标。您可以了解有关使用多个目标的更多信息here。

编辑:正如marchinram's answer 指出的那样,如果您从代码中加载情节提要文件并且不使用 Xcode 中的“主情节提要”设置来设置初始情节提要,则可以使用单个目标。

对我来说,维护多个目标和接口文件所增加的复杂性成本似乎超过了使用自动布局的好处。除了少数特殊情况,如果需要 iOS4-5 兼容性,您可能最好使用普通的旧自动调整大小(或代码中的 layoutSubViews)。

【讨论】:

自动布局需要 iOS 6 或更高版本。它不适用于 iOS 5!请在发布前验证您的声明。 iOS 5 根本没有所需的 API(例如 NSLayoutConstraint 类)。如果您不相信我,请查看其他用户尝试在 iOS 5 中使用 Autolayout 时的体验:***.com/questions/11252057/…***.com/questions/11198981/… 是的,我认为你是对的,我想我在一些 wwdc 视频中听到了它,但现在我在 iOS 5.0 设备上对其进行了测试,结果它崩溃了。我会浏览这些视频并检查我在哪里听到的尽管你是对的它确实在 iOS 5.0 上崩溃 @ImreKelényi 您将如何将其提交到应用商店?我对这个过程不是很熟悉,但我认为您提交了一个 .app 文件的压缩存档。有两个目标会让这个过程变得更难吗?谢谢。【参考方案6】:

你真的需要两个目标吗?我让它像这样工作,我有 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 个目标也可以,但对我来说似乎有点矫枉过正

【讨论】:

你是对的。只要您从代码中加载情节提要并且不使用 Xcode 中目标的“主情节提要”设置,这将起作用。我在我的帖子中添加了对您答案的引用。 SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO 太过分了。只需针对 nil 测试一个仅限 iOS 6 的类。见developer.apple.com/library/mac/#documentation/developertools/… 是的,好点,当我最初回答时并没有真正考虑恢复 咳咳,不要使用 willFinishLaunchingWithOptions - 我得到一个“由于未捕获的异常 'NSInvalidUnarchiveOperationException' 导致应用程序终止,原因:'无法实例化名为 NSLayoutConstraint 的类'”在 iOS 5.1 下运行模拟器。 didFinishLaunchingWithOptions 不会出现此问题。

以上是关于在 iOS 6 中启用自动布局,同时保持向后兼容 iOS 5的主要内容,如果未能解决你的问题,请参考以下文章

在 iOS 6 中启用自动布局,在 < iOS6 中禁用自动布局

Xcode 6 自适应 UI 如何向后兼容 iOS 7 和 iOS 6?

自动布局 iOS7 -> 带状态栏的 iOS6

Apple iOS 是不是支持向后兼容?

iOS 6 - 从自动布局中免除 ViewController

EF 自动迁移向后兼容