๐Ÿ make QRcode and QRcode Reader Tutorial

Overview

QRCodeReaderTutorial-iOS

๐Ÿ make QRcode and QRcode Reader Tutorial QR์ฝ”๋“œ์™€ ๋ฆฌ๋”๊ธฐ๋ฅผ ๋งŒ๋“œ๋Š” ์˜คํ”ˆ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์žˆ์ง€๋งŒ ์ž์ฒด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํ™œ์šฉํ•ด์„œ ๋งŒ๋“ค์–ด๋ณด๊ธฐ๋กœ ํ–ˆ๋‹ค.

๋ชฉ์ฐจ

์™„์„ฑ

Main.storyboard

ํ™”๋ฉด์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ตฌ์„ฑํ–ˆ๋‹ค. ์™ผ์ชฝ ์Šคํ† ๋ฆฌ๋ณด๋“œ๋ถ€ํ„ฐ 123 ์ˆœ์„œ.

  • ViewController : (1) ๋ฉ”์ธ ๋ทฐ์ปจํŠธ๋กค๋Ÿฌ
  • QRCodeModalViewController : (2) qr ์ฝ”๋“œ ๋ฅผ ๋งŒ๋“ค์–ด์„œ ๋ชจ๋‹ฌ์ฐฝ์œผ๋กœ ๋„์šฐ๊ธฐ
  • QRCodeReaderViewController : (3) qr ์ฝ”๋“œ ๋ฆฌ๋”๊ธฐ๋ฅผ ๋งŒ๋“ค์–ด์„œ ํ™”๋ฉด์ „ํ™˜


QR์ฝ”๋“œ ๋งŒ๋“ค๊ธฐ

CIQRCodeGenerator CIFilter ๋ฅผ ํ†ตํ•ด์„œ qr code ๋ฅผ ๋งŒ๋“ค ๊ฒƒ์ด๋‹ค.

๊ฐœ๋ฐœ์ž ๋ฌธ์„œ๋ฅผ ์‚ดํŽด๋ณด์ž

Apple Developer - Core Image Filter Reference(CIQRCodeGenerator)

์ด ํ•„ํ„ฐ๋Š” ๋‘๊ฐ€์ง€ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๊ฐ€์ง„๋‹ค.

  • inputMessage : QR์ฝ”๋“œ๋กœ ์ธ์ฝ”๋”ฉํ•  ๋ฐ์ดํ„ฐ์ž…๋‹ˆ๋‹ค. NSData ๊ฐœ์ฒด์ž…๋‹ˆ๋‹ค.
  • inputCorrectionLevel : ์˜ค๋ฅ˜ ์ˆ˜์ • ํ˜•์‹์„ ์ง€์ •ํ•˜๋Š” ๋‹จ์ผ ๋ฌธ์ž์ž…๋‹ˆ๋‹ค. NSString ๊ฐœ์ฒด์ž…๋‹ˆ๋‹ค. ๊ธฐ๋ณธ๊ฐ’์€ M.

String ๋˜๋Š” URL์—์„œ QR ์ฝ”๋“œ๋ฅผ ์ƒ์„ฑํ•˜๋ ค๋ฉด NSISOLatin1StringEncoding ๋ฌธ์ž์—ด ์ธ์ฝ”๋”ฉ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ด๋ฅผ NSData ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

inputCorrectionLevel ๋งค๊ฐœ๋ณ€์ˆ˜๋Š” ์˜ค๋ฅ˜์ˆ˜์ •์„ ์œ„ํ•ด์„œ ์ถœ๋ ฅ์ด๋ฏธ์ง€์— ์ธ์ฝ”๋”ฉ๋œ ์ถ”๊ฐ€ ๋ฐ์ดํ„ฐ์–‘์„ ์ œ์–ดํ•ฉ๋‹ˆ๋‹ค.

(QR์ฝ”๋“œ๋Š” ์ฝ”๋“œ์˜ ์˜ค์—ผ์ด๋‚˜ ์†์ƒ์—๋„ ์ฝ”๋“œ ์ž์ฒด์— ๋ฐ์ดํ„ฐ๋ฅผ ๋ณต์›ํ•˜๋Š” ๊ธฐ๋Šฅ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋ ˆ๋ฒจ์„ ์˜ฌ๋ฆฌ๋ฉด ์˜ค๋ฅ˜๋ณต์› ๋Šฅ๋ ฅ์€ ํ–ฅ์ƒ๋˜์ง€๋งŒ ๋ฐ์ดํ„ฐ๊ฐ€ ์ฆ๊ฐ€๋˜์–ด ์ฝ”๋“œ์˜ ํฌ๊ธฐ๊ฐ€ ์ปค์ง‘๋‹ˆ๋‹ค.)

  • L: 7%
  • M: 15%(๊ธฐ๋ณธ๊ฐ’)
  • Q: 25%
  • H: 30%

qr code ์˜ ์˜ค๋ฅ˜ ๋ณต์› ๊ธฐ๋Šฅ์— ๋Œ€ํ•ด์„œ๋Š” ์•„๋ž˜ ์‚ฌ์ดํŠธ๋ฅผ ์ฐธ๊ณ ํ•ด๋ณด์ž

์˜ค๋ฅ˜ ๋ณต์› ๊ธฐ๋Šฅ์— ๋Œ€ํ•ด

  • QRCodeView.swift : QR code ๋ฅผ ๋งŒ๋“œ๋Š” ํด๋ž˜์Šค
import Foundation
import UIKit

class QRCodeView: UIView {

    // โœ… CIQRCodeGenerator : QR code ์ƒ์„ฑ ํ•„ํ„ฐ๋ฅผ ์‹๋ณ„ํ•˜๊ธฐ ์œ„ํ•œ ์†์„ฑ.
    var filter = CIFilter(name: "CIQRCodeGenerator")

    // โœ… QRCode CIImage ๋ฅผ ๋งŒ๋“ค์–ด์„œ ์ถ”๊ฐ€ํ•  UIImageView.
    var imageView = UIImageView()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        addSubview(imageView)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        imageView.frame = bounds
    }

    // โœ… QRCode ์ด๋ฏธ์ง€๋ฅผ ๋งŒ๋“ค ๋•Œ ๋‹ค์–‘ํ•œ ์ƒ‰์œผ๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋„๋ก parameter ๋ฅผ ๋ฐ›์•˜๋‹ค.
    func generateCode(_ string: String, foregroundColor: UIColor = .black, backgroundColor: UIColor = .white) {
        
        // โœ… ์ฃผ์–ด์ง„ ์ธ์ฝ”๋”ฉ์„(=using) ์‚ฌ์šฉํ•ด์„œ NSData ๊ฐœ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
        guard let filter = filter, let data = string.data(using: .isoLatin1, allowLossyConversion: false) else {
            return
        }
        
        // โœ… ๋‘๊ฐ€์ง€ ํŒŒ๋ผ๋ฏธํ„ฐ ์„ค์ •.
        filter.setValue(data, forKey: "inputMessage")
        filter.setValue("M", forKey: "inputCorrectionLevel")

        // โœ… .outputImage : ํ•„ํ„ฐ์— ๊ตฌ์„ฑ๋œ ์ž‘์—…์„ ์บก์Šํ™”ํ•˜๋Š” CIImage ๊ฐœ์ฒด์ด๋‹ค. ์ฆ‰, ๊ฒฐ๊ณผ๋ฌผ
        guard let ciImage = filter.outputImage else {
            return
        }

        // โ—๏ธ ์ด๋ ‡๊ฒŒ ๋๋‚ด๋ฉด qr code ๊ฐ€ ์„ ๋ช…ํ•˜์ง€ ์•Š๊ฒŒ ๋‚˜์˜จ๋‹ค.
        //imageView.image = UIImage(ciImage: ciImage, scale: 2.0, orientation: .up)
        
        // โœ… ๋‹ค์Œ์€ ์ด๋ฏธ์ง€ ์„ ๋ช…ํ•˜๊ฒŒ ๋ณ€ํ™˜ํ•˜๋Š” ๊ณผ์ •์ด๋‹ค.
        // โœ… ์›๋ž˜ ์ด๋ฏธ์ง€์— affine transform(by ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์˜๋ฏธ.) ์„ ์ ์šฉํ•œ ์ƒˆ ์ด๋ฏธ์ง€๋ฅผ ๋ฐ˜ํ™˜. ์ด๋ฏธ์ง€์˜ ๋„“์ด์™€ ๋†’์ด๋ฅผ 10๋ฐฐ ์ฆ๊ฐ€์‹œํ‚ด.
        let transformed = ciImage.transformed(by: CGAffineTransform.init(scaleX: 10, y: 10))
        
        // โœ… ๋‹ค์Œ์€ QR code ์ƒ‰ ์ปค์Šคํ…€ ์„ค์ •ํ•˜๋Š” ๊ณผ์ •์ด๋‹ค. ํ•„ํ„ฐ ์ƒ์„ฑํ•˜๊ณ  ์ด๋ฏธ์ง€ ์ ์šฉ.
        // โœ… CIColorInvert : ์ƒ‰์ƒ์„ ๋ฐ˜์ „์‹œํ‚ค๊ธฐ ์œ„ํ•œ ํ•„ํ„ฐ์ด๋‹ค.
        let invertFilter = CIFilter(name: "CIColorInvert")
        invertFilter?.setValue(transformed, forKey: kCIInputImageKey)

        // โœ… CIMaskToAlpha : grayscale ๋กœ ๋ณ€ํ™˜๋œ ์ด๋ฏธ์ง€๋ฅผ alpha ๋กœ ๋งˆ์Šคํ‚น๋œ ํฐ์ƒ‰์ด๋ฏธ์ง€๋กœ ๋ณ€ํ™˜.
        let alphaFilter = CIFilter(name: "CIMaskToAlpha")
        alphaFilter?.setValue(invertFilter?.outputImage, forKey: kCIInputImageKey)
        
        // โœ… ๋ฐ›์€ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ imageView ์˜ ์†์„ฑ์„ ์„ค์ •. 
        if let ouputImage = alphaFilter?.outputImage {
            imageView.tintColor = foregroundColor
            imageView.backgroundColor = backgroundColor

            // โœ… withRenderingMode(.alwaysTemplate) : ์›๋ณธ ์ด๋ฏธ์ง€์˜ ์ปฌ๋Ÿฌ์ •๋ณด๊ฐ€ ์‚ฌ๋ผ์ง€๊ณ  ๋ถˆํˆฌ๋ช…ํ•œ ๋ถ€๋ถ„์„ tintColor ๋กœ ์„ค์ •.
            imageView.image = UIImage(ciImage: ouputImage, scale: 2.0, orientation: .up).withRenderingMode(.alwaysTemplate)
        } else {
            return
        }
    }
}
  • โœ… CIColorInvert : ์ƒ‰์ƒ์„ ๋ฐ˜์ „์‹œํ‚ค๊ธฐ ์œ„ํ•œ ํ•„ํ„ฐ์ด๋‹ค.

2-1

  • โœ… CIMaskToAlpha : grayscale ๋กœ ๋ณ€ํ™˜๋œ ์ด๋ฏธ์ง€๋ฅผ alpha ๋กœ ๋งˆ์Šคํ‚น๋œ ํฐ์ƒ‰์ด๋ฏธ์ง€๋กœ ๋ณ€ํ™˜.

2

  • QRCodeModalViewController.swift

QRCodeView ํด๋ž˜์Šค๋ฅผ ํ†ตํ•ด์„œ qr code ๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž.

class QRCodeModalViewController: UIViewController {

    @IBOutlet weak var qrcodeView: UIView!
    
    override func viewDidLoad() {
        super.viewDidLoad()

        let frame = CGRect(origin: .zero, size: qrcodeView.frame.size)
        let qrcode = QRCodeView(frame: frame)

        // โœ… ๋‚ด ๊นƒํ—ˆ๋ธŒ ์ฃผ์†Œ ๋ฌธ์ž์—ด์„ data ๋กœ ๊ฐ€์ง€๋Š” qr code.
        qrcode.generateCode("https://github.com/hyun99999", foregroundColor: #colorLiteral(red: 0.2745098174, green: 0.4862745106, blue: 0.1411764771, alpha: 1), backgroundColor: #colorLiteral(red: 0.721568644, green: 0.8862745166, blue: 0.5921568871, alpha: 1))
        
        qrcodeView.addSubview(qrcode)
    }
}

๊ฒฐ๊ณผ


์ฐธ๊ณ :

Swift QRcode make

iOS์—์„œ QR ์ฝ”๋“œ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•

Apple Developer - Core Image Filter Reference(CIFilter)

QR์ฝ”๋“œ Reader ๋งŒ๋“ค๊ธฐ

์นด๋ฉ”๋ผ ๊ถŒํ•œ ์–ป๊ธฐ

  • info.plist ์— ์ถ”๊ฐ€ํ•œ๋‹ค.

AVCaptureSession

๊ฐœ๋ฐœ์ž ๋ฌธ์„œ๋ฅผ ์‚ดํŽด๋ณด์ž

์บก์ฒ˜ ํ™œ๋™์„ ๊ด€๋ฆฌํ•˜๊ณ  ์ž…๋ ฅ ์žฅ์น˜์˜ ๋ฐ์ดํ„ฐ ํ๋ฆ„์„ ์กฐ์ •ํ•˜์—ฌ ์ถœ๋ ฅ์„ ์บก์ฒ˜ํ•˜๋Š” ๊ฐœ์ฒด์ž…๋‹ˆ๋‹ค.

overview

์‹ค์‹œ๊ฐ„ ์บก์ฒ˜๋ฅผ ์ˆ˜ํ–‰ํ•˜๋ ค๋ฉด AVCaptureSession ๊ฐœ์ฒด๋ฅผ ์ธ์Šคํ„ด์Šคํ™”ํ•˜๊ณ  ์ ์ ˆํ•œ inputs ๋ฐ outputs ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

startRunning() ์„ ํ˜ธ์ถœํ•˜์—ฌ input ์—์„œ output ์œผ๋กœ์˜ ๋ฐ์ดํ„ฐ ํ๋ฆ„์„ ์‹œ์ž‘ํ•˜๊ณ  stopRunning()์„ ํ˜ธ์ถœํ•˜์—ฌ ํ๋ฆ„์„ ์ค‘์ง€ํ•ฉ๋‹ˆ๋‹ค.

startRunning() ๋ฉ”์„œ๋“œ๋Š” ์‹œ๊ฐ„์ด ๊ฑธ๋ฆฌ๋Š” blocking call ์ด๋ฏ€๋กœ main queue ๊ฐ€ ์ฐจ๋‹จ๋˜์ง€ ์•Š๋„๋ก serial queue ์—์„œ ์„ธ์…˜ ์„ค์ •์„ ์ˆ˜ํ–‰ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. (UI ๋ฅผ ๋ฐ˜์‘์ ์œผ๋กœ ์œ ์ง€ํ•˜๊ฒŒ ํ•ด์ค€๋‹ค.)

seesionPreset ์†์„ฑ์„ ์‚ฌ์šฉํ•ด์„œ ์ถœ๋ ฅ์— ๋Œ€ํ•œ ํ’ˆ์งˆ ์ˆ˜์ค€, ๋น„ํŠธ ์ „์†ก๋ฅ  ๋˜๋Š” ๊ธฐํƒ€ ์„ค์ •์„ ์‚ฌ์šฉ์ž ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.(๊ธฐ๋ณธ๊ฐ’์€ high ์ž…๋‹ˆ๋‹ค.

  • ์ „๋ฐ˜์ ์ธ ์ˆœ์„œ์™€ metadata ์— ๋Œ€ํ•ด์„œ ๋จผ์ € ์•Œ์•„๋ณด์ž

1๏ธโƒฃ AVCaptureSession ๊ฐœ์ฒด๋ฅผ ์ธ์Šคํ„ด์Šคํ™”

2๏ธโƒฃ ์ ์ ˆํ•œ inputs ์„ค์ •

3๏ธโƒฃ ์ ์ ˆํ•œ ouputs ์„ค์ •

4๏ธโƒฃ startRunning() ๊ณผ stopRunning() ๋กœ ํ๋ฆ„ ํ†ต์ œ

metadata : ์‚ฌ์ง„ ํŒŒ์ผ output ์— ํฌํ•จํ•  metadata keys ์™€ values ์˜ ๋”•์…”๋„ˆ๋ฆฌ.

์ฆ‰, ์—ฌ๊ธฐ์„œ๋Š” photo ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์˜๋ฏธํ•œ๋‹ค.

  • QRCodeReaderViewController : QR์ฝ”๋“œ reader ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ์ฝ์€ ์ •๋ณด๋ฅผ ๋‹ค๋ฃจ๋Š” ๋ทฐ์ปจํŠธ๋กค๋Ÿฌ
import UIKit
import AVFoundation

class QRCodeReaderViewController: UIViewController {

    // 1๏ธโƒฃ ์‹ค์‹œ๊ฐ„ ์บก์ฒ˜๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•ด์„œ AVCaptureSession ๊ฐœ์ฒด๋ฅด ์ธ์Šคํ„ด์Šคํ™”.
    private let captureSession = AVCaptureSession()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        basicSetting()
    }
    
}
extension QRCodeReaderViewController {
    
    private func basicSetting() {
        
        // โœ… AVCaptureDevice : capture sessions ์— ๋Œ€ํ•œ ์ž…๋ ฅ(audio or video)๊ณผ ํ•˜๋“œ์›จ์–ด๋ณ„ ์บก์ฒ˜ ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ์ œ์–ด๋ฅผ ์ œ๊ณตํ•˜๋Š” ์žฅ์น˜.
        // โœ… ์ฆ‰, ์บก์ฒ˜ํ•  ๋ฐฉ์‹์„ ์ •ํ•˜๋Š” ์ฝ”๋“œ.
        guard let captureDevice = AVCaptureDevice.default(for: AVMediaType.video) else {
        
        // โœ… ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ์—์„œ๋Š” ์นด๋ฉ”๋ผ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ์—์„œ ์‹คํ–‰ํ•˜๋ฉด ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.          
        fatalError("No video device found")
        }
        do {

            // 2๏ธโƒฃ ์ ์ ˆํ•œ inputs ์„ค์ •
            // โœ… AVCaptureDeviceInput : capture device ์—์„œ capture session ์œผ๋กœ media ๋ฅผ ์ œ๊ณตํ•˜๋Š” capture input. 
            // โœ… ์ฆ‰, ํŠน์ • device ๋ฅผ ์‚ฌ์šฉํ•ด์„œ input ๋ฅผ ์ดˆ๊ธฐํ™”.
            let input = try AVCaptureDeviceInput(device: captureDevice)

            // โœ… session ์— ์ฃผ์–ด์ง„ input ๋ฅผ ์ถ”๊ฐ€.
            captureSession.addInput(input)
            
            // 3๏ธโƒฃ ์ ์ ˆํ•œ outputs ์„ค์ •
            // โœ… AVCaptureMetadataOutput : capture session ์— ์˜ํ•ด์„œ ์ƒ์„ฑ๋œ ์‹œ๊ฐ„์ œํ•œ metadata ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ capture output.
            // โœ… ์ฆ‰, ์˜์ƒ์œผ๋กœ ์ดฌ์˜ํ•˜๋ฉด์„œ ์ง€์†์ ์œผ๋กœ ์ƒ์„ฑ๋˜๋Š” metadata ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” output ์ด๋ผ๋Š” ๋ง.
            let output = AVCaptureMetadataOutput()

            // โœ… session ์— ์ฃผ์–ด์ง„ output ๋ฅผ ์ถ”๊ฐ€.
            captureSession.addOutput(output)

            // โœ… AVCaptureMetadataOutputObjectsDelegate ํฌ๋กœํ† ์ฝœ์„ ์ฑ„ํƒํ•˜๋Š” delegate ์™€ dispatch queue ๋ฅผ ์„ค์ •ํ•œ๋‹ค.
            // โœ… queue : delegate ์˜ ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰ํ•  ํ์ด๋‹ค. ์ด ํ๋Š” metadata ๊ฐ€ ๋ฐ›์€ ์ˆœ์„œ๋Œ€๋กœ ์ „๋‹ฌ๋˜๋ ค๋ฉด ๋ฐ˜๋“œ์‹œ serial queue(์ง๋ ฌํ) ์—ฌ์•ผ ํ•œ๋‹ค.     
            output.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
            
            // โœ… ๋ฆฌ๋”๊ธฐ๊ฐ€ ์ธ์‹ํ•  ์ˆ˜ ์žˆ๋Š” ์ฝ”๋“œ ํƒ€์ž…์„ ์ •ํ•œ๋‹ค. ์ด ํ”„๋กœ์ ํŠธ์˜ ๊ฒฝ์šฐ qr.
            output.metadataObjectTypes = [AVMetadataObject.ObjectType.qr]

            // โœ… ์นด๋ฉ”๋ผ ์˜์ƒ์ด ๋‚˜์˜ค๋Š” layer ์™€ + ๋ชจ์–‘ ๊ฐ€์ด๋“œ ๋ผ์ธ์„ ๋ทฐ์— ์ถ”๊ฐ€ํ•˜๋Š” ํ•จ์ˆ˜ ํ˜ธ์ถœ.           
            setVideoLayer()
            setGuideCrossLineView()
            
            // 4๏ธโƒฃ startRunning() ๊ณผ stopRunning() ๋กœ ํ๋ฆ„ ํ†ต์ œ
            // โœ… input ์—์„œ output ์œผ๋กœ์˜ ๋ฐ์ดํ„ฐ ํ๋ฆ„์„ ์‹œ์ž‘
            captureSession.startRunning()
        }
        catch {
            print("error")
        }
    }

    // โœ… ์นด๋ฉ”๋ผ ์˜์ƒ์ด ๋‚˜์˜ค๋Š” layer ๋ฅผ ๋ทฐ์— ์ถ”๊ฐ€
    private func setVideoLayer() {
        // ์˜์ƒ์„ ๋‹ด์„ ๊ณต๊ฐ„.
        let videoLayer = AVCaptureVideoPreviewLayer(session: captureSession)
        // ์นด๋ฉ”๋ผ์˜ ํฌ๊ธฐ ์ง€์ •
        videoLayer.frame = view.layer.bounds
        // ์นด๋ฉ”๋ผ์˜ ๋น„์œจ์ง€์ •
        videoLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
        view.layer.addSublayer(videoLayer)
    }

    // โœ… + ๋ชจ์–‘ ๊ฐ€์ด๋“œ๋ผ์ธ์„ ๋ทฐ์— ์ถ”๊ฐ€
    private func setGuideCrossLineView() {
        let guideCrossLine = UIImageView()
        guideCrossLine.image = UIImage(systemName: "plus")
        guideCrossLine.tintColor = .green
        guideCrossLine.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(guideCrossLine)
        NSLayoutConstraint.activate([
            guideCrossLine.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            guideCrossLine.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            guideCrossLine.widthAnchor.constraint(equalToConstant: 30),
            guideCrossLine.heightAnchor.constraint(equalToConstant: 30)
        ])
    }
}

// โœ… metadata capture ouput ์—์„œ ์ƒ์„ฑ๋œ metatdata ๋ฅผ ์ˆ˜์‹ ํ•˜๋Š” ๋ฉ”์„œ๋“œ.
// โœ… ์ด ํ”„๋กœํ† ์ฝœ์€ ์œ„์—์„œ์ฒ˜๋Ÿผ AVCaptureMetadataOutput object ๊ฐ€ ์ฑ„ํƒํ•ด์•ผ๋งŒ ํ•œ๋‹ค. ๋‹จ์ผ ๋ฉ”์„œ๋“œ๊ฐ€ ์žˆ๊ณ  ์˜ต์…˜์ด๋‹ค.
// โœ… ์ด ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด capture metadata ouput object ๊ฐ€ connection ์„ ํ†ตํ•ด์„œ ๊ด€๋ จ๋œ metadata objects ๋ฅผ ์ˆ˜์‹ ํ•  ๋•Œ ์‘๋‹ตํ•  ์ˆ˜ ์žˆ๋‹ค.(์•„๋ž˜ ๋ฉ”์„œ๋“œ์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ํ†ตํ•ด ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๋‹ค.)
// โœ… ์ฆ‰, ์ด ํ”„๋กœํ† ์ฝœ์„ ํ†ตํ•ด์„œ metadata ๋ฅผ ์ˆ˜์‹ ํ•ด์„œ ๋ฐ˜์‘ํ•  ์ˆ˜ ์žˆ๋‹ค.
extension QRCodeReaderViewController: AVCaptureMetadataOutputObjectsDelegate {

    // โœ… caputure output object ๊ฐ€ ์ƒˆ๋กœ์šด metadata objects ๋ฅผ ๋ณด๋ƒˆ์„ ๋•Œ ์•Œ๋ฆฐ๋‹ค.
    func metadataOutput(_ captureOutput: AVCaptureMetadataOutput,
                        didOutput metadataObjects: [AVMetadataObject],
                        from connection: AVCaptureConnection) {

        // โœ… metadataObjects : ์ƒˆ๋กœ ๋‚ด๋ณด๋‚ธ AVMetadataObject ์ธ์Šคํ„ด์Šค ๋ฐฐ์—ด์ด๋‹ค.
        if let metadataObject = metadataObjects.first {

            // โœ… AVMetadataObject ๋Š” ์ถ”์ƒ ํด๋ž˜์Šค์ด๋ฏ€๋กœ ์ด ๋ฐฐ์—ด์˜ object ๋Š” ํ•ญ์ƒ ๊ตฌ์ฒด์ ์ธ ํ•˜์œ„ ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค์—ฌ์•ผ ํ•œ๋‹ค.
            // โœ… AVMetadataObject ๋ฅผ ์ง์ ‘ ์„œ๋ธŒํด๋ž˜์‹ฑํ•ด์„  ์•ˆ๋œ๋‹ค. ๋Œ€์‹  AVFroundation ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ ์ œ๊ณตํ•˜๋Š” ์ •์˜๋œ ํ•˜์œ„ ํด๋ž˜์Šค ์ค‘ ํ•˜๋‚˜๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.
            // โœ… AVMetadataMachineReadableCodeObject : ๋ฐ”์ฝ”๋“œ์˜ ๊ธฐ๋Šฅ์„ ์ •์˜ํ•˜๋Š” AVMetadataObject ์˜ ๊ตฌ์ฒด์ ์ธ ํ•˜์œ„ ํด๋ž˜์Šค. ์ธ์Šคํ„ด์Šค๋Š” ์ด๋ฏธ์ง€์—์„œ ๊ฐ์ง€๋œ ํŒ๋… ๊ฐ€๋Šฅํ•œ ๋ฐ”์ฝ”๋“œ์˜ ๊ธฐ๋Šฅ๊ณผ payload ๋ฅผ ์„ค๋ช…ํ•˜๋Š” immutable object ๋ฅผ ๋‚˜ํƒ€๋‚ธ๋‹ค.
            // โœ… (์ฐธ๊ณ ๋กœ ์ด์™ธ์—๋„ AVMetadataFaceObject ๋ผ๋Š” ๊ฐ์ง€๋œ ๋‹จ์ผ ์–ผ๊ตด์˜ ๊ธฐ๋Šฅ์„ ์ •์˜ํ•˜๋Š” subclass ๋„ ์žˆ๋‹ค.)
            guard let readableObject = metadataObject as? AVMetadataMachineReadableCodeObject, let stringValue = readableObject.stringValue else {
                return
            }

            // โœ… qr์ฝ”๋“œ๊ฐ€ ๊ฐ€์ง„ ๋ฌธ์ž์—ด์ด URL ํ˜•ํƒœ๋ฅผ ๋ˆ๋‹ค๋ฉด ์ถœ๋ ฅ.(์•„๋ฌด๋Ÿฐ qr์ฝ”๋“œ๋‚˜ ์ฐ๋Š”๋‹ค๊ณ  ์ถœ๋ ฅ์‹œํ‚ค๋ฉด ์•ˆ๋˜๋‹ˆ๊นŒ ์—ฌ๊ธฐ์„œ ๋ถ„๊ธฐ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ. )
            if stringValue.hasPrefix("http://") || stringValue.hasPrefix("https://")  {
                print(stringValue)

                // 4๏ธโƒฃ startRunning() ๊ณผ stopRunning() ๋กœ ํ๋ฆ„ ํ†ต์ œ
                // โœ… input ์—์„œ output ์œผ๋กœ์˜ ํ๋ฆ„ ์ค‘์ง€
                self.captureSession.stopRunning()
                self.dismiss(animated: true, completion: nil)
            }
        }
    }
}

๊ฒฐ๊ณผ

์ƒ๊ฐํ•ด๋ณผ ์ 

์•ž์„œ QR์ฝ”๋“œ ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์–ด์„œ ์‚ฌ์šฉํ–ˆ๋˜ ๋ฐ˜๋ฉด QR์ฝ”๋“œ reader ๋Š” ์ •๋ณด๋ฅผ ์ฝ๊ณ  ๋‹ค๋ค„์•ผ ํ•˜๋Š” ๊ธฐ๋Šฅ์— ์ข€ ๋” ์ง‘์ค‘ํ•ด๋ณด๊ธฐ ์œ„ํ•ด์„œ ํŽธ์˜์ƒ ๋ทฐ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ๋‹ค๋ฃจ์–ด ์ฃผ์—ˆ๋‹ค.

์•„๋ž˜์˜ ๋ธ”๋กœ๊ทธ๋ฅผ ์ฐธ๊ณ ํ•˜๋ฉด reader ๋ฅผ ํด๋ž˜์Šค๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค. ํด๋ž˜์Šค๋กœ ๋งŒ๋“ค์—ˆ์„ ๋•Œ ์šฐ๋ฆฌ๊ฐ€ ๊ณ ๋ คํ•ด์•ผํ•˜๋Š” ์ ์€ ๋ทฐ์ปจํŠธ๋กค๋Ÿฌ์— QR์ฝ”๋“œ๋ฅผ ์ฝ์€ ๊ฒฐ๊ณผ๋ฅผ ์ „๋‹ฌํ•  delegate ๋งŒ๋“ค๊ณ  ๋ทฐ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ๊ทธ delegate ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ถ€๋ถ„์„ ๊ตฌํ˜„ํ•ด์•ผ ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

QR, Barcode ๋ฆฌ๋”๊ธฐ ๋งŒ๋“ค๊ธฐ - ๋€”๋€”(swieeft)์˜ ๊ฐœ๋ฐœ์ƒˆ๋ฐœ๊ธฐ

์ฐธ๊ณ  :

[iOS] QR Code Scanner ๋งŒ๋“ค๊ธฐ - AvFoundation ์ด์šฉ

IOS) QRCode ๋ฆฌ๋”๊ธฐ ๋งŒ๋“ค๊ธฐ


์›ํ•˜๋Š” ์˜์—ญ์—์„œ๋งŒ QR์ฝ”๋“œ ์ฝ๊ธฐ

์šฐ๋ฆฌ๊ฐ€ ์ ‘ํ•˜๋Š” QR์ฝ”๋“œ ๋ฆฌ๋”๊ธฐ๋Š” ํŠน์ • ์˜์—ญ์•ˆ์—์„œ QR์ฝ”๋“œ๊ฐ€ ์ฝํžŒ๋‹ค. ๊ทธ ์ด์œ ๋กœ ๋‚˜๋Š” ๋งŽ์€ QR์ฝ”๋“œ๊ฐ€ ์นด๋ฉ”๋ผ์— ์žกํžˆ์ง€ ์•Š๋„๋ก ์‚ฌ์šฉ์ž๋ฅผ ์œ ๋„ํ•จ๊ณผ ๋™์‹œ์— ์‚ฌ์šฉ์ž๊ฐ€ ๋ณธ์ธ์˜ QR์ฝ”๋“œ๋ฅผ ์ž…๋ ฅํ•˜๋Š” ์ธ์‹์„ ์ฃผ๊ธฐ ์œ„ํ•จ์ด๋ผ๊ณ  ์ƒ๊ฐ์ด ๋“ ๋‹ค. ํ•ธ๋“œํฐ์„ ์ œ๋Œ€๋กœ ๊ฐ€์ ธ๋‹ค ๋Œ€์ง€๋„ ์•Š์•˜๋Š”๋ฐ ์กฐ๊ธˆ์ด๋ผ๋„ ์นด๋ฉ”๋ผ์— ๋…ธ์ถœ๋œ QR์ฝ”๋“œ๊ฐ€ ๋ฐ”๋กœ ์ฝํžŒ๋‹ค๋ฉด ์‚ฌ์šฉ์ž๋Š” ๋ถ„๋ช… ๋‹นํ™ฉ์Šค๋Ÿฌ์šธ ๊ฒƒ์ด๋‹ค.

๊ทธ๋ž˜์„œ ์ด๋Ÿฐ ์ด์œ ๋กœ QR์ฝ”๋“œ ์˜์—ญ์„ ์ง€์ •ํ•˜๋Š” ๊ฒƒ์„ ํ•ด๋ณด๋ ค ํ•œ๋‹ค.

  • AVCaputureMetadataOutput ์˜ rectOfInterest ์†์„ฑ์„ ์ด์šฉํ•˜๋ฉด ๋œ๋‹ค. ๋จผ์ € ๊ฐœ๋ฐœ์ž ๋ฌธ์„œ๋ฅผ ์‚ดํŽด๋ณด์ž.

rectOfInterest

  • ์‹œ๊ฐ์  metatdata ์˜ ๊ฒ€์ƒ‰ ์˜์—ญ์„ ์ œํ•œํ•˜๊ธฐ ์œ„ํ•œ ์‚ฌ๊ฐํ˜•์„ ๊ฒฐ์ •ํ•˜๋Š” CGRect ๊ฐ’์ด๋‹ค.
  • ์‚ฌ๊ฐํ˜•์˜ origin(์›์ ) ์€ ์™ผ์ชฝ ์ƒ๋‹จ์ด๊ณ  metatdat ๋ฅผ ์ œ๊ณตํ•˜๋Š” ์žฅ์น˜์˜ ์ขŒํ‘œ๊ณต๊ฐ„์„ ๊ธฐ์ค€์œผ๋กœ ํ•œ๋‹ค.
  • rectangle of interest ๋ฅผ ์ง€์ •ํ•˜๋ฉด ํŠน์ • ์œ ํ˜•์˜ metadata ์— ๋Œ€ํ•œ ๊ฐ์ง€ ๋Šฅ๋ ฅ์ด ํ–ฅ์ƒ๋  ์ˆ˜ ์žˆ๋‹ค. rectOfInterest ๋ฅผ ๊ฐ€๋กœ์ง€๋ฅด๋Š” metadata ojects ์˜ bounds ๊ฐ€ ์•„๋‹ˆ๋ฉด ๋ฆฌํ„ดํ•˜์ง€ ์•Š๋Š”๋‹ค.(์ฆ‰ ์‚ฌ๊ฐํ˜•์•ˆ์— ์žˆ์ง€ ์•Š์œผ๋ฉด ์ธ์‹ํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๋ง.)
  • ๊ธฐ๋ณธ๊ฐ’์€ (0.0, 0.0, 1.0, 1.0) ์ด๋‹ค.

๊ธฐ๋ณธ๊ฐ’์ด....? ์กฐ๊ธˆ ์ด์ƒํ•˜๋‹ค. width ์™€ height ์— ๊ธฐ๋ณธ๊ฐ’์ด 1.0 ์ด๋ผ.. ์‹ค์ œ๋กœ ๊ฐ’์„ ๋‚˜์ค‘์— ์ถœ๋ ฅํ•ด๋ณด๋ฉด ์•Œ๊ฒ ์ง€๋งŒ 0~100% ๊นŒ์ง€ ๋น„์œจ์˜ ํ”„๋ ˆ์ž„์„ ์ฑ„์šด๋‹ค๊ณ  ํ•œ๋‹ค. ๊ทธ๋ž˜์„œ ์•„๋ฌด๋Ÿฐ ์†์„ฑ์„ค์ • ์—†๋Š” ๊ธฐ๋ณธ๊ฐ’์—์„œ๋Š” ์ „์ฒด ํ”„๋ ˆ์ž„์—์„œ qr์ฝ”๋“œ๋ฅผ ์ธ์‹ํ–ˆ์—ˆ๋‹ค.

๊ทธ๋ž˜์„œ ์šฐ๋ฆฌ๋Š” metadataOutputRectConverted ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋น„์œจ๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.

metadataOutputRectConverted

  • preview layer(์—ฌ๊ธฐ์„œ๋Š” AVCaptureVideoPreviewLayer ๊ฐœ์ฒด์— ํ•ด๋‹น) ์˜ metadata ouputs ์— ์‚ฌ์šฉ๋˜๋Š” ์ขŒํ‘œ๊ณ„์˜ ์‚ฌ๊ฐํ˜•์œผ๋กœ ๋ณ€ํ™˜.

+ ๋ชจ์–‘์ด์—ˆ๋˜ ๊ฐ€์ด๋“œ ๋ผ์ธ์—์„œ ์ •์‚ฌ๊ฐํ˜•์˜ ๊ฐ€์ด๋“œ๋ผ์ธ์„ ๋ณด์—ฌ์ฃผ๊ณ  ๊ฑฐ๊ธฐ์„œ๋งŒ QR์ฝ”๋“œ๊ฐ€ ์ฝํžˆ๋„๋ก ํ•ด๋ณด์ž.

๊ฐ€์ด๋“œ๋ผ์ธ์„ ๊ทธ๋ฆฌ๋Š” ์ž‘์—…์€ ์ตœ๊ทผ์— ์ฝ์—ˆ๋˜ zedd ๋‹˜์˜ UIBezierPath ์— ๊ด€ํ•œ ๊ธ€์„ ์ฝ์–ด๋ดค๊ณ  ํ™œ์šฉํ•ด๋ณด๊ธฐ๋กœ ํ–ˆ๋‹ค.

iOS ) UIBezierPath (3) - Triangle, Circle

์™„์„ฑ

์ฝ”๋“œ

  • QRCodeReaderViewController.swift

โœ… ์˜์—ญ ์ œํ•œ

CGRect{ // ์˜์ƒ์„ ๋‹ด์„ ๊ณต๊ฐ„. let videoLayer = AVCaptureVideoPreviewLayer(session: captureSession) //์นด๋ฉ”๋ผ์˜ ํฌ๊ธฐ ์ง€์ • videoLayer.frame = view.layer.bounds //์นด๋ฉ”๋ผ์˜ ๋น„์œจ์ง€์ • videoLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill view.layer.addSublayer(videoLayer) return videoLayer.metadataOutputRectConverted(fromLayerRect: rectOfInterest) } private func setGuideCrossLineView(rectOfInterest: CGRect) { //... } } ">
extension QRCodeReaderViewController {
    
    private func basicSetting() {
        
        guard let captureDevice = AVCaptureDevice.default(for: AVMediaType.video) else {
            fatalError("No video device found")
        }
        do {
            // โœ… ์ œํ•œํ•˜๊ณ  ์‹ถ์€ ์˜์—ญ
            let rectOfInterest = CGRect(x: (UIScreen.main.bounds.width - 200) / 2 , y: (UIScreen.main.bounds.height - 200) / 2, width: 200, height: 200)
            
            let input = try AVCaptureDeviceInput(device: captureDevice)
            captureSession.addInput(input)
            
            let output = AVCaptureMetadataOutput()
            captureSession.addOutput(output)
            
            output.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
            output.metadataObjectTypes = [AVMetadataObject.ObjectType.qr]
            
            // โœ… preview layer ์—์„œ์˜ ์˜์—ญ ๋ณ€ํ™˜๊ฐ’์„ ๋ฆฌํ„ด๋ฐ›์•„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ ๊ธฐ์กด ์ฝ”๋“œ์—์„œ ์ˆ˜์ •ํ•ด์ฃผ์—ˆ๋‹ค.
            let rectConverted = setVideoLayer(rectOfInterest: rectOfInterest)

            // โœ… rectOfInterest ๋ฅผ ์„ค์ •(=์ œํ•œ์˜์—ญ ์„ค์ • ์™„๋ฃŒ)
            output.rectOfInterest = rectConverted

            // โœ… ์ •์‚ฌ๊ฐํ˜• ๊ฐ€์ด๋“œ ๋ผ์ธ ์ถ”๊ฐ€
            setGuideCrossLineView(rectOfInterest: rectOfInterest)
            
            captureSession.startRunning()
        }
        catch {
            print("error")
        }
    }
    
    // โœ… preview layer ์—์„œ์˜ ์˜์—ญ ๋ณ€ํ™˜๊ฐ’์„ ๋ฆฌํ„ด๋ฐ›์•„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ ๊ธฐ์กด ์ฝ”๋“œ์—์„œ ์ˆ˜์ •ํ•ด์ฃผ์—ˆ๋‹ค.
    private func setVideoLayer(rectOfInterest: CGRect) -> CGRect{
        // ์˜์ƒ์„ ๋‹ด์„ ๊ณต๊ฐ„.
        let videoLayer = AVCaptureVideoPreviewLayer(session: captureSession)
        //์นด๋ฉ”๋ผ์˜ ํฌ๊ธฐ ์ง€์ •
        videoLayer.frame = view.layer.bounds
        //์นด๋ฉ”๋ผ์˜ ๋น„์œจ์ง€์ •
        videoLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
        view.layer.addSublayer(videoLayer)
        
        return videoLayer.metadataOutputRectConverted(fromLayerRect: rectOfInterest)
    }

    private func setGuideCrossLineView(rectOfInterest: CGRect) { 
    //...
    }
}

โœ… ๊ฐ€์ด๋“œ๋ผ์ธ ์ถ”๊ฐ€

extension QRCodeReaderViewController {

    // ...

    private func setGuideCrossLineView(rectOfInterest: CGRect) {

        // ์ƒ๋žต๋œ ์ฝ”๋“œ๋Š” + ๋ชจ์–‘ ๊ฐ€์ด๋“œ๋ผ์ธ ์ถ”๊ฐ€ ์ฝ”๋“œ์ด๋‹ค.
        // ...
        
        let cornerLength: CGFloat = 20
        let cornerLineWidth: CGFloat = 5
        
        // โœ… ๊ฐ€์ด๋“œ๋ผ์ธ์˜ ๊ฐ ๋ชจ์„œ๋ฆฌ point
        let upperLeftPoint = CGPoint(x: rectOfInterest.minX, y: rectOfInterest.minY)
        let upperRightPoint = CGPoint(x: rectOfInterest.maxX, y: rectOfInterest.minY)
        let lowerRightPoint = CGPoint(x: rectOfInterest.maxX, y: rectOfInterest.maxY)
        let lowerLeftPoint = CGPoint(x: rectOfInterest.minX, y: rectOfInterest.maxY)
        
        // โœ… ๊ฐ ๋ชจ์„œ๋ฆฌ๋ฅผ ์ค‘์‹ฌ์œผ๋กœ ํ•œ Edge๋ฅผ ๊ทธ๋ฆผ.
        let upperLeftCorner = UIBezierPath()
        upperLeftCorner.lineWidth = cornerLineWidth
        upperLeftCorner.move(to: CGPoint(x: upperLeftPoint.x + cornerLength, y: upperLeftPoint.y))
        upperLeftCorner.addLine(to: CGPoint(x: upperLeftPoint.x, y: upperLeftPoint.y))
        upperLeftCorner.addLine(to: CGPoint(x: upperLeftPoint.x, y: upperLeftPoint.y + cornerLength))
        
        let upperRightCorner = UIBezierPath()
        upperRightCorner.lineWidth = cornerLineWidth
        upperRightCorner.move(to: CGPoint(x: upperRightPoint.x - cornerLength, y: upperRightPoint.y))
        upperRightCorner.addLine(to: CGPoint(x: upperRightPoint.x, y: upperRightPoint.y))
        upperRightCorner.addLine(to: CGPoint(x: upperRightPoint.x, y: upperRightPoint.y + cornerLength))
        
        let lowerRightCorner = UIBezierPath()
        lowerRightCorner.lineWidth = cornerLineWidth
        lowerRightCorner.move(to: CGPoint(x: lowerRightPoint.x, y: lowerRightPoint.y - cornerLength))
        lowerRightCorner.addLine(to: CGPoint(x: lowerRightPoint.x, y: lowerRightPoint.y))
        lowerRightCorner.addLine(to: CGPoint(x: lowerRightPoint.x - cornerLength, y: lowerRightPoint.y))
        
        let lowerLeftCorner = UIBezierPath()
        lowerLeftCorner.lineWidth = cornerLineWidth
        lowerLeftCorner.move(to: CGPoint(x: lowerLeftPoint.x + cornerLength, y: lowerLeftPoint.y))
        lowerLeftCorner.addLine(to: CGPoint(x: lowerLeftPoint.x, y: lowerLeftPoint.y))
        lowerLeftCorner.addLine(to: CGPoint(x: lowerLeftPoint.x, y: lowerLeftPoint.y - cornerLength))
        
        upperLeftCorner.stroke()
        upperRightCorner.stroke()
        lowerRightCorner.stroke()
        lowerLeftCorner.stroke()
        
        // โœ… layer ์— ์ถ”๊ฐ€
        
        let upperLeftCornerLayer = CAShapeLayer()
        upperLeftCornerLayer.path = upperLeftCorner.cgPath
        upperLeftCornerLayer.strokeColor = UIColor.black.withAlphaComponent(0.5).cgColor
        upperLeftCornerLayer.fillColor = UIColor.clear.cgColor
        upperLeftCornerLayer.lineWidth = cornerLineWidth
        
        let upperRightCornerLayer = CAShapeLayer()
        upperRightCornerLayer.path = upperRightCorner.cgPath
        upperRightCornerLayer.strokeColor = UIColor.black.withAlphaComponent(0.5).cgColor
        upperRightCornerLayer.fillColor = UIColor.clear.cgColor
        upperRightCornerLayer.lineWidth = cornerLineWidth
        
        let lowerRightCornerLayer = CAShapeLayer()
        lowerRightCornerLayer.path = lowerRightCorner.cgPath
        lowerRightCornerLayer.strokeColor = UIColor.black.withAlphaComponent(0.5).cgColor
        lowerRightCornerLayer.fillColor = UIColor.clear.cgColor
        lowerRightCornerLayer.lineWidth = cornerLineWidth
 
        let lowerLeftCornerLayer = CAShapeLayer()
        lowerLeftCornerLayer.path = lowerLeftCorner.cgPath
        lowerLeftCornerLayer.strokeColor = UIColor.black.withAlphaComponent(0.5).cgColor
        lowerLeftCornerLayer.fillColor = UIColor.clear.cgColor
        lowerLeftCornerLayer.lineWidth = cornerLineWidth
        
        view.layer.addSublayer(upperLeftCornerLayer)
        view.layer.addSublayer(upperRightCornerLayer)
        view.layer.addSublayer(lowerRightCornerLayer)
        view.layer.addSublayer(lowerLeftCornerLayer)
    }
}

**์ฐธ๊ณ  : ** [iOS] Swift QRCode ์ฝ๊ธฐ

You might also like...
A QRCode generator written in Swift.
A QRCode generator written in Swift.

QRCode ๐Ÿ”ณ A QRCode generator written in Swift. Overview Create a new QRCode representing a URL, a string or arbitrary data. The following examples all

QRCode Scanner using Apple in build Vision framework.

๐Ÿ”ฒ iOS13 DKQRReader Example A quick example showing how to use the Vision system-framework in iOS 13 and Swift 5. Prerequisites Xcode 13 and later Get

1D and 2D barcodes reader and generators for iOS 8 with delightful controls. Now Swift.

RSBarcodes, now in Swift. RSBarcodes allows you to read 1D and 2D barcodes using the metadata scanning capabilities introduced with iOS 7 and generate

Paul Hudson redesigned and remastered the original MoonshotApp. And so I followed the tutorial
Paul Hudson redesigned and remastered the original MoonshotApp. And so I followed the tutorial

Moonshot App for iOS. parsing some json files, using generics, and creating multi page app. Application provides simple informations from the Apollo m

Free and open source manga reader for iOS and iPadOS.

Aidoku A free and open source manga reading application for iOS and iPadOS. Features Ad free Robust WASM source system Online reading through external

A credit card reader and parser for iOS Using Native Vision/VisionKit

card-reader-ios A credit card reader and parser for iOS Using Native Vision/VisionKit May-14-2021.00-43-17.mp4 Instructions Hold camera up to a card a

Tutorial GraphQL + Node Express + MySQL, and sample for Android / iOS client

GraphQL-tutorial Tutorial for GraphQL + Node Express + MySQL, and sample for Android / iOS client Blog NeoRoman's GraphQL-tutorial (Korean) Materials

RSS reader specific for Swift and SwiftUI based feeds.

Swift News Jam! Getting Started The idea behind this app was to provide the SwiftUI community a single app to curate the numerous RSS feeds that have

๐Ÿ“š A Swift ePub reader and parser framework for iOS.
๐Ÿ“š A Swift ePub reader and parser framework for iOS.

FolioReaderKit is an ePub reader and parser framework for iOS written in Swift. Features ePub 2 and ePub 3 support Custom Fonts Custom Text Size Text

Folowed HackingWithSwift.com tutorial and created a simple flag guessing game.
Folowed HackingWithSwift.com tutorial and created a simple flag guessing game.

GuessTheFlag Getting more and more into SwiftUI. Created app as a simple flag guessing (knowing) game. Paul said once in a video... if you can do it b

RSS reader for macOS and iOS.

NetNewsWire Itโ€™s a free and open-source feed reader for macOS and iOS. It supports RSS, Atom, JSON Feed, and RSS-in-JSON formats. More info: https://n

A free and open source xkcd comic reader for iOS.
A free and open source xkcd comic reader for iOS.

A free, ad-free, open-source, native, and universal xkcd.com reader for iOS. Download it from the app store now! Architecture AFNetworking for network

Awesome tool for create tutorial walkthrough or coach tour
Awesome tool for create tutorial walkthrough or coach tour

AwesomeSpotlightView is a nice and simple library for iOS written on Swift 5. It's highly customizable and easy-to-use tool. Works perfectly for tutor

๐Ÿคจ Apple Push Notification service tutorial
๐Ÿคจ Apple Push Notification service tutorial

APNsTutorial-iOS ๐Ÿคจ Apple Push Notification service tutorial ๋‹จ์ˆœํžˆ ์ˆœ์„œ๋ฅผ ๋”ฐ๋ผ์„œ ๊ฐ€๋ฉด ๋  ์ค„ ์•Œ์•˜๋Š”๋ฐ ์•Œ์•„์•ผํ•  ๊ฒƒ๋„ ์žˆ์—ˆ๊ณ  ๊ฒฝ์šฐ์— ๋”ฐ๋ผ์„œ ์š”๊ตฌํ•˜๋Š” ํŒŒ์ผ๋„ ๋‹ฌ๋ž๋‹ค. ๊ทธ๋Ÿฌ๋‹ˆ ์ฒœ์ฒœํžˆ ์ฝ์–ด์ฃผ์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค. ๋จผ์ € ์–ด๋–ค ์„œ๋ฒ„ ํ™˜๊ฒฝ

This is a sample project that supplements the tutorial written over at my blog on 'Building a music recognization app in SwiftUI with ShazamKit'
This is a sample project that supplements the tutorial written over at my blog on 'Building a music recognization app in SwiftUI with ShazamKit'

Shazam-Kit-Tutorial This is a sample project that supplements the tutorial written over at my blog on 'Building a music recognization app in SwiftUI w

๐ŸฅบPinterest Layout Tutorial
๐ŸฅบPinterest Layout Tutorial

PinterestTutorial-iOS ๐Ÿฅบ Pinterest Layout Tutorial ์ด๋ฏธ์ง€ ํฌ๊ธฐ์— ๋”ฐ๋ผ์„œ ๋™์ ์œผ๋กœ ์…€์˜ ๋ ˆ์ด์•„์›ƒ์„ ์„ค์ •ํ•˜๋Š” ํ•€ํ„ฐ๋ ˆ์ŠคํŠธ ๋ ˆ์ด์•„์›ƒ ๊ตฌํ˜„ํ•ด ๋ณด์•˜๋‹ค. ์™„์„ฑ ์ฝ”๋“œ UICollectionViewDelegateFlowLayout ์˜ ์„œ๋ธŒํด๋ž˜์Šค

๐Ÿ‘ทโ€โ™€๏ธ login tutorial using Kakao iOS SDK
๐Ÿ‘ทโ€โ™€๏ธ login tutorial using Kakao iOS SDK

KakaoLoginTutorial-iOS ๐Ÿ‘ทโ€โ™€๏ธ login tutorial using Kakao iOS SDK ๋ชฉ์ฐจ ๋””์ž์ธ ๊ฐ€์ด๋“œ ์„ค์ •๋‹จ๊ณ„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋“ฑ๋ก CocoaPods ํ†ตํ•ด ๋ชจ๋“ˆ ์„ค์น˜ Info.plist ์„ค์ • ์ดˆ๊ธฐํ™” ์‹œ์ž‘ํ•˜๊ธฐ ์ „ ์นด์นด์˜คํ†ก์œผ๋กœ ๋กœ๊ทธ์ธ ๊ธฐ๋ณธ ์›น

An iOS widget-based HN reader
An iOS widget-based HN reader

Benuse, an iOS widget-based HN reader Why did you build this app? There already exist some great native Hacker News clients for iOS. I would recommend

A Hacker News reader in Swift
A Hacker News reader in Swift

SwiftHN A Hacker News reader in Swift using the best features of both the language and iOS 8 latest API (well, that's the end goal) SwiftHN is now ava

Owner
Hyungyu Kim
๋‚˜๋Š” ํ˜„๊ทœ๋‹ค
Hyungyu Kim
QRCode Scanner using Apple in build Vision framework.

?? iOS13 DKQRReader Example A quick example showing how to use the Vision system-framework in iOS 13 and Swift 5. Prerequisites Xcode 13 and later Get

Dineshkumar Kandasamy 3 Jun 22, 2022
GraphQL-GitHub-Reader - Simple sample project that demonstrates how to connect with GitHub's API using GraphQL + Apollo

GHPViewer A beatiful way to see your GitHub Profile Installation The project is splitted in two folders: GHPViewer: Contains the iOS Application relat

Andrรฉs Pesate 1 Jan 2, 2022
:mag_right: A simple and beautiful barcode scanner.

Description BarcodeScanner is a simple and beautiful wrapper around the camera with barcode capturing functionality and a great user experience. Barco

HyperRedink 1.6k Dec 30, 2022
A better way to operate QR Code in Swift, support iOS, macOS, watchOS and tvOS.

EFQRCode is a lightweight, pure-Swift library for generating stylized QRCode images with watermark or icon, and for recognizing QRCode from images, in

EFPrefix 4.3k Dec 29, 2022
๐Ÿ”BarcodeScanner โ€“ simple & easy application that helps you to scan both EAN8 and EAN13 barcodes.

?? Simple & easy application that helps you to scan both EAN8 and EAN13 barcodes.

Tamerlan Satualdypov 16 Apr 22, 2022
A better way to operate QR Code in Swift, support iOS, macOS, watchOS and tvOS.

EFQRCode is a lightweight, pure-Swift library for generating stylized QRCode images with watermark or icon, and for recognizing QRCode from images, in

EFPrefix 4.3k Dec 29, 2022
A Simple iOS QR code scanner that allows users to enable their camera and local Photo Library accesses to scan the contents of the input QR codes.

iOS QR Code Scanner A Simple iOS QR code scanner using Swift UI. Jump to: ContentView.swift screenshot 1.1.5 NOTE: be aware of the new horizontal line

Krystal Zhang 0 Jan 1, 2023
Simple QRCode reader in Swift

QRCodeReader.swift is a simple code reader (initially only QRCode) for iOS in Swift. It is based on the AVFoundation framework from Apple in order to

Yannick Loriot 1.3k Dec 22, 2022
Simple QRCode reader in Swift

QRCodeReader.swift is a simple code reader (initially only QRCode) for iOS in Swift. It is based on the AVFoundation framework from Apple in order to

Yannick Loriot 1.3k Jan 5, 2023
This app is a sample app that recognizes specific voice commands such as "make it red", "make it blue", "make it green", and "make it black" and change the background color of the view in the frame.

VoiceOperationSample This app is a sample app that recognizes specific voice commands such as "make it red", "make it blue", "make it green", and "mak

Takuya Aso 3 Dec 3, 2021