[PM-26060] Consolidate UIViewController extensions (#2072)

This commit is contained in:
Katherine Bertelsen 2025-10-29 09:23:32 -05:00 committed by GitHub
parent 01973e7ef7
commit 52a3da8deb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 3 additions and 132 deletions

View File

@ -1,22 +0,0 @@
import UIKit
extension UIViewController {
/// Returns the topmost view controller starting from this view controller and navigating down the view hierarchy.
///
/// - Returns: The topmost view controller from this view controller.
///
func topmostViewController() -> UIViewController {
if let presentedViewController {
presentedViewController.topmostViewController()
} else {
switch self {
case let navigationController as UINavigationController:
navigationController.topViewController?.topmostViewController() ?? navigationController
case let tabBarController as UITabBarController:
tabBarController.selectedViewController?.topmostViewController() ?? tabBarController
default:
self
}
}
}
}

View File

@ -1,83 +0,0 @@
import XCTest
@testable import AuthenticatorShared
class UIViewControllerTests: BitwardenTestCase {
/// `topmostViewController` returns the top view controller for a view controller.
func test_topmostViewController_isViewController() {
let subject = UIViewController()
XCTAssertEqual(subject.topmostViewController(), subject)
}
/// `topmostViewController` returns the top view controller for a navigation controller.
func test_topmostViewController_isNavigationController() {
let subject = UINavigationController()
XCTAssertEqual(subject.topmostViewController(), subject)
let viewControllerA = UIViewController()
let viewControllerB = UIViewController()
subject.viewControllers = [viewControllerA, viewControllerB]
XCTAssertEqual(subject.topmostViewController(), viewControllerB)
subject.popViewController(animated: false)
XCTAssertEqual(subject.topmostViewController(), viewControllerA)
}
/// `topmostViewController` returns the top view controller for a tab bar controller.
func test_topmostViewController_isTabBarController() {
let subject = UITabBarController()
XCTAssertEqual(subject.topmostViewController(), subject)
let viewControllerA = UIViewController()
let viewControllerB = UIViewController()
subject.viewControllers = [viewControllerA, viewControllerB]
XCTAssertEqual(subject.topmostViewController(), viewControllerA)
subject.selectedIndex = 1
XCTAssertEqual(subject.topmostViewController(), viewControllerB)
}
/// `topmostViewController` returns the top view controller when presenting a view controller.
func test_topmostViewController_presentingViewController() {
let subject = UIViewController()
setKeyWindowRoot(viewController: subject)
XCTAssertEqual(subject.topmostViewController(), subject)
let viewController = UIViewController()
subject.present(viewController, animated: false, completion: nil)
waitFor { subject.presentedViewController != nil }
XCTAssertEqual(subject.topmostViewController(), viewController)
}
/// `topmostViewController` returns the top view controller when presenting a navigation controller.
func test_topmostViewController_presentingNavigationController() {
let subject = UIViewController()
let navigationController = UINavigationController()
setKeyWindowRoot(viewController: subject)
subject.present(navigationController, animated: false, completion: nil)
XCTAssertEqual(subject.topmostViewController(), navigationController)
let viewController = UIViewController()
navigationController.setViewControllers([viewController], animated: false)
XCTAssertEqual(subject.topmostViewController(), viewController)
}
/// `topmostViewController` returns the top view controller when presenting a tab bar controller.
func test_topmostViewController_presentingTabBarController() {
let subject = UIViewController()
let tabBarController = UITabBarController()
setKeyWindowRoot(viewController: subject)
subject.present(tabBarController, animated: false, completion: nil)
let viewControllerA = UIViewController()
let navigationControllerA = UINavigationController(rootViewController: viewControllerA)
let viewControllerB = UIViewController()
let navigationControllerB = UINavigationController(rootViewController: viewControllerB)
tabBarController.viewControllers = [navigationControllerA, navigationControllerB]
XCTAssertEqual(subject.topmostViewController(), viewControllerA)
tabBarController.selectedIndex = 1
XCTAssertEqual(subject.topmostViewController(), viewControllerB)
}
}

View File

@ -287,28 +287,6 @@ extension UINavigationController: StackNavigator {
present(controller, animated: animated, onCompletion: onCompletion)
}
public func present(
_ viewController: UIViewController,
animated: Bool,
overFullscreen: Bool = false,
onCompletion: (() -> Void)? = nil,
) {
var presentedChild = presentedViewController
var availablePresenter: UIViewController? = self
while presentedChild != nil {
availablePresenter = presentedChild
presentedChild = presentedChild?.presentedViewController
}
if overFullscreen {
viewController.modalPresentationStyle = .overFullScreen
}
availablePresenter?.present(
viewController,
animated: animated,
completion: onCompletion,
)
}
public func replace<Content: View>(_ view: Content, animated: Bool) {
let animated = self.view.window != nil ? animated : false
setViewControllers([UIHostingController(rootView: view)], animated: animated)

View File

@ -1,7 +1,6 @@
import BitwardenKit
import UIKit
extension UIViewController {
public extension UIViewController {
/// Presents a view controller modally. Supports presenting on top of presented modals if necessary.
///
/// - Parameters:
@ -10,7 +9,7 @@ extension UIViewController {
/// - overFullscreen: Whether or not the presented modal should cover the full screen.
/// - onCompletion: A closure to call on completion.
///
public func present(
func present(
_ viewController: UIViewController,
animated: Bool = UI.animated,
overFullscreen: Bool = false,

View File

@ -1,7 +1,6 @@
import BitwardenKit
import XCTest
@testable import BitwardenShared
class UIViewControllerTests: BitwardenTestCase {
/// `topmostViewController` returns the top view controller for a view controller.
func test_topmostViewController_isViewController() {