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で指定します。

/post/ios-watermark-uiwindowsscenedelegate/img1.png

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

/*
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)
    }
}