WEBサイト制作・アプリ開発・システム開発・ブランディングデザイン制作に関するご相談はお気軽にご連絡ください。
構想段階からじっくりとヒアリングし、お客様の課題にあわせたアプローチ手法でお客様の“欲しかった”をカタチにしてご提案いたします。
Blog スタッフブログ
iOS
システム開発
[iOS]UIBezierPathを利用した自由描画と拡大縮小
こんにちは、株式会社MIXシステム開発担当のBloomです。
早速本題のUIBezierPathを利用した自由描画と拡大縮小について、お仕事の中で得た知見を共有させていただきたいと思います。
UIBezierPathによる自由描画
では、早速自由描画を行うサンプルコードを掲載します。
import UIKit
class DrawView: UIView {
var drawImageView: UIImageView!
var lastPoint: CGPoint?
var firstPoint: CGPoint?
var drawGesture: UILongPressGestureRecognizer!
var freeLineBezierPath: UIBezierPath?
func setup() {
drawImageView = UIImageView(frame: self.bounds)
self.addSubview(drawImageView)
drawGesture = UILongPressGestureRecognizer(target: self, action: #selector(drawGesture(_:)))
drawGesture.minimumPressDuration = 0
self.addGestureRecognizer(drawGesture)
}
@objc func drawGesture(_ sender: AnyObject) {
guard let drawGesture = sender as? UILongPressGestureRecognizer else { return }
//タッチ座標を取得
let touchPoint = drawGesture.location(in: self.drawImageView)
switch drawGesture.state {
case .began:
lastPoint = touchPoint
freeLineBezierPath = UIBezierPath()
freeLineBezierPath?.lineCapStyle = .round
freeLineBezierPath?.lineWidth = 2.0
freeLineBezierPath?.move(to: lastPoint!)
case .changed:
drawImageView.image = drawGestureAtChanged(lastPoint: lastPoint!, newPoint: touchPoint)
lastPoint = touchPoint
case .ended: fallthrough
case .cancelled:
drawFreeLineFinished(bezierPath: freeLineBezierPath!)
lastPoint = nil
firstPoint = nil
break
default:
lastPoint = nil
firstPoint = nil
break
}
}
func drawGestureAtChanged(lastPoint: CGPoint, newPoint: CGPoint) -> UIImage
{
let middlePoint = CGPoint(x: (newPoint.x + lastPoint.x) / 2.0, y: (newPoint.y + lastPoint.y) / 2.0)
freeLineBezierPath?.addQuadCurve(to: middlePoint, controlPoint: lastPoint)
UIGraphicsBeginImageContextWithOptions(drawImageView.frame.size, false, 0.0)
let canvasRect = CGRect(x: 0, y: 0, width: self.frame.size.width, height: self.frame.size.height)
UIColor.red.setStroke()
freeLineBezierPath?.stroke()
let imageAfterDraw = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return imageAfterDraw!
}
func drawFreeLineFinished(bezierPath: UIBezierPath)
{}
}
class ViewController: UIViewController {
@IBOutlet weak var draw: DrawView!
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
draw.setup()
draw.layer.borderColor = UIColor.gray.cgColor
draw.layer.borderWidth = 1.0
}
}
実行結果
これでまず自由描画を行うことができました。このままこの描画結果を拡大・縮小してみましょう。UIImageへ変換してから拡大・縮小しても良いですが、そうすると線の太さまで変わってしまいます。今回はBezierPathのまま操作してみましょう。
import UIKit
class DrawResultView: UIView {
var freeLineBezierPath: UIBezierPath?
override func draw(_ rect: CGRect) {
guard let freeLineBezierPath = freeLineBezierPath else { return }
UIColor.red.setStroke()
freeLineBezierPath.lineWidth = 2.0
freeLineBezierPath.stroke()
}
}
protocol DrawViewDelegate {
func freeLineBezierPathCreated(path: UIBezierPath)
}
class DrawView: UIView {
func drawFreeLineFinished(bezierPath: UIBezierPath)
{
delegate?.freeLineBezierPathCreated(path: bezierPath)
}
}
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var result1: DrawResultView!
@IBOutlet weak var result2: DrawResultView!
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
draw.setup()
draw.delegate = self
draw.layer.borderColor = UIColor.gray.cgColor
draw.layer.borderWidth = 1.0
result1.layer.borderColor = UIColor.gray.cgColor
result1.layer.borderWidth = 1.0
result2.layer.borderColor = UIColor.gray.cgColor
result2.layer.borderWidth = 1.0
}
}
extension ViewController: DrawViewDelegate {
func freeLineBezierPathCreated(path: UIBezierPath) {
// 左上詰め
let clipRect = path.bounds
let translation = CGAffineTransform(translationX: -clipRect.origin.x, y: -clipRect.origin.y)
let translatedPath = path.copy() as! UIBezierPath
translatedPath.apply(translation)
let path1 = translatedPath.copy() as! UIBezierPath
let scalehalf = CGAffineTransform(scaleX: 0.5, y: 0.5)
path1.apply(scalehalf)
result1.freeLineBezierPath = path1
let path2 = translatedPath.copy() as! UIBezierPath
let scale2x = CGAffineTransform(scaleX: 2.0, y: 2.0)
path2.apply(scale2x)
result2.freeLineBezierPath = path2
result1.setNeedsDisplay()
result2.setNeedsDisplay()
}
}
実行結果
途中の処理でパスを左上詰めすることで画像として利用しやすくしています。あとはアフィン変換でパス位置を変換するだけで拡大・縮小を表現することができます。
これで線の幅を維持したまま描画結果の拡大縮小を行うことができました。良かったですね。