diff --git a/BarcodeScanner.podspec b/BarcodeScanner.podspec index 154de9a..fcf96d7 100644 --- a/BarcodeScanner.podspec +++ b/BarcodeScanner.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "BarcodeScanner" s.summary = "Simple and beautiful barcode scanner." - s.version = "3.0.0" + s.version = "3.0.1" s.homepage = "https://github.com/hyperoslo/BarcodeScanner" s.license = 'MIT' s.author = { "Hyper Interaktiv AS" => "ios@hyper.no" } diff --git a/BarcodeScanner.xcodeproj/project.pbxproj b/BarcodeScanner.xcodeproj/project.pbxproj index c47f8e7..19f57c6 100644 --- a/BarcodeScanner.xcodeproj/project.pbxproj +++ b/BarcodeScanner.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 2DBF9E0E1F169DEF006B5AA8 /* FocusViewType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DBF9E0D1F169DEF006B5AA8 /* FocusViewType.swift */; }; + D504555F1FD8714700E46826 /* UIView+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D504555E1FD8714700E46826 /* UIView+Extensions.swift */; }; D50BE3E91C9FE7A80000A34C /* flashOff@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = D50BE3E51C9FE7A80000A34C /* flashOff@3x.png */; }; D50BE3EA1C9FE7A80000A34C /* flashOn@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = D50BE3E61C9FE7A80000A34C /* flashOn@3x.png */; }; D50BE3EB1C9FE7A80000A34C /* info@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = D50BE3E71C9FE7A80000A34C /* info@3x.png */; }; @@ -20,18 +21,19 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ - 2DBF9E0D1F169DEF006B5AA8 /* FocusViewType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FocusViewType.swift; sourceTree = ""; }; + 2DBF9E0D1F169DEF006B5AA8 /* FocusViewType.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = FocusViewType.swift; sourceTree = ""; tabWidth = 2; }; + D504555E1FD8714700E46826 /* UIView+Extensions.swift */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = "UIView+Extensions.swift"; sourceTree = ""; tabWidth = 2; }; D50BE3E51C9FE7A80000A34C /* flashOff@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "flashOff@3x.png"; sourceTree = ""; }; D50BE3E61C9FE7A80000A34C /* flashOn@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "flashOn@3x.png"; sourceTree = ""; }; D50BE3E71C9FE7A80000A34C /* info@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "info@3x.png"; sourceTree = ""; }; D5B2E89F1C3A780C00C0327D /* BarcodeScanner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = BarcodeScanner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - D5C4E08C1CA0BFB9008D9269 /* InfoView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InfoView.swift; sourceTree = ""; }; - D5C4E08D1CA0BFB9008D9269 /* TorchMode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TorchMode.swift; sourceTree = ""; }; + D5C4E08C1CA0BFB9008D9269 /* InfoView.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = InfoView.swift; sourceTree = ""; tabWidth = 2; }; + D5C4E08D1CA0BFB9008D9269 /* TorchMode.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = TorchMode.swift; sourceTree = ""; tabWidth = 2; }; D5C6298B1C3A8BBD007F7B7C /* Info-iOS.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-iOS.plist"; sourceTree = ""; }; - D5F1C1C51C9C5113001E17A6 /* Config.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Config.swift; sourceTree = ""; }; - D5F1C1C61C9C5113001E17A6 /* BarcodeScannerController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BarcodeScannerController.swift; sourceTree = ""; }; - D5F1C1D21C9C5809001E17A6 /* HeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeaderView.swift; sourceTree = ""; }; - D5FC8AD61D252A12004BED88 /* State.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = State.swift; sourceTree = ""; }; + D5F1C1C51C9C5113001E17A6 /* Config.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = Config.swift; sourceTree = ""; tabWidth = 2; }; + D5F1C1C61C9C5113001E17A6 /* BarcodeScannerController.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = BarcodeScannerController.swift; sourceTree = ""; tabWidth = 2; }; + D5F1C1D21C9C5809001E17A6 /* HeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = HeaderView.swift; sourceTree = ""; tabWidth = 2; }; + D5FC8AD61D252A12004BED88 /* State.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = State.swift; sourceTree = ""; tabWidth = 2; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -91,6 +93,7 @@ D5F1C1C51C9C5113001E17A6 /* Config.swift */, D5F1C1C61C9C5113001E17A6 /* BarcodeScannerController.swift */, D5F1C1D21C9C5809001E17A6 /* HeaderView.swift */, + D504555E1FD8714700E46826 /* UIView+Extensions.swift */, ); path = Sources; sourceTree = ""; @@ -182,6 +185,7 @@ D5F1C1C91C9C5113001E17A6 /* Config.swift in Sources */, D5F1C1CA1C9C5113001E17A6 /* BarcodeScannerController.swift in Sources */, 2DBF9E0E1F169DEF006B5AA8 /* FocusViewType.swift in Sources */, + D504555F1FD8714700E46826 /* UIView+Extensions.swift in Sources */, D5FC8AD71D252A12004BED88 /* State.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Example/BarcodeScannerExample/Podfile.lock b/Example/BarcodeScannerExample/Podfile.lock index bfc3c1a..4d90a9d 100644 --- a/Example/BarcodeScannerExample/Podfile.lock +++ b/Example/BarcodeScannerExample/Podfile.lock @@ -1,5 +1,5 @@ PODS: - - BarcodeScanner (2.1.2) + - BarcodeScanner (3.0.1) DEPENDENCIES: - BarcodeScanner (from `../../`) @@ -9,7 +9,7 @@ EXTERNAL SOURCES: :path: ../../ SPEC CHECKSUMS: - BarcodeScanner: e519f9497d196f228b5c1dd96af0468c666a2880 + BarcodeScanner: f884e5a1e0e8e6c95eb7d0c4b00d69b72fd2be83 PODFILE CHECKSUM: eae1a5fa9feaba2427db94d37a48c50c283939b8 diff --git a/Sources/BarcodeScannerController.swift b/Sources/BarcodeScannerController.swift index 5d71b5b..2c4bf03 100644 --- a/Sources/BarcodeScannerController.swift +++ b/Sources/BarcodeScannerController.swift @@ -28,18 +28,17 @@ public protocol BarcodeScannerDismissalDelegate: class { - Not found error message */ open class BarcodeScannerController: UIViewController { - /// Video capture device. This may be nil when running in Simulator. - lazy var captureDevice: AVCaptureDevice! = AVCaptureDevice.default(for: AVMediaType.video) + private lazy var captureDevice: AVCaptureDevice! = AVCaptureDevice.default(for: AVMediaType.video) /// Capture session. - lazy var captureSession: AVCaptureSession = AVCaptureSession() + private lazy var captureSession: AVCaptureSession = AVCaptureSession() /// Header view with title and close button. - lazy var headerView: HeaderView = HeaderView() + private lazy var headerView: HeaderView = HeaderView() /// Information view with description label. - lazy var infoView: InfoView = InfoView() + private lazy var infoView: InfoView = InfoView() /// Button to change torch mode. public lazy var flashButton: UIButton = { [unowned self] in @@ -49,7 +48,7 @@ open class BarcodeScannerController: UIViewController { }() /// Animated focus view. - lazy var focusView: UIView = { + private lazy var focusView: UIView = { let view = UIView() view.layer.borderColor = UIColor.white.cgColor view.layer.borderWidth = 2 @@ -64,7 +63,7 @@ open class BarcodeScannerController: UIViewController { }() /// Button that opens settings to allow camera usage. - lazy var settingsButton: UIButton = { [unowned self] in + private lazy var settingsButton: UIButton = { [unowned self] in let button = UIButton(type: .system) let title = NSAttributedString(string: SettingsButton.text, attributes: [ @@ -80,10 +79,10 @@ open class BarcodeScannerController: UIViewController { }() /// Video preview layer. - var videoPreviewLayer: AVCaptureVideoPreviewLayer? + private var videoPreviewLayer: AVCaptureVideoPreviewLayer? /// The current controller's status mode. - var status: Status = Status(state: .scanning) { + private var status: Status = Status(state: .scanning) { didSet { let duration = status.animated && (status.state == .processing @@ -128,7 +127,7 @@ open class BarcodeScannerController: UIViewController { public var barCodeFocusViewType: FocusViewType = .animated /// The current torch mode on the capture device. - var torchMode: TorchMode = .off { + private var torchMode: TorchMode = .off { didSet { guard let captureDevice = captureDevice, captureDevice.hasFlash else { return } @@ -143,7 +142,7 @@ open class BarcodeScannerController: UIViewController { } /// Calculated frame for the info view. - var infoFrame: CGRect { + private var infoFrame: CGRect { let height = status.state != .processing ? 75 : view.bounds.height return CGRect(x: 0, y: view.bounds.height - height, width: view.bounds.width, height: height) @@ -163,7 +162,7 @@ open class BarcodeScannerController: UIViewController { public weak var dismissalDelegate: BarcodeScannerDismissalDelegate? /// Flag to lock session from capturing. - var locked = false + private var locked = false // MARK: - Initialization @@ -228,7 +227,7 @@ open class BarcodeScannerController: UIViewController { /** `UIApplicationWillEnterForegroundNotification` action. */ - @objc func appWillEnterForeground() { + @objc private func appWillEnterForeground() { torchMode = .off animateFocusView() } @@ -238,7 +237,7 @@ open class BarcodeScannerController: UIViewController { /** Sets up camera and checks for camera permissions. */ - func setupCamera() { + private func setupCamera() { let authorizationStatus = AVCaptureDevice.authorizationStatus(for: AVMediaType.video) if authorizationStatus == .authorized { @@ -263,7 +262,7 @@ open class BarcodeScannerController: UIViewController { /** Sets up capture input, output and session. */ - func setupSession() { + private func setupSession() { guard let captureDevice = captureDevice else { return } @@ -307,7 +306,7 @@ open class BarcodeScannerController: UIViewController { /** Resets the current state. */ - func resetState() { + private func resetState() { let alpha: CGFloat = status.state == .scanning ? 1 : 0 torchMode = .off @@ -323,32 +322,25 @@ open class BarcodeScannerController: UIViewController { } // MARK: - Layout - func setupFrame() { + private func setupFrame() { let flashButtonSize: CGFloat = 37 let isLandscape = view.frame.width > view.frame.height + let insets = view.viewInsets + // On iPhone X devices, extend the size of the top nav bar + let navbarSize: CGFloat = isLandscape ? 32 : insets.top > 0 ? 88 : 64 - var rightSafeAreaInset: CGFloat = 0 - var topSafeAreaInset: CGFloat = 0 - if #available(iOS 11.0, *) { - rightSafeAreaInset = view.safeAreaInsets.right - topSafeAreaInset = view.safeAreaInsets.top - } - - var navbarSize: CGFloat = 0 - if (isLandscape) { - navbarSize = 32 - } - else { - // On iPhone X devices, extend the size of the top nav bar - navbarSize = topSafeAreaInset > 0 ? 88 : 64 - } - headerView.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: navbarSize) - flashButton.frame = CGRect(x: view.frame.width - 50 - rightSafeAreaInset, y: navbarSize + 10 + (flashButtonSize / 2), width: flashButtonSize, height: flashButtonSize) + flashButton.frame = CGRect( + x: view.frame.width - 50 - insets.right, + y: navbarSize + 10 + (flashButtonSize / 2), + width: flashButtonSize, + height: flashButtonSize + ) infoView.frame = infoFrame if let videoPreviewLayer = videoPreviewLayer { videoPreviewLayer.frame = view.layer.bounds + if let connection = videoPreviewLayer.connection, connection.isVideoOrientationSupported { switch (UIApplication.shared.statusBarOrientation) { case .portrait: connection.videoOrientation = .portrait @@ -365,6 +357,7 @@ open class BarcodeScannerController: UIViewController { } else { center(subview: focusView, inSize: CGSize(width: 218, height: 150)) } + center(subview: settingsButton, inSize: CGSize(width: 150, height: 50)) } @@ -374,12 +367,13 @@ open class BarcodeScannerController: UIViewController { - Parameter subview: The subview. - Parameter size: A new size. */ - func center(subview: UIView, inSize size: CGSize) { + private func center(subview: UIView, inSize size: CGSize) { subview.frame = CGRect( x: (view.frame.width - size.width) / 2, y: (view.frame.height - size.height) / 2, width: size.width, - height: size.height) + height: size.height + ) } // MARK: - Animations @@ -389,7 +383,7 @@ open class BarcodeScannerController: UIViewController { - Parameter processing: Flag to set the current state to `.Processing`. */ - func animateFlash(whenProcessing: Bool = false) { + private func animateFlash(whenProcessing: Bool = false) { let flashView = UIView(frame: view.bounds) flashView.backgroundColor = UIColor.white flashView.alpha = 1 @@ -413,7 +407,7 @@ open class BarcodeScannerController: UIViewController { /** Performs focus view animation. */ - func animateFocusView() { + private func animateFocusView() { focusView.layer.removeAllAnimations() focusView.isHidden = false @@ -434,7 +428,7 @@ open class BarcodeScannerController: UIViewController { /** Opens setting to allow camera usage. */ - @objc func settingsButtonDidPress() { + @objc private func settingsButtonDidPress() { DispatchQueue.main.async { if let settingsURL = URL(string: UIApplicationOpenSettingsURLString) { UIApplication.shared.openURL(settingsURL) @@ -445,7 +439,7 @@ open class BarcodeScannerController: UIViewController { /** Sets the next torch mode. */ - @objc func flashButtonDidPress() { + @objc private func flashButtonDidPress() { torchMode = torchMode.next } } @@ -453,8 +447,9 @@ open class BarcodeScannerController: UIViewController { // MARK: - AVCaptureMetadataOutputObjectsDelegate extension BarcodeScannerController: AVCaptureMetadataOutputObjectsDelegate { - - public func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) { + public func metadataOutput(_ output: AVCaptureMetadataOutput, + didOutput metadataObjects: [AVMetadataObject], + from connection: AVCaptureConnection) { guard !locked else { return } guard !metadataObjects.isEmpty else { return } @@ -485,7 +480,6 @@ extension BarcodeScannerController: AVCaptureMetadataOutputObjectsDelegate { // MARK: - HeaderViewDelegate extension BarcodeScannerController: HeaderViewDelegate { - func headerViewDidPressClose(_ headerView: HeaderView) { dismissalDelegate?.barcodeScannerDidDismiss(self) } diff --git a/Sources/Config.swift b/Sources/Config.swift index 6c40294..d15d4eb 100644 --- a/Sources/Config.swift +++ b/Sources/Config.swift @@ -69,7 +69,6 @@ func localizedString(_ key: String) -> String { return key } - /** `AVCaptureMetadataOutput` metadata object types. */ @@ -91,5 +90,5 @@ public var metadata = [ ] extension AVMetadataObject.ObjectType { - public static let upca: AVMetadataObject.ObjectType = AVMetadataObject.ObjectType(rawValue: "org.gs1.UPC-A") + public static let upca: AVMetadataObject.ObjectType = .init(rawValue: "org.gs1.UPC-A") } diff --git a/Sources/FocusViewType.swift b/Sources/FocusViewType.swift index b4ebd03..123bbce 100644 --- a/Sources/FocusViewType.swift +++ b/Sources/FocusViewType.swift @@ -1,7 +1,7 @@ import UIKit public enum FocusViewType { - case animated - case oneDimension - case twoDimensions + case animated + case oneDimension + case twoDimensions } diff --git a/Sources/HeaderView.swift b/Sources/HeaderView.swift index b597c8f..3569f02 100644 --- a/Sources/HeaderView.swift +++ b/Sources/HeaderView.swift @@ -7,10 +7,9 @@ protocol HeaderViewDelegate: class { /** Header view that simulates a navigation bar. */ -class HeaderView: UIView { - +final class HeaderView: UIView { /// Title label. - lazy var label: UILabel = { + private lazy var label: UILabel = { let label = UILabel() label.text = Title.text label.font = Title.font @@ -22,13 +21,12 @@ class HeaderView: UIView { }() /// Close button. - lazy var button: UIButton = { + private lazy var button: UIButton = { let button = UIButton(type: .system) button.setTitle(CloseButton.text, for: UIControlState()) button.titleLabel?.font = CloseButton.font button.tintColor = CloseButton.color button.addTarget(self, action: #selector(buttonDidPress), for: .touchUpInside) - return button }() @@ -61,25 +59,22 @@ class HeaderView: UIView { override func layoutSubviews() { super.layoutSubviews() - var leftSafeAreaPadding: CGFloat = 0 - var topSafeAreaPadding: CGFloat = 0 - if #available(iOS 11, *) { - leftSafeAreaPadding = safeAreaInsets.left - topSafeAreaPadding = safeAreaInsets.top - } - - let leadingPadding: CGFloat = 15 + leftSafeAreaPadding - let topPadding: CGFloat = 8 + topSafeAreaPadding + let insets = viewInsets + let leadingPadding: CGFloat = 15 + insets.left + let topPadding: CGFloat = 8 + insets.top let labelHeight: CGFloat = 40 button.sizeToFit() - button.frame.origin = CGPoint(x: leadingPadding, - y: ((frame.height - button.frame.height) / 2) + topPadding) + button.frame.origin = CGPoint( + x: leadingPadding, + y: ((frame.height - button.frame.height) / 2) + topPadding + ) label.frame = CGRect( x: 0, y: ((frame.height - labelHeight) / 2) + topPadding, - width: frame.width, height: labelHeight) + width: frame.width, height: labelHeight + ) } // MARK: - Actions @@ -87,7 +82,7 @@ class HeaderView: UIView { /** Close button action handler. */ - @objc func buttonDidPress() { + @objc private func buttonDidPress() { delegate?.headerViewDidPressClose(self) } } diff --git a/Sources/InfoView.swift b/Sources/InfoView.swift index f159b4c..a5bff27 100644 --- a/Sources/InfoView.swift +++ b/Sources/InfoView.swift @@ -3,28 +3,25 @@ import UIKit /** Info view is an overlay with loading and error messages. */ -class InfoView: UIVisualEffectView { - +final class InfoView: UIVisualEffectView { /// Text label. - lazy var label: UILabel = { + private lazy var label: UILabel = { let label = UILabel() label.numberOfLines = 3 - return label }() /// Info image view. - lazy var imageView: UIImageView = { + private lazy var imageView: UIImageView = { let image = imageNamed("info").withRenderingMode(.alwaysTemplate) let imageView = UIImageView(image: image) - return imageView }() /// Border view. - lazy var borderView: UIView = { + private lazy var borderView: UIView = { let view = UIView() - view.backgroundColor = UIColor.clear + view.backgroundColor = .clear view.layer.borderWidth = 2 view.layer.cornerRadius = 10 @@ -86,14 +83,7 @@ class InfoView: UIVisualEffectView { override func layoutSubviews() { super.layoutSubviews() - var leftSafeAreaPadding: CGFloat = 0 - var rightSafeAreaPadding: CGFloat = 0 - - if #available(iOS 11.0, *) { - leftSafeAreaPadding = safeAreaInsets.left - rightSafeAreaPadding = safeAreaInsets.right - } - + let insets = viewInsets let padding: CGFloat = 10 let labelHeight: CGFloat = 40 let imageSize = CGSize(width: 30, height: 27) @@ -101,35 +91,40 @@ class InfoView: UIVisualEffectView { if status.state != .processing && status.state != .notFound { imageView.frame = CGRect( - x: padding + leftSafeAreaPadding, + x: padding + insets.left, y: (frame.height - imageSize.height) / 2, width: imageSize.width, - height: imageSize.height) + height: imageSize.height + ) label.frame = CGRect( x: imageView.frame.maxX + padding, y: 0, - width: frame.width - imageView.frame.maxX - 2 * padding - rightSafeAreaPadding, - height: frame.height) + width: frame.width - imageView.frame.maxX - 2 * padding - insets.right, + height: frame.height + ) } else { imageView.frame = CGRect( x: (frame.width - imageSize.width) / 2, y: (frame.height - imageSize.height) / 2 - 60, width: imageSize.width, - height: imageSize.height) + height: imageSize.height + ) label.frame = CGRect( x: padding, y: imageView.frame.maxY + 14, width: frame.width - 2 * padding, - height: labelHeight) + height: labelHeight + ) } borderView.frame = CGRect( x: (frame.width - borderSize) / 2, y: imageView.frame.minY - 12, width: borderSize, - height: borderSize) + height: borderSize + ) } // MARK: - Animations @@ -149,7 +144,7 @@ class InfoView: UIVisualEffectView { - Parameter style: The current blur style. */ - func animate(blurStyle style: UIBlurEffectStyle) { + private func animate(blurStyle style: UIBlurEffectStyle) { guard status.state == .processing else { return } UIView.animate(withDuration: 2.0, delay: 0.5, options: [.beginFromCurrentState], @@ -165,7 +160,7 @@ class InfoView: UIVisualEffectView { - Parameter angle: Rotation angle. */ - func animate(borderViewAngle: CGFloat) { + private func animate(borderViewAngle: CGFloat) { guard status.state == .processing else { borderView.transform = CGAffineTransform.identity return diff --git a/Sources/UIView+Extensions.swift b/Sources/UIView+Extensions.swift new file mode 100644 index 0000000..4011c9d --- /dev/null +++ b/Sources/UIView+Extensions.swift @@ -0,0 +1,11 @@ +import UIKit + +extension UIView { + var viewInsets: UIEdgeInsets { + if #available(iOS 11, *) { + return safeAreaInsets + } + + return .zero + } +}