parent
dff48390b3
commit
a8b4576243
|
@ -1,5 +1,9 @@
|
|||
# Change log
|
||||
|
||||
## Version 10.0.0
|
||||
|
||||
- [ADD] Allowing the scan area configuration (#157 #106)
|
||||
|
||||
## [Version 9.0.0](https://github.com/yannickl/QRCodeReader.swift/releases/tag/9.0.0)
|
||||
Release on 2018-09-19
|
||||
|
||||
|
|
|
@ -30,7 +30,11 @@ import UIKit
|
|||
class ViewController: UIViewController, QRCodeReaderViewControllerDelegate {
|
||||
@IBOutlet weak var previewView: QRCodeReaderView! {
|
||||
didSet {
|
||||
previewView.setupComponents(showCancelButton: false, showSwitchCameraButton: false, showTorchButton: false, showOverlayView: true, reader: reader)
|
||||
previewView.setupComponents(with: QRCodeReaderViewControllerBuilder {
|
||||
$0.reader = reader
|
||||
$0.showTorchButton = false
|
||||
$0.showSwitchCameraButton = false
|
||||
})
|
||||
}
|
||||
}
|
||||
lazy var reader: QRCodeReader = QRCodeReader()
|
||||
|
@ -39,6 +43,7 @@ class ViewController: UIViewController, QRCodeReaderViewControllerDelegate {
|
|||
$0.reader = QRCodeReader(metadataObjectTypes: [.qr], captureDevicePosition: .back)
|
||||
$0.showTorchButton = true
|
||||
$0.preferredStatusBarStyle = .lightContent
|
||||
$0.rectOfInterest = CGRect(x: 0.15, y: 0.15, width: 0.7, height: 0.7)
|
||||
|
||||
$0.reader.stopScanningWhenCodeIsFound = false
|
||||
}
|
||||
|
|
|
@ -37,8 +37,8 @@ public final class QRCodeReader: NSObject, AVCaptureMetadataOutputObjectsDelegat
|
|||
private let sessionQueue = DispatchQueue(label: "session queue")
|
||||
private let metadataObjectsQueue = DispatchQueue(label: "com.yannickloriot.qr", attributes: [], target: nil)
|
||||
|
||||
var defaultDevice: AVCaptureDevice? = AVCaptureDevice.default(for: .video)
|
||||
var frontDevice: AVCaptureDevice? = {
|
||||
let defaultDevice: AVCaptureDevice? = AVCaptureDevice.default(for: .video)
|
||||
let frontDevice: AVCaptureDevice? = {
|
||||
if #available(iOS 10, *) {
|
||||
return AVCaptureDevice.default(.builtInWideAngleCamera, for: AVMediaType.video, position: .front)
|
||||
}
|
||||
|
@ -67,8 +67,8 @@ public final class QRCodeReader: NSObject, AVCaptureMetadataOutputObjectsDelegat
|
|||
return nil
|
||||
}()
|
||||
|
||||
public var metadataOutput = AVCaptureMetadataOutput()
|
||||
var session = AVCaptureSession()
|
||||
let session = AVCaptureSession()
|
||||
let metadataOutput = AVCaptureMetadataOutput()
|
||||
|
||||
weak var lifeCycleDelegate: QRCodeReaderLifeCycleDelegate?
|
||||
|
||||
|
|
|
@ -73,16 +73,16 @@ final public class QRCodeReaderView: UIView, QRCodeReaderDisplayable {
|
|||
|
||||
private weak var reader: QRCodeReader?
|
||||
|
||||
public func setupComponents(showCancelButton: Bool, showSwitchCameraButton: Bool, showTorchButton: Bool, showOverlayView: Bool, reader: QRCodeReader?) {
|
||||
self.reader = reader
|
||||
public func setupComponents(with builder: QRCodeReaderViewControllerBuilder) {
|
||||
self.reader = builder.reader
|
||||
reader?.lifeCycleDelegate = self
|
||||
|
||||
addComponents()
|
||||
|
||||
cancelButton?.isHidden = !showCancelButton
|
||||
switchCameraButton?.isHidden = !showSwitchCameraButton
|
||||
toggleTorchButton?.isHidden = !showTorchButton
|
||||
overlayView?.isHidden = !showOverlayView
|
||||
cancelButton?.isHidden = !builder.showCancelButton
|
||||
switchCameraButton?.isHidden = !builder.showSwitchCameraButton
|
||||
toggleTorchButton?.isHidden = !builder.showTorchButton
|
||||
overlayView?.isHidden = !builder.showOverlayView
|
||||
|
||||
guard let cb = cancelButton, let scb = switchCameraButton, let ttb = toggleTorchButton, let ov = overlayView else { return }
|
||||
|
||||
|
@ -90,7 +90,7 @@ final public class QRCodeReaderView: UIView, QRCodeReaderDisplayable {
|
|||
|
||||
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[cv]|", options: [], metrics: nil, views: views))
|
||||
|
||||
if showCancelButton {
|
||||
if builder.showCancelButton {
|
||||
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[cv][cb(40)]|", options: [], metrics: nil, views: views))
|
||||
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-[cb]-|", options: [], metrics: nil, views: views))
|
||||
}
|
||||
|
@ -98,12 +98,12 @@ final public class QRCodeReaderView: UIView, QRCodeReaderDisplayable {
|
|||
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[cv]|", options: [], metrics: nil, views: views))
|
||||
}
|
||||
|
||||
if showSwitchCameraButton {
|
||||
if builder.showSwitchCameraButton {
|
||||
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-[scb(50)]", options: [], metrics: nil, views: views))
|
||||
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:[scb(70)]|", options: [], metrics: nil, views: views))
|
||||
}
|
||||
|
||||
if showTorchButton {
|
||||
if builder.showTorchButton {
|
||||
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-[ttb(50)]", options: [], metrics: nil, views: views))
|
||||
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[ttb(70)]", options: [], metrics: nil, views: views))
|
||||
}
|
||||
|
@ -111,6 +111,10 @@ final public class QRCodeReaderView: UIView, QRCodeReaderDisplayable {
|
|||
for attribute in Array<NSLayoutConstraint.Attribute>([.left, .top, .right, .bottom]) {
|
||||
addConstraint(NSLayoutConstraint(item: ov, attribute: attribute, relatedBy: .equal, toItem: cameraView, attribute: attribute, multiplier: 1, constant: 0))
|
||||
}
|
||||
|
||||
if let readerOverlayView = overlayView as? ReaderOverlayView {
|
||||
readerOverlayView.rectOfInterest = builder.rectOfInterest
|
||||
}
|
||||
}
|
||||
|
||||
public override func layoutSubviews() {
|
||||
|
|
|
@ -47,15 +47,11 @@ public protocol QRCodeReaderDisplayable {
|
|||
func setNeedsUpdateOrientation()
|
||||
|
||||
/**
|
||||
Method called by the container to allows you to layout your view properly using the given flags.
|
||||
Method called by the container to allows you to layout your view properly using the QR code reader builder.
|
||||
|
||||
- Parameter showCancelButton: Flag to know whether you should display the cancel button.
|
||||
- Parameter showSwitchCameraButton: Flag to know whether you should display the switch camera button.
|
||||
- Parameter showTorchButton: Flag to know whether you should display the toggle torch button.
|
||||
- Parameter showOverlayView: Flag to know whether you should display the overlay.
|
||||
- Parameter reader: A reference to the code reader.
|
||||
- Parameter builder: A QR code reader builder.
|
||||
*/
|
||||
func setupComponents(showCancelButton: Bool, showSwitchCameraButton: Bool, showTorchButton: Bool, showOverlayView: Bool, reader: QRCodeReader?)
|
||||
func setupComponents(with builder: QRCodeReaderViewControllerBuilder)
|
||||
}
|
||||
|
||||
/// The `QRCodeReaderContainer` structure embed the view displayed by the controller. The embeded view must be conform to the `QRCodeReaderDisplayable` protocol.
|
||||
|
@ -75,7 +71,7 @@ public struct QRCodeReaderContainer {
|
|||
|
||||
// MARK: - Convenience Methods
|
||||
|
||||
func setupComponents(showCancelButton: Bool, showSwitchCameraButton: Bool, showTorchButton: Bool, showOverlayView: Bool, reader: QRCodeReader? = nil) {
|
||||
displayable.setupComponents(showCancelButton: showCancelButton, showSwitchCameraButton: showSwitchCameraButton, showTorchButton: showTorchButton, showOverlayView: showOverlayView, reader: reader)
|
||||
func setupComponents(with builder: QRCodeReaderViewControllerBuilder) {
|
||||
displayable.setupComponents(with: builder)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,16 +29,12 @@ import AVFoundation
|
|||
|
||||
/// Convenient controller to display a view to scan/read 1D or 2D bar codes like the QRCodes. It is based on the `AVFoundation` framework from Apple. It aims to replace ZXing or ZBar for iOS 7 and over.
|
||||
public class QRCodeReaderViewController: UIViewController {
|
||||
/// The code reader object used to scan the bar code.
|
||||
public let codeReader: QRCodeReader
|
||||
private let builder: QRCodeReaderViewControllerBuilder
|
||||
|
||||
let readerView: QRCodeReaderContainer
|
||||
let startScanningAtLoad: Bool
|
||||
let showCancelButton: Bool
|
||||
let showSwitchCameraButton: Bool
|
||||
let showTorchButton: Bool
|
||||
let showOverlayView: Bool
|
||||
let customPreferredStatusBarStyle: UIStatusBarStyle?
|
||||
/// The code reader object used to scan the bar code.
|
||||
public var codeReader: QRCodeReader {
|
||||
return builder.reader
|
||||
}
|
||||
|
||||
// MARK: - Managing the Callback Responders
|
||||
|
||||
|
@ -62,14 +58,7 @@ public class QRCodeReaderViewController: UIViewController {
|
|||
- parameter builder: A QRCodeViewController builder object.
|
||||
*/
|
||||
required public init(builder: QRCodeReaderViewControllerBuilder) {
|
||||
readerView = builder.readerView
|
||||
startScanningAtLoad = builder.startScanningAtLoad
|
||||
codeReader = builder.reader
|
||||
showCancelButton = builder.showCancelButton
|
||||
showSwitchCameraButton = builder.showSwitchCameraButton
|
||||
showTorchButton = builder.showTorchButton
|
||||
showOverlayView = builder.showOverlayView
|
||||
customPreferredStatusBarStyle = builder.preferredStatusBarStyle
|
||||
self.builder = builder
|
||||
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
|
||||
|
@ -77,7 +66,7 @@ public class QRCodeReaderViewController: UIViewController {
|
|||
|
||||
codeReader.didFindCode = { [weak self] resultAsObject in
|
||||
if let weakSelf = self {
|
||||
if let qrv = weakSelf.readerView.displayable as? QRCodeReaderView {
|
||||
if let qrv = builder.readerView.displayable as? QRCodeReaderView {
|
||||
qrv.addGreenBorder()
|
||||
}
|
||||
weakSelf.completionBlock?(resultAsObject)
|
||||
|
@ -85,26 +74,17 @@ public class QRCodeReaderViewController: UIViewController {
|
|||
}
|
||||
}
|
||||
|
||||
codeReader.didFailDecoding = { [weak self] in
|
||||
if let weakSelf = self {
|
||||
if let qrv = weakSelf.readerView.displayable as? QRCodeReaderView {
|
||||
codeReader.didFailDecoding = {
|
||||
if let qrv = builder.readerView.displayable as? QRCodeReaderView {
|
||||
qrv.addRedBorder()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setupUIComponentsWithCancelButtonTitle(builder.cancelButtonTitle)
|
||||
}
|
||||
|
||||
required public init?(coder aDecoder: NSCoder) {
|
||||
codeReader = QRCodeReader()
|
||||
readerView = QRCodeReaderContainer(displayable: QRCodeReaderView())
|
||||
startScanningAtLoad = false
|
||||
showCancelButton = false
|
||||
showTorchButton = false
|
||||
showSwitchCameraButton = false
|
||||
showOverlayView = false
|
||||
customPreferredStatusBarStyle = nil
|
||||
self.builder = QRCodeReaderViewControllerBuilder()
|
||||
|
||||
super.init(coder: aDecoder)
|
||||
}
|
||||
|
@ -114,8 +94,8 @@ public class QRCodeReaderViewController: UIViewController {
|
|||
override public func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
if startScanningAtLoad {
|
||||
readerView.displayable.setNeedsUpdateOrientation()
|
||||
if builder.startScanningAtLoad {
|
||||
builder.readerView.displayable.setNeedsUpdateOrientation()
|
||||
|
||||
startScanning()
|
||||
}
|
||||
|
@ -134,38 +114,35 @@ public class QRCodeReaderViewController: UIViewController {
|
|||
}
|
||||
|
||||
public override var preferredStatusBarStyle: UIStatusBarStyle {
|
||||
return customPreferredStatusBarStyle ?? super.preferredStatusBarStyle
|
||||
return builder.preferredStatusBarStyle ?? super.preferredStatusBarStyle
|
||||
}
|
||||
|
||||
// MARK: - Initializing the AV Components
|
||||
|
||||
private func setupUIComponentsWithCancelButtonTitle(_ cancelButtonTitle: String) {
|
||||
view.addSubview(readerView.view)
|
||||
view.addSubview(builder.readerView.view)
|
||||
|
||||
let sscb = showSwitchCameraButton && codeReader.hasFrontDevice
|
||||
let stb = showTorchButton && codeReader.isTorchAvailable
|
||||
|
||||
readerView.view.translatesAutoresizingMaskIntoConstraints = false
|
||||
readerView.setupComponents(showCancelButton: showCancelButton, showSwitchCameraButton: sscb, showTorchButton: stb, showOverlayView: showOverlayView, reader: codeReader)
|
||||
builder.readerView.view.translatesAutoresizingMaskIntoConstraints = false
|
||||
builder.readerView.setupComponents(with: builder)
|
||||
|
||||
// Setup action methods
|
||||
|
||||
readerView.displayable.switchCameraButton?.addTarget(self, action: #selector(switchCameraAction), for: .touchUpInside)
|
||||
readerView.displayable.toggleTorchButton?.addTarget(self, action: #selector(toggleTorchAction), for: .touchUpInside)
|
||||
readerView.displayable.cancelButton?.setTitle(cancelButtonTitle, for: .normal)
|
||||
readerView.displayable.cancelButton?.addTarget(self, action: #selector(cancelAction), for: .touchUpInside)
|
||||
builder.readerView.displayable.switchCameraButton?.addTarget(self, action: #selector(switchCameraAction), for: .touchUpInside)
|
||||
builder.readerView.displayable.toggleTorchButton?.addTarget(self, action: #selector(toggleTorchAction), for: .touchUpInside)
|
||||
builder.readerView.displayable.cancelButton?.setTitle(cancelButtonTitle, for: .normal)
|
||||
builder.readerView.displayable.cancelButton?.addTarget(self, action: #selector(cancelAction), for: .touchUpInside)
|
||||
|
||||
// Setup constraints
|
||||
|
||||
for attribute in [.left, .top, .right] as [NSLayoutConstraint.Attribute] {
|
||||
NSLayoutConstraint(item: readerView.view, attribute: attribute, relatedBy: .equal, toItem: view, attribute: attribute, multiplier: 1, constant: 0).isActive = true
|
||||
NSLayoutConstraint(item: builder.readerView.view, attribute: attribute, relatedBy: .equal, toItem: view, attribute: attribute, multiplier: 1, constant: 0).isActive = true
|
||||
}
|
||||
|
||||
if #available(iOS 11.0, *) {
|
||||
view.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: readerView.view.bottomAnchor).isActive = true
|
||||
view.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: builder.readerView.view.bottomAnchor).isActive = true
|
||||
}
|
||||
else {
|
||||
NSLayoutConstraint(item: readerView.view, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1, constant: 0).isActive = true
|
||||
NSLayoutConstraint(item: builder.readerView.view, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1, constant: 0).isActive = true
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -68,12 +68,28 @@ public final class QRCodeReaderViewControllerBuilder {
|
|||
/**
|
||||
Flag to display the switch camera button.
|
||||
*/
|
||||
public var showSwitchCameraButton = true
|
||||
public var showSwitchCameraButton: Bool {
|
||||
get {
|
||||
return _showSwitchCameraButton && reader.hasFrontDevice
|
||||
}
|
||||
set {
|
||||
_showSwitchCameraButton = newValue
|
||||
}
|
||||
}
|
||||
private var _showSwitchCameraButton: Bool = true
|
||||
|
||||
/**
|
||||
Flag to display the toggle torch button. If the value is true and there is no torch the button will not be displayed.
|
||||
*/
|
||||
public var showTorchButton = false
|
||||
public var showTorchButton: Bool {
|
||||
get {
|
||||
return _showTorchButton && reader.isTorchAvailable
|
||||
}
|
||||
set {
|
||||
_showTorchButton = newValue
|
||||
}
|
||||
}
|
||||
private var _showTorchButton = true
|
||||
|
||||
/**
|
||||
Flag to display the guide view.
|
||||
|
@ -91,6 +107,22 @@ public final class QRCodeReaderViewControllerBuilder {
|
|||
*/
|
||||
public var preferredStatusBarStyle: UIStatusBarStyle? = nil
|
||||
|
||||
/**
|
||||
Specifies a rectangle of interest for limiting the search area for visual metadata.
|
||||
|
||||
The value of this property is a CGRect that determines the receiver's rectangle of interest for each frame of video. The rectangle's origin is top left and is relative to the coordinate space of the device providing the metadata. Specifying a rectOfInterest may improve detection performance for certain types of metadata. The default value of this property is the value CGRectMake(0, 0, 1, 1). Metadata objects whose bounds do not intersect with the rectOfInterest will not be returned.
|
||||
*/
|
||||
public var rectOfInterest: CGRect = CGRect(x: 0, y: 0, width: 1, height: 1) {
|
||||
didSet {
|
||||
reader.metadataOutput.rectOfInterest = CGRect(
|
||||
x: min(max(rectOfInterest.origin.x, 0), 1),
|
||||
y: min(max(rectOfInterest.origin.y, 0), 1),
|
||||
width: min(max(rectOfInterest.width, 0), 1),
|
||||
height: min(max(rectOfInterest.height, 0), 1)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Initializing a Flap View
|
||||
|
||||
/**
|
||||
|
|
|
@ -58,24 +58,25 @@ public final class ReaderOverlayView: UIView {
|
|||
|
||||
var overlayColor: UIColor = UIColor.white {
|
||||
didSet {
|
||||
self.overlay.strokeColor = overlayColor.cgColor
|
||||
overlay.strokeColor = overlayColor.cgColor
|
||||
|
||||
self.setNeedsDisplay()
|
||||
setNeedsDisplay()
|
||||
}
|
||||
}
|
||||
|
||||
var rectOfInterest: CGRect = CGRect(x: 0, y: 0, width: 1, height: 1) {
|
||||
didSet {
|
||||
setNeedsDisplay()
|
||||
}
|
||||
}
|
||||
|
||||
public override func draw(_ rect: CGRect) {
|
||||
var innerRect = rect.insetBy(dx: 50, dy: 50)
|
||||
let minSize = min(innerRect.width, innerRect.height)
|
||||
|
||||
if innerRect.width != minSize {
|
||||
innerRect.origin.x += (innerRect.width - minSize) / 2
|
||||
innerRect.size.width = minSize
|
||||
}
|
||||
else if innerRect.height != minSize {
|
||||
innerRect.origin.y += (innerRect.height - minSize) / 2
|
||||
innerRect.size.height = minSize
|
||||
}
|
||||
let innerRect = CGRect(
|
||||
x: rect.width * rectOfInterest.minX,
|
||||
y: rect.height * rectOfInterest.minY,
|
||||
width: rect.width * rectOfInterest.width,
|
||||
height: rect.height * rectOfInterest.height
|
||||
)
|
||||
|
||||
overlay.path = UIBezierPath(roundedRect: innerRect, cornerRadius: 5).cgPath
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue