<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>导航控制器 &#8211; 编程技术记录</title>
	<atom:link href="https://blog.z6z8.cn/tag/%E5%AF%BC%E8%88%AA%E6%8E%A7%E5%88%B6%E5%99%A8/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.z6z8.cn</link>
	<description>世界你好!</description>
	<lastBuildDate>Thu, 17 Oct 2019 05:14:26 +0000</lastBuildDate>
	<language>zh-Hans</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.8.3</generator>
	<item>
		<title>iOS13适配：UIWindow窗口和导航控制器</title>
		<link>https://blog.z6z8.cn/2019/10/17/ios13%e9%80%82%e9%85%8d%ef%bc%9auiwindow%e7%aa%97%e5%8f%a3%e5%92%8c%e5%af%bc%e8%88%aa%e6%8e%a7%e5%88%b6%e5%99%a8/</link>
		
		<dc:creator><![CDATA[holdsky]]></dc:creator>
		<pubDate>Thu, 17 Oct 2019 05:14:26 +0000</pubDate>
				<category><![CDATA[iOS]]></category>
		<category><![CDATA[学习笔记]]></category>
		<category><![CDATA[iOS13]]></category>
		<category><![CDATA[导航控制器]]></category>
		<guid isPermaLink="false">http://blog.z6z8.cn/?p=512</guid>

					<description><![CDATA[@ 这次iOS13的发布，其改动步子有点大了，尤其是是其多场景窗口（多任务）已经颠覆了老应用的设计基础了--- [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>@<br />
这次iOS13的发布，其改动步子有点大了，尤其是是其多场景窗口（多任务）已经颠覆了老应用的设计基础了----数据的协同共享处理机制（本文不谈，哈哈）<br />
这里记录下一些界面层面的适配体会：</p>
<p>如果是<code>Xcode 10</code>及以下创建的老项目，用<code>Xcode 11</code>打开，老项目基本能正常运行。但是如果用<code>Xcode 11</code>创建新项目，还按照老项目思路写代码就会有坑了。</p>
<p>用<code>Xcode 11</code>创建一个<code>Single View App</code>项目，会多生成一些文件和代码</p>
<ul>
<li>多了SceneDelegate代理</li>
<li>Info.plist里面多了Application Scene Manifest配置</li>
</ul>
<p>多出来的这些文件和代码，影响最直观的是多场景窗口和导航控制器。</p>
<h1>适配方案——不支持多场景窗口</h1>
<p>这种适配方案最简单。<br />
将多出来的文件和代码删除就好了</p>
<ul>
<li>删除SceneDelegate代理文件 (可选)</li>
<li>删除 Info.plist里面的Application Scene Manifest配置（一定要删除）</li>
<li>删除 AppDelegate代理的两个方法：<br />
<code>application:configurationForConnectingSceneSession:options:</code><br />
<code>application: didDiscardSceneSessions:</code><br />
这两个方法一定要删除，否则使用纯代码创建的<code>Window</code>和导航控制器<code>UINavigationController</code>不会生效。</li>
</ul>
<h1>适配方案——支持多场景窗口</h1>
<p>先说我遇到的一些现象。<br />
尽管我不会为每个应用自定义窗口和导航，但我我依然会使用纯代码创建<code>UIWindow</code>和<code>UINavigationController</code>，具体如下</p>
<pre><code class="language-objectivec">//AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    //在Xcode11创建的项目中，需要自行给AppDelegate添加属性window
    //自定义Window
    self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    //自定义导航控制器
    UINavigationController *rootNavgationController = [[UINavigationController alloc] initWithRootViewController:[ViewController new]];
    //导航控制是rootViewController
    self.window.rootViewController = rootNavgationController;
    //现实Window
    [self.window makeKeyAndVisible];
    return YES;
}</code></pre>
<p><code>Xcode 11</code>创建的项目中，写入上述代码运行App，结果发现这部分代码虽然执行了，但是通过UIViewController的<code>self.navigationController</code>获取的导航竟然是<code>nil</code>。<br />
从现象反推过程，既然代码执行了，那么很大可能是<code>self.window</code>没有显示在屏幕上。<br />
查看iOS13下<code>UIWindow</code>的定义，有这么一条</p>
<pre><code class="language-objectivec">// If nil, window will not appear on any screen.
// changing the UIWindowScene may be an expensive operation and should not be done in performance-sensitive code
@property(nullable, nonatomic, weak) UIWindowScene *windowScene API_AVAILABLE(ios(13.0));</code></pre>
<p>如果<code>UIWindow</code>的属性<code>windowScene</code>为nil，那么这个<code>UIWindow</code>则不会显示在任何屏幕上。</p>
<p>既然问题找到了，那么解决起来也就容易了，一番断点调试跟踪代码后，加单的解决办法是在<code>SceneDelegate</code>的方法<code>scene:willConnectToSession:options:</code>中创建<code>UIWindow</code>和<code>UINavigationController</code></p>
<pre><code class="language-objectivec">//SceneDelegate
- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions {
    self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    self.window.windowScene = (UIWindowScene*)scene;
    UINavigationController *rootNavgationController = [[UINavigationController alloc] initWithRootViewController:[ViewController new]];
    self.window.rootViewController = rootNavgationController;
    [self.window makeKeyAndVisible];
}</code></pre>
<h2>同时兼容iOS13和iOS12及以下</h2>
<p>多场景窗口、SceneDelegate等只有在iOS13才可以，若要考虑iOS12及以下的运行环境，那么上述解决方案就要考虑环境版本匹配了，完整代码如下</p>
<p>AppDelegate部分代码</p>
<pre><code class="language-objectivec">//AppDelegate.h

@interface AppDelegate : UIResponder &lt;UIApplicationDelegate&gt;

@property (strong, nonatomic) UIWindow *window;

@end

//AppDelegate.m
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    if (@available(iOS 13,*)) {
        return YES;
    } else {
        self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
        UINavigationController *rootNavgationController = [[UINavigationController alloc] initWithRootViewController:[ViewController new]];
        self.window.rootViewController = rootNavgationController;
        [self.window makeKeyAndVisible];
        return YES;
    }
}

#pragma mark - UISceneSession lifecycle

- (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options {
    return [[UISceneConfiguration alloc] initWithName:@"Default Configuration" sessionRole:connectingSceneSession.role];
}

@end</code></pre>
<p>SceneDelegate部分代码</p>
<pre><code class="language-objectivec">//SceneDelegate.h
@interface SceneDelegate : UIResponder &lt;UIWindowSceneDelegate&gt;

@property (strong, nonatomic) UIWindow * window;

@end

//SceneDelegate.m
@implementation SceneDelegate
- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions {
    self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    self.window.windowScene = (UIWindowScene*)scene;
    UINavigationController *rootNavgationController = [[UINavigationController alloc] initWithRootViewController:[ViewController new]];
    self.window.rootViewController = rootNavgationController;
    [self.window makeKeyAndVisible];
}

@end</code></pre>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
