Swift - UIBezierPath でクーポン風画像のパスを描く
検証環境:
Xcode 12.4
Swift 5.3.2
UIBezierPath のよるお絵描きシリーズ。
今回は UIBezierPath を使って、以下のようなクーポン用画像としてよく使われる図形をを描いてみる。
Playground コードサンプル
import UIKit import PlaygroundSupport class CouponFrameView: UIView { private let lineWidth: CGFloat = 8 private let strokeColor = UIColor.systemYellow private lazy var borderLayer: CAShapeLayer = { let shapeLayer = CAShapeLayer() shapeLayer.lineWidth = lineWidth shapeLayer.strokeColor = strokeColor.cgColor shapeLayer.fillColor = UIColor.white.cgColor shapeLayer.lineJoin = .round // パスの接続部分を丸くする return shapeLayer }() override init(frame: CGRect) { super.init(frame: frame) configure() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func layoutSubviews() { super.layoutSubviews() borderLayer.path = makeCouoponPath(rect: bounds) } private func configure() { // layer.backgroundColor = UIColor.systemGray4.cgColor // 矩形確認用 layer.addSublayer(borderLayer) } private func makeCouoponPath(rect: CGRect) -> CGPath { let half = lineWidth / 2 let rect = rect.insetBy(dx: half, dy: half) // 矩形内に収まるように線幅の半分小さくする let cornerRadius: CGFloat = 32 let hollowRadius: CGFloat = 16 let path = UIBezierPath() path.move(to: CGPoint(x: rect.minX + cornerRadius, y: rect.minY)) // 1 path.addLine(to: CGPoint(x: rect.maxX - cornerRadius, y: rect.minY)) // 2 path.addArc(withCenter: CGPoint(x: rect.maxX - cornerRadius, y: rect.minY + cornerRadius), // 3 radius: cornerRadius, startAngle: -radian(90), endAngle: 0, clockwise: true) path.addLine(to: CGPoint(x: rect.maxX, y: rect.midY - hollowRadius)) // 4 path.addArc(withCenter: CGPoint(x: rect.maxX, y: rect.midY), // 5 radius: hollowRadius, startAngle: -radian(90), endAngle: radian(90), clockwise: false) path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY - cornerRadius)) // 6 path.addArc(withCenter: CGPoint(x: rect.maxX - cornerRadius, y: rect.maxY - cornerRadius), // 7 radius: cornerRadius, startAngle: 0, endAngle: radian(90), clockwise: true) path.addLine(to: CGPoint(x: rect.minX + cornerRadius, y: rect.maxY)) // 8 path.addArc(withCenter: CGPoint(x: rect.minX + cornerRadius, y: rect.maxY - cornerRadius), // 9 radius: cornerRadius, startAngle: radian(90), endAngle: radian(180), clockwise: true) path.addLine(to: CGPoint(x: rect.minX, y: rect.midY + hollowRadius)) // 10 path.addArc(withCenter: CGPoint(x: rect.minX, y: rect.midY), // 11 radius: hollowRadius, startAngle: radian(90), endAngle: -radian(90), clockwise: false) path.addLine(to: CGPoint(x: rect.minX, y: rect.minY + cornerRadius)) // 12 path.addArc(withCenter: CGPoint(x: rect.minX + cornerRadius, y: rect.minY + cornerRadius), // 13 radius: cornerRadius, startAngle: radian(180), endAngle: -radian(90), clockwise: true) path.close() return path.cgPath } private func radian(_ degree: CGFloat) -> CGFloat { return degree * .pi / 180.0 } } class MyViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .systemGray5 let couponFrameView = CouponFrameView() view.addSubview(couponFrameView) couponFrameView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ couponFrameView.centerXAnchor.constraint(equalTo: view.centerXAnchor), couponFrameView.centerYAnchor.constraint(equalTo: view.centerYAnchor), couponFrameView.widthAnchor.constraint(equalToConstant: 500), couponFrameView.heightAnchor.constraint(equalToConstant: 200), ]) } } PlaygroundPage.current.liveView = MyViewController()
パスの描画順序