如何绘制环绕圆形按钮的圆形矩形?
How to draw rounded rect that wraps around circular button?
我想使用 swift 中的贝塞尔曲线路径设计下面屏幕截图中曲线的突出显示部分。
您可以在 mac 上使用 Paint code 应用程序,使用钢笔工具绘制曲线,然后复制它的结果,因为该应用程序提供了 Drawings-to-code
只需一点三角函数就可以计算出右下角那个按钮周围的弧线,例如
func updatePath() {
let buttonCenter = CGPoint(x: bounds.maxX - circleRadius, y: bounds.maxY - circleRadius)
circleShapeLayer.path = UIBezierPath(arcCenter: buttonCenter, radius: circleRadius, startAngle: 0, endAngle: .pi * 2, clockwise: true).cgPath // red
let angle1 = acos((circleRadius - cornerRadius) / (circleRadius + spaceRadius + cornerRadius))
let angle2 = acos((circleRadius - bottomDistance - cornerRadius) / (circleRadius + spaceRadius + cornerRadius))
let arc1Center = CGPoint(x: bounds.maxX - cornerRadius,
y: buttonCenter.y - (circleRadius + cornerRadius + spaceRadius) * sin(angle1))
let path = UIBezierPath()
path.move(to: CGPoint(x: bounds.maxX, y: bounds.minY + cornerRadius))
path.addArc(withCenter: arc1Center, radius: cornerRadius, startAngle: 0, endAngle: .pi / 2 + (.pi / 2 - angle1), clockwise: true) // blue
path.addArc(withCenter: buttonCenter, radius: circleRadius + spaceRadius, startAngle: 2 * .pi - angle1, endAngle: .pi / 2 + angle2, clockwise: false) // green
let arc2Center = CGPoint(x: buttonCenter.x - (circleRadius + cornerRadius + spaceRadius) * sin(angle2), y: bounds.maxY - bottomDistance - cornerRadius)
path.addArc(withCenter: arc2Center, radius: cornerRadius, startAngle: -(.pi / 2 - angle2), endAngle: .pi / 2, clockwise: true) // yellow
path.addArc(withCenter: CGPoint(x: bounds.minX + cornerRadius, y: bounds.maxY - (bottomDistance + cornerRadius)), radius: cornerRadius, startAngle: .pi / 2, endAngle: .pi, clockwise: true) // cyan
path.addArc(withCenter: CGPoint(x: bounds.minX + cornerRadius, y: bounds.minY + cornerRadius), radius: cornerRadius, startAngle: .pi, endAngle: .pi * 3 / 2, clockwise: true) // white
path.addArc(withCenter: CGPoint(x: bounds.maxX - cornerRadius, y: bounds.minY + cornerRadius), radius: cornerRadius, startAngle: .pi * 3 / 2, endAngle: 2 * .pi, clockwise: true) // black
path.close()
backgroundShapeLayer.path = path.cgPath
}
产量:
或者,如果你想匹配上面的笔画,这里用颜色编码到上面代码中的注释:
例如:
@IBDesignable
class BackgroundView: UIView {
@IBInspectable var cornerRadius: CGFloat = 10 { didSet { setNeedsLayout() } }
@IBInspectable var spaceRadius: CGFloat = 10 { didSet { setNeedsLayout() } }
@IBInspectable var circleRadius: CGFloat = 50 { didSet { setNeedsLayout() } }
@IBInspectable var bottomDistance: CGFloat = 30 { didSet { setNeedsLayout() } }
private let backgroundShapeLayer: CAShapeLayer = {
let shapeLayer = CAShapeLayer()
shapeLayer.fillColor = UIColor.white.cgColor
shapeLayer.strokeColor = UIColor.clear.cgColor
return shapeLayer
}()
private let circleShapeLayer: CAShapeLayer = {
let shapeLayer = CAShapeLayer()
shapeLayer.fillColor = UIColor.white.cgColor
shapeLayer.strokeColor = UIColor.clear.cgColor
return shapeLayer
}()
override init(frame: CGRect = .zero) {
super.init(frame: frame)
configure()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
configure()
}
override func layoutSubviews() {
super.layoutSubviews()
updatePath()
}
}
private extension BackgroundView {
func configure() {
layer.addSublayer(circleShapeLayer)
layer.addSublayer(backgroundShapeLayer)
}
func updatePath() {
let buttonCenter = CGPoint(x: bounds.maxX - circleRadius, y: bounds.maxY - circleRadius)
circleShapeLayer.path = UIBezierPath(arcCenter: buttonCenter, radius: circleRadius, startAngle: 0, endAngle: .pi * 2, clockwise: true).cgPath
let angle1 = acos((circleRadius - cornerRadius) / (circleRadius + spaceRadius + cornerRadius))
let angle2 = acos((circleRadius - bottomDistance - cornerRadius) / (circleRadius + spaceRadius + cornerRadius))
let arc1Center = CGPoint(x: bounds.maxX - cornerRadius,
y: buttonCenter.y - (circleRadius + cornerRadius + spaceRadius) * sin(angle1))
let path = UIBezierPath()
path.move(to: CGPoint(x: bounds.maxX, y: bounds.minY + cornerRadius))
path.addArc(withCenter: arc1Center, radius: cornerRadius, startAngle: 0, endAngle: .pi / 2 + (.pi / 2 - angle1), clockwise: true)
path.addArc(withCenter: buttonCenter, radius: circleRadius + spaceRadius, startAngle: 2 * .pi - angle1, endAngle: .pi / 2 + angle2, clockwise: false)
let arc2Center = CGPoint(x: buttonCenter.x - (circleRadius + cornerRadius + spaceRadius) * sin(angle2), y: bounds.maxY - bottomDistance - cornerRadius)
path.addArc(withCenter: arc2Center, radius: cornerRadius, startAngle: -(.pi / 2 - angle2), endAngle: .pi / 2, clockwise: true)
path.addArc(withCenter: CGPoint(x: bounds.minX + cornerRadius, y: bounds.maxY - (bottomDistance + cornerRadius)), radius: cornerRadius, startAngle: .pi / 2, endAngle: .pi, clockwise: true)
path.addArc(withCenter: CGPoint(x: bounds.minX + cornerRadius, y: bounds.minY + cornerRadius), radius: cornerRadius, startAngle: .pi, endAngle: .pi * 3 / 2, clockwise: true)
path.addArc(withCenter: CGPoint(x: bounds.maxX - cornerRadius, y: bounds.minY + cornerRadius), radius: cornerRadius, startAngle: .pi * 3 / 2, endAngle: 2 * .pi, clockwise: true)
path.close()
backgroundShapeLayer.path = path.cgPath
}
}
我想使用 swift 中的贝塞尔曲线路径设计下面屏幕截图中曲线的突出显示部分。
您可以在 mac 上使用 Paint code 应用程序,使用钢笔工具绘制曲线,然后复制它的结果,因为该应用程序提供了 Drawings-to-code
只需一点三角函数就可以计算出右下角那个按钮周围的弧线,例如
func updatePath() {
let buttonCenter = CGPoint(x: bounds.maxX - circleRadius, y: bounds.maxY - circleRadius)
circleShapeLayer.path = UIBezierPath(arcCenter: buttonCenter, radius: circleRadius, startAngle: 0, endAngle: .pi * 2, clockwise: true).cgPath // red
let angle1 = acos((circleRadius - cornerRadius) / (circleRadius + spaceRadius + cornerRadius))
let angle2 = acos((circleRadius - bottomDistance - cornerRadius) / (circleRadius + spaceRadius + cornerRadius))
let arc1Center = CGPoint(x: bounds.maxX - cornerRadius,
y: buttonCenter.y - (circleRadius + cornerRadius + spaceRadius) * sin(angle1))
let path = UIBezierPath()
path.move(to: CGPoint(x: bounds.maxX, y: bounds.minY + cornerRadius))
path.addArc(withCenter: arc1Center, radius: cornerRadius, startAngle: 0, endAngle: .pi / 2 + (.pi / 2 - angle1), clockwise: true) // blue
path.addArc(withCenter: buttonCenter, radius: circleRadius + spaceRadius, startAngle: 2 * .pi - angle1, endAngle: .pi / 2 + angle2, clockwise: false) // green
let arc2Center = CGPoint(x: buttonCenter.x - (circleRadius + cornerRadius + spaceRadius) * sin(angle2), y: bounds.maxY - bottomDistance - cornerRadius)
path.addArc(withCenter: arc2Center, radius: cornerRadius, startAngle: -(.pi / 2 - angle2), endAngle: .pi / 2, clockwise: true) // yellow
path.addArc(withCenter: CGPoint(x: bounds.minX + cornerRadius, y: bounds.maxY - (bottomDistance + cornerRadius)), radius: cornerRadius, startAngle: .pi / 2, endAngle: .pi, clockwise: true) // cyan
path.addArc(withCenter: CGPoint(x: bounds.minX + cornerRadius, y: bounds.minY + cornerRadius), radius: cornerRadius, startAngle: .pi, endAngle: .pi * 3 / 2, clockwise: true) // white
path.addArc(withCenter: CGPoint(x: bounds.maxX - cornerRadius, y: bounds.minY + cornerRadius), radius: cornerRadius, startAngle: .pi * 3 / 2, endAngle: 2 * .pi, clockwise: true) // black
path.close()
backgroundShapeLayer.path = path.cgPath
}
产量:
或者,如果你想匹配上面的笔画,这里用颜色编码到上面代码中的注释:
例如:
@IBDesignable
class BackgroundView: UIView {
@IBInspectable var cornerRadius: CGFloat = 10 { didSet { setNeedsLayout() } }
@IBInspectable var spaceRadius: CGFloat = 10 { didSet { setNeedsLayout() } }
@IBInspectable var circleRadius: CGFloat = 50 { didSet { setNeedsLayout() } }
@IBInspectable var bottomDistance: CGFloat = 30 { didSet { setNeedsLayout() } }
private let backgroundShapeLayer: CAShapeLayer = {
let shapeLayer = CAShapeLayer()
shapeLayer.fillColor = UIColor.white.cgColor
shapeLayer.strokeColor = UIColor.clear.cgColor
return shapeLayer
}()
private let circleShapeLayer: CAShapeLayer = {
let shapeLayer = CAShapeLayer()
shapeLayer.fillColor = UIColor.white.cgColor
shapeLayer.strokeColor = UIColor.clear.cgColor
return shapeLayer
}()
override init(frame: CGRect = .zero) {
super.init(frame: frame)
configure()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
configure()
}
override func layoutSubviews() {
super.layoutSubviews()
updatePath()
}
}
private extension BackgroundView {
func configure() {
layer.addSublayer(circleShapeLayer)
layer.addSublayer(backgroundShapeLayer)
}
func updatePath() {
let buttonCenter = CGPoint(x: bounds.maxX - circleRadius, y: bounds.maxY - circleRadius)
circleShapeLayer.path = UIBezierPath(arcCenter: buttonCenter, radius: circleRadius, startAngle: 0, endAngle: .pi * 2, clockwise: true).cgPath
let angle1 = acos((circleRadius - cornerRadius) / (circleRadius + spaceRadius + cornerRadius))
let angle2 = acos((circleRadius - bottomDistance - cornerRadius) / (circleRadius + spaceRadius + cornerRadius))
let arc1Center = CGPoint(x: bounds.maxX - cornerRadius,
y: buttonCenter.y - (circleRadius + cornerRadius + spaceRadius) * sin(angle1))
let path = UIBezierPath()
path.move(to: CGPoint(x: bounds.maxX, y: bounds.minY + cornerRadius))
path.addArc(withCenter: arc1Center, radius: cornerRadius, startAngle: 0, endAngle: .pi / 2 + (.pi / 2 - angle1), clockwise: true)
path.addArc(withCenter: buttonCenter, radius: circleRadius + spaceRadius, startAngle: 2 * .pi - angle1, endAngle: .pi / 2 + angle2, clockwise: false)
let arc2Center = CGPoint(x: buttonCenter.x - (circleRadius + cornerRadius + spaceRadius) * sin(angle2), y: bounds.maxY - bottomDistance - cornerRadius)
path.addArc(withCenter: arc2Center, radius: cornerRadius, startAngle: -(.pi / 2 - angle2), endAngle: .pi / 2, clockwise: true)
path.addArc(withCenter: CGPoint(x: bounds.minX + cornerRadius, y: bounds.maxY - (bottomDistance + cornerRadius)), radius: cornerRadius, startAngle: .pi / 2, endAngle: .pi, clockwise: true)
path.addArc(withCenter: CGPoint(x: bounds.minX + cornerRadius, y: bounds.minY + cornerRadius), radius: cornerRadius, startAngle: .pi, endAngle: .pi * 3 / 2, clockwise: true)
path.addArc(withCenter: CGPoint(x: bounds.maxX - cornerRadius, y: bounds.minY + cornerRadius), radius: cornerRadius, startAngle: .pi * 3 / 2, endAngle: 2 * .pi, clockwise: true)
path.close()
backgroundShapeLayer.path = path.cgPath
}
}