Как нарисовать пользовательскую фигуру с дугой в центре, используя UIBezierPath

Я пытаюсь создать пользовательскую форму вида с дугой в центре, но раньше я не работал с этими инструментами.

Я создаю форму, какую хочу, но не элегантную, как нужно.

Что я делаю неправильно?

Я чувствую, где делаю ошибки, но не могу их исправить. Как работать с этими инструментами я использую этот туториал - https://ayusinghi96.medium.com/draw-custom-shapes-and-views-with-uiberzierpath-ios-1737f5cb975

Результат, который нужен: введите здесь описание изображения

Мой результат: введите здесь описание изображения

Мой код:

import Foundation
import UIKit

/// Custom card view class
///
class CardView : UIView
{
  // init the view with a rectangular frame
  override init(frame: CGRect)
  {
    super.init(frame: frame)
    backgroundColor = UIColor.clear
  }
  // init the view by deserialisation
  required init?(coder aDecoder: NSCoder)
  {
    super.init(coder: aDecoder)
    backgroundColor = UIColor.clear
  }
  /// override the draw(_:) to draw your own view
  ///
  /// Default implementation - `rectangular view`
  ///
  override func draw(_ rect: CGRect)
  {
    let padding: CGFloat = 5.0
    let centerButtonHeight: CGFloat = 50
    let f = CGFloat(centerButtonHeight / 2.0) + padding

    let halfW = frame.width/2.0
    let r = CGFloat(11)
    
    // Card view corner radius
    let cardRadius = CGFloat(7)
    // Button slot arc radius
    let buttonSlotRadius = CGFloat(30)
    
    // Card view frame dimensions
    let viewSize = self.bounds.size
    // Effective height of the view
    let effectiveViewHeight = viewSize.height - buttonSlotRadius
    // Get a path to define and traverse
    let path = UIBezierPath()
    // Shift origin to left corner of top straight line
    path.move(to: CGPoint(x: cardRadius, y: 0))
    
    // top line
    path.addLine(to: CGPoint(x: viewSize.width - cardRadius, y: 0))
    
    path.addLine(to: CGPoint(x: halfW-f-(r/2.0), y: 0))
//
    path.addQuadCurve(to: CGPoint(x: halfW-f, y: (r/2.0)), controlPoint: CGPoint(x: halfW-f, y: 0))
//
    path.addArc(withCenter: CGPoint(x: halfW, y: (r/7.0)), radius: f, startAngle: .pi, endAngle: 0, clockwise: false)
//
    path.addQuadCurve(to: CGPoint(x: halfW+f+(r/5.5), y: 0), controlPoint: CGPoint(x: halfW+f, y: 0))
    
    // top-right corner arc
    path.addArc(
      withCenter: CGPoint(
        x: viewSize.width - cardRadius,
        y: cardRadius
      ),
      radius: cardRadius,
      startAngle: CGFloat(Double.pi * 3 / 2),
      endAngle: CGFloat(0),
      clockwise: true
    )
    // right line
    path.addLine(
      to: CGPoint(x: viewSize.width, y: effectiveViewHeight)
    )
    
    
    // bottom-right corner arc
    path.addArc(
      withCenter: CGPoint(
        x: viewSize.width - cardRadius,
        y: effectiveViewHeight - cardRadius
      ),
      radius: cardRadius,
      startAngle: CGFloat(0),
      endAngle: CGFloat(Double.pi / 2),
      clockwise: true
    )
    // right half of bottom line
    path.addLine(
      to: CGPoint(x: viewSize.width / 4 * 3, y: effectiveViewHeight)
    )

    // left half of bottom line
    path.addLine(
      to: CGPoint(x: cardRadius, y: effectiveViewHeight)
    )
    // bottom-left corner arc
    path.addArc(
      withCenter: CGPoint(
        x: cardRadius,
        y: effectiveViewHeight - cardRadius
      ),
      radius: cardRadius,
      startAngle: CGFloat(Double.pi / 2),
      endAngle: CGFloat(Double.pi),
      clockwise: true
    )
    // left line
    path.addLine(to: CGPoint(x: 0, y: cardRadius))
    // top-left corner arc
    path.addArc(
      withCenter: CGPoint(x: cardRadius, y: cardRadius),
      radius: cardRadius,
      startAngle: CGFloat(Double.pi),
      endAngle: CGFloat(Double.pi / 2 * 3),
      clockwise: true
    )
    
    // close path join to origin
    path.close()
    // Set the background color of the view
    UIColor.init(red: 0.118, green: 0.118, blue: 0.15, alpha: 1).set()
    path.fill()
  }
}

person Ice    schedule 27.11.2020    source источник
comment
Спасибо, что приняли мой ответ, буду признателен за голосование!   -  person Witek Bobrowski    schedule 28.11.2020
comment
Ваш код рисует форму, но не копирует форму, как в примере выше, радиус вашего центра круга ниже, чем нужно.   -  person Ice    schedule 16.12.2020
comment
я имею в виду, что это просто вопрос параметров, мне удалось исправить ошибки в вашем примере кода. Ваша фигура не была похожа на ту, что на картинке. Все, что вам нужно сделать, это настроить параметры, и все готово.   -  person Witek Bobrowski    schedule 16.12.2020
comment
Я пытался настроить параметры, но не смог это исправить, через несколько дней я переписал код и получил хороший результат - stackoverflow.com/questions/65090595/   -  person Ice    schedule 16.12.2020


Ответы (1)


Вам нужно изменить эти две строки:

path.addArc(withCenter: CGPoint(x: halfW, y: (r/7.0)), radius: f, startAngle: .pi, endAngle: 0, clockwise: false)
//
path.addQuadCurve(to: CGPoint(x: halfW+f+(r/5.5), y: 0), controlPoint: CGPoint(x: halfW+f, y: 0))

to:

path.addArc(withCenter: CGPoint(x: halfW, y: (r/2.0)), radius: f, startAngle: .pi, endAngle: 0, clockwise: false)
//
path.addQuadCurve(to: CGPoint(x: halfW+f+(r/2), y: 0), controlPoint: CGPoint(x: halfW+f, y: 0))

А вот исправленный и немного переработанный код внутри func draw(_ rect: CGRect)

override func draw(_ rect: CGRect) {
    let padding: CGFloat = 5
    let centerButtonHeight: CGFloat = 50
    let f: CGFloat = centerButtonHeight / 2 + padding

    let halfW = frame.width / 2
    let r: CGFloat = 11

    // Card view corner radius
    let cardRadius: CGFloat = 7
    // Button slot arc radius
    let buttonSlotRadius: CGFloat = 30

    // Card view frame dimensions
    let viewSize = self.bounds.size
    // Effective height of the view
    let effectiveViewHeight = viewSize.height - buttonSlotRadius
    // Get a path to define and traverse
    let path = UIBezierPath()
    // Shift origin to left corner of top straight line
    path.move(to: CGPoint(x: cardRadius, y: 0))

    // top line
    path.addLine(to: CGPoint(x: viewSize.width - cardRadius, y: 0))

    path.addLine(to: CGPoint(x: halfW - f - (r / 2), y: 0))

    path.addQuadCurve(
        to: CGPoint(x: halfW - f, y: (r / 2)),
        controlPoint: CGPoint(x: halfW - f, y: 0)
    )

    path.addArc(
        withCenter: CGPoint(x: halfW, y: (r / 2)),
        radius: f, startAngle: .pi, endAngle: 0, clockwise: false
    )
    //
    path.addQuadCurve(
        to: CGPoint(x: halfW + f + (r / 2), y: 0),
        controlPoint: CGPoint(x: halfW + f, y: 0)
    )

    // top-right corner arc
    path.addArc(
        withCenter: CGPoint(
            x: viewSize.width - cardRadius,
            y: cardRadius
        ),
        radius: cardRadius, startAngle: .pi * 3 / 2, endAngle: 0, clockwise: true
    )
    // right line
    path.addLine(
        to: CGPoint(x: viewSize.width, y: effectiveViewHeight)
    )


    // bottom-right corner arc
    path.addArc(
        withCenter: CGPoint(
            x: viewSize.width - cardRadius,
            y: effectiveViewHeight - cardRadius
        ),
        radius: cardRadius, startAngle: 0, endAngle: .pi / 2, clockwise: true
    )
    // right half of bottom line
    path.addLine(
        to: CGPoint(x: viewSize.width / 4 * 3, y: effectiveViewHeight)
    )

    // left half of bottom line
    path.addLine(
        to: CGPoint(x: cardRadius, y: effectiveViewHeight)
    )
    // bottom-left corner arc
    path.addArc(
        withCenter: CGPoint(
            x: cardRadius,
            y: effectiveViewHeight - cardRadius
        ),
        radius: cardRadius, startAngle: .pi / 2, endAngle: .pi, clockwise: true
    )
    // left line
    path.addLine(to: CGPoint(x: 0, y: cardRadius))
    // top-left corner arc
    path.addArc(
        withCenter: CGPoint(x: cardRadius, y: cardRadius),
        radius: cardRadius, startAngle: .pi, endAngle: .pi / 2 * 3, clockwise: true
    )

    // close path join to origin
    path.close()
    // Set the background color of the view
    UIColor(red: 0.118, green: 0.118, blue: 0.15, alpha: 1).set()
    path.fill()
}
person Witek Bobrowski    schedule 27.11.2020