わふうの人が書いてます。

iOSアプリケーション開発、BLEのファームウェアとハードウェア試作のフリーランスエンジニア。

iOSで全画面にウォータマークを入れるのにUIWiddowSceneDelegateを使うやり方

Appleが提供しているCOVID-19の濃厚接触通知のサンプルプロジェクトで、アプリのあらゆる画面に”REFERENCE ONLY”を表示させるコード部分が、面白い実装だと思ったのでメモします。

プロジェクトファイルは、Building an App to Notify Users of COVID-19 Exposure https://developer.apple.com/documentation/exposurenotification/building_an_app_to_notify_users_of_covid-19_exposure にあります。この中の ExposureNotificationApp/SceneDelegate.swift が次のコードです。

iOS13から、例えばiPadなどで1つのアプリで複数の画面を切り替えながら使う用途で、シーンという概念が導入されています。このシーンを扱う UIWindowSceneDelegate で常に前面に”Reference only”のテキストラベルを表示するUIWindowを生成して、アプリで使わせています。iOS13のSceneDelegate周りのアプリの起動シーケンス https://qiita.com/omochimetaru/items/31df103ef98a9d84ae6b が参考になります。

UIWindowSceneDelegate, Additional methods that you use to manage app-specific tasks occurring in a scene. https://developer.apple.com/documentation/uikit/uiwindowscenedelegate

アプリがシーンに対応しているのか、複数のUIWindowを扱えるのか、UIWindowSceneDelegateの実装クラス名は、Info.plistで指定します。

Application Scene Manifest、の下に Enable Multiple Windows NO があります。複数のUIWindowは持ちません。さらに下に、DelegateClassNameがあります。ここでクラス名を指定しています。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
/*
See LICENSE folder for this sample’s licensing information.

Abstract:
The scene delegate.
*/

import UIKit

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

var window: WatermarkWindow!

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
window = WatermarkWindow(windowScene: scene as! UIWindowScene)
window.rootViewController = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController()
(window.rootViewController as! UITabBarController).selectedIndex = 1
window.makeKeyAndVisible()
}

func sceneDidBecomeActive(_ scene: UIScene) {
let rootViewController = window!.rootViewController!
if !LocalStore.shared.isOnboarded && rootViewController.presentedViewController == nil {
rootViewController.performSegue(withIdentifier: "ShowOnboarding", sender: nil)
}
}
}

class WatermarkWindow: UIWindow {

let watermark = UILabel()

override init(windowScene: UIWindowScene) {
super.init(windowScene: windowScene)

watermark.text = "REFERENCE ONLY"
watermark.font = .boldSystemFont(ofSize: 48.0)
watermark.textColor = .quaternaryLabel
watermark.translatesAutoresizingMaskIntoConstraints = false
watermark.transform = .init(rotationAngle: -.pi / 4.0)
watermark.isAccessibilityElement = false
addSubview(watermark)
NSLayoutConstraint.activate([
centerXAnchor.constraint(equalTo: watermark.centerXAnchor),
centerYAnchor.constraint(equalTo: watermark.centerYAnchor)
])
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

override func didAddSubview(_ subview: UIView) {
super.didAddSubview(subview)
bringSubviewToFront(watermark)
}
}