SwiftでレーダーのUIを作ってみた。[iOS、iPhoneアプリ]

# レーダーを作ってみたい
 映画「トップガンマーヴェリック」を見て、戦闘機に付いているようなレーダーを再現してみたいと思い作ってみました。

#### 完成図
<img alt="スクリーンショット 2022-06-01 11.12.28.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1070398/c0cd46c1-28b2-8cfb-c655-cb5e33e0f56e.png" width="300">


# カスタムクラスを作る
今回はRadarViewというカスタムクラスを作って実装しました。

```swift:RadarView.swift
func configureUI() {
    isUserInteractionEnabled = false
    backgroundColor = UIColor.black
    layer.cornerRadius = bounds.width / 2
    layer.borderColor = UIColor(red: 0.0, green: 0.9, blue: 0.0, alpha: 1.0).cgColor
    layer.borderWidth = 1.0
        
    drawCircle()
    drawCenterCircle()
    rotateRadarImageView()
    startAnimating()
    addRadarImageView()
}
```
この関数でUIを作成し、ソナーを回転させる処理も行なっています。
まず
```swift
private func drawCircle() {
    let width = bounds.width / 2
    for i in 1...3 {
            
        let grayPath = UIBezierPath()
        grayPath.addArc(withCenter: CGPoint(x: width, y: width), // 中心
                            radius: (width / 4) * CGFloat(i), // 半径
                            startAngle: 0, // 開始角度
                            endAngle: .pi * 2.0, // 終了角度
                            clockwise: true) // 時計回り
        let grayLayer = CAShapeLayer()
        grayLayer.path = grayPath.cgPath
        grayLayer.fillColor = UIColor.clear.cgColor // 塗り色
        grayLayer.strokeColor = UIColor(red: 0.0, green: 0.9, blue: 0.0, alpha: 1.0).cgColor // 線の色
        grayLayer.lineWidth = 1.0 // 線の幅
        layer.addSublayer(grayLayer)            
    }
}
```
この関数で
レーダー画面の中に三つ円を描きます。

以下の関数ではレーダー画面の真ん中に点を描いています。
```swift
private func drawCenterCircle() {
    let width = bounds.width / 2
    let grayPath = UIBezierPath()
    grayPath.addArc(withCenter: CGPoint(x: width, y: width), // 中心
                        radius: 2.0, // 半径
                        startAngle: 0, // 開始角度
                        endAngle: .pi * 2.0, // 終了角度
                        clockwise: true) // 時計回り
    let grayLayer = CAShapeLayer()
    grayLayer.path = grayPath.cgPath
    grayLayer.fillColor = UIColor(red: 0.0, green: 0.9, blue: 0.0, alpha: 1.0).cgColor // 塗り色
    grayLayer.strokeColor = UIColor(red: 0.0, green: 0.9, blue: 0.0, alpha: 1.0).cgColor // 線の色
    grayLayer.lineWidth = 0.5 // 線の幅
    layer.addSublayer(grayLayer)        
}
```
次に、背景が透明な以下の様な画像を用意します。
自分はGIMPで作成しました。
<img alt="haikei.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1070398/63caf4d2-e69e-adc1-049d-19a2ef397c85.png" width="300">
この画像をレーダー画面の上に載せて、回転させれば完成です。

以下にコード全文載せておくので確認していただけると幸いです。

```swift:RadarView.swift
//
//  RadarView.swift
//  radarApp
//
//

import Foundation
import UIKit


class RadarView : UIView {
    
    var screenView = UIView()
    var radarLine = UIView()
    
    let arcLayer = CAShapeLayer()
    
    var isAnimating : Bool = false
    
    var animationTimer: Timer?
    var blinkingTimer: Timer?
    
    let radarImageView = UIImageView(image: UIImage(named: "haikei"))
    
    let redDot = UIView()
    
    //無いといけない奴ら
    override init(frame: CGRect) {
        super.init(frame: frame)
//        configureUI()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
//        configureUI()
    }
    
    func configureUI() {
        
        isUserInteractionEnabled = false
        backgroundColor = UIColor.black
        layer.cornerRadius = bounds.width / 2
        layer.borderColor = UIColor(red: 0.0, green: 0.9, blue: 0.0, alpha: 1.0).cgColor
        layer.borderWidth = 1.0
        
        drawCircle()
        drawCenterCircle()
        rotateRadarImageView()
        startAnimating()
        addRadarImageView()

    }
    
    private func addRadarImageView() {
        
        addSubview(radarImageView)
        radarImageView.center = center
        radarImageView.frame = bounds
        radarImageView.contentMode = .scaleAspectFill
        radarImageView.isUserInteractionEnabled = false
        
    }
    
    private func drawRadarLine() {
        
        let width = bounds.width / 2
        radarLine.backgroundColor = UIColor(red: 0.0, green: 0.9, blue: 0.0, alpha: 1.0)
        addSubview(radarLine)
        radarLine.layer.anchorPoint = CGPoint(x: 0.0, y: 0.0)
        radarLine.frame = CGRect(x: width, y: width, width: width, height: 0.7)
        
    }
    
    private func drawCircle() {
        
        let width = bounds.width / 2
        for i in 1...3 {
            
            let grayPath = UIBezierPath()
            grayPath.addArc(withCenter: CGPoint(x: width, y: width), // 中心
                            radius: (width / 4) * CGFloat(i), // 半径
                            startAngle: 0, // 開始角度
                            endAngle: .pi * 2.0, // 終了角度
                            clockwise: true) // 時計回り
            let grayLayer = CAShapeLayer()
            grayLayer.path = grayPath.cgPath
            grayLayer.fillColor = UIColor.clear.cgColor // 塗り色
            grayLayer.strokeColor = UIColor(red: 0.0, green: 0.9, blue: 0.0, alpha: 1.0).cgColor // 線の色
            grayLayer.lineWidth = 1.0 // 線の幅
            layer.addSublayer(grayLayer)
            
        }

    }
    
    private func drawCenterCircle() {
        
        let width = bounds.width / 2
        let grayPath = UIBezierPath()
        grayPath.addArc(withCenter: CGPoint(x: width, y: width), // 中心
                        radius: 2.0, // 半径
                        startAngle: 0, // 開始角度
                        endAngle: .pi * 2.0, // 終了角度
                        clockwise: true) // 時計回り
        let grayLayer = CAShapeLayer()
        grayLayer.path = grayPath.cgPath
        grayLayer.fillColor = UIColor(red: 0.0, green: 0.9, blue: 0.0, alpha: 1.0).cgColor // 塗り色
        grayLayer.strokeColor = UIColor(red: 0.0, green: 0.9, blue: 0.0, alpha: 1.0).cgColor // 線の色
        grayLayer.lineWidth = 0.5 // 線の幅
        layer.addSublayer(grayLayer)
    }

    
    @objc func moveRadarLine() {
        
        let moveAnimation: CABasicAnimation = CABasicAnimation(keyPath: "transform.rotation")
        moveAnimation.isRemovedOnCompletion = false
        moveAnimation.fillMode = CAMediaTimingFillMode.forwards
        moveAnimation.fromValue = 0
        moveAnimation.toValue = Double.pi * 2
        moveAnimation.duration = 6.0
        radarLine.layer.add(moveAnimation, forKey: "animation")
    }
    
    @objc func rotateRadarImageView() {
        let moveAnimation: CABasicAnimation = CABasicAnimation(keyPath: "transform.rotation")
        moveAnimation.isRemovedOnCompletion = false
        moveAnimation.fillMode = CAMediaTimingFillMode.forwards
        moveAnimation.fromValue = 0
        moveAnimation.toValue = Double.pi * 2
        moveAnimation.duration = 6.0
        radarImageView.layer.add(moveAnimation, forKey: "animation")
    }
    
    @objc func startAnimating() {
        
        animationTimer = Timer.scheduledTimer(timeInterval: 6.0, target: self, selector: #selector(rotateRadarImageView), userInfo: nil, repeats: true)
        
    }
    
    @objc func stopAnimating() {
        
        self.animationTimer?.invalidate()
        
    }
    
}

```

最後に、このカスタムクラスをViewControllerの方で呼んであげればアプリの完成です。
ですが、addSublayerで追加されたCALayerはビューのサイズ変更に追従してくれないそうなので
```swift:ViewController
override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    radarView.configureUI()        
}
```
このようにViewControllerの方でUIをセッティングしてあげる必要があります。

https://qiita.com/Yaruki00/items/45a347fa78156a58d950


# 最後に
今回はレーダーのUIを作ってみました。
これを何かに使うわけではないですが、いつか本物のレーダーとして使えればと思います。

また、今回は説明がざっくり過ぎてごめんなさない(>_<)
説明することが多くなりそうでざっくりにしてしまいました。
わからないことがあれば質問お願いします。