UIScene生命周期适配
在iOS27由于多窗口机制,iOS底层增加了UIScene场景管理,需要添加新的SceneDeleage实现多窗口机制管理,针对旧的app需要做如下迁移
创建SceneDeleage
SceneDeleage.h
objectivec
#import <UIKit/UIKit.h>
API_AVAILABLE(ios(13.0))
@interface SceneDelegate : UIResponder <UIWindowSceneDelegate>
@property (strong, nonatomic) UIWindow * window;
@end
SceneDeleage.m
objectivec
#import "SceneDelegate.h"
API_AVAILABLE(ios(13.0))
@implementation SceneDelegate
- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions {
if (![scene isKindOfClass:[UIWindowScene class]]) return;
UIWindowScene *windowScene = (UIWindowScene *)scene;
self.window = [[UIWindow alloc] initWithWindowScene:windowScene];
// =========替换成你项目真实根控制器=========
UIViewController *rootVC = [[UIViewController alloc] init];
self.window.rootViewController = rootVC;
[self.window makeKeyAndVisible];
}
- (void)sceneDidDisconnect:(UIScene *)scene {}
- (void)sceneDidBecomeActive:(UIScene *)scene {}
- (void)sceneWillResignActive:(UIScene *)scene {}
- (void)sceneWillEnterForeground:(UIScene *)scene {}
- (void)sceneDidEnterBackground:(UIScene *)scene {}
@end
注意替换项目真是根控制器,将相应appdeleage的代码转移到此处
原有AppDelegate的处理
保留 window 属性,用于 iOS12 及以下兼容
objectivec
#import <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@end
移除原来rootcontroller的设置,使用系统配置sceneDelegate,增加两个新的deleage方法,Default Configuration命名可以自定但需要跟info.plist中的配置相对应
objectivec
#import "AppDelegate.h"
#import "SceneDelegate.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if (@available(iOS 13.0, *)) {
// iOS13+ window交给SceneDelegate处理,此处不要操作self.window
} else {
// iOS12及更低版本 老逻辑保持不变
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
// 替换为你的根控制器
UIViewController *rootVC = [[UIViewController alloc] init];
self.window.rootViewController = rootVC;
[self.window makeKeyAndVisible];
}
// 全局SDK初始化、第三方配置写在这里
return YES;
}
#pragma mark - UISceneSession Lifecycle
API_AVAILABLE(ios(13.0))
- (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options {
return [[UISceneConfiguration alloc] initWithName:@"Default Configuration" sessionRole:connectingSceneSession.role];
}
API_AVAILABLE(ios(13.0))
- (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet<UISceneSession *> *)sceneSessions {
}
@end
变更info.plist
objectivec
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<!-- 注意!!把下面 MyApp 替换为你实际 Product Module Name -->
<key>UISceneDelegateClassName</key>
<string>MyApp.SceneDelegate</string>
</dict>
</array>
</dict>
</dict>
这里需要注意MyApp的设定,如果工程是defines Module为true的,则使用product module name作为MyApp的提黄
如果defines module为false
objectivec
__attribute__((objc_runtime_name("SceneDelegate")))
@interface SceneDelegate : UIResponder<UIWindowSceneDelegate>
...
<key>UISceneDelegateClassName</key>
<string>SceneDelegate</string>
使用运行态SceneDelegate进行替换
这里注意的是info.plist把UIApplicationSupportsMultipleScenes设置为false,如果设置为true,需要处理各个场景下keywindow的处理逻辑
废弃写法 [UIApplication sharedApplication].keyWindow 不要再用;
获取当前活跃 window OC 版本:
objectivec
- (UIWindow *)getCurrentKeyWindow {
for (UIScene *scene in [UIApplication sharedApplication].connectedScenes) {
if (scene.activationState == UISceneActivationStateForegroundActive && [scene isKindOfClass:[UIWindowScene class]]) {
UIWindowScene *ws = (UIWindowScene *)scene;
for (UIWindow *win in ws.windows) {
if (win.isKeyWindow) {
return win;
}
}
}
}
return nil;
}