UIBezierPathで半円をつくる

上と下を分割して半円をくっつけて正円を作るようなUIを実現するためにUIBezierPathを使って実装してみました。

最終的に以下のようなUIを作りました

ダメな例

Viewを2つくっつけて配置して一部だけ角丸にすれば良いと考えて試してみたんですが、横幅と縦幅違うので綺麗に丸まってくれませんでした。

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var topView: UIView!
    @IBOutlet weak var bottomView: UIView!
    override func viewDidLoad() {
        super.viewDidLoad()
        
        topView.layer.cornerRadius = 50
        topView.layer.maskedCorners =  [.layerMinXMinYCorner, .layerMaxXMinYCorner]
        
        bottomView.layer.cornerRadius = 50
        bottomView.layer.maskedCorners =  [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
        
    }
}

UIBezierPathを使った方法

import UIKit
import PlaygroundSupport

let view = UIView()
view.frame = CGRect(x: 0, y: 0, width: 400, height: 400)
view.backgroundColor = .white
PlaygroundPage.current.liveView = view

let topCircleLayer = CAShapeLayer()
topCircleLayer.frame = CGRect(x: 0, y: 0, width: 400, height: 400)
topCircleLayer.lineWidth = 1.0
topCircleLayer.strokeColor = UIColor.gray.cgColor
topCircleLayer.fillColor = UIColor.green.cgColor
view.layer.addSublayer(topCircleLayer)

let bottomCircleLayer = CAShapeLayer()
bottomCircleLayer.frame = CGRect(x: 0, y: 0, width: 400, height: 400)
bottomCircleLayer.lineWidth = 1.0
bottomCircleLayer.strokeColor = UIColor.gray.cgColor
bottomCircleLayer.fillColor = UIColor.cyan.cgColor
view.layer.addSublayer(bottomCircleLayer)


let center = CGPoint(x: 200, y: 200)
let radius = CGFloat(100)
let topStartAngle = CGFloat(1.0 * .pi)
let topEndAngle = CGFloat(0.0 * .pi)
let bottomStartAngle = CGFloat(0.0 * .pi)
let bottomEndAngle = CGFloat(1.0 * .pi)

let topArc = UIBezierPath(arcCenter: center,
                          radius: radius,
                          startAngle: topStartAngle,
                          endAngle: topEndAngle,
                          clockwise: true)

topCircleLayer.path = topArc.cgPath

let bottomArc = UIBezierPath(arcCenter: center,
                       radius: radius,
                       startAngle: bottomStartAngle,
                       endAngle: bottomEndAngle,
                       clockwise: true)
bottomCircleLayer.path = bottomArc.cgPath

補足

View一面にlayerを広げるように円のサイズを指定してしまうとボーダーが切れてしまうので、一面に広げる場合は円の半径を-1してボーダーが切れないようにする必要がありそうです。