From 469807e3c067b8d9fac61ae3c0b05ecb0009daf1 Mon Sep 17 00:00:00 2001 From: Pradeep Elankumaran Date: Thu, 17 Sep 2026 14:16:51 -0700 Subject: [PATCH] fix(ios): apply background color behind status bar --- .../Capacitor/CAPBridgeViewController.swift | 19 +++++++++++ .../CapacitorTests/CapacitorTests.swift | 32 +++++++++++++++++-- 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/ios/Capacitor/Capacitor/CAPBridgeViewController.swift b/ios/Capacitor/Capacitor/CAPBridgeViewController.swift index a7285213d8..9b1bb00321 100644 --- a/ios/Capacitor/Capacitor/CAPBridgeViewController.swift +++ b/ios/Capacitor/Capacitor/CAPBridgeViewController.swift @@ -9,6 +9,7 @@ import Cordova } public fileprivate(set) var webView: WKWebView? + internal private(set) var statusBarBackgroundView: UIView? public var isStatusBarVisible = true public var statusBarStyle: UIStatusBarStyle = .default @@ -44,6 +45,7 @@ import Cordova let delegationHandler = WebViewDelegationHandler() prepareWebView(with: configuration, assetHandler: assetHandler, delegationHandler: delegationHandler) view = webView + installStatusBarBackground(using: configuration.backgroundColor ?? .systemBackground) // create the bridge capacitorBridge = CapacitorBridge(with: configuration, delegate: self, @@ -324,6 +326,23 @@ extension CAPBridgeViewController { } } + private func installStatusBarBackground(using color: UIColor) { + guard let webView else { return } + + let background = UIView() + background.backgroundColor = color + background.isUserInteractionEnabled = false + background.translatesAutoresizingMaskIntoConstraints = false + webView.addSubview(background) + NSLayoutConstraint.activate([ + background.topAnchor.constraint(equalTo: webView.topAnchor), + background.leadingAnchor.constraint(equalTo: webView.leadingAnchor), + background.trailingAnchor.constraint(equalTo: webView.trailingAnchor), + background.bottomAnchor.constraint(equalTo: webView.safeAreaLayoutGuide.topAnchor), + ]) + statusBarBackgroundView = background + } + private func updateBinaryVersion() { guard isNewBinary else { return diff --git a/ios/Capacitor/CapacitorTests/CapacitorTests.swift b/ios/Capacitor/CapacitorTests/CapacitorTests.swift index bd89d326bb..7ae96eb93f 100644 --- a/ios/Capacitor/CapacitorTests/CapacitorTests.swift +++ b/ios/Capacitor/CapacitorTests/CapacitorTests.swift @@ -4,6 +4,24 @@ import XCTest class MockBridgeViewController: CAPBridgeViewController { } +class BackgroundColorBridgeViewController: CAPBridgeViewController { + let descriptor: InstanceDescriptor + + init(descriptor: InstanceDescriptor) { + self.descriptor = descriptor + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + descriptor = InstanceDescriptor.init() + super.init(coder: coder) + } + + override func instanceDescriptor() -> InstanceDescriptor { + return descriptor + } +} + class MockAssetHandler: WebViewAssetHandler { } @@ -26,8 +44,16 @@ class CapacitorTests: XCTestCase { bridge = MockBridge(with: InstanceConfiguration(with: descriptor, isDebug: true), delegate: MockBridgeViewController(), cordovaConfiguration: descriptor.cordovaConfiguration, assetHandler: MockAssetHandler(router: CapacitorRouter()), delegationHandler: MockDelegationHandler()) } - override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. - super.tearDown() + func testConfiguredBackgroundCoversStatusBarSafeArea() { + let descriptor = InstanceDescriptor.init() + descriptor.backgroundColor = .red + let controller = BackgroundColorBridgeViewController(descriptor: descriptor) + + controller.loadView() + + let statusBarBackground = controller.statusBarBackgroundView + XCTAssertEqual(statusBarBackground?.superview, controller.webView) + XCTAssertEqual(statusBarBackground?.backgroundColor, .red) + XCTAssertFalse(statusBarBackground?.isUserInteractionEnabled ?? true) } }