your3i’s blog

iOSエンジニア。頑張る⚉

QRコードスキャンぽいUIの作り方

QRコードスキャンぽいUIとは

こういう感じの、dimming viewがあって、真ん中空いてて、四角にちょっとそれぽいものがあるUIである。

f:id:your3i:20181025214016p:plain

ちょっと難しいポイント

  • 真ん中の空いてる正方形
  • 角丸じゃないけど、4つのあれ

作る

画面のビューの構成

自分がこういう画面を作ったとき、こういう構成にした⬇︎
今回の内容は、mask viewの作り方を説明する。

f:id:your3i:20181025215827p:plain

ScanQRMaskView(この命名はただの例)

ScanQRMaskViewというxibファイルを作成

その中の真ん中にcaptureViewという正方形のviewを置いた。そしてよしなにサイズを決めて、真ん中に配置するようにconstraintをはる。

captureViewを置く目的は、ただ正方形のサイズと位置を把握しやすくにするためである。

ScanQRMaskView.swift で簡単なスタイルセットアップをやる

captureViewのIBOutletを作って、そして以下のように設定。

    private func setupAppearance() {
        // とりあえず背景色全部.clearにする
        backgroundColor = .clear
        captureView.backgroundColor = .clear
        // captureViewに白いボーダーをつける(デザイン通り)
        captureView.layer.borderWidth = 0.5
        captureView.layer.borderColor = UIColor.white.cgColor
    }

drawRectで描画

drawRectでdimming背景、透明の正方形、4つのあれを描画。

    override func draw(_ rect: CGRect) {
        super.draw(rect)
         // dimming背景を描画
        let dimmingColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.6)
        dimmingColor.setFill()
        UIRectFill(bounds)

         // 透明の正方形を描画
        let captureRect = captureView.frame
        UIColor.clear.setFill()
        UIRectFill(captureRect)

         // 角にある4つのあれをUIBezierPathを使って描画
        let cornerLength: CGFloat = 20.0
        let cornerWidth: CGFloat = 4.0
        let fillColor = UIColor.blue
        let path = UIBezierPath()

         // 左上
        let leftTop = CGPoint(x: captureRect.origin.x, y: captureRect.origin.y)
        path.move(to: leftTop)
        path.addLine(to: CGPoint(x: leftTop.x, y: leftTop.y + cornerLength))
        path.addLine(to: CGPoint(x: leftTop.x + cornerWidth, y: leftTop.y + cornerLength))
        path.addLine(to: CGPoint(x: leftTop.x + cornerWidth, y: leftTop.y + cornerWidth))
        path.addLine(to: CGPoint(x: leftTop.x + cornerLength, y: leftTop.y + cornerWidth))
        path.addLine(to: CGPoint(x: leftTop.x + cornerLength, y: leftTop.y))
        path.close()
        fillColor.setFill()
        path.fill()
         
         // 右上
        let rightTop = CGPoint(x: captureRect.origin.x + captureRect.width, y: captureRect.origin.y)
        path.move(to: rightTop)
        path.addLine(to: CGPoint(x: rightTop.x - cornerLength, y: rightTop.y))
        path.addLine(to: CGPoint(x: rightTop.x - cornerLength, y: rightTop.y + cornerWidth))
        path.addLine(to: CGPoint(x: rightTop.x - cornerWidth, y: rightTop.y + cornerWidth))
        path.addLine(to: CGPoint(x: rightTop.x - cornerWidth, y: rightTop.y + cornerLength))
        path.addLine(to: CGPoint(x: rightTop.x, y: rightTop.y + cornerLength))
        path.close()
        fillColor.setFill()
        path.fill()
         
         // 右下
        let rightBottom = CGPoint(x: captureRect.origin.x + captureRect.width, y: captureRect.origin.y + captureRect.height)
        path.move(to: rightBottom)
        path.addLine(to: CGPoint(x: rightBottom.x, y: rightBottom.y - cornerLength))
        path.addLine(to: CGPoint(x: rightBottom.x - cornerWidth, y: rightBottom.y - cornerLength))
        path.addLine(to: CGPoint(x: rightBottom.x - cornerWidth, y: rightBottom.y - cornerWidth))
        path.addLine(to: CGPoint(x: rightBottom.x - cornerLength, y: rightBottom.y - cornerWidth))
        path.addLine(to: CGPoint(x: rightBottom.x - cornerLength, y: rightBottom.y))
        path.close()
        fillColor.setFill()
        path.fill()
         
         // 左下
        let leftBottom = CGPoint(x: captureRect.origin.x, y: captureRect.origin.y + captureRect.height)
        path.move(to: leftBottom)
        path.addLine(to: CGPoint(x: leftBottom.x, y: leftBottom.y - cornerLength))
        path.addLine(to: CGPoint(x: leftBottom.x + cornerWidth, y: leftBottom.y - cornerLength))
        path.addLine(to: CGPoint(x: leftBottom.x + cornerWidth, y: leftBottom.y - cornerWidth))
        path.addLine(to: CGPoint(x: leftBottom.x + cornerLength, y: leftBottom.y - cornerWidth))
        path.addLine(to: CGPoint(x: leftBottom.x + cornerLength, y: leftBottom.y))
        path.close()
        fillColor.setFill()
        path.fill()
    }

おわりに

captureViewを用意するのほかの理由は:

  • AutoLayoutで各画面サイズと回転を簡単に対応できる
  • 簡単に、QRCodeスキャンのcontroller側に、スキャンできる領域を伝えることができる
  • 正方形のレイアウトのコード書きたくない