Swift5 フォーカスViewの設定

こんにちは、アプリ開発エンジニアの　永田です。

Swift5~ フォーカスViewの設定を公開いたします。
自身の製作中のアプリで実装している一部です。

##動作
![Focus.gif](https://qiita-image-store.s3.amazonaws.com/0/131469/df54c816-0de1-f0b6-347c-39d80bdfa6ad.gif)



2018/6/30で審査待ちの状態です。GIF加工技術を実装しました。
####<a href="https://t.co/CNKqYgA28h?amp=1">TalkingRecord</a>
次はAR技術の実装に挑戦しようと思います。　

##<a href="https://github.com/TimOliver/TOCropViewController"> 参考サイト</a>
カメラ実装のオープンソースです。実装はobjecの実装です。
まるっきり同じようにSwiftで実装挑戦する価値も大きかったのですが、
オリジナルの部分を作りたく、構造を読み解かせていただきました。

##<a href="https://qiita.com/morizotter/items/423ccb29782e5b453d49"> 参考サイト</a>
こちらはViewを切り抜くロジックです。Swift2系だと思います。
動的な実装にして、Blurの部分も追加して、最新言語にアップデートしました。

##全体のソース
<a href="https://github.com/daisukenagata/SampleFocus"> SampleFocus</a>



#フォーカスのデザイン部分

CALayer()メソッドで、座標、サイズの設定
CAShapeLayer()メソッドでくり抜く部分を作り、
UIBezierPathメソッドでくり抜く座標、サイズを作ります。

四角のデザイン部分は左上の座標変換メソッドと同じ計算で実装をしています。

```
//角のUI設定 左上の例
girdViewLeftTopWidth.backgroundColor = UIColor.white
girdViewLeftTopWidth.frame = CGRect(x: vc.lineDashView.frame.origin.x, y: vc.lineDashView.frame.origin.y-wide, width: width, height: wide)
```

<img width="80" alt="スクリーンショット 2018-06-30 21.40.53.png" src="https://qiita-image-store.s3.amazonaws.com/0/131469/c48470af-1b6a-f1dd-887f-70479d259fcf.png">



このサンプルでは、SEに合わせていますが、
アプリ内では全デバイス対応の計算式を実装しています。
<img width="379" alt="スクリーンショット 2018-06-30 21.52.28.png" src="https://qiita-image-store.s3.amazonaws.com/0/131469/3c0504ec-f7f1-ff74-3689-a9cee336fa63.png">


```
 override func draw(_ rect: CGRect) {
        //フォーカス内の罫線の設定
        var i: CGFloat = screenHeight
        i -= screenHeight / 3
        lineColor.move(to: CGPoint(x:screenWidth, y: i*0.5))
        lineColor.addLine(to: CGPoint(x: 0 , y: i*0.5))
        UIColor.white.setStroke()
        lineColor.stroke()
        
        lineColor2.move(to: CGPoint(x:screenWidth, y: i))
        lineColor2.addLine(to: CGPoint(x: 0 , y: i))
        UIColor.white.setStroke()
        lineColor2.stroke()
        
        var i3: CGFloat = screenWidth
        i3 = screenWidth / 3
        lineColor3.move(to: CGPoint(x:i3, y: 0))
        lineColor3.addLine(to: CGPoint(x: i3 , y: screenHeight))
        UIColor.white.setStroke()
        lineColor3.stroke()
        
        lineColor4.move(to: CGPoint(x:i3*2, y: 0))
        lineColor4.addLine(to: CGPoint(x: i3*2 , y: screenHeight))
        UIColor.white.setStroke()
        lineColor4.stroke()
    }
```

#タップされた位置情報の認識

###CGPoint()のcontainsメソッド
シーケンスに、指定された述語を満たす要素が含まれているかどうかを示すブール値を返します。
containsメソッドで画面をタップした場合に、タップされた位置がどの部分か、判定する際に実装しています。


https://developer.apple.com/documentation/swift/array/2297359-contains

左上だったら左上の座標変換メソッドの実行というアーキテクチャです。

```
        //タップした領域を取得
        let frame = views.lineDashView.frame
        frame.insetBy(dx: -CGFloat(32), dy: -CGFloat(32))
        var topLeftRect: CGRect = frame
        topLeftRect.size.height = CGFloat(64)
        topLeftRect.size.width = CGFloat(64)

        if topLeftRect.contains(point) {
            return TouchFlag.touchTopLeft
        }

```


左上の座標変換メソッド
左上の場合は、幅と高さが可変しますが、originのpointは変更しません。<-値の中身は変更していますが、プログラムの変数は可変させていません。

```
 case .touchTopLeft:
            if endPoint.y != 0 {
                views.lineDashView.frame.origin.x = point.x
                views.lineDashView.frame.size.width = -point.x + endFrame.maxX
                views.lineDashView.frame.origin.y = point.y
                views.lineDashView.frame.size.height =  -point.y + endFrame.maxY
            } else {
                views.lineDashView.frame.origin.x =  point.x
                views.lineDashView.frame.size.width =  -point.x + views.imageView.frame.width
                views.lineDashView.frame.origin.y = point.y
                views.lineDashView.frame.size.height = -point.y + views.lineDashView.screenWidth + 60
                break
            }
        }

```
同様に右上、右下、横、上、下等、計算をしたメソッドをケースで分岐させているアーキテクチャです。


#フォーカスの計算後、ズーム機能

<img width="360" alt="スクリーンショット 2018-06-30 21.42.37.png" src="https://qiita-image-store.s3.amazonaws.com/0/131469/2c1520d0-c569-0bdf-3756-9c3514a494ac.png">

ViewController内のUIPanGestureRecognizerから指が離れた３秒後にanimetionSetメソッドを発行して、gestureObject.matchGround(views: self, imageView: imageView)メソッドでズーム機能を実装しています。

この実装のズーム幅も自作で設定しています。
最後に画面から指が離れた際のフォーカスの形で、そのパターンからズーム域を算出しています。


```
 @objc func animetionSet() {
        if timerFlag == false {
            gestureObject.matchGround(views: self, imageView: imageView)
            timerFlag = true
            timer.invalidate()
        }
    }
```


##まとめ
フォーカスを作るオリジナルロジックなので、一部を参考にする形でも良いかもしれません。
フォーカス内の罫線を全デバイスで合わせるロジックを抜いてみました。

プログラマーの方でしたら、その部分を自身のスキルで補えると思います。
仮に補えない場合は、その技術が足りないことになります。<-全デバイスに対応しました。18/7/22
シュミレーターですと罫線が見えないかも。

変更点　<-全デバイスに対応しました。

```
self.view.addSubview(self.lineDashView)を２番目から３番目にしました。

class ViewController
 DispatchQueue.main.async {
        self.cALayerView.effect(vc: self,bool: true, boolSecound: true)
        self.cALayerView.tori(vc: self, bool: false)
        self.view.addSubview(self.lineDashView)
        }
```

```
 タップ域がなくなるので、長さを調整しました。
 var screenHeight = UIScreen.main.bounds.height - 30
```

### 全デバイスに合う理由 <-全デバイスに対応しました。18/7/22
Viewのくり抜き後に罫線をaddSubするので、あとは汎用的に設定されるだけです。


最新の技術を実装する場合に、様々に補える技術があるのが前提です。

なので、何の技術が必要になるかを把握することが重要と考えています。
把握したうえで、勉強や研究するのも、実装をする近道かもしれません。

動画をGIF化する技術もARの進捗次第で公開していきたいと思います。