首先,需要创建一个自定义的 UIViewControllerRepresentable
来使用 AVCaptureSession
扫描二维码。使用UIApplication.shared.canOpenURL(url)来判断是否可以打开扫描到的内容,可以的话就使用WKWebView加载扫描到的网址,不可以的话,就显示出来扫描的什么内容。
注意:在使用这个代码之前一定要加摄像头权限,如果要访问相册,还要添加相册权限,添加权限的文章:https://xiaoshen.blog.csdn.net/article/details/141218299
1. 创建扫描二维码的 ScannerView
首先,需要创建一个自定义的 UIViewControllerRepresentable
来使用 AVCaptureSession
扫描二维码。
import SwiftUI
import AVFoundation
struct ScannerView: UIViewControllerRepresentable {
class Coordinator: NSObject, AVCaptureMetadataOutputObjectsDelegate {
var parent: ScannerView
init(parent: ScannerView) {
self.parent = parent
}
func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
if let metadataObject = metadataObjects.first {
guard let readableObject = metadataObject as? AVMetadataMachineReadableCodeObject else { return }
guard let stringValue = readableObject.stringValue else { return }
AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
parent.didFindCode(stringValue)
}
}
}
var didFindCode: (String) -> Void
func makeCoordinator() -> Coordinator {
return Coordinator(parent: self)
}
func makeUIViewController(context: Context) -> UIViewController {
let viewController = UIViewController()
let captureSession = AVCaptureSession()
guard let videoCaptureDevice = AVCaptureDevice.default(for: .video) else { return viewController }
let videoInput: AVCaptureDeviceInput
do {
videoInput = try AVCaptureDeviceInput(device: videoCaptureDevice)
} catch {
return viewController
}
if captureSession.canAddInput(videoInput) {
captureSession.addInput(videoInput)
} else {
return viewController
}
let metadataOutput = AVCaptureMetadataOutput()
if captureSession.canAddOutput(metadataOutput) {
captureSession.addOutput(metadataOutput)
metadataOutput.setMetadataObjectsDelegate(context.coordinator, queue: DispatchQueue.main)
metadataOutput.metadataObjectTypes = [.qr]
} else {
return viewController
}
let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
previewLayer.frame = viewController.view.layer.bounds
previewLayer.videoGravity = .resizeAspectFill
viewController.view.layer.addSublayer(previewLayer)
captureSession.startRunning()
return viewController
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {}
}
2. 创建 WebView
接下来,创建一个简单的 WebView
来加载网址。
import SwiftUI
import WebKit
struct WebView: UIViewRepresentable {
let url: URL
func makeUIView(context: Context) -> WKWebView {
return WKWebView()
}
func updateUIView(_ uiView: WKWebView, context: Context) {
let request = URLRequest(url: url)
uiView.load(request)
}
}
3. 主视图 ContentView
最后,创建主视图来处理扫描到的结果。如果结果是网址,则显示 WebView
,否则显示二维码的内容。
import SwiftUI
struct ContentView: View {
// 扫描二维码并加载网站
@State private var scannedCode: String? = nil
@State private var isShowingScanner = false
var body: some View {
VStack {
if let code = scannedCode, let url = URL(string: code), UIApplication.shared.canOpenURL(url) {
WebView(url: url)
} else if let code = scannedCode {
Text("Scanned Code: \(code)")
.padding()
} else {
Button(action: {
isShowingScanner = true
}) {
Text("Scan QR Code")
.font(.headline)
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
}
.sheet(isPresented: $isShowingScanner) {
ScannerView { code in
self.scannedCode = code
}
.edgesIgnoringSafeArea(.all)
}
}
}
}
}
@main
struct QRCodeScannerApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
最后的效果如下: