【问题标题】:Set specific value to round corners of UIBezierPath将特定值设置为 UIBezierPath 的圆角
【发布时间】:2020-05-23 16:16:08
【问题描述】:

我知道有一些类似的问题,但就我而言,我想为我的图层设置cornerRadius = 8。

如果我设置:

shapeLayer.lineCap = CAShapeLayerLineCap.round

显示如下:

-> 与设计不符。

我的代码:

import Foundation
import UIKit

final class OnboardingCell: UICollectionViewCell {
  @IBOutlet weak var boxView: UIView!
  @IBOutlet weak var continueButton: UIButton!
  @IBOutlet weak var progressView: UIView!

  var onTap: (() -> Void)?
  var levelProgress: CGFloat = 0.1 {
    didSet {
      fgLayer.strokeEnd = levelProgress
    }
  }

  let bgLayer = CAShapeLayer()
  let fgLayer = CAShapeLayer()

  override func awakeFromNib() {
    super.awakeFromNib()
    setup()
    configure()
  }

  override func layoutSubviews() {
    super.layoutSubviews()
    setupShapeLayer(shapeLayer: bgLayer)
    setupShapeLayer(shapeLayer: fgLayer)
  }

  private func setup() {
    bgLayer.lineWidth = 50
    bgLayer.fillColor = nil
    bgLayer.strokeStart = 470 / 1590 + 0.008
    bgLayer.strokeEnd = 1
    bgLayer.cornerRadius = 8
    progressView.layer.addSublayer(bgLayer)
    fgLayer.lineWidth = 50
    fgLayer.fillColor = nil
    fgLayer.strokeStart = 0
    fgLayer.strokeEnd = 470 / 1590 - 0.008
    progressView.layer.addSublayer(fgLayer)
  }

  private func configure() {
    bgLayer.strokeColor = UIColor(rgb: 0xE3EDF7).cgColor
    fgLayer.strokeColor = UIColor(rgb: 0x18D4F4).cgColor
  }

  private func setupShapeLayer(shapeLayer: CAShapeLayer) {
    let linePath = UIBezierPath(arcCenter: CGPoint(x: progressView.bounds.midX, y: progressView.bounds.midY), radius: progressView.frame.height / 2, startAngle: 9/11 * CGFloat.pi, endAngle: 2/11 * CGFloat.pi, clockwise: true)
    linePath.lineWidth = 10
    shapeLayer.path = linePath.cgPath
  }

  override func draw(_ rect: CGRect) {
    super.draw(rect)
    boxView.addFloatEffect()
    continueButton.addFloatEffectForButton()
    bgLayer.addSankEffect()
    fgLayer.addFloatEffect()
  }

  @IBAction func continueButtonTapped(_ sender: UIButton) {
    onTap?()
  }
}

【问题讨论】:

  • 请分享您的代码
  • 我刚刚更新了。
  • 添加bgLayer.masksToBounds = true
  • 我试过了,不行

标签: ios swift uibezierpath


【解决方案1】:

cornerRadius 只调整图层边界的角,而不是路径的角。如果你想要圆角的路径,你必须自己描边。

有两种方法可以渲染具有特定圆角半径的圆弧:

  1. 如果渲染一个简单的实体渲染,一种非常简单的方法是渲染两条弧线,一条顺时针,一条逆时针,使用不同的半径。这些单独圆弧的线宽应该是最终形状所需圆角的两倍。然后,如果您使用匹配的笔触颜色和填充颜色渲染这两条弧线,您将获得所需的形状。

    这里是动画,所以你可以看到发生了什么:

    这是代码(没有动画):

    class ArcView: UIView {
        var startAngle: CGFloat = .pi * 3 / 4
        var endAngle: CGFloat = .pi * 5 / 4
        var clockwise: Bool = true
    
        /// Radius of center of this arc
        var radius: CGFloat = 100
    
        /// The linewidth of this thick arc
        var lineWidth: CGFloat = 50
    
        /// The corner radius of this thick arc
        var cornerRadius: CGFloat = 10
    
        static override var layerClass: AnyClass { return CAShapeLayer.self }
        var shapeLayer: CAShapeLayer { return layer as! CAShapeLayer }
    
        override func layoutSubviews() {
            super.layoutSubviews()
            updatePath()
        }
    
        func updatePath() {
            let center = CGPoint(x: bounds.midX, y: bounds.midY)
            let innerRadius = radius - lineWidth / 2 + cornerRadius
            let innerAngularDelta = asin(cornerRadius / innerRadius) * (clockwise ? 1 : -1)
            let outerRadius = radius + lineWidth / 2 - cornerRadius
            let outerAngularDelta = asin(cornerRadius / outerRadius) * (clockwise ? 1 : -1)
    
            let path = UIBezierPath(arcCenter: center, radius: innerRadius, startAngle: startAngle + innerAngularDelta, endAngle: endAngle - innerAngularDelta, clockwise: clockwise)
            path.addArc(withCenter: center, radius: outerRadius, startAngle: endAngle - outerAngularDelta, endAngle: startAngle + outerAngularDelta, clockwise: !clockwise)
            path.close()
    
            // configure shapeLayer
    
            shapeLayer.lineWidth = cornerRadius * 2
            shapeLayer.fillColor = UIColor.blue.cgColor
            shapeLayer.strokeColor = UIColor.blue.cgColor
            shapeLayer.lineJoin = .round
            shapeLayer.path = path.cgPath
        }
    
    }
    

    上面唯一的技巧是如何调整这些内圆弧和外圆弧的起始角和终止角,以使它们完美地被所需的最终形状包围。但是可以使用一点三角函数来计算出这些角度增量,如上所示。

  2. 另一种方法是为所需形状的轮廓定义路径。计算内圆弧和外圆弧是类似的,但您必须手动计算四个圆角的角度。只是一点三角函数,但是有点毛茸茸的:

    class ArcView: UIView {
        var startAngle: CGFloat = .pi * 3 / 4
        var endAngle: CGFloat = .pi * 5 / 4
        var clockwise: Bool = true
    
        /// Radius of center of this arc
        var radius: CGFloat = 100
    
        /// The linewidth of this thick arc
        var lineWidth: CGFloat = 100
    
        /// The corner radius of this thick arc
        var cornerRadius: CGFloat = 10
    
        static override var layerClass: AnyClass { return CAShapeLayer.self }
        var shapeLayer: CAShapeLayer { return layer as! CAShapeLayer }
    
        override func layoutSubviews() {
            super.layoutSubviews()
            updatePath()
        }
    
        func updatePath() {
            let center = CGPoint(x: bounds.midX, y: bounds.midY)
            let innerRadius = radius - lineWidth / 2
            let innerAngularDelta = asin(cornerRadius / (innerRadius + cornerRadius)) * (clockwise ? 1 : -1)
            let outerRadius = radius + lineWidth / 2
            let outerAngularDelta = asin(cornerRadius / (outerRadius - cornerRadius)) * (clockwise ? 1 : -1)
    
            let path = UIBezierPath(arcCenter: center, radius: innerRadius, startAngle: startAngle + innerAngularDelta, endAngle: endAngle - innerAngularDelta, clockwise: clockwise)
    
            var angle = endAngle - innerAngularDelta
            var cornerStartAngle = angle + .pi * (clockwise ? 1 : -1)
            var cornerEndAngle = endAngle + .pi / 2 * (clockwise ? 1 : -1)
            var cornerCenter = CGPoint(x: center.x + (innerRadius + cornerRadius) * cos(angle), y: center.y + (innerRadius + cornerRadius) * sin(angle))
            path.addArc(withCenter: cornerCenter, radius: cornerRadius, startAngle: cornerStartAngle, endAngle: cornerEndAngle, clockwise: !clockwise)
    
            angle = endAngle - outerAngularDelta
            cornerStartAngle = cornerEndAngle
            cornerEndAngle = endAngle - outerAngularDelta
            cornerCenter = CGPoint(x: center.x + (outerRadius - cornerRadius) * cos(angle), y: center.y + (outerRadius - cornerRadius) * sin(angle))
            path.addArc(withCenter: cornerCenter, radius: cornerRadius, startAngle: cornerStartAngle, endAngle: cornerEndAngle, clockwise: !clockwise)
    
            path.addArc(withCenter: center, radius: outerRadius, startAngle: endAngle - outerAngularDelta, endAngle: startAngle + outerAngularDelta, clockwise: !clockwise)
    
            angle = startAngle + outerAngularDelta
            cornerStartAngle = angle
            cornerEndAngle = startAngle - .pi / 2 * (clockwise ? 1 : -1)
            cornerCenter = CGPoint(x: center.x + (outerRadius - cornerRadius) * cos(angle), y: center.y + (outerRadius - cornerRadius) * sin(angle))
            path.addArc(withCenter: cornerCenter, radius: cornerRadius, startAngle: cornerStartAngle, endAngle: cornerEndAngle, clockwise: !clockwise)
    
            angle = startAngle + innerAngularDelta
            cornerStartAngle = cornerEndAngle
            cornerEndAngle = angle + .pi * (clockwise ? 1 : -1)
            cornerCenter = CGPoint(x: center.x + (innerRadius + cornerRadius) * cos(angle), y: center.y + (innerRadius + cornerRadius) * sin(angle))
            path.addArc(withCenter: cornerCenter, radius: cornerRadius, startAngle: cornerStartAngle, endAngle: cornerEndAngle, clockwise: !clockwise)
    
            path.close()
    
            // configure shapeLayer
    
            shapeLayer.fillColor = UIColor.blue.cgColor
            shapeLayer.strokeColor = UIColor.clear.cgColor
            shapeLayer.lineJoin = .round
            shapeLayer.path = path.cgPath
        }
    }
    

【讨论】:

  • 感谢您的帮助,但我认为这会很复杂,因为我必须为圆圈设置内阴影和外阴影。
  • @Khuong - 这就是我给你第二个版本的原因。它应该适用于那些阴影。
  • 嗨@Rob,您的第二个解决方案对我有用,但有一件事是我无法添加动画来运行进度,我使用了strokeEnd:|
  • 是的,对于这样的渲染,您必须为自己设置动画,例如使用CADisplayLink
  • 好的,谢谢@Rob ^^
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-16
相关资源
最近更新 更多