@capgo/camera-preview 7.24.15 → 7.26.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.
@@ -34,7 +34,7 @@ extension UIWindow {
34
34
  */
35
35
  @objc(CameraPreview)
36
36
  public class CameraPreview: CAPPlugin, CAPBridgedPlugin, CLLocationManagerDelegate {
37
- private let pluginVersion: String = "7.24.15"
37
+ private let pluginVersion: String = "7.26.0"
38
38
  public let identifier = "CameraPreviewPlugin"
39
39
  public let jsName = "CameraPreview"
40
40
  public let pluginMethods: [CAPPluginMethod] = [
@@ -106,6 +106,10 @@ public class CameraPreview: CAPPlugin, CAPBridgedPlugin, CLLocationManagerDelega
106
106
  private var waitingForLocation: Bool = false
107
107
  private var isPresentingPermissionAlert: Bool = false
108
108
 
109
+ // Store original webview colors to restore them when stopping
110
+ private var originalWebViewBackgroundColor: UIColor?
111
+ private var originalWebViewSubviewColors: [UIView: UIColor] = [:]
112
+
109
113
  // MARK: - Helper Methods for Aspect Ratio
110
114
 
111
115
  /// Parses aspect ratio string and returns the appropriate ratio for the current orientation
@@ -141,6 +145,30 @@ public class CameraPreview: CAPPlugin, CAPBridgedPlugin, CLLocationManagerDelega
141
145
  private func makeWebViewTransparent() {
142
146
  guard let webView = self.webView else { return }
143
147
 
148
+ // IMPORTANT: Save colors synchronously FIRST to prevent race condition
149
+ // If we don't have saved colors yet, save them now (before going async)
150
+ if self.originalWebViewBackgroundColor == nil {
151
+ self.originalWebViewBackgroundColor = webView.backgroundColor
152
+ self.originalWebViewSubviewColors.removeAll()
153
+
154
+ // Define a recursive function to traverse and save colors
155
+ func saveSubviewColors(_ view: UIView) {
156
+ // Save the original background color before changing it
157
+ if let bgColor = view.backgroundColor, bgColor != .clear {
158
+ self.originalWebViewSubviewColors[view] = bgColor
159
+ }
160
+
161
+ // Recurse for all subviews
162
+ for subview in view.subviews {
163
+ saveSubviewColors(subview)
164
+ }
165
+ }
166
+
167
+ // Save all subview colors synchronously
168
+ saveSubviewColors(webView)
169
+ }
170
+
171
+ // Now make the changes asynchronously on main thread
144
172
  DispatchQueue.main.async {
145
173
  _ = CFAbsoluteTimeGetCurrent()
146
174
 
@@ -170,6 +198,51 @@ public class CameraPreview: CAPPlugin, CAPBridgedPlugin, CLLocationManagerDelega
170
198
  }
171
199
  }
172
200
 
201
+ private func restoreWebViewBackground(_ webView: UIView) {
202
+ // Restore the saved background colors
203
+ func restoreSubviewsBackground(_ view: UIView) {
204
+ // Restore the saved background color for this view
205
+ if let savedColor = self.originalWebViewSubviewColors[view] {
206
+ view.backgroundColor = savedColor
207
+ } else {
208
+ // Fallback: If no saved color, intelligently restore based on view type
209
+ let className = String(describing: type(of: view))
210
+ if className.contains("WKScrollView") || className.contains("WKContentView") {
211
+ // Only restore if it's currently clear (meaning we likely made it transparent)
212
+ if view.backgroundColor == .clear || view.backgroundColor == nil {
213
+ view.backgroundColor = .white
214
+ }
215
+ }
216
+ }
217
+
218
+ // Recurse for all subviews
219
+ for subview in view.subviews {
220
+ restoreSubviewsBackground(subview)
221
+ }
222
+ }
223
+
224
+ // Restore the main webview background color
225
+ if let originalColor = self.originalWebViewBackgroundColor {
226
+ webView.backgroundColor = originalColor
227
+ } else {
228
+ // Fallback: If no saved color and webview is clear, restore to white
229
+ if webView.backgroundColor == .clear || webView.backgroundColor == nil {
230
+ webView.backgroundColor = .white
231
+ }
232
+ }
233
+
234
+ // Restore all subviews
235
+ restoreSubviewsBackground(webView)
236
+
237
+ // Clear the saved colors dictionary
238
+ self.originalWebViewSubviewColors.removeAll()
239
+ self.originalWebViewBackgroundColor = nil
240
+
241
+ // Force a layout pass to apply changes
242
+ webView.setNeedsLayout()
243
+ webView.layoutIfNeeded()
244
+ }
245
+
173
246
  private func presentCameraPermissionAlert(title: String,
174
247
  message: String,
175
248
  openSettingsText: String,
@@ -580,20 +653,54 @@ public class CameraPreview: CAPPlugin, CAPBridgedPlugin, CLLocationManagerDelega
580
653
  print(" - positioning: \(call.getString("positioning") ?? "top")")
581
654
  print(" - initialZoomLevel: \(call.getFloat("initialZoomLevel") ?? 1.0)")
582
655
  print(" - disableFocusIndicator: \(call.getBool("disableFocusIndicator") ?? false)")
656
+ print(" - force: \(call.getBool("force") ?? false)")
583
657
 
584
- if self.isInitializing {
585
- call.reject("camera initialization in progress")
586
- return
587
- }
588
- if self.isInitialized {
589
- call.reject("camera already started")
590
- return
591
- }
592
- // Guard against starting while a deferred stop is pending due to in-flight capture
593
- if self.cameraController.isCapturingPhoto || self.cameraController.stopRequestedAfterCapture {
594
- call.reject("camera is stopping or busy, please retry shortly")
595
- return
658
+ let force = call.getBool("force") ?? false
659
+
660
+ // If force is true, kill everything and restart no matter what
661
+ if force {
662
+ if self.isInitializing || self.isInitialized || self.cameraController.isCapturingPhoto || self.cameraController.stopRequestedAfterCapture {
663
+ // Force stop everything synchronously
664
+ DispatchQueue.main.sync {
665
+ self.cameraController.removeGridOverlay()
666
+ if let previewView = self.previewView {
667
+ previewView.removeFromSuperview()
668
+ self.previewView = nil
669
+ }
670
+
671
+ // Restore webView to opaque state with original background
672
+ if let webView = self.webView {
673
+ webView.isOpaque = true
674
+ // Restore the original background colors that were saved
675
+ self.restoreWebViewBackground(webView)
676
+ }
677
+
678
+ // Force stop the camera controller regardless of state
679
+ self.cameraController.stopRequestedAfterCapture = false
680
+ self.cameraController.cleanup()
681
+ NotificationCenter.default.removeObserver(self, name: UIDevice.orientationDidChangeNotification, object: nil)
682
+ UIDevice.current.endGeneratingDeviceOrientationNotifications()
683
+ self.isInitialized = false
684
+ self.isInitializing = false
685
+ }
686
+ }
687
+ } else {
688
+ // Normal checks only when force is false
689
+ if self.isInitializing {
690
+ call.reject("camera initialization in progress")
691
+ return
692
+ }
693
+ if self.isInitialized {
694
+ call.reject("camera already started")
695
+ return
696
+ }
697
+ // Guard against starting while a deferred stop is pending due to in-flight capture
698
+ if self.cameraController.isCapturingPhoto || self.cameraController.stopRequestedAfterCapture {
699
+ call.reject("camera is stopping or busy, please retry shortly")
700
+ return
701
+ }
596
702
  }
703
+
597
704
  self.isInitializing = true
598
705
 
599
706
  self.cameraPosition = call.getString("position") ?? "rear"
@@ -653,7 +760,7 @@ public class CameraPreview: CAPPlugin, CAPBridgedPlugin, CLLocationManagerDelega
653
760
  return
654
761
  }
655
762
  let beginStart: () -> Void = {
656
- if self.cameraController.captureSession?.isRunning ?? false {
763
+ if (self.cameraController.captureSession?.isRunning ?? false) && !force {
657
764
  DispatchQueue.main.async {
658
765
  self.isInitializing = false
659
766
  call.reject("camera already started")
@@ -826,13 +933,18 @@ public class CameraPreview: CAPPlugin, CAPBridgedPlugin, CLLocationManagerDelega
826
933
  }
827
934
 
828
935
  @objc func stop(_ call: CAPPluginCall) {
829
- if self.isInitializing {
830
- call.reject("cannot stop camera while initialization is in progress")
831
- return
832
- }
833
- if !self.isInitialized {
834
- call.reject("camera not initialized")
835
- return
936
+ let force = call.getBool("force") ?? false
937
+
938
+ // If force is true, skip all checks and force stop
939
+ if !force {
940
+ if self.isInitializing {
941
+ call.reject("cannot stop camera while initialization is in progress")
942
+ return
943
+ }
944
+ if !self.isInitialized {
945
+ call.reject("camera not initialized")
946
+ return
947
+ }
836
948
  }
837
949
 
838
950
  // UI operations must be on main thread
@@ -845,7 +957,13 @@ public class CameraPreview: CAPPlugin, CAPBridgedPlugin, CLLocationManagerDelega
845
957
  self.previewView = nil
846
958
  }
847
959
 
848
- self.webView?.isOpaque = true
960
+ // Restore webView to opaque state with original background
961
+ if let webView = self.webView {
962
+ webView.isOpaque = true
963
+ // Restore the original background colors that were saved
964
+ self.restoreWebViewBackground(webView)
965
+ }
966
+
849
967
  self.isInitialized = false
850
968
  self.isInitializing = false
851
969
 
@@ -854,11 +972,14 @@ public class CameraPreview: CAPPlugin, CAPBridgedPlugin, CLLocationManagerDelega
854
972
  NotificationCenter.default.removeObserver(self, name: UIDevice.orientationDidChangeNotification, object: nil)
855
973
  UIDevice.current.endGeneratingDeviceOrientationNotifications()
856
974
 
857
- if self.cameraController.isCapturingPhoto {
858
- // Defer heavy cleanup until capture callback completes
975
+ if self.cameraController.isCapturingPhoto && !force {
976
+ // Defer heavy cleanup until capture callback completes (only if not forcing)
859
977
  self.cameraController.stopRequestedAfterCapture = true
860
978
  } else {
861
- // No capture pending; cleanup now
979
+ // Force stop or no capture pending; cleanup now
980
+ if force {
981
+ self.cameraController.stopRequestedAfterCapture = false
982
+ }
862
983
  self.cameraController.cleanup()
863
984
  }
864
985
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/camera-preview",
3
- "version": "7.24.15",
3
+ "version": "7.26.0",
4
4
  "description": "Camera preview",
5
5
  "license": "MIT",
6
6
  "repository": {