capacitor-plugin-status-bar 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,62 @@
1
+ 'use strict';
2
+
3
+ var core = require('@capacitor/core');
4
+
5
+ exports.Style = void 0;
6
+ (function (Style) {
7
+ Style["LIGHT"] = "LIGHT";
8
+ Style["DARK"] = "DARK";
9
+ Style["CUSTOM"] = "CUSTOM";
10
+ })(exports.Style || (exports.Style = {}));
11
+ exports.StatusBarAnimation = void 0;
12
+ (function (StatusBarAnimation) {
13
+ StatusBarAnimation["NONE"] = "none";
14
+ StatusBarAnimation["FADE"] = "fade";
15
+ StatusBarAnimation["SLIDE"] = "slide";
16
+ })(exports.StatusBarAnimation || (exports.StatusBarAnimation = {}));
17
+
18
+ const StatusBar = core.registerPlugin('StatusBar', {
19
+ web: () => Promise.resolve().then(function () { return web; }).then((m) => new m.StatusBarWeb()),
20
+ });
21
+
22
+ class StatusBarWeb extends core.WebPlugin {
23
+ async setStyle(options) {
24
+ console.log('setStyle', options);
25
+ }
26
+ async show(options) {
27
+ console.log('show', options);
28
+ }
29
+ async hide(options) {
30
+ console.log('hide', options);
31
+ }
32
+ async setOverlaysWebView(options) {
33
+ console.log('setOverlaysWebView', options);
34
+ }
35
+ async setBackground(options) {
36
+ console.log('setBackground', options);
37
+ }
38
+ async getSafeAreaInsets() {
39
+ // On web, we can use CSS environment variables to get safe area insets
40
+ // These are set by the browser on devices with notches, etc.
41
+ const getInsetValue = (variable) => {
42
+ const value = getComputedStyle(document.documentElement).getPropertyValue(variable).trim();
43
+ return value ? parseInt(value, 10) : 0;
44
+ };
45
+ const insets = {
46
+ top: getInsetValue('env(safe-area-inset-top)') || getInsetValue('constant(safe-area-inset-top)') || 0,
47
+ bottom: getInsetValue('env(safe-area-inset-bottom)') || getInsetValue('constant(safe-area-inset-bottom)') || 0,
48
+ left: getInsetValue('env(safe-area-inset-left)') || getInsetValue('constant(safe-area-inset-left)') || 0,
49
+ right: getInsetValue('env(safe-area-inset-right)') || getInsetValue('constant(safe-area-inset-right)') || 0,
50
+ };
51
+ console.log('getSafeAreaInsets', insets);
52
+ return insets;
53
+ }
54
+ }
55
+
56
+ var web = /*#__PURE__*/Object.freeze({
57
+ __proto__: null,
58
+ StatusBarWeb: StatusBarWeb
59
+ });
60
+
61
+ exports.StatusBar = StatusBar;
62
+ //# sourceMappingURL=plugin.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.cjs.js","sources":["esm/definitions.js","esm/index.js","esm/web.js"],"sourcesContent":["export var Style;\n(function (Style) {\n Style[\"LIGHT\"] = \"LIGHT\";\n Style[\"DARK\"] = \"DARK\";\n Style[\"CUSTOM\"] = \"CUSTOM\";\n})(Style || (Style = {}));\nexport var StatusBarAnimation;\n(function (StatusBarAnimation) {\n StatusBarAnimation[\"NONE\"] = \"none\";\n StatusBarAnimation[\"FADE\"] = \"fade\";\n StatusBarAnimation[\"SLIDE\"] = \"slide\";\n})(StatusBarAnimation || (StatusBarAnimation = {}));\n//# sourceMappingURL=definitions.js.map","import { registerPlugin } from '@capacitor/core';\nconst StatusBar = registerPlugin('StatusBar', {\n web: () => import('./web').then((m) => new m.StatusBarWeb()),\n});\nexport * from './definitions';\nexport { StatusBar };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nexport class StatusBarWeb extends WebPlugin {\n async setStyle(options) {\n console.log('setStyle', options);\n }\n async show(options) {\n console.log('show', options);\n }\n async hide(options) {\n console.log('hide', options);\n }\n async setOverlaysWebView(options) {\n console.log('setOverlaysWebView', options);\n }\n async setBackground(options) {\n console.log('setBackground', options);\n }\n async getSafeAreaInsets() {\n // On web, we can use CSS environment variables to get safe area insets\n // These are set by the browser on devices with notches, etc.\n const getInsetValue = (variable) => {\n const value = getComputedStyle(document.documentElement).getPropertyValue(variable).trim();\n return value ? parseInt(value, 10) : 0;\n };\n const insets = {\n top: getInsetValue('env(safe-area-inset-top)') || getInsetValue('constant(safe-area-inset-top)') || 0,\n bottom: getInsetValue('env(safe-area-inset-bottom)') || getInsetValue('constant(safe-area-inset-bottom)') || 0,\n left: getInsetValue('env(safe-area-inset-left)') || getInsetValue('constant(safe-area-inset-left)') || 0,\n right: getInsetValue('env(safe-area-inset-right)') || getInsetValue('constant(safe-area-inset-right)') || 0,\n };\n console.log('getSafeAreaInsets', insets);\n return insets;\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["Style","StatusBarAnimation","registerPlugin","WebPlugin"],"mappings":";;;;AAAWA;AACX,CAAC,UAAU,KAAK,EAAE;AAClB,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,OAAO;AAC5B,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,MAAM;AAC1B,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,QAAQ;AAC9B,CAAC,EAAEA,aAAK,KAAKA,aAAK,GAAG,EAAE,CAAC,CAAC;AACdC;AACX,CAAC,UAAU,kBAAkB,EAAE;AAC/B,IAAI,kBAAkB,CAAC,MAAM,CAAC,GAAG,MAAM;AACvC,IAAI,kBAAkB,CAAC,MAAM,CAAC,GAAG,MAAM;AACvC,IAAI,kBAAkB,CAAC,OAAO,CAAC,GAAG,OAAO;AACzC,CAAC,EAAEA,0BAAkB,KAAKA,0BAAkB,GAAG,EAAE,CAAC,CAAC;;ACV9C,MAAC,SAAS,GAAGC,mBAAc,CAAC,WAAW,EAAE;AAC9C,IAAI,GAAG,EAAE,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,YAAY,EAAE,CAAC;AAChE,CAAC;;ACFM,MAAM,YAAY,SAASC,cAAS,CAAC;AAC5C,IAAI,MAAM,QAAQ,CAAC,OAAO,EAAE;AAC5B,QAAQ,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,OAAO,CAAC;AACxC,IAAI;AACJ,IAAI,MAAM,IAAI,CAAC,OAAO,EAAE;AACxB,QAAQ,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC;AACpC,IAAI;AACJ,IAAI,MAAM,IAAI,CAAC,OAAO,EAAE;AACxB,QAAQ,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC;AACpC,IAAI;AACJ,IAAI,MAAM,kBAAkB,CAAC,OAAO,EAAE;AACtC,QAAQ,OAAO,CAAC,GAAG,CAAC,oBAAoB,EAAE,OAAO,CAAC;AAClD,IAAI;AACJ,IAAI,MAAM,aAAa,CAAC,OAAO,EAAE;AACjC,QAAQ,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,OAAO,CAAC;AAC7C,IAAI;AACJ,IAAI,MAAM,iBAAiB,GAAG;AAC9B;AACA;AACA,QAAQ,MAAM,aAAa,GAAG,CAAC,QAAQ,KAAK;AAC5C,YAAY,MAAM,KAAK,GAAG,gBAAgB,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE;AACtG,YAAY,OAAO,KAAK,GAAG,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,CAAC;AAClD,QAAQ,CAAC;AACT,QAAQ,MAAM,MAAM,GAAG;AACvB,YAAY,GAAG,EAAE,aAAa,CAAC,0BAA0B,CAAC,IAAI,aAAa,CAAC,+BAA+B,CAAC,IAAI,CAAC;AACjH,YAAY,MAAM,EAAE,aAAa,CAAC,6BAA6B,CAAC,IAAI,aAAa,CAAC,kCAAkC,CAAC,IAAI,CAAC;AAC1H,YAAY,IAAI,EAAE,aAAa,CAAC,2BAA2B,CAAC,IAAI,aAAa,CAAC,gCAAgC,CAAC,IAAI,CAAC;AACpH,YAAY,KAAK,EAAE,aAAa,CAAC,4BAA4B,CAAC,IAAI,aAAa,CAAC,iCAAiC,CAAC,IAAI,CAAC;AACvH,SAAS;AACT,QAAQ,OAAO,CAAC,GAAG,CAAC,mBAAmB,EAAE,MAAM,CAAC;AAChD,QAAQ,OAAO,MAAM;AACrB,IAAI;AACJ;;;;;;;;;"}
package/dist/plugin.js ADDED
@@ -0,0 +1,65 @@
1
+ var capStatusBar = (function (exports, core) {
2
+ 'use strict';
3
+
4
+ exports.Style = void 0;
5
+ (function (Style) {
6
+ Style["LIGHT"] = "LIGHT";
7
+ Style["DARK"] = "DARK";
8
+ Style["CUSTOM"] = "CUSTOM";
9
+ })(exports.Style || (exports.Style = {}));
10
+ exports.StatusBarAnimation = void 0;
11
+ (function (StatusBarAnimation) {
12
+ StatusBarAnimation["NONE"] = "none";
13
+ StatusBarAnimation["FADE"] = "fade";
14
+ StatusBarAnimation["SLIDE"] = "slide";
15
+ })(exports.StatusBarAnimation || (exports.StatusBarAnimation = {}));
16
+
17
+ const StatusBar = core.registerPlugin('StatusBar', {
18
+ web: () => Promise.resolve().then(function () { return web; }).then((m) => new m.StatusBarWeb()),
19
+ });
20
+
21
+ class StatusBarWeb extends core.WebPlugin {
22
+ async setStyle(options) {
23
+ console.log('setStyle', options);
24
+ }
25
+ async show(options) {
26
+ console.log('show', options);
27
+ }
28
+ async hide(options) {
29
+ console.log('hide', options);
30
+ }
31
+ async setOverlaysWebView(options) {
32
+ console.log('setOverlaysWebView', options);
33
+ }
34
+ async setBackground(options) {
35
+ console.log('setBackground', options);
36
+ }
37
+ async getSafeAreaInsets() {
38
+ // On web, we can use CSS environment variables to get safe area insets
39
+ // These are set by the browser on devices with notches, etc.
40
+ const getInsetValue = (variable) => {
41
+ const value = getComputedStyle(document.documentElement).getPropertyValue(variable).trim();
42
+ return value ? parseInt(value, 10) : 0;
43
+ };
44
+ const insets = {
45
+ top: getInsetValue('env(safe-area-inset-top)') || getInsetValue('constant(safe-area-inset-top)') || 0,
46
+ bottom: getInsetValue('env(safe-area-inset-bottom)') || getInsetValue('constant(safe-area-inset-bottom)') || 0,
47
+ left: getInsetValue('env(safe-area-inset-left)') || getInsetValue('constant(safe-area-inset-left)') || 0,
48
+ right: getInsetValue('env(safe-area-inset-right)') || getInsetValue('constant(safe-area-inset-right)') || 0,
49
+ };
50
+ console.log('getSafeAreaInsets', insets);
51
+ return insets;
52
+ }
53
+ }
54
+
55
+ var web = /*#__PURE__*/Object.freeze({
56
+ __proto__: null,
57
+ StatusBarWeb: StatusBarWeb
58
+ });
59
+
60
+ exports.StatusBar = StatusBar;
61
+
62
+ return exports;
63
+
64
+ })({}, capacitorExports);
65
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sources":["esm/definitions.js","esm/index.js","esm/web.js"],"sourcesContent":["export var Style;\n(function (Style) {\n Style[\"LIGHT\"] = \"LIGHT\";\n Style[\"DARK\"] = \"DARK\";\n Style[\"CUSTOM\"] = \"CUSTOM\";\n})(Style || (Style = {}));\nexport var StatusBarAnimation;\n(function (StatusBarAnimation) {\n StatusBarAnimation[\"NONE\"] = \"none\";\n StatusBarAnimation[\"FADE\"] = \"fade\";\n StatusBarAnimation[\"SLIDE\"] = \"slide\";\n})(StatusBarAnimation || (StatusBarAnimation = {}));\n//# sourceMappingURL=definitions.js.map","import { registerPlugin } from '@capacitor/core';\nconst StatusBar = registerPlugin('StatusBar', {\n web: () => import('./web').then((m) => new m.StatusBarWeb()),\n});\nexport * from './definitions';\nexport { StatusBar };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nexport class StatusBarWeb extends WebPlugin {\n async setStyle(options) {\n console.log('setStyle', options);\n }\n async show(options) {\n console.log('show', options);\n }\n async hide(options) {\n console.log('hide', options);\n }\n async setOverlaysWebView(options) {\n console.log('setOverlaysWebView', options);\n }\n async setBackground(options) {\n console.log('setBackground', options);\n }\n async getSafeAreaInsets() {\n // On web, we can use CSS environment variables to get safe area insets\n // These are set by the browser on devices with notches, etc.\n const getInsetValue = (variable) => {\n const value = getComputedStyle(document.documentElement).getPropertyValue(variable).trim();\n return value ? parseInt(value, 10) : 0;\n };\n const insets = {\n top: getInsetValue('env(safe-area-inset-top)') || getInsetValue('constant(safe-area-inset-top)') || 0,\n bottom: getInsetValue('env(safe-area-inset-bottom)') || getInsetValue('constant(safe-area-inset-bottom)') || 0,\n left: getInsetValue('env(safe-area-inset-left)') || getInsetValue('constant(safe-area-inset-left)') || 0,\n right: getInsetValue('env(safe-area-inset-right)') || getInsetValue('constant(safe-area-inset-right)') || 0,\n };\n console.log('getSafeAreaInsets', insets);\n return insets;\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["Style","StatusBarAnimation","registerPlugin","WebPlugin"],"mappings":";;;AAAWA;IACX,CAAC,UAAU,KAAK,EAAE;IAClB,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,OAAO;IAC5B,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,MAAM;IAC1B,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,QAAQ;IAC9B,CAAC,EAAEA,aAAK,KAAKA,aAAK,GAAG,EAAE,CAAC,CAAC;AACdC;IACX,CAAC,UAAU,kBAAkB,EAAE;IAC/B,IAAI,kBAAkB,CAAC,MAAM,CAAC,GAAG,MAAM;IACvC,IAAI,kBAAkB,CAAC,MAAM,CAAC,GAAG,MAAM;IACvC,IAAI,kBAAkB,CAAC,OAAO,CAAC,GAAG,OAAO;IACzC,CAAC,EAAEA,0BAAkB,KAAKA,0BAAkB,GAAG,EAAE,CAAC,CAAC;;ACV9C,UAAC,SAAS,GAAGC,mBAAc,CAAC,WAAW,EAAE;IAC9C,IAAI,GAAG,EAAE,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,YAAY,EAAE,CAAC;IAChE,CAAC;;ICFM,MAAM,YAAY,SAASC,cAAS,CAAC;IAC5C,IAAI,MAAM,QAAQ,CAAC,OAAO,EAAE;IAC5B,QAAQ,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,OAAO,CAAC;IACxC,IAAI;IACJ,IAAI,MAAM,IAAI,CAAC,OAAO,EAAE;IACxB,QAAQ,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC;IACpC,IAAI;IACJ,IAAI,MAAM,IAAI,CAAC,OAAO,EAAE;IACxB,QAAQ,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC;IACpC,IAAI;IACJ,IAAI,MAAM,kBAAkB,CAAC,OAAO,EAAE;IACtC,QAAQ,OAAO,CAAC,GAAG,CAAC,oBAAoB,EAAE,OAAO,CAAC;IAClD,IAAI;IACJ,IAAI,MAAM,aAAa,CAAC,OAAO,EAAE;IACjC,QAAQ,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,OAAO,CAAC;IAC7C,IAAI;IACJ,IAAI,MAAM,iBAAiB,GAAG;IAC9B;IACA;IACA,QAAQ,MAAM,aAAa,GAAG,CAAC,QAAQ,KAAK;IAC5C,YAAY,MAAM,KAAK,GAAG,gBAAgB,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE;IACtG,YAAY,OAAO,KAAK,GAAG,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,CAAC;IAClD,QAAQ,CAAC;IACT,QAAQ,MAAM,MAAM,GAAG;IACvB,YAAY,GAAG,EAAE,aAAa,CAAC,0BAA0B,CAAC,IAAI,aAAa,CAAC,+BAA+B,CAAC,IAAI,CAAC;IACjH,YAAY,MAAM,EAAE,aAAa,CAAC,6BAA6B,CAAC,IAAI,aAAa,CAAC,kCAAkC,CAAC,IAAI,CAAC;IAC1H,YAAY,IAAI,EAAE,aAAa,CAAC,2BAA2B,CAAC,IAAI,aAAa,CAAC,gCAAgC,CAAC,IAAI,CAAC;IACpH,YAAY,KAAK,EAAE,aAAa,CAAC,4BAA4B,CAAC,IAAI,aAAa,CAAC,iCAAiC,CAAC,IAAI,CAAC;IACvH,SAAS;IACT,QAAQ,OAAO,CAAC,GAAG,CAAC,mBAAmB,EAAE,MAAM,CAAC;IAChD,QAAQ,OAAO,MAAM;IACrB,IAAI;IACJ;;;;;;;;;;;;;;;"}
@@ -0,0 +1,325 @@
1
+ import Foundation
2
+ import UIKit
3
+ import Capacitor
4
+
5
+ @objc public class StatusBar: NSObject {
6
+ // Tag to identify the status bar background view
7
+ private static let statusBarViewTag = 38482458
8
+ // Store the current background color to restore when showing
9
+ private var currentBackgroundColor: UIColor?
10
+ // Track whether overlays web view mode is active
11
+ private var isOverlayMode = false
12
+
13
+ @objc public func applyDefaultStyle() {
14
+ DispatchQueue.main.async {
15
+ let isDarkMode = self.isSystemInDarkMode()
16
+ let style = isDarkMode ? "DARK" : "LIGHT"
17
+ print("StatusBar: Applying default style based on system theme - isDarkMode=\(isDarkMode), style=\(style)")
18
+ self.setStyle(style: style, colorHex: nil)
19
+ }
20
+ }
21
+
22
+ @objc public func setStyle(style: String, colorHex: String?) {
23
+ DispatchQueue.main.async {
24
+ guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene else { return }
25
+ guard let window = windowScene.windows.first else { return }
26
+ guard let statusBarManager = windowScene.statusBarManager else { return }
27
+
28
+ let upperStyle = style.uppercased()
29
+ var backgroundColor: UIColor?
30
+ var statusBarStyle: UIStatusBarStyle = .default
31
+
32
+ // Determine the status bar style and background color
33
+ if upperStyle == "LIGHT" {
34
+ // Light style: light background with dark content
35
+ statusBarStyle = .darkContent
36
+ backgroundColor = .white
37
+ } else if upperStyle == "DARK" {
38
+ // Dark style: dark background with light content
39
+ statusBarStyle = .lightContent
40
+ backgroundColor = .black
41
+ } else if upperStyle == "CUSTOM" {
42
+ // Custom style: use provided color and determine content style based on brightness
43
+ if let colorHex = colorHex, let color = self.colorFromHex(colorHex) {
44
+ backgroundColor = color
45
+ let brightness = self.getColorBrightness(color)
46
+ // If background is light, use dark content; if dark, use light content
47
+ statusBarStyle = brightness > 0.5 ? .darkContent : .lightContent
48
+ } else {
49
+ // No color provided, use system default
50
+ statusBarStyle = .default
51
+ backgroundColor = nil
52
+ }
53
+ } else {
54
+ // Default: use system default
55
+ statusBarStyle = .default
56
+ backgroundColor = nil
57
+ }
58
+
59
+ // Set the status bar style using KVC to avoid deprecation warnings
60
+ UIApplication.shared.setValue(statusBarStyle.rawValue, forKey: "statusBarStyle")
61
+
62
+ // Store the background color for later restoration
63
+ self.currentBackgroundColor = backgroundColor
64
+
65
+ // Skip background color update when overlays web view is active
66
+ if self.isOverlayMode {
67
+ print("StatusBar: setStyle - overlay mode active, skipping background color")
68
+ } else {
69
+ // Create or update the status bar background view
70
+ self.updateStatusBarBackgroundView(in: window,
71
+ height: statusBarManager.statusBarFrame.height,
72
+ color: backgroundColor)
73
+ }
74
+
75
+ print("StatusBar: setStyle - style=\(upperStyle), backgroundColor=\(String(describing: backgroundColor)), statusBarStyle=\(statusBarStyle)")
76
+ }
77
+ }
78
+
79
+ @objc public func show(animated: Bool) {
80
+ DispatchQueue.main.async {
81
+ // Note: Status bar visibility is controlled through view controllers in modern iOS.
82
+ // This plugin requires UIViewControllerBasedStatusBarAppearance to be set to NO
83
+ // in the app's Info.plist for programmatic show/hide to work.
84
+
85
+ // Log current status bar state via status bar manager
86
+ if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
87
+ let statusBarManager = windowScene.statusBarManager {
88
+ print("StatusBar: show() - Current hidden state: \(statusBarManager.isStatusBarHidden)")
89
+ }
90
+
91
+ // Set visibility using the application-level API
92
+ // Note: This requires UIViewControllerBasedStatusBarAppearance = NO
93
+ self.setStatusBarVisibility(hidden: false, animated: animated)
94
+
95
+ // Restore the background view color when showing
96
+ self.restoreStatusBarBackgroundColor()
97
+ }
98
+ }
99
+
100
+ @objc public func hide(animation: String) {
101
+ DispatchQueue.main.async {
102
+ let animationType = animation.lowercased()
103
+
104
+ // Log current status bar state via status bar manager
105
+ if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
106
+ let statusBarManager = windowScene.statusBarManager {
107
+ print("StatusBar: hide() - animation=\(animationType), Current hidden state: \(statusBarManager.isStatusBarHidden)")
108
+ }
109
+
110
+ if animationType == "fade" {
111
+ // Fade mode: Make background transparent without removing status bar
112
+ print("StatusBar: hide() - fade mode: making background transparent")
113
+ self.makeStatusBarBackgroundTransparent()
114
+ } else if animationType == "slide" {
115
+ // Slide mode: Hide the status bar completely (current behavior)
116
+ print("StatusBar: hide() - slide mode: hiding bars completely")
117
+ // Note: Status bar visibility is controlled through view controllers in modern iOS.
118
+ // This plugin requires UIViewControllerBasedStatusBarAppearance to be set to NO
119
+ // in the app's Info.plist for programmatic show/hide to work.
120
+ self.setStatusBarVisibility(hidden: true, animated: true)
121
+ // Also make the background view transparent when hiding
122
+ self.makeStatusBarBackgroundTransparent()
123
+ } else {
124
+ print("StatusBar: hide() - unknown animation type '\(animationType)', defaulting to slide")
125
+ self.setStatusBarVisibility(hidden: true, animated: true)
126
+ self.makeStatusBarBackgroundTransparent()
127
+ }
128
+ }
129
+ }
130
+
131
+ // MARK: - Private Methods
132
+
133
+ /// Sets the status bar visibility.
134
+ /// - Parameters:
135
+ /// - hidden: Whether the status bar should be hidden
136
+ /// - animated: Whether the change should be animated
137
+ private func setStatusBarVisibility(hidden: Bool, animated: Bool) {
138
+ // Use KVC to set status bar state without triggering deprecation warnings
139
+ // This approach is necessary when UIViewControllerBasedStatusBarAppearance is NO
140
+ UIApplication.shared.setValue(hidden, forKey: "statusBarHidden")
141
+ }
142
+
143
+ /// Updates or creates the status bar background view with the specified color.
144
+ /// - Parameters:
145
+ /// - window: The window where the status bar view will be added
146
+ /// - height: The height of the status bar
147
+ /// - color: The background color (nil to remove the view)
148
+ private func updateStatusBarBackgroundView(in window: UIWindow, height: CGFloat, color: UIColor?) {
149
+ // Find existing status bar view
150
+ let existingView = window.viewWithTag(StatusBar.statusBarViewTag)
151
+
152
+ if let color = color {
153
+ // Create or update the status bar background view
154
+ let statusBarView: UIView
155
+
156
+ if let existing = existingView {
157
+ statusBarView = existing
158
+ } else {
159
+ statusBarView = UIView(frame: CGRect(x: 0, y: 0, width: window.bounds.width, height: height))
160
+ statusBarView.tag = StatusBar.statusBarViewTag
161
+ statusBarView.autoresizingMask = [.flexibleWidth]
162
+ window.addSubview(statusBarView)
163
+ }
164
+
165
+ // Update the frame and color
166
+ statusBarView.frame = CGRect(x: 0, y: 0, width: window.bounds.width, height: height)
167
+ statusBarView.backgroundColor = color
168
+
169
+ // Ensure the view is on top
170
+ window.bringSubviewToFront(statusBarView)
171
+ } else {
172
+ // Remove the status bar view if color is nil
173
+ existingView?.removeFromSuperview()
174
+ }
175
+ }
176
+
177
+ @objc public func setOverlaysWebView(value: Bool) {
178
+ DispatchQueue.main.async {
179
+ guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
180
+ let window = windowScene.windows.first,
181
+ let statusBarManager = windowScene.statusBarManager else {
182
+ print("StatusBar: setOverlaysWebView - Unable to get window or status bar manager")
183
+ return
184
+ }
185
+
186
+ self.isOverlayMode = value
187
+
188
+ if value {
189
+ // Overlay mode: make the status bar background transparent so web content shows through
190
+ let statusBarView = window.viewWithTag(StatusBar.statusBarViewTag)
191
+ statusBarView?.backgroundColor = .clear
192
+ print("StatusBar: setOverlaysWebView(true) - content extends behind status bar")
193
+ } else {
194
+ // Non-overlay mode: restore the status bar background from the current style
195
+ if let color = self.currentBackgroundColor {
196
+ self.updateStatusBarBackgroundView(in: window,
197
+ height: statusBarManager.statusBarFrame.height,
198
+ color: color)
199
+ print("StatusBar: setOverlaysWebView(false) - restored background color")
200
+ } else {
201
+ // No style was set; apply default style based on system theme
202
+ self.applyDefaultStyle()
203
+ print("StatusBar: setOverlaysWebView(false) - applied default style from config")
204
+ }
205
+ }
206
+ }
207
+ }
208
+
209
+ @objc public func setBackground(colorHex: String?) {
210
+ DispatchQueue.main.async {
211
+ guard let colorHex = colorHex, let color = self.colorFromHex(colorHex) else {
212
+ print("StatusBar: setBackground - Invalid color or nil")
213
+ return
214
+ }
215
+
216
+ guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
217
+ let window = windowScene.windows.first else {
218
+ print("StatusBar: setBackground - Unable to get window")
219
+ return
220
+ }
221
+
222
+ window.backgroundColor = color
223
+ print("StatusBar: setBackground - Set window background to \(colorHex)")
224
+ }
225
+ }
226
+
227
+ @objc public func getSafeAreaInsets(completion: @escaping ([String: CGFloat]) -> Void) {
228
+ DispatchQueue.main.async {
229
+ var insets: [String: CGFloat] = ["top": 0, "bottom": 0, "left": 0, "right": 0]
230
+
231
+ if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
232
+ let window = windowScene.windows.first {
233
+ let safeAreaInsets = window.safeAreaInsets
234
+ insets["top"] = safeAreaInsets.top
235
+ insets["bottom"] = safeAreaInsets.bottom
236
+ insets["left"] = safeAreaInsets.left
237
+ insets["right"] = safeAreaInsets.right
238
+
239
+ print("StatusBar: getSafeAreaInsets - top=\(safeAreaInsets.top), bottom=\(safeAreaInsets.bottom), left=\(safeAreaInsets.left), right=\(safeAreaInsets.right)")
240
+ } else {
241
+ print("StatusBar: getSafeAreaInsets - Unable to get window, returning zero insets")
242
+ }
243
+
244
+ completion(insets)
245
+ }
246
+ }
247
+
248
+ /// Makes the status bar background view transparent
249
+ private func makeStatusBarBackgroundTransparent() {
250
+ guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
251
+ let window = windowScene.windows.first,
252
+ let statusBarView = window.viewWithTag(StatusBar.statusBarViewTag) else {
253
+ return
254
+ }
255
+
256
+ statusBarView.backgroundColor = .clear
257
+ print("StatusBar: Made background transparent")
258
+ }
259
+
260
+ /// Restores the status bar background view to its original color
261
+ private func restoreStatusBarBackgroundColor() {
262
+ guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
263
+ let window = windowScene.windows.first,
264
+ let statusBarManager = windowScene.statusBarManager else {
265
+ return
266
+ }
267
+
268
+ // Only restore if we have a stored color
269
+ if let color = self.currentBackgroundColor {
270
+ self.updateStatusBarBackgroundView(in: window,
271
+ height: statusBarManager.statusBarFrame.height,
272
+ color: color)
273
+ print("StatusBar: Restored background color: \(color)")
274
+ }
275
+ }
276
+
277
+ // MARK: - Helper Methods
278
+
279
+ private func colorFromHex(_ hex: String) -> UIColor? {
280
+ var hexSanitized = hex.trimmingCharacters(in: .whitespacesAndNewlines)
281
+ hexSanitized = hexSanitized.replacingOccurrences(of: "#", with: "")
282
+
283
+ var rgb: UInt64 = 0
284
+ guard Scanner(string: hexSanitized).scanHexInt64(&rgb) else { return nil }
285
+
286
+ let length = hexSanitized.count
287
+ let r, g, b, a: CGFloat
288
+
289
+ if length == 6 {
290
+ r = CGFloat((rgb & 0xFF0000) >> 16) / 255.0
291
+ g = CGFloat((rgb & 0x00FF00) >> 8) / 255.0
292
+ b = CGFloat(rgb & 0x0000FF) / 255.0
293
+ a = 1.0
294
+ } else if length == 8 {
295
+ r = CGFloat((rgb & 0xFF000000) >> 24) / 255.0
296
+ g = CGFloat((rgb & 0x00FF0000) >> 16) / 255.0
297
+ b = CGFloat((rgb & 0x0000FF00) >> 8) / 255.0
298
+ a = CGFloat(rgb & 0x000000FF) / 255.0
299
+ } else {
300
+ return nil
301
+ }
302
+
303
+ return UIColor(red: r, green: g, blue: b, alpha: a)
304
+ }
305
+
306
+ private func getColorBrightness(_ color: UIColor) -> CGFloat {
307
+ var red: CGFloat = 0
308
+ var green: CGFloat = 0
309
+ var blue: CGFloat = 0
310
+ var alpha: CGFloat = 0
311
+
312
+ color.getRed(&red, green: &green, blue: &blue, alpha: &alpha)
313
+
314
+ // Calculate relative luminance using the formula for sRGB
315
+ return (0.299 * red + 0.587 * green + 0.114 * blue)
316
+ }
317
+
318
+ private func isSystemInDarkMode() -> Bool {
319
+ guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
320
+ let window = windowScene.windows.first else {
321
+ return false
322
+ }
323
+ return window.traitCollection.userInterfaceStyle == .dark
324
+ }
325
+ }
@@ -0,0 +1,81 @@
1
+ import Foundation
2
+ import Capacitor
3
+
4
+ /**
5
+ * Please read the Capacitor iOS Plugin Development Guide
6
+ * here: https://capacitorjs.com/docs/plugins/ios
7
+ */
8
+ @objc(StatusBarPlugin)
9
+ public class StatusBarPlugin: CAPPlugin, CAPBridgedPlugin {
10
+ public let identifier = "StatusBarPlugin"
11
+ public let jsName = "StatusBar"
12
+ public let pluginMethods: [CAPPluginMethod] = [
13
+ CAPPluginMethod(name: "setStyle", returnType: CAPPluginReturnPromise),
14
+ CAPPluginMethod(name: "show", returnType: CAPPluginReturnPromise),
15
+ CAPPluginMethod(name: "hide", returnType: CAPPluginReturnPromise),
16
+ CAPPluginMethod(name: "setOverlaysWebView", returnType: CAPPluginReturnPromise),
17
+ CAPPluginMethod(name: "setBackground", returnType: CAPPluginReturnPromise),
18
+ CAPPluginMethod(name: "getSafeAreaInsets", returnType: CAPPluginReturnPromise)
19
+ ]
20
+ private let implementation = StatusBar()
21
+
22
+ override public func load() {
23
+ super.load()
24
+ // Apply default style based on system theme on plugin load
25
+ implementation.applyDefaultStyle()
26
+ }
27
+
28
+ @objc func setStyle(_ call: CAPPluginCall) {
29
+ guard let style = call.getString("style") else {
30
+ call.reject("style is required")
31
+ return
32
+ }
33
+ let color = call.getString("color")
34
+ implementation.setStyle(style: style, colorHex: color)
35
+ call.resolve()
36
+ }
37
+
38
+ @objc func show(_ call: CAPPluginCall) {
39
+ let animated = call.getBool("animated") ?? true
40
+ implementation.show(animated: animated)
41
+ call.resolve()
42
+ }
43
+
44
+ @objc func hide(_ call: CAPPluginCall) {
45
+ guard let animation = call.getString("animation") else {
46
+ call.reject("animation is required")
47
+ return
48
+ }
49
+ implementation.hide(animation: animation)
50
+ call.resolve()
51
+ }
52
+
53
+ @objc func setOverlaysWebView(_ call: CAPPluginCall) {
54
+ guard let value = call.getBool("value") else {
55
+ call.reject("value is required")
56
+ return
57
+ }
58
+ implementation.setOverlaysWebView(value: value)
59
+ call.resolve()
60
+ }
61
+
62
+ @objc func setBackground(_ call: CAPPluginCall) {
63
+ guard let color = call.getString("color") else {
64
+ call.reject("color is required")
65
+ return
66
+ }
67
+ implementation.setBackground(colorHex: color)
68
+ call.resolve()
69
+ }
70
+
71
+ @objc func getSafeAreaInsets(_ call: CAPPluginCall) {
72
+ implementation.getSafeAreaInsets { insets in
73
+ call.resolve([
74
+ "top": insets["top"] ?? 0,
75
+ "bottom": insets["bottom"] ?? 0,
76
+ "left": insets["left"] ?? 0,
77
+ "right": insets["right"] ?? 0
78
+ ])
79
+ }
80
+ }
81
+ }
@@ -0,0 +1,15 @@
1
+ import XCTest
2
+ @testable import StatusBarPlugin
3
+
4
+ class StatusBarTests: XCTestCase {
5
+ func testEcho() {
6
+ // This is an example of a functional test case for a plugin.
7
+ // Use XCTAssert and related functions to verify your tests produce the correct results.
8
+
9
+ let implementation = StatusBar()
10
+ let value = "Hello, World!"
11
+ let result = implementation.echo(value)
12
+
13
+ XCTAssertEqual(value, result)
14
+ }
15
+ }
package/package.json ADDED
@@ -0,0 +1,99 @@
1
+ {
2
+ "name": "capacitor-plugin-status-bar",
3
+ "version": "2.0.0",
4
+ "description": "Capacitor plugin for managing the status bar style, visibility, and color on iOS and Android. Control overlay modes, background colors, and appearance for native mobile applications.",
5
+ "main": "dist/plugin.cjs.js",
6
+ "module": "dist/esm/index.js",
7
+ "types": "dist/esm/index.d.ts",
8
+ "unpkg": "dist/plugin.js",
9
+ "files": [
10
+ "android/src/main/",
11
+ "android/build.gradle",
12
+ "dist/",
13
+ "ios/Sources",
14
+ "ios/Tests",
15
+ "Package.swift",
16
+ "CapacitorPluginStatusBar.podspec"
17
+ ],
18
+ "author": "Abdelfattah Ashour",
19
+ "license": "MIT",
20
+ "homepage": "https://github.com/abdelfatah-ashour/cap-status-bar#readme",
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://github.com/abdelfatah-ashour/cap-status-bar.git"
24
+ },
25
+ "bugs": {
26
+ "url": "https://github.com/abdelfatah-ashour/cap-status-bar/issues"
27
+ },
28
+ "keywords": [
29
+ "capacitor",
30
+ "capacitor-plugin",
31
+ "capacitor-community",
32
+ "plugin",
33
+ "native",
34
+ "status-bar",
35
+ "statusbar",
36
+ "android",
37
+ "ios",
38
+ "mobile",
39
+ "ionic",
40
+ "cordova",
41
+ "hybrid-app",
42
+ "native-bridge",
43
+ "mobile-app",
44
+ "status-bar-style",
45
+ "status-bar-color",
46
+ "overlay",
47
+ "immersive",
48
+ "navigation-bar"
49
+ ],
50
+ "scripts": {
51
+ "verify": "npm run verify:ios && npm run verify:android && npm run verify:web",
52
+ "verify:ios": "xcodebuild -scheme CapacitorPluginStatusBar -destination generic/platform=iOS",
53
+ "verify:android": "cd android && ./gradlew clean build test && cd ..",
54
+ "verify:web": "npm run build",
55
+ "lint": "npm run eslint && npm run prettier -- --check && npm run swiftlint -- lint",
56
+ "fmt": "npm run eslint -- --fix && npm run prettier -- --write && npm run swiftlint -- --fix --format",
57
+ "eslint": "eslint . --ext ts",
58
+ "prettier": "prettier \"**/*.{css,html,ts,js,java}\" --plugin=prettier-plugin-java",
59
+ "swiftlint": "node-swiftlint",
60
+ "docgen": "docgen --api StatusBarPlugin --output-readme README.md --output-json dist/docs.json",
61
+ "build": "npm run clean && npm run docgen && tsc && rollup -c rollup.config.mjs",
62
+ "clean": "rimraf ./dist",
63
+ "watch": "tsc --watch",
64
+ "prepublishOnly": "npm run build",
65
+ "bump": "bash scripts/bump-version.sh"
66
+ },
67
+ "devDependencies": {
68
+ "@capacitor/android": "^8.0.0",
69
+ "@capacitor/core": "^8.0.0",
70
+ "@capacitor/docgen": "^0.3.0",
71
+ "@capacitor/ios": "^8.0.0",
72
+ "@ionic/eslint-config": "^0.4.0",
73
+ "@ionic/prettier-config": "^4.0.0",
74
+ "@ionic/swiftlint-config": "^2.0.0",
75
+ "eslint": "^8.57.0",
76
+ "prettier": "^3.4.2",
77
+ "prettier-plugin-java": "^2.6.6",
78
+ "rimraf": "^6.0.1",
79
+ "rollup": "^4.30.1",
80
+ "swiftlint": "^2.0.0",
81
+ "typescript": "~4.1.5"
82
+ },
83
+ "peerDependencies": {
84
+ "@capacitor/core": ">=8.0.0"
85
+ },
86
+ "prettier": "@ionic/prettier-config",
87
+ "swiftlint": "@ionic/swiftlint-config",
88
+ "eslintConfig": {
89
+ "extends": "@ionic/eslint-config/recommended"
90
+ },
91
+ "capacitor": {
92
+ "ios": {
93
+ "src": "ios"
94
+ },
95
+ "android": {
96
+ "src": "android"
97
+ }
98
+ }
99
+ }