You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

UIBezierPath绘制椭圆/圆角矩形问题:线条突出+半径适配异常

解决方案

问题1:匹配目标形状的圆角半径

目标是类椭圆的圆角矩形,要让圆角弧度和矩形比例一致才协调。你的矩形宽320、高400,宽高比4:5,直接用宽高各一半的半径(width*0.5、height*0.5)就能生成完整椭圆;要是想要圆角稍小的矩形,就按比例缩小,比如用width*0.4、height*0.4,你可以对着目标图慢慢调整这个比例。

问题2:线条突出、形状不干净

这问题主要出在没处理stroke宽度、mask路径没组合对、路径和视图边界不匹配,改这几点就能解决:

  • 创建路径时要向内缩lineWidth/2,因为stroke是画在路径中间的,不缩的话一半线条会超出视图边界,要么被截断要么突出。
  • 把注释掉的backgroundPath.append(path)打开,这样mask用evenOdd规则才能把中间区域挖空,和边框对应上。
  • 别用固定的preferedSize,改用视图的bounds,布局变化时形状才会跟着适配。
  • 给shapeLayer和maskLayer都设好frame为视图bounds,避免路径位置跑偏。

修改后的代码

override func layoutSubviews() {
    super.layoutSubviews() 
    
    layer.sublayers?.forEach { $0.removeFromSuperlayer() }

    // 路径向内缩,给stroke留空间
    let insetRect = bounds.insetBy(dx: lineWidth/2, dy: lineWidth/2)
    let path = makePath(rect: insetRect)
    path.lineJoinStyle = .round
    path.lineCapStyle = .round
    
    let shapeLayer = CAShapeLayer()
    shapeLayer.frame = bounds
    shapeLayer.shouldRasterize = true
    shapeLayer.rasterizationScale = UIScreen.main.scale
    
    shapeLayer.lineJoin = .round
    shapeLayer.path = path.cgPath
    shapeLayer.strokeColor = strokeColor.cgColor
    shapeLayer.lineWidth = lineWidth
    shapeLayer.lineCap = .round
    shapeLayer.fillColor = UIColor.clear.cgColor // 只显示边框,填充透明
    
    layer.addSublayer(shapeLayer)
    layer.backgroundColor = overlayColor.cgColor
    
    // 组合路径实现中间挖空
    let backgroundPath = UIBezierPath(rect: bounds)
    backgroundPath.append(path) // 取消注释,把中间路径加进去

    let maskLayer = CAShapeLayer()
    maskLayer.frame = bounds
    maskLayer.shouldRasterize = true
    maskLayer.rasterizationScale = UIScreen.main.scale
    
    maskLayer.fillRule = .evenOdd
    maskLayer.path = backgroundPath.cgPath
    layer.mask = maskLayer

    addAdditionalLayersIfNeeded(bounds) // 传bounds而非rect
}


override func makePath(rect: CGRect) -> UIBezierPath {
    // 0.5是完整椭圆,0.4是稍小圆角,自己调比例匹配目标
    let radiusRatio: CGFloat = 0.5
    let cornerRadii = CGSize(width: rect.width * radiusRatio, height: rect.height * radiusRatio)
    return UIBezierPath(roundedRect: rect, byRoundingCorners: .allCorners, cornerRadii: cornerRadii)
}

额外提示

  • 要是必须用固定尺寸的形状,就把insetRect改成preferedSize.insetBy(dx: lineWidth/2, dy: lineWidth/2),同时把shapeLayer和maskLayer的frame设为preferedSize,但还是建议用bounds适配布局更稳妥。
  • 要是线条还有锯齿,直接关掉shouldRasterize,有时候开光栅化反而会弄巧成拙,系统自带的抗锯齿就够了。

内容的提问来源于stack exchange,提问作者iamIcarus

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 08:46:01