web-dev-qa-db-ja.com

iOS 13でUIWindowがコンテンツの上に表示されない

IOS 13で定義されている新しいUISceneパターンを使用するようにアプリをアップグレードしていますが、アプリの重要な部分が機能しなくなりました。私はUIWindowを使用して画面上の現在のコンテンツをカバーし、ユーザーに新しい情報を提示していますが、現在のベータ版(iOS + XCodeベータ3)で作業していますが、ウィンドウが表示されますが、すぐに消えます。

これが私が使用していたコードですが、今は機能しません:

let window = UIWindow(frame: UIScreen.main.bounds)
let viewController = UIViewController()
viewController.view.backgroundColor = .clear
window.rootViewController = viewController
window.windowLevel = UIWindow.Level.statusBar + 1
window.makeKeyAndVisible()
viewController.present(self, animated: true, completion: nil)

私はWindowScenesを使用して新しいUIWindowを提示するなど、さまざまなことを試しましたが、実際のドキュメントや例はありません。

私の試みの1つ(動作しませんでした-すぐにウィンドウが表示されて閉じられるのと同じ動作)

let windowScene = UIApplication.shared.connectedScenes.first
if let windowScene = windowScene as? UIWindowScene {
    let window = UIWindow(windowScene: windowScene)
    let viewController = UIViewController()
    viewController.view.backgroundColor = .clear
    window.rootViewController = viewController
    window.windowLevel = UIWindow.Level.statusBar + 1
    window.makeKeyAndVisible()
    viewController.present(self, animated: true, completion: nil)
}

IOS 13ベータ版でこれを行うことができる人はいますか?

ありがとう

[〜#〜]編集[〜#〜]

これを要求してから、iOS 13の最終バージョンがリリースされるまでに時間がかかります。以下に多くの答えがありますが、それらのほとんどすべてに1つのものが含まれています-IWindowへの強い/より強い参照の追加。新しいシーンに関連するコードを含める必要があるかもしれませんが、最初に強参照を追加してみてください。

27
mHopkins

IOS 13シーンパターンのコードをアップグレードしているときに同じ問題が発生しました。 2番目のコードスニペットの一部を使用してすべてを修正し、ウィンドウが再び表示されるようにしました。私は最後の行を除いてあなたと同じことをしていました。 viewController.present(...)を削除してみてください。これが私のコードです:

_let windowScene = UIApplication.shared
                .connectedScenes
                .filter { $0.activationState == .foregroundActive }
                .first
if let windowScene = windowScene as? UIWindowScene {
    popupWindow = UIWindow(windowScene: windowScene)
}
_

それから私はあなたがするようにそれを提示します:

_popupWindow?.frame = UIScreen.main.bounds
popupWindow?.backgroundColor = .clear
popupWindow?.windowLevel = UIWindow.Level.statusBar + 1
popupWindow?.rootViewController = self as? UIViewController
popupWindow?.makeKeyAndVisible()
_

とにかく、私は個人的に問題はviewController.present(...)にあると思います。そのコントローラーでウィンドウを表示し、すぐに「自己」を提示するため、それは「自己」が何であるかに依存します。

また、コントローラーの内部から移動しているウィンドウへの参照を保存することにも言及する価値があります。これがまだ役に立たない場合は、このコードを使用する小さな repo しか表示できません。 _AnyPopupController.Swift_ファイルと_Popup.Swift_ファイルをご覧ください。

@SirOzのお役に立てば幸い

21
glassomoss

提案されたすべてのソリューションに基づいて、私は自分のバージョンのコードを提供できます。

private var window: UIWindow!

extension UIAlertController {
    func present(animated: Bool, completion: (() -> Void)?) {
        window = UIWindow(frame: UIScreen.main.bounds)
        window.rootViewController = UIViewController()
        window.windowLevel = .alert + 1
        window.makeKeyAndVisible()
        window.rootViewController?.present(self, animated: animated, completion: completion)
    }

    open override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        window = nil
    }
}

使い方:

// Show message (from any place)
let alert = UIAlertController(title: "Title", message: "Message", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Button", style: .cancel))
alert.present(animated: true, completion: nil)
11
Vergiliy

提示したいUIWindowの参照strongを保存するだけです。表示されたフードビューコントローラーの下では、ウィンドウを参照していないようです。

3
Andrey M.

@glassomossありがとうございます。私の問題はUIAlertControllerにあります。

私はこの方法で問題を解決しました:

  • 変数を追加しました
var windowsPopUp: UIWindow?
  • PopUpを表示するようにコードを変更しました。
public extension UIAlertController {
    func showPopUp() {
        windowsPopUp = UIWindow(frame: UIScreen.main.bounds)
        let vc = UIViewController()
        vc.view.backgroundColor = .clear
        windowsPopUp!.rootViewController = vc
        windowsPopUp!.windowLevel = UIWindow.Level.alert + 1
        windowsPopUp!.makeKeyAndVisible()
        vc.present(self, animated: true)
    }
}
  • 追加したUIAlertControllerのアクションで:
windowsPopUp = nil

最後の行がない場合、PopUpは閉じられますが、ウィンドウはアクティブなままで、アプリケーション(アプリケーションウィンドウを使用)での反復は許可されません。

3
SirOz

他の皆が述べたように、問題はウィンドウへの強い参照が必要であるということです。したがって、このウィンドウが使用後に再び確実に削除されるようにするために、必要なすべてのものを独自のクラスでカプセル化しました。

ここに少しSwift 5スニペットがあります:

class DebugCheatSheet {

    private var window: UIWindow?

    func present() {
        let vc = UIViewController()
        vc.view.backgroundColor = .clear

        window = UIWindow(frame: UIScreen.main.bounds)
        window?.rootViewController = vc
        window?.windowLevel = UIWindow.Level.alert + 1
        window?.makeKeyAndVisible()

        vc.present(sheet(), animated: true, completion: nil)
    }

    private func sheet() -> UIAlertController {
        let alert = UIAlertController.init(title: "Cheatsheet", message: nil, preferredStyle: .actionSheet)
        addAction(title: "Ok", style: .default, to: alert) {
            print("Alright...")
        }
        addAction(title: "Cancel", style: .cancel, to: alert) {
            print("Cancel")
        }
        return alert
    }

    private func addAction(title: String?, style: UIAlertAction.Style, to alert: UIAlertController, action: @escaping () -> ()) {
        let action = UIAlertAction.init(title: title, style: style) { [weak self] _ in
            action()
            alert.dismiss(animated: true, completion: nil)
            self?.window = nil
        }
        alert.addAction(action)
    }
}

そして、これが私がそれをどのように使用するかです。それは、アプリ全体のビュー階層で最も低いビューコントローラからのものですが、他のどこからでも使用できます。

private let cheatSheet = DebugCheatSheet()

override func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?) {
    if motion == .motionShake {
        cheatSheet.present()
    }
}
2
Wiingaard

iOS 13は、アラートを管理するためのヘルパー機能を壊しました。

複数のアラートを同時に表示する必要がある場合があるため(たとえば、「はい」または「いいえ」のアラートを表示していて、その間にWebサービスが表示するエラーで戻る場合など)アラートを介して(これは制限ケースですが、発生する可能性があります)、

私の解決策は、このようにUIAlertControllerを拡張し、それから表示される独自のalertWindowを持たせることです。

長所は、強い参照が残っているため、アラートを閉じるとウィンドウが自動的に閉じられるため、これ以上の変更は実装されないということです。

免責事項:実装したばかりなので、一貫性があるかどうかを確認する必要があります...

class AltoAlertController: UIAlertController {

var alertWindow : UIWindow!

func show(animated: Bool, completion: (()->(Void))?)
{
    alertWindow = UIWindow(frame: UIScreen.main.bounds)
    alertWindow.rootViewController = UIViewController()
    alertWindow.windowLevel = UIWindow.Level.alert + 1
    alertWindow.makeKeyAndVisible()
    alertWindow.rootViewController?.present(self, animated: animated, completion: completion)
}

}

1
Il Pisanello

IOS 13で新しいウィンドウにビューコントローラを表示する手順は次のとおりです。

  1. フォーカスUIWindowSceneを検出します。
extension UIWindowScene {
    static var focused: UIWindowScene? {
        return UIApplication.shared.connectedScenes
            .first { $0.activationState == .foregroundActive && $0 is UIWindowScene } as? UIWindowScene
    }
}
  1. フォーカスされたシーンのUIWindowを作成します。
if let window = UIWindowScene.focused.map(UIWindow.init(windowScene:)) {
  // ...
}
  1. そのウィンドウにUIViewControllerを表示します。
let myViewController = UIViewController()

if let window = UIWindowScene.focused.map(UIWindow.init(windowScene:)) {
    window.rootViewController = myViewController
    window.makeKeyAndVisible()
}
1
Vadim Bulavin

Ios13用に作成されたウィンドウにポインターが必要です。

私のコードの例:

 extension UIAlertController {

    private static var _aletrWindow: UIWindow?
    private static var aletrWindow: UIWindow {
        if let window = _aletrWindow {
            return window
        } else {
            let window = UIWindow(frame: UIScreen.main.bounds)
            window.rootViewController = UIViewController()
            window.windowLevel = UIWindowLevelAlert + 1
            window.backgroundColor = .clear
            _aletrWindow = window
            return window
        }
    }

    func presentGlobally(animated: Bool, completion: (() -> Void)? = nil) {
        UIAlertController.aletrWindow.makeKeyAndVisible()
        UIAlertController.aletrWindow.rootViewController?.present(self, animated: animated, completion: completion)
    }

    open override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        UIAlertController.aletrWindow.isHidden = true
    }

}

使用する:

let alert = UIAlertController(...
...

alert.presentGlobally(animated: true)
0
RomanV

これは、作成されたUIWindowへの強い参照を保持し、提示されたビューコントローラーが破棄され、割り当てが解除された後に解放する、少しハックな方法です。参照サイクルを行わないようにしてください。

private final class WindowHoldingViewController: UIViewController {

    private var window: UIWindow?

    convenience init(window: UIWindow) {
        self.init()

        self.window = window
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = UIColor.clear
    }

    override func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
        let view = DeallocatingView()
        view.onDeinit = { [weak self] in
            self?.window = nil
        }
        viewControllerToPresent.view.addSubview(view)

        super.present(viewControllerToPresent, animated: flag, completion: completion)
    }

    private final class DeallocatingView: UIView {

        var onDeinit: (() -> Void)?

        deinit {
            onDeinit?()
        }
    }
}

使用法:

let vcToPresent: UIViewController = ...
let window = UIWindow() // or create via window scene
...
window.rootViewController = WindowHoldingViewController(window: window)
...
window.rootViewController?.present(vcToPresent, animated: animated, completion: completion)
0
younke