Viewの一つの角を角丸にする
こういうViewを作りたく
- 高さ30の長方形
- 左下はサイズ24の角丸
Try CACornerMask (Failed)
iOS11から使えるようになったCACornerMaskを使ってみる。
let view = UIView(frame: CGRect(x: 0, y: 0, width: 60, height: 30)) view.backgroundColor = .cyan view.layer.cornerRadius = 24 view.layer.maskedCorners = [.layerMinXMaxYCorner] PlaygroundPage.current.liveView = view
こんな感じになった、ダメそう
Try UIBezierPath Part1(Failed)
CACornerMaskはどうせiOS11以上しか使えないから、あっさり諦めた。
次はUIBezierPathのいつものやり方でやってみる。
let view = UIView(frame: CGRect(x: 0, y: 0, width: 60, height: 30)) view.backgroundColor = .cyan let path = UIBezierPath(roundedRect: view.bounds, byRoundingCorners: .bottomLeft, cornerRadii: CGSize(width: 24, height: 24)) let mask = CAShapeLayer() mask.path = path.cgPath view.layer.mask = mask PlaygroundPage.current.liveView = view
今回はこんな感じ。良さそうでは?でもよくみたら、角丸のサイズは24じゃなさそう。
init(roundedRect:byRoundingCorners:cornerRadii:)のcornerRadiiを調べてみたら。どうやら長方形の幅か高さかの半分を超えた値を設定すると、自動的に半分にしてもらうらしい。要するに、高さ30の長方形だから、15の角丸にされた。
The radius of each corner oval. Values larger than half the rectangle’s width or height are clamped appropriately to half the width or height.
半分を超えたサイズの角丸はもう角丸じゃなくなったかな。
Try UIBezierPath Part2(Succeeded)
描くしかない…でも角丸はどうやって描くんだ😅
A -> B -> C -> ... -> E -> A 多分こう。
let view = UIView(frame: CGRect(x: 0, y: 0, width: 60, height: 30)) view.backgroundColor = .cyan // pathを描く let path = UIBezierPath() path.move(to: CGPoint(x: 0, y: view.bounds.height - 24)) path.addLine(to: .zero) path.addLine(to: CGPoint(x: view.bounds.width, y: 0)) path.addLine(to: CGPoint(x: view.bounds.width, y: view.bounds.height)) path.addLine(to: CGPoint(x: 24, y: view.bounds.height)) path.addArc(withCenter: CGPoint(x: 24, y: view.bounds.height - 24), radius: 24, startAngle: CGFloat.pi / 2, endAngle: -(CGFloat.pi / 4), clockwise: true) let mask = CAShapeLayer() mask.path = path.cgPath view.layer.mask = mask PlaygroundPage.current.liveView = view
いい感じになった。YEAH~
そのあとハマったところ
viewのサイズがruntimeで変わることがある場合(autolayoutで)、maskのサイズは自動的に変わらないから、viewのサイズが変わる度にmaskのサイズを指定し直す必要がある。
viewのサイズ変わること、外から検知できなさそうだから、subclass作るしかなさそう。
最終的に
class LeftBottomRoundCornerView: UIView { override init(frame: CGRect) { super.init(frame: frame) setupMask() } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) setupMask() } override var bounds: CGRect { didSet { setupMask() } } private func setupMask() { let path = UIBezierPath() path.move(to: CGPoint(x: 0, y: bounds.height - 24)) path.addLine(to: .zero) path.addLine(to: CGPoint(x: bounds.width, y: 0)) path.addLine(to: CGPoint(x: bounds.width, y: bounds.height)) path.addLine(to: CGPoint(x: 24, y: bounds.height)) path.addArc(withCenter: CGPoint(x: 24, y: bounds.height - 24), radius: 24, startAngle: CGFloat.pi / 2, endAngle: -(CGFloat.pi / 4), clockwise: true) let mask = CAShapeLayer() mask.path = path.cgPath layer.mask = mask } }