-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
♻️ Update modal context traits to present in their own UIWindow
- Loading branch information
Showing
3 changed files
with
116 additions
and
12 deletions.
There are no files selected for viewing
95 changes: 95 additions & 0 deletions
95
Sources/AppcuesKit/Presentation/ExperienceRendering/ModalContextManager.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
// | ||
// ModalContextManager.swift | ||
// AppcuesKit | ||
// | ||
// Created by Matt on 2024-09-25. | ||
// Copyright © 2024 Appcues. All rights reserved. | ||
// | ||
|
||
import UIKit | ||
|
||
@available(iOS 13.0, *) | ||
internal class ModalContextManager { | ||
var presentingWindow: AppcuesUIWindow? | ||
|
||
func present(viewController: UIViewController, useSameWindow: Bool = false, completion: (() -> Void)?) throws { | ||
guard !useSameWindow else { | ||
try presentInSameWindow(viewController: viewController, completion: completion) | ||
return | ||
} | ||
|
||
guard let windowScene = UIApplication.shared.activeWindowScenes.first else { | ||
throw AppcuesTraitError(description: "No active window scene") | ||
} | ||
|
||
let window = AppcuesUIWindow(windowScene: windowScene) | ||
|
||
guard let rootViewController = window.rootViewController else { | ||
throw AppcuesTraitError(description: "No root view controller") | ||
} | ||
|
||
rootViewController.present(viewController, animated: true, completion: completion) | ||
|
||
presentingWindow = window | ||
} | ||
|
||
private func presentInSameWindow(viewController: UIViewController, completion: (() -> Void)?) throws { | ||
guard let topViewController = UIApplication.shared.topViewController() else { | ||
throw AppcuesTraitError(description: "No top VC found") | ||
} | ||
|
||
topViewController.present(viewController, animated: true, completion: completion) | ||
} | ||
|
||
func remove(viewController: UIViewController, completion: (() -> Void)?) { | ||
viewController.dismiss(animated: true) { | ||
self.presentingWindow = nil | ||
completion?() | ||
} | ||
} | ||
} | ||
|
||
@available(iOS 13.0, *) | ||
extension ModalContextManager { | ||
class AppcuesUIWindow: UIWindow { | ||
override init(windowScene: UIWindowScene) { | ||
super.init(windowScene: windowScene) | ||
|
||
rootViewController = AppcuesWindowRootViewController() | ||
isHidden = false | ||
} | ||
|
||
@available(*, unavailable) | ||
required init?(coder: NSCoder) { | ||
fatalError("init(coder:) has not been implemented") | ||
} | ||
|
||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { | ||
guard let hitView = super.hitTest(point, with: event), hitView != self else { | ||
return nil | ||
} | ||
|
||
// Ignore UITransitionViews to allow interaction with the underlying app while the window is overlayed. | ||
if String(describing: type(of: hitView)) == "UITransitionView" { | ||
return nil | ||
} | ||
|
||
return hitView | ||
} | ||
} | ||
|
||
class AppcuesWindowRootViewController: UIViewController { | ||
init() { | ||
super.init(nibName: nil, bundle: nil) | ||
|
||
// Ensure this root view isn't eligible for a hitTest | ||
// (any ViewController it presents will be though) | ||
view.alpha = 0 | ||
} | ||
|
||
@available(*, unavailable) | ||
required init?(coder: NSCoder) { | ||
fatalError("init(coder:) has not been implemented") | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters