実行環境
Swift | 5.6.1 |
Xcode | 14.0 |
macOS | 12.6 |
詳細
QRScannerは、OSSになったメルペイのQRコードスキャン機能です。
下の動画のようにApple標準のようなシンプルなQRスキャンを実装できます。
メルカリのQRScannerのGitHubはこちらから。
また、本記事ではSwiftUI上でStoryboardを扱います。
詳しく知りたい方は、SwiftUIからStoryboardを表示させる方法をご覧ください。
実装
デモ動画
全体のソースコードはこちらのGitHubにあげています。
ライブラリのインストール
Swift Package Managerで、QRScannerをインストールします。
インストール時は、公式の下のリンクをご活用ください。
https://github.com/mercari/QRScanner
ファイルの作成
下のように、必要なファイルを作成します。
内部のコーディングは後述します。
コーディング
コードは、
基本的にメルカリGitHubでの公式リファレンスを参考にしています。
● ContentView
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
NavigationLink(destination: QRView()){
Text("QRコード起動")
}
.navigationBarTitleDisplayMode(.inline)
.navigationTitle("メイン画面")
}
}
}
● QRView
import SwiftUI
struct QRView: View {
var body: some View {
QRViewControllerWrapper {
Text("Hello, World!")
}.navigationTitle("QRスキャン画面")
}
}
struct QRViewControllerWrapper<Content: View>: UIViewControllerRepresentable {
// 表示するView Controllerのタイプ
typealias UIViewControllerType = QRViewController
var content: () -> Content
// ViewControllerのオブジェクトを作成し、初期状態を構成
func makeUIViewController(context: Context) -> QRViewController {
let viewControllerWithStoryboard = QRViewController()
return viewControllerWithStoryboard
}
// ViewControllerの状態をSwiftUIからの新しい情報で更新
func updateUIViewController(_ uiviewController: QRViewController, context: Context) {
}
}
● QRViewController
import UIKit
import QRScanner
import AVFoundation
class QRViewController: UIViewController {
@IBOutlet weak var qrScannerView: QRScannerView!
@IBOutlet weak var flashButton: FlashButton!
override func viewDidLoad() {
super.viewDidLoad()
setupQRScanner()
}
private func setupQRScanner() {
switch AVCaptureDevice.authorizationStatus(for: .video) {
case .authorized:
setupQRScannerView()
case .notDetermined:
AVCaptureDevice.requestAccess(for: .video) { [weak self] granted in
if granted {
DispatchQueue.main.async { [weak self] in
self?.setupQRScannerView()
}
}
}
default:
showAlert()
}
}
private func setupQRScannerView() {
qrScannerView.configure(delegate: self, input: .init(isBlurEffectEnabled: true))
qrScannerView.startRunning()
}
private func showAlert() {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in
let alert = UIAlertController(title: "Error", message: "Camera is required to use in this application", preferredStyle: .alert)
alert.addAction(.init(title: "OK", style: .default))
self?.present(alert, animated: true)
}
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
qrScannerView.stopRunning()
}
@IBAction func tapFlashButton(_ sender: UIButton) {
qrScannerView.setTorchActive(isOn: !sender.isSelected)
}
}
// MARK: - QRScannerViewDelegate
extension QRViewController: QRScannerViewDelegate {
func qrScannerView(_ qrScannerView: QRScannerView, didFailure error: QRScannerError) {
print(error.localizedDescription)
}
func qrScannerView(_ qrScannerView: QRScannerView, didSuccess code: String) {
if let url = URL(string: code), (url.scheme == "http" || url.scheme == "https") {
openWeb(url: url)
} else {
showAlert(code: code)
}
}
func qrScannerView(_ qrScannerView: QRScannerView, didChangeTorchActive isOn: Bool) {
flashButton.isSelected = isOn
}
}
private extension QRViewController {
func openWeb(url: URL) {
UIApplication.shared.open(url, options: [:], completionHandler: { [weak self] _ in
self?.qrScannerView.rescan()
})
}
func showAlert(code: String) {
let alertController = UIAlertController(title: code, message: nil, preferredStyle: .actionSheet)
let copyAction = UIAlertAction(title: "Copy", style: .default) { [weak self] _ in
UIPasteboard.general.string = code
self?.qrScannerView.rescan()
}
alertController.addAction(copyAction)
let searchWebAction = UIAlertAction(title: "Search Web", style: .default) { [weak self] _ in
UIApplication.shared.open(URL(string: "https://www.google.com/search?q=\(code)")!, options: [:], completionHandler: nil)
self?.qrScannerView.rescan()
}
alertController.addAction(searchWebAction)
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: { [weak self] _ in
self?.qrScannerView.rescan()
})
alertController.addAction(cancelAction)
present(alertController, animated: true, completion: nil)
}
}
● FlashButton
import UIKit
final class FlashButton: UIButton {
override init(frame: CGRect) {
super.init(frame: frame)
settings()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
settings()
}
// MARK: - Properties
override var isSelected: Bool {
didSet {
let color: UIColor = isSelected ? .gray : .lightGray
backgroundColor = color.withAlphaComponent(0.7)
}
}
}
private extension FlashButton {
func settings() {
setTitleColor(.darkGray, for: .normal)
setTitleColor(.white, for: .selected)
setTitle("OFF", for: .normal)
setTitle("ON", for: .selected)
tintColor = .clear
titleLabel?.font = .boldSystemFont(ofSize: 16)
layer.cornerRadius = frame.size.width / 2
isSelected = false
}
}
View・Buttonの配置
XIBファイルでViewを配置します。
配置後は、下のようにClassを指定しておきます。
Viewと同様に、
Buttonも配置してClassを指定しておきます。
適当に、AutoLayoutの制約もつけておきます。
Permissionの設定
QRをスキャンするためにカメラを使用するので、
Permissionも許可しておきます。
まとめ
ということで、本記事はメルカリQRScannerをSwiftUI × Storyboardで使う方法をまとめました。
アドバイスや改善などあれば本記事の最後のコメント欄からお願いします。
最後まで読んでいただきありがとうございました!
作業効率がグッと上がるPC道具
リンク
リンク
間違いなしのSwift書籍2冊
リンク
リンク