レーダーを作ってみたい
映画「トップガンマーヴェリック」を見て、戦闘機に付いているようなレーダーを再現してみたいと思い作ってみました。
完成図
カスタムクラスを作る
今回はRadarViewというカスタムクラスを作って実装しました。
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を作成し、ソナーを回転させる処理も行なっています。
まず
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)
}
次に、背景が透明な以下の様な画像を用意します。
自分はGIMPで作成しました。
この画像をレーダー画面の上に載せて、回転させれば完成です。
以下にコード全文載せておくので確認していただけると幸いです。
//
// 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はビューのサイズ変更に追従してくれないそうなので
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
radarView.configureUI()
}
このようにViewControllerの方でUIをセッティングしてあげる必要があります。
最後に
今回はレーダーのUIを作ってみました。
これを何かに使うわけではないですが、いつか本物のレーダーとして使えればと思います。
また、今回は説明がざっくり過ぎてごめんなさない(>_<)
説明することが多くなりそうでざっくりにしてしまいました。
わからないことがあれば質問お願いします。