gpt4 book ai didi

ios - 使用 UIBezierPath 绘制自定义 View 会导致非对称形状

转载 作者:搜寻专家 更新时间:2023-11-01 06:14:15 25 4
gpt4 key购买 nike

我正在尝试绘制带有一些“曲线边缘”的 UIView。

它应该是这样的:

enter image description here

这是我得到的:

enter image description here

注意右上角 (TR) 与右下角 (BR) 不对称吗? BR 角与我想要实现的非常相似,但我无法让 TR 角正确对齐(玩了一堆不同的开始和结束角度)。

代码如下:

struct Constants {
static let cornerRadius: CGFloat = 15.0 // used for left-top and left-bottom curvature
static let rightTipWidth: CGFloat = 40.0 // the max. width for the right tip thingy
static let rightCornerRadius: CGFloat = 10.0 // the radius for the right tip
static let rightEdgeRadius: CGFloat = 10.0 // the radius for the top right and bottom right curvature
}
override func draw(_ rect: CGRect) {
super.draw(rect)

// Initialize the path.
let path = UIBezierPath()

// starting point
let startingPoint = CGPoint(x: Constants.cornerRadius, y: 0.0)
path.move(to: startingPoint)

// create a center point for the arc for the top left corner
let leftTopCircleCenterPoint = CGPoint(x: Constants.cornerRadius, y: Constants.cornerRadius)
path.addArc(withCenter: leftTopCircleCenterPoint, radius: Constants.cornerRadius, startAngle: 270.degreesToRadians, endAngle: 180.degreesToRadians, clockwise: false)

// move the path to the bottom left corner
path.addLine(to: CGPoint(x: 0.0, y: frame.size.height - Constants.cornerRadius))

// add the arc to bottom left
let leftBottomCircleCenterPoint = CGPoint(x: Constants.cornerRadius, y: frame.size.height - Constants.cornerRadius)
path.addArc(withCenter: leftBottomCircleCenterPoint, radius: Constants.cornerRadius, startAngle: 180.degreesToRadians, endAngle: 90.degreesToRadians, clockwise: false)

// move along the bottom to the right edge - rightTipWidth
let maxXRightEdge = frame.size.width - Constants.rightTipWidth
path.addLine(to: CGPoint(x: maxXRightEdge, y: frame.size.height))

// add a curve at the bottom before tipping up at 45 degrees
let bottomRightEdgeControlPoint = CGPoint(x: maxXRightEdge, y: frame.size.height - Constants.rightEdgeRadius)
path.addArc(withCenter: bottomRightEdgeControlPoint, radius: Constants.rightEdgeRadius, startAngle: 90.degreesToRadians, endAngle: 45.degreesToRadians, clockwise: false)

// figure out the center for the right side curvature
let rightMidPointY = frame.size.height / 2.0
let halfRadius = (Constants.rightCornerRadius / 2.0)

// move up till the mid point corner radius
path.addLine(to: CGPoint(x: frame.size.width - Constants.rightCornerRadius, y: (rightMidPointY + halfRadius)))

// the destination for the curve (end point of the curve)
let rightEndPoint = CGPoint(x: frame.size.width - Constants.rightCornerRadius, y: (rightMidPointY - halfRadius))

// figure out the right side tip's control point (See: https://developer.apple.com/documentation/uikit/uibezierpath/1624351-addquadcurve)
let rightControlPoint = CGPoint(x: frame.size.width - halfRadius, y: rightMidPointY)

// add the curve for the right side tip
path.addQuadCurve(to: rightEndPoint, controlPoint: rightControlPoint)

// move up at 45 degrees
path.addLine(to: CGPoint(x: maxXRightEdge + Constants.rightEdgeRadius, y: Constants.rightEdgeRadius))

let topRightEdgeControlPoint = CGPoint(x: maxXRightEdge, y: Constants.rightEdgeRadius)
path.addArc(withCenter: topRightEdgeControlPoint, radius: Constants.rightEdgeRadius, startAngle: 315.degreesToRadians, endAngle: 270.degreesToRadians, clockwise: false) // straight

path.close()

// Specify the fill color and apply it to the path.
UIColor.orange.setFill()
path.fill()

// Specify a border (stroke) color.
UIColor.orange.setStroke()
path.stroke()
}

extension BinaryInteger {
var degreesToRadians: CGFloat { return CGFloat(Int(self)) * .pi / 180 }
}

简单总结一下我的思考过程:

  • 创建一个 bezierPath 并将其移动到 startingPoint
  • 添加 LT(左上)曲线并将线向下移动
  • 沿左边缘移动直线并添加 LB(左下)曲线以及沿着底部到右边缘的移动线
  • 将线移动到 frame.size.width - Constants.rightTipWidth
  • 添加一个圆弧,圆心位于 x = currentPointy = height-rightEdgeRadius
  • 向上移动直线直到 y = (height/2.0) +
    (Constants.rightCornerRadius/2.0)
  • 添加终点为 y = (height/2.0) 的 QuadCurve -
    (Constants.rightCornerRadius/2.0)
  • 向上移动直线直到 x = maxXRightEdge + Constants.rightEdgeRadius
  • 添加右上角 (TR) 曲线 ---> 导致非对称曲率

最佳答案

这是另一个演绎:

@IBDesignable
open class PointerView: UIView {

/// The left-top and left-bottom curvature
@IBInspectable var cornerRadius: CGFloat = 15 { didSet { updatePath() } }

/// The radius for the right tip
@IBInspectable var rightCornerRadius: CGFloat = 10 { didSet { updatePath() } }

/// The radius for the top right and bottom right curvature
@IBInspectable var rightEdgeRadius: CGFloat = 10 { didSet { updatePath() } }

/// The fill color
@IBInspectable var fillColor: UIColor = .blue { didSet { shapeLayer.fillColor = fillColor.cgColor } }

/// The stroke color
@IBInspectable var strokeColor: UIColor = .clear { didSet { shapeLayer.strokeColor = strokeColor.cgColor } }

/// The angle of the tip
@IBInspectable var angle: CGFloat = 90 { didSet { updatePath() } }

/// The line width
@IBInspectable var lineWidth: CGFloat = 0 { didSet { updatePath() } }

/// The shape layer for the pointer
private lazy var shapeLayer: CAShapeLayer = {
let _shapeLayer = CAShapeLayer()
_shapeLayer.fillColor = fillColor.cgColor
_shapeLayer.strokeColor = strokeColor.cgColor
_shapeLayer.lineWidth = lineWidth
return _shapeLayer
}()

public override init(frame: CGRect) {
super.init(frame: frame)

configure()
}

public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)

configure()
}

private func configure() {
layer.addSublayer(shapeLayer)
}

open override func layoutSubviews() {
super.layoutSubviews()

updatePath()
}

private func updatePath() {
let path = UIBezierPath()

let offset = lineWidth / 2
let boundingRect = bounds.insetBy(dx: offset, dy: offset)

let arrowTop = CGPoint(x: boundingRect.maxX - boundingRect.height / 2 / tan(angle * .pi / 180 / 2), y: boundingRect.minY)
let arrowRight = CGPoint(x: boundingRect.maxX, y: boundingRect.midY)
let arrowBottom = CGPoint(x: boundingRect.maxX - boundingRect.height / 2 / tan(angle * .pi / 180 / 2), y: boundingRect.maxY)
let start = CGPoint(x: boundingRect.minX + cornerRadius, y: boundingRect.minY)

// top left
path.move(to: start)
path.addQuadCurve(to: CGPoint(x: boundingRect.minX, y: boundingRect.minY + cornerRadius), controlPoint: CGPoint(x: boundingRect.minX, y: boundingRect.minY))

// left
path.addLine(to: CGPoint(x: boundingRect.minX, y: boundingRect.maxY - cornerRadius))

// lower left
path.addQuadCurve(to: CGPoint(x: boundingRect.minX + cornerRadius, y: boundingRect.maxY), controlPoint: CGPoint(x: boundingRect.minX, y: boundingRect.maxY))

// bottom
path.addLine(to: calculate(from: path.currentPoint, to: arrowBottom, less: rightEdgeRadius))

// bottom right (before tip)
path.addQuadCurve(to: calculate(from: arrowRight, to: arrowBottom, less: rightEdgeRadius), controlPoint: arrowBottom)

// bottom edge of tip
path.addLine(to: calculate(from: path.currentPoint, to: arrowRight, less: rightCornerRadius))

// tip
path.addQuadCurve(to: calculate(from: arrowTop, to: arrowRight, less: rightCornerRadius), controlPoint: arrowRight)

// top edge of tip
path.addLine(to: calculate(from: path.currentPoint, to: arrowTop, less: rightEdgeRadius))

// top right (after tip)
path.addQuadCurve(to: calculate(from: start, to: arrowTop, less: rightEdgeRadius), controlPoint: arrowTop)

path.close()

shapeLayer.lineWidth = lineWidth
shapeLayer.path = path.cgPath
}

/// Calculate some point between `startPoint` and `endPoint`, but `distance` from `endPoint
///
/// - Parameters:
/// - startPoint: The starting point.
/// - endPoint: The ending point.
/// - distance: Distance from the ending point
/// - Returns: Returns the point that is `distance` from the `endPoint` as you travel from `startPoint` to `endPoint`.

private func calculate(from startPoint: CGPoint, to endPoint: CGPoint, less distance: CGFloat) -> CGPoint {
let angle = atan2(endPoint.y - startPoint.y, endPoint.x - startPoint.x)
let totalDistance = hypot(endPoint.y - startPoint.y, endPoint.x - startPoint.x) - distance

return CGPoint(x: startPoint.x + totalDistance * cos(angle),
y: startPoint.y + totalDistance * sin(angle))
}
}

因为那是 @IBDesignable,我可以将它放在一个单独的框架目标中,然后有选择地在 Interface Builder 中使用它(并自定义它):

enter image description here

我对参数所做的唯一更改是不使用尖端的宽度,而是使用尖端的角度。这样,如果尺寸随着约束(或其他)的变化而变化,它会保留所需的形状。

我还更改了它以使用 CAShapeLayer 而不是自定义 draw(_:) 方法来享受 Apple 内置的任何形状图层的效率。

关于ios - 使用 UIBezierPath 绘制自定义 View 会导致非对称形状,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48469093/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com