@cleanuidev/react-native-scanner 1.0.0-beta.3 β 1.0.0-beta.5
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.
- package/README.md +181 -17
- package/Scanner.podspec +1 -1
- package/ios/CameraManager.swift +143 -7
- package/ios/ScannerViewImpl.swift +30 -0
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# π± React Native Scanner
|
|
4
4
|
|
|
5
|
-
**A powerful, native barcode and QR code scanner for React Native with configurable target area scanning
|
|
5
|
+
**A powerful, native barcode and QR code scanner for React Native with configurable target area scanning. Limit scan area and restrict scanning to a specific region.**
|
|
6
6
|
|
|
7
7
|

|
|
8
8
|
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
**Built with β€οΈ by [CleanUI.dev](https://cleanui.dev)**
|
|
15
15
|
|
|
16
|
-
[Features](#-features) β’ [Installation](#-installation) β’ [
|
|
16
|
+
[Features](#-features) β’ [Why Choose](#-why-choose-this-library) β’ [Comparison](#-comparison-with-other-libraries) β’ [Quick Start](#-quick-start) β’ [Installation](#-installation) β’ [FAQ](#-frequently-asked-questions)
|
|
17
17
|
|
|
18
18
|
</div>
|
|
19
19
|
|
|
@@ -36,6 +36,56 @@
|
|
|
36
36
|
|
|
37
37
|
---
|
|
38
38
|
|
|
39
|
+
## π― Why Choose @cleanuidev/react-native-scanner?
|
|
40
|
+
|
|
41
|
+
- **π― Target Area Scanning**: Unlike other libraries, built-in support for limiting scan area, restricting scanning region, and scanning within configurable target areas for precise detection
|
|
42
|
+
- **π High Performance**: Uses native CameraX & ML Kit (Android) and AVFoundation & Vision (iOS) for optimal performance
|
|
43
|
+
- **π± New Architecture Ready**: Full support for React Native's new architecture (Fabric) on both platforms
|
|
44
|
+
- **π§ Easy Integration**: Simple API with sensible defaults - get started in minutes
|
|
45
|
+
- **π Multiple Scan Strategies**: Process one, all, largest, or sorted barcodes with built-in strategies
|
|
46
|
+
- **π¨ Highly Customizable**: Configurable target areas, barcode frames, and scanning behavior
|
|
47
|
+
- **π¦ Lightweight**: Minimal dependencies, optimized bundle size
|
|
48
|
+
- **β
Active Maintenance**: Regularly updated with bug fixes and new features
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## π Comparison with Other Libraries
|
|
53
|
+
|
|
54
|
+
| Feature | @cleanuidev/react-native-scanner | react-native-vision-camera | expo-camera |
|
|
55
|
+
|---------|--------------------------------|---------------------------|-------------|
|
|
56
|
+
| **Target Area Scanning / Limit Scan Area** | β
Built-in (limit scan area, restrict scan region) | β Manual implementation | β Manual implementation |
|
|
57
|
+
| **New Architecture Support** | β
Full Fabric support | β
Yes | β
Yes |
|
|
58
|
+
| **Native Performance** | β
CameraX + ML Kit / AVFoundation + Vision | β
Yes | β
Yes |
|
|
59
|
+
| **Expo Support** | β
Dev builds | β
Dev builds | β
Expo Go |
|
|
60
|
+
| **Barcode Frame Visualization** | β
Built-in | β οΈ Custom | β No |
|
|
61
|
+
| **Multiple Scan Strategies** | β
4 strategies (ONE, ALL, BIGGEST, SORT_BY_BIGGEST) | β οΈ Custom | β No |
|
|
62
|
+
| **Setup Complexity** | ββ Simple | βββ Moderate | β Easy |
|
|
63
|
+
| **Bundle Size** | Small | Medium | Small |
|
|
64
|
+
| **Active Maintenance** | β
Active | β
Active | β
Active |
|
|
65
|
+
| **License** | MIT | MIT | MIT |
|
|
66
|
+
| **Best For** | Barcode/QR scanning with target areas | General camera + scanning | Expo projects |
|
|
67
|
+
|
|
68
|
+
> **π‘ When to choose this library**: If you need barcode/QR code scanning with the ability to limit scan area, restrict scanning region, or scan within target areas, this library provides a simpler API and built-in features compared to general-purpose camera libraries.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## β‘ Quick Start
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
npm install @cleanuidev/react-native-scanner
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
import ScannerView, { BarcodeFormat } from '@cleanuidev/react-native-scanner';
|
|
80
|
+
|
|
81
|
+
<ScannerView
|
|
82
|
+
barcodeTypes={[BarcodeFormat.QR_CODE]}
|
|
83
|
+
onBarcodeScanned={(e) => console.log(e.nativeEvent)}
|
|
84
|
+
/>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
39
89
|
## π¦ Installation
|
|
40
90
|
|
|
41
91
|
### Install Beta Version
|
|
@@ -53,9 +103,9 @@ yarn add @cleanuidev/react-native-scanner@beta
|
|
|
53
103
|
To install a specific beta version:
|
|
54
104
|
|
|
55
105
|
```bash
|
|
56
|
-
npm install @cleanuidev/react-native-scanner@1.0.0-beta.
|
|
106
|
+
npm install @cleanuidev/react-native-scanner@1.0.0-beta.5
|
|
57
107
|
# or
|
|
58
|
-
yarn add @cleanuidev/react-native-scanner@1.0.0-beta.
|
|
108
|
+
yarn add @cleanuidev/react-native-scanner@1.0.0-beta.5
|
|
59
109
|
```
|
|
60
110
|
|
|
61
111
|
> **Note**: Once the library reaches stable release (1.0.0), you can install it without the `@beta` tag:
|
|
@@ -67,7 +117,71 @@ yarn add @cleanuidev/react-native-scanner@1.0.0-beta.1
|
|
|
67
117
|
|
|
68
118
|
## Platform Setup
|
|
69
119
|
|
|
70
|
-
###
|
|
120
|
+
### Expo Setup
|
|
121
|
+
|
|
122
|
+
> **β οΈ Important**: This library uses native code and requires an **Expo development build**. It cannot run in Expo Go.
|
|
123
|
+
|
|
124
|
+
#### Prerequisites
|
|
125
|
+
|
|
126
|
+
- Expo SDK 53+ (recommended)
|
|
127
|
+
- EAS CLI installed: `npm install -g eas-cli`
|
|
128
|
+
- Expo development build configured
|
|
129
|
+
|
|
130
|
+
#### Installation
|
|
131
|
+
|
|
132
|
+
1. **Install the package:**
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
npx expo install @cleanuidev/react-native-scanner
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
2. **Configure app.json or app.config.js:**
|
|
139
|
+
|
|
140
|
+
Add camera permissions directly to your Expo configuration:
|
|
141
|
+
|
|
142
|
+
```json
|
|
143
|
+
{
|
|
144
|
+
"expo": {
|
|
145
|
+
"ios": {
|
|
146
|
+
"infoPlist": {
|
|
147
|
+
"NSCameraUsageDescription": "This app needs camera access to scan barcodes and QR codes"
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
"android": {
|
|
151
|
+
"permissions": [
|
|
152
|
+
"android.permission.CAMERA",
|
|
153
|
+
"android.permission.WAKE_LOCK"
|
|
154
|
+
]
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
3. **Create a development build:**
|
|
161
|
+
|
|
162
|
+
For iOS:
|
|
163
|
+
```bash
|
|
164
|
+
eas build --profile development --platform ios
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
For Android:
|
|
168
|
+
```bash
|
|
169
|
+
eas build --profile development --platform android
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Or build locally:
|
|
173
|
+
```bash
|
|
174
|
+
npx expo run:ios
|
|
175
|
+
# or
|
|
176
|
+
npx expo run:android
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
5. **Install the development build on your device:**
|
|
180
|
+
|
|
181
|
+
- For EAS builds: Download and install the build from the EAS dashboard
|
|
182
|
+
- For local builds: The build will be installed automatically
|
|
183
|
+
|
|
184
|
+
### Android Setup (Bare React Native)
|
|
71
185
|
|
|
72
186
|
Add the following permissions to your `android/app/src/main/AndroidManifest.xml`:
|
|
73
187
|
|
|
@@ -79,7 +193,7 @@ Add the following permissions to your `android/app/src/main/AndroidManifest.xml`
|
|
|
79
193
|
<uses-feature android:name="android.hardware.camera.flash" android:required="false" />
|
|
80
194
|
```
|
|
81
195
|
|
|
82
|
-
### iOS Setup
|
|
196
|
+
### iOS Setup (Bare React Native)
|
|
83
197
|
|
|
84
198
|
For iOS, add camera usage description to your `ios/YourApp/Info.plist`:
|
|
85
199
|
|
|
@@ -129,7 +243,7 @@ const styles = StyleSheet.create({
|
|
|
129
243
|
});
|
|
130
244
|
```
|
|
131
245
|
|
|
132
|
-
### Scanner with Target Area
|
|
246
|
+
### Scanner with Target Area - Limit Scan Area Example
|
|
133
247
|
|
|
134
248
|
```tsx
|
|
135
249
|
import React, { useState } from 'react';
|
|
@@ -139,9 +253,9 @@ import ScannerView, { BarcodeFormat } from '@cleanuidev/react-native-scanner';
|
|
|
139
253
|
export default function FocusAreaScanner() {
|
|
140
254
|
const [torchEnabled, setTorchEnabled] = useState(false);
|
|
141
255
|
|
|
142
|
-
// Target area configuration
|
|
256
|
+
// Target area configuration - limit scan area to specific region
|
|
143
257
|
const focusAreaConfig = {
|
|
144
|
-
enabled: true, //
|
|
258
|
+
enabled: true, // Limit scan area - only scan barcodes within the target area
|
|
145
259
|
showOverlay: true, // Show overlay outside the target area
|
|
146
260
|
size: 300, // Size of the target area (square)
|
|
147
261
|
color: '#00FF00', // Color of the target area border
|
|
@@ -198,7 +312,7 @@ export default function FocusAreaScanner() {
|
|
|
198
312
|
| Prop | Type | Default | Description |
|
|
199
313
|
|------|------|---------|-------------|
|
|
200
314
|
| `barcodeTypes` | `BarcodeFormat[]` | `[BarcodeFormat.QR_CODE]` | Array of barcode formats to scan |
|
|
201
|
-
| `focusArea` | `FocusAreaConfig` | - |
|
|
315
|
+
| `focusArea` | `FocusAreaConfig` | - | Limit scan area and restrict scanning region - target area configuration for precise scanning |
|
|
202
316
|
| `barcodeFrames` | `BarcodeFramesConfig` | - | Barcode frame visualization configuration |
|
|
203
317
|
| `torch` | `boolean` | `false` | Enable/disable torch/flashlight |
|
|
204
318
|
| `zoom` | `number` | `1.0` | Camera zoom level |
|
|
@@ -213,8 +327,8 @@ export default function FocusAreaScanner() {
|
|
|
213
327
|
|
|
214
328
|
```tsx
|
|
215
329
|
type FocusAreaConfig = {
|
|
216
|
-
enabled?: boolean; //
|
|
217
|
-
size?: FrameSize; // Size of the target area
|
|
330
|
+
enabled?: boolean; // Limit scan area - whether to restrict scanning to target area only
|
|
331
|
+
size?: FrameSize; // Size of the target area (scan region limit)
|
|
218
332
|
color?: string; // Color of target area border
|
|
219
333
|
showOverlay?: boolean; // Whether to draw overlay outside the target area
|
|
220
334
|
};
|
|
@@ -311,9 +425,9 @@ BarcodeFormat.ITF // ITF (Interleaved 2 of 5)
|
|
|
311
425
|
}
|
|
312
426
|
```
|
|
313
427
|
|
|
314
|
-
## Target Area Configuration
|
|
428
|
+
## Target Area Configuration - Limit Scan Area
|
|
315
429
|
|
|
316
|
-
The target area feature provides precise control over where barcodes are scanned:
|
|
430
|
+
The target area feature provides precise control over where barcodes are scanned. You can limit scan area, restrict scanning region, and confine barcode detection to a specific area on the screen:
|
|
317
431
|
|
|
318
432
|
### Basic Target Area
|
|
319
433
|
|
|
@@ -328,20 +442,24 @@ The target area feature provides precise control over where barcodes are scanned
|
|
|
328
442
|
/>
|
|
329
443
|
```
|
|
330
444
|
|
|
331
|
-
###
|
|
445
|
+
### Limit Scan Area - Restricted Scanning
|
|
446
|
+
|
|
447
|
+
Limit scan area and restrict scanning to a specific region:
|
|
332
448
|
|
|
333
449
|
```tsx
|
|
334
450
|
<ScannerView
|
|
335
451
|
focusArea={{
|
|
336
|
-
enabled: true, //
|
|
452
|
+
enabled: true, // Limit scan area - only scan barcodes within the target area
|
|
337
453
|
showOverlay: true, // Show overlay outside the target area
|
|
338
454
|
size: 300, // 300x300 pixel square
|
|
339
455
|
color: '#00FF00', // Green border
|
|
340
456
|
}}
|
|
341
|
-
// Only scans within the
|
|
457
|
+
// Only scans within the limited scan area
|
|
342
458
|
/>
|
|
343
459
|
```
|
|
344
460
|
|
|
461
|
+
This configuration restricts scanning region and limits scan area to the defined target area only.
|
|
462
|
+
|
|
345
463
|
### Rectangular Target Area
|
|
346
464
|
|
|
347
465
|
```tsx
|
|
@@ -582,6 +700,52 @@ const requestCameraPermission = async () => {
|
|
|
582
700
|
|
|
583
701
|
See the `example/` directory for complete working examples, including the "New Props Example" that demonstrates the updated prop structure.
|
|
584
702
|
|
|
703
|
+
## β Frequently Asked Questions
|
|
704
|
+
|
|
705
|
+
### Is this library compatible with Expo?
|
|
706
|
+
Yes! This library works with Expo development builds (SDK 53+). It does not work with Expo Go due to native code requirements. See the [Expo Setup](#expo-setup) section for detailed instructions.
|
|
707
|
+
|
|
708
|
+
### Does it support React Native's new architecture?
|
|
709
|
+
Yes! Full support for Fabric (new architecture) on both Android and iOS. The library is built with the new architecture in mind.
|
|
710
|
+
|
|
711
|
+
### How does it compare to react-native-vision-camera?
|
|
712
|
+
This library focuses specifically on barcode/QR code scanning with built-in target area support and multiple scan strategies. `react-native-vision-camera` is a general-purpose camera library that requires more setup for barcode scanning. If you only need barcode scanning, this library provides a simpler API and built-in features.
|
|
713
|
+
|
|
714
|
+
### Can I limit the scan area or restrict scanning to a specific region?
|
|
715
|
+
Yes! You can limit the scan area and restrict scanning to a specific region. The target area is optional. By default, you can scan the entire camera view. Set `focusArea.enabled: true` to limit scan area and restrict scanning to a specific region only.
|
|
716
|
+
|
|
717
|
+
### Is it possible to limit scan area?
|
|
718
|
+
Yes! This library provides built-in support for limiting scan area. You can restrict scanning to a specific region using the `focusArea` prop with `enabled: true`. This allows you to limit scan area to a defined region on the screen, perfect for precise barcode detection.
|
|
719
|
+
|
|
720
|
+
### Can I scan barcodes outside the target area?
|
|
721
|
+
Yes! The target area is optional. By default, you can scan the entire camera view. Set `focusArea.enabled: true` to restrict scanning to a specific area.
|
|
722
|
+
|
|
723
|
+
### What barcode formats are supported?
|
|
724
|
+
QR Code, Code128, Code39, EAN-13, EAN-8, UPC-A, UPC-E, Data Matrix, PDF417, Aztec, and ITF (Interleaved 2 of 5). See the [Barcode Formats](#barcode-formats) section for the complete list.
|
|
725
|
+
|
|
726
|
+
### Is it production-ready?
|
|
727
|
+
The library is currently in beta (1.0.0-beta.5) but is stable and actively maintained. Production use is recommended with proper testing. We're working towards a stable 1.0.0 release.
|
|
728
|
+
|
|
729
|
+
### Does it work with React Native 0.83+?
|
|
730
|
+
Yes! The library supports React Native 0.83 and newer versions, including full support for the new architecture.
|
|
731
|
+
|
|
732
|
+
### Can I customize the target area appearance?
|
|
733
|
+
Yes! You can customize the target area size, position, color, and overlay. See the [Target Area Configuration](#target-area-configuration) section for details.
|
|
734
|
+
|
|
735
|
+
### How do I handle multiple barcodes?
|
|
736
|
+
The library supports multiple scan strategies: process all barcodes, only the first one, only the largest, or all sorted by size. See the [Barcode Scan Strategy](#barcode-scan-strategy) section.
|
|
737
|
+
|
|
738
|
+
## πΌ Use Cases
|
|
739
|
+
|
|
740
|
+
- **Retail & E-commerce**: Product barcode scanning for inventory and checkout (limit scan area for precise product detection)
|
|
741
|
+
- **Inventory Management**: Stock tracking and warehouse management systems (restrict scan region for accurate scanning)
|
|
742
|
+
- **Event Management**: QR code ticket scanning and attendee check-in (limit scan area for ticket validation)
|
|
743
|
+
- **Authentication**: QR code-based login and two-factor authentication (restrict scanning region for security)
|
|
744
|
+
- **Payment Systems**: QR code payment processing and transaction scanning (limit scan area for payment codes)
|
|
745
|
+
- **Document Management**: Document barcode scanning and tracking (restrict scan region for document processing)
|
|
746
|
+
- **Asset Tracking**: QR/barcode-based asset management systems (limit scan area for asset identification)
|
|
747
|
+
- **Healthcare**: Medical device and medication barcode scanning (restrict scanning region for medical accuracy)
|
|
748
|
+
|
|
585
749
|
## Contributing
|
|
586
750
|
|
|
587
751
|
1. Fork the repository
|
package/Scanner.podspec
CHANGED
|
@@ -11,7 +11,7 @@ Pod::Spec.new do |s|
|
|
|
11
11
|
s.authors = package["author"]
|
|
12
12
|
|
|
13
13
|
s.platforms = { :ios => min_ios_version_supported }
|
|
14
|
-
s.source = { :git => "https://github.com/
|
|
14
|
+
s.source = { :git => "https://github.com/cleanuidev/react-native-scanner.git", :tag => "#{s.version}" }
|
|
15
15
|
|
|
16
16
|
s.source_files = "ios/**/*.{h,m,mm,swift,cpp}"
|
|
17
17
|
s.private_header_files = "ios/**/*.h"
|
package/ios/CameraManager.swift
CHANGED
|
@@ -47,6 +47,9 @@ class CameraManager: NSObject, CameraControlProtocol {
|
|
|
47
47
|
private var desiredTorchLevel: Float = 1.0
|
|
48
48
|
private var desiredZoomLevel: CGFloat = 1.0
|
|
49
49
|
|
|
50
|
+
/// Observer for subject area change (refocus when scene changes, like native Camera app)
|
|
51
|
+
private var subjectAreaObserver: NSObjectProtocol?
|
|
52
|
+
|
|
50
53
|
// MARK: - Initialization
|
|
51
54
|
|
|
52
55
|
override init() {
|
|
@@ -81,9 +84,10 @@ class CameraManager: NSObject, CameraControlProtocol {
|
|
|
81
84
|
self.isSessionRunning = true
|
|
82
85
|
}
|
|
83
86
|
|
|
84
|
-
// Apply
|
|
87
|
+
// Apply settings after session is running (native camera app behavior)
|
|
85
88
|
self.applyZoom()
|
|
86
89
|
self.applyTorch()
|
|
90
|
+
self.setupFocusAndExposure()
|
|
87
91
|
|
|
88
92
|
DispatchQueue.main.async {
|
|
89
93
|
self.delegate?.cameraManagerDidStart(self)
|
|
@@ -94,6 +98,7 @@ class CameraManager: NSObject, CameraControlProtocol {
|
|
|
94
98
|
|
|
95
99
|
/// Stop the camera session
|
|
96
100
|
func stopCamera() {
|
|
101
|
+
unregisterSubjectAreaChangeObserver()
|
|
97
102
|
sessionQueue.async { [weak self] in
|
|
98
103
|
guard let self = self, self.isSessionRunning else { return }
|
|
99
104
|
|
|
@@ -126,6 +131,17 @@ class CameraManager: NSObject, CameraControlProtocol {
|
|
|
126
131
|
}
|
|
127
132
|
}
|
|
128
133
|
|
|
134
|
+
/// Set focus and exposure point of interest for better scanning of small barcodes/QR codes.
|
|
135
|
+
/// Call this after the camera has started (e.g. when focus area is known).
|
|
136
|
+
/// - Parameters:
|
|
137
|
+
/// - normalizedX: X in range 0...1 (0.5 = center)
|
|
138
|
+
/// - normalizedY: Y in range 0...1 (0.5 = center)
|
|
139
|
+
func setFocusPointOfInterest(normalizedX: CGFloat, normalizedY: CGFloat) {
|
|
140
|
+
sessionQueue.async { [weak self] in
|
|
141
|
+
self?.applyFocusPointOfInterest(normalizedX: normalizedX, normalizedY: normalizedY)
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
129
145
|
/// Check if torch is available
|
|
130
146
|
/// - Returns: True if torch is available
|
|
131
147
|
func isTorchAvailable() -> Bool {
|
|
@@ -144,7 +160,14 @@ class CameraManager: NSObject, CameraControlProtocol {
|
|
|
144
160
|
/// Configure the capture session
|
|
145
161
|
private func configureCaptureSession() {
|
|
146
162
|
captureSession.beginConfiguration()
|
|
147
|
-
|
|
163
|
+
// Prefer full HD for sharper small barcode/QR capture when supported (e.g. iPhone 15)
|
|
164
|
+
if captureSession.canSetSessionPreset(.hd1920x1080) {
|
|
165
|
+
captureSession.sessionPreset = .hd1920x1080
|
|
166
|
+
print("[CameraManager] Session preset: hd1920x1080")
|
|
167
|
+
} else {
|
|
168
|
+
captureSession.sessionPreset = .high
|
|
169
|
+
print("[CameraManager] Session preset: high")
|
|
170
|
+
}
|
|
148
171
|
|
|
149
172
|
// Setup camera input
|
|
150
173
|
guard setupCameraInput() else {
|
|
@@ -178,7 +201,16 @@ class CameraManager: NSObject, CameraControlProtocol {
|
|
|
178
201
|
print("[CameraManager] No camera device available")
|
|
179
202
|
return false
|
|
180
203
|
}
|
|
181
|
-
|
|
204
|
+
|
|
205
|
+
// Bias autofocus towards near objects (ideal for barcodes/QR codes held close to camera).
|
|
206
|
+
// Must be configured before adding the device to an active session to be effective.
|
|
207
|
+
lockDeviceForConfiguration(device) { device in
|
|
208
|
+
if device.isAutoFocusRangeRestrictionSupported {
|
|
209
|
+
device.autoFocusRangeRestriction = .near
|
|
210
|
+
print("[CameraManager] β
Auto-focus range restriction set to NEAR")
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
182
214
|
do {
|
|
183
215
|
let input = try AVCaptureDeviceInput(device: device)
|
|
184
216
|
|
|
@@ -261,6 +293,103 @@ class CameraManager: NSObject, CameraControlProtocol {
|
|
|
261
293
|
}
|
|
262
294
|
}
|
|
263
295
|
|
|
296
|
+
// MARK: - Focus and Exposure (for small barcode/QR scanning)
|
|
297
|
+
|
|
298
|
+
/// Enable continuous autofocus and continuous auto-exposure (native Camera app behavior).
|
|
299
|
+
/// Applied after the session is running so the device is fully active.
|
|
300
|
+
private func setupFocusAndExposure() {
|
|
301
|
+
guard let device = currentDevice else { return }
|
|
302
|
+
lockDeviceForConfiguration(device) { device in
|
|
303
|
+
// Continuous autofocus: camera keeps refocusing so small codes stay sharp
|
|
304
|
+
if device.isFocusModeSupported(.continuousAutoFocus) {
|
|
305
|
+
device.focusMode = .continuousAutoFocus
|
|
306
|
+
print("[CameraManager] β
Continuous autofocus enabled")
|
|
307
|
+
} else if device.isFocusModeSupported(.autoFocus) {
|
|
308
|
+
device.focusMode = .autoFocus
|
|
309
|
+
print("[CameraManager] β
Auto focus enabled (continuous not supported)")
|
|
310
|
+
}
|
|
311
|
+
// Continuous auto-exposure: adapts to lighting for better small code readability
|
|
312
|
+
if device.isExposureModeSupported(.continuousAutoExposure) {
|
|
313
|
+
device.exposureMode = .continuousAutoExposure
|
|
314
|
+
print("[CameraManager] β
Continuous auto-exposure enabled")
|
|
315
|
+
}
|
|
316
|
+
// Default focus point at center (device coords); overridden by setFocusPointOfInterest
|
|
317
|
+
let center = CGPoint(x: 0.5, y: 0.5)
|
|
318
|
+
if device.isFocusPointOfInterestSupported && device.isFocusModeSupported(.continuousAutoFocus) {
|
|
319
|
+
device.focusPointOfInterest = center
|
|
320
|
+
}
|
|
321
|
+
if device.isExposurePointOfInterestSupported && device.isExposureModeSupported(.continuousAutoExposure) {
|
|
322
|
+
device.exposurePointOfInterest = center
|
|
323
|
+
}
|
|
324
|
+
// Native Camera behavior: refocus when scene changes (lighting, movement, etc.)
|
|
325
|
+
if device.isSubjectAreaChangeMonitoringEnabled == false {
|
|
326
|
+
device.isSubjectAreaChangeMonitoringEnabled = true
|
|
327
|
+
print("[CameraManager] β
Subject area change monitoring enabled")
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
// Observe scene changes and re-apply continuous focus (like native Camera app)
|
|
331
|
+
registerSubjectAreaChangeObserverIfNeeded()
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/// When the scene changes, re-apply continuous focus so the camera refocuses.
|
|
335
|
+
private func registerSubjectAreaChangeObserverIfNeeded() {
|
|
336
|
+
guard subjectAreaObserver == nil else { return }
|
|
337
|
+
subjectAreaObserver = NotificationCenter.default.addObserver(
|
|
338
|
+
forName: .AVCaptureDeviceSubjectAreaDidChange,
|
|
339
|
+
object: currentDevice,
|
|
340
|
+
queue: .main
|
|
341
|
+
) { [weak self] _ in
|
|
342
|
+
self?.sessionQueue.async {
|
|
343
|
+
self?.reapplyContinuousFocus()
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
print("[CameraManager] Subject area change observer registered")
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
private func unregisterSubjectAreaChangeObserver() {
|
|
350
|
+
if let observer = subjectAreaObserver {
|
|
351
|
+
NotificationCenter.default.removeObserver(observer)
|
|
352
|
+
subjectAreaObserver = nil
|
|
353
|
+
print("[CameraManager] Subject area change observer removed")
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/// Re-apply continuous focus and exposure (called when scene changes).
|
|
358
|
+
private func reapplyContinuousFocus() {
|
|
359
|
+
guard let device = currentDevice else { return }
|
|
360
|
+
lockDeviceForConfiguration(device) { device in
|
|
361
|
+
if device.isFocusModeSupported(.continuousAutoFocus) {
|
|
362
|
+
device.focusMode = .continuousAutoFocus
|
|
363
|
+
}
|
|
364
|
+
if device.isExposureModeSupported(.continuousAutoExposure) {
|
|
365
|
+
device.exposureMode = .continuousAutoExposure
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/// Set focus and exposure point of interest (e.g. center of focus area) so the camera
|
|
371
|
+
/// prioritizes that region. The inputs are expected to already be in device coordinates
|
|
372
|
+
/// (0,0 = top-left, 1,1 = bottom-right) as returned by AVCaptureVideoPreviewLayer helpers.
|
|
373
|
+
private func applyFocusPointOfInterest(normalizedX: CGFloat, normalizedY: CGFloat) {
|
|
374
|
+
guard let device = currentDevice else { return }
|
|
375
|
+
let devicePoint = CGPoint(x: normalizedX, y: normalizedY)
|
|
376
|
+
lockDeviceForConfiguration(device) { device in
|
|
377
|
+
if device.isFocusPointOfInterestSupported {
|
|
378
|
+
device.focusPointOfInterest = devicePoint
|
|
379
|
+
if device.isFocusModeSupported(.continuousAutoFocus) {
|
|
380
|
+
device.focusMode = .continuousAutoFocus
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
if device.isExposurePointOfInterestSupported {
|
|
384
|
+
device.exposurePointOfInterest = devicePoint
|
|
385
|
+
if device.isExposureModeSupported(.continuousAutoExposure) {
|
|
386
|
+
device.exposureMode = .continuousAutoExposure
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
print("[CameraManager] Focus point of interest set (view: \(normalizedX), \(normalizedY) β device: \(devicePoint.x), \(devicePoint.y))")
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
264
393
|
/// Setup video output
|
|
265
394
|
/// - Returns: True if setup successful
|
|
266
395
|
@discardableResult
|
|
@@ -296,12 +425,18 @@ class CameraManager: NSObject, CameraControlProtocol {
|
|
|
296
425
|
/// Get the default camera device (back camera)
|
|
297
426
|
/// - Returns: The camera device or nil
|
|
298
427
|
private func getDefaultCameraDevice() -> AVCaptureDevice? {
|
|
299
|
-
// Prefer
|
|
428
|
+
// Prefer the physical wideβangle back camera for barcode scanning.
|
|
429
|
+
// Using the virtual multiβcamera (e.g. builtInTripleCamera) on newer iPhones
|
|
430
|
+
// can cause macro / closeβrange focus issues for small barcodes.
|
|
431
|
+
if let wideBack = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) {
|
|
432
|
+
print("[CameraManager] β
Using back wideβangle camera: \(wideBack.localizedName)")
|
|
433
|
+
return wideBack
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// Fallback: look for any other back camera with torch (dual, telephoto, etc.)
|
|
300
437
|
let deviceTypes: [AVCaptureDevice.DeviceType] = [
|
|
301
|
-
.builtInTripleCamera,
|
|
302
438
|
.builtInDualCamera,
|
|
303
439
|
.builtInDualWideCamera,
|
|
304
|
-
.builtInWideAngleCamera,
|
|
305
440
|
.builtInTelephotoCamera,
|
|
306
441
|
.builtInUltraWideCamera,
|
|
307
442
|
]
|
|
@@ -313,7 +448,7 @@ class CameraManager: NSObject, CameraControlProtocol {
|
|
|
313
448
|
)
|
|
314
449
|
|
|
315
450
|
if let torchBack = discovery.devices.first(where: { $0.hasTorch }) {
|
|
316
|
-
print("[CameraManager] β
Using back camera with torch: \(torchBack.localizedName)")
|
|
451
|
+
print("[CameraManager] β
Using fallback back camera with torch: \(torchBack.localizedName)")
|
|
317
452
|
return torchBack
|
|
318
453
|
}
|
|
319
454
|
|
|
@@ -371,6 +506,7 @@ class CameraManager: NSObject, CameraControlProtocol {
|
|
|
371
506
|
}
|
|
372
507
|
|
|
373
508
|
deinit {
|
|
509
|
+
unregisterSubjectAreaChangeObserver()
|
|
374
510
|
stopCamera()
|
|
375
511
|
print("[CameraManager] Deinitialized")
|
|
376
512
|
}
|
|
@@ -146,6 +146,8 @@ class ScannerViewImpl: UIView {
|
|
|
146
146
|
@objc func configureFocusArea(_ config: [String: Any]) {
|
|
147
147
|
focusAreaConfig = FocusAreaConfig.from(dict: config)
|
|
148
148
|
focusAreaOverlay.updateFocusArea(config: focusAreaConfig)
|
|
149
|
+
// Update camera focus point to focus area center for better small barcode scanning
|
|
150
|
+
setupFocusPointOfInterest()
|
|
149
151
|
print("[ScannerViewImpl] Focus area configured")
|
|
150
152
|
}
|
|
151
153
|
|
|
@@ -520,10 +522,38 @@ extension ScannerViewImpl: CameraManagerDelegate {
|
|
|
520
522
|
// Check bounds multiple times with delay to catch layout
|
|
521
523
|
self.attemptToAddPreviewLayer(attempt: 0)
|
|
522
524
|
|
|
525
|
+
// Set focus point of interest (center or focus area center) for better small barcode/QR scanning
|
|
526
|
+
self.setupFocusPointOfInterest()
|
|
527
|
+
|
|
523
528
|
self.emitLoadEvent(success: true)
|
|
524
529
|
}
|
|
525
530
|
}
|
|
526
531
|
|
|
532
|
+
/// Set the camera focus point of interest to the focus area center (or screen center)
|
|
533
|
+
/// so continuous autofocus prioritizes that region and small barcodes/QR codes stay sharp.
|
|
534
|
+
private func setupFocusPointOfInterest() {
|
|
535
|
+
guard bounds.width > 0, bounds.height > 0 else { return }
|
|
536
|
+
guard let previewLayer = self.previewLayer else { return }
|
|
537
|
+
|
|
538
|
+
// 1) Get center point in view coordinates (either focus area center or view center)
|
|
539
|
+
let centerPointInView: CGPoint
|
|
540
|
+
if focusAreaConfig.enabled, let focusRect = focusAreaOverlay.getFocusAreaFrame() {
|
|
541
|
+
centerPointInView = CGPoint(x: focusRect.midX, y: focusRect.midY)
|
|
542
|
+
} else {
|
|
543
|
+
centerPointInView = CGPoint(x: bounds.midX, y: bounds.midY)
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// 2) Convert from view/layer space into device focus coordinates using AVFoundation helper
|
|
547
|
+
let pointInLayer = previewLayer.convert(centerPointInView, from: self.layer)
|
|
548
|
+
let devicePoint = previewLayer.captureDevicePointConverted(fromLayerPoint: pointInLayer)
|
|
549
|
+
|
|
550
|
+
// 3) Pass device-normalized coordinates (0,0 top-left, 1,1 bottom-right) to camera manager
|
|
551
|
+
cameraManager.setFocusPointOfInterest(
|
|
552
|
+
normalizedX: devicePoint.x,
|
|
553
|
+
normalizedY: devicePoint.y
|
|
554
|
+
)
|
|
555
|
+
}
|
|
556
|
+
|
|
527
557
|
private func attemptToAddPreviewLayer(attempt: Int) {
|
|
528
558
|
guard !isPreviewLayerAdded else { return }
|
|
529
559
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cleanuidev/react-native-scanner",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.5",
|
|
4
4
|
"description": "High-performance native barcode and QR code scanner for React Native. Scan barcodes in configurable target areas for precise detection. Built with CameraX & ML Kit (Android) and AVFoundation & Vision (iOS).",
|
|
5
5
|
"main": "./lib/module/index.js",
|
|
6
6
|
"types": "./lib/typescript/src/index.d.ts",
|
|
@@ -73,14 +73,14 @@
|
|
|
73
73
|
],
|
|
74
74
|
"repository": {
|
|
75
75
|
"type": "git",
|
|
76
|
-
"url": "git+https://github.com/
|
|
76
|
+
"url": "git+https://github.com/cleanuidev/react-native-scanner.git"
|
|
77
77
|
},
|
|
78
78
|
"author": "Rahul Gupta <rahulgwebdev@gmail.com> (https://github.com/rahulgwebdev)",
|
|
79
79
|
"license": "MIT",
|
|
80
80
|
"bugs": {
|
|
81
|
-
"url": "https://github.com/
|
|
81
|
+
"url": "https://github.com/cleanuidev/react-native-scanner/issues"
|
|
82
82
|
},
|
|
83
|
-
"homepage": "https://github.com/
|
|
83
|
+
"homepage": "https://github.com/cleanuidev/react-native-scanner#readme",
|
|
84
84
|
"publishConfig": {
|
|
85
85
|
"registry": "https://registry.npmjs.org/",
|
|
86
86
|
"access": "public"
|