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

如何解决UIBezierPath圆角半径跳变,绘制老式完美圆角矩形?

解决iOS7+圆角半径跳变问题,实现平滑老式圆角矩形

iOS7之后苹果调整了UIBezierPath bezierPathWithRoundedRect:cornerRadius:的圆角绘制算法:当圆角半径超过矩形短边的1/3时,系统会自动调整圆角的曲率以维持视觉圆润感,这就导致从0到最大半径(短边一半)的过程中,半径变化出现跳变。要实现iOS6及之前的平滑过渡圆角,需要手动构建贝塞尔路径,确保每个圆角都是标准的四分之一圆弧。

Objective-C实现(UIBezierPath分类)

@implementation UIBezierPath (LegacyRoundedRect)

+ (instancetype)legacy_bezierPathWithRoundedRect:(CGRect)rect cornerRadius:(CGFloat)cornerRadius {
    // 限制圆角半径不超过短边的一半
    CGFloat maxRadius = MIN(rect.size.width, rect.size.height) / 2.0;
    CGFloat clampedRadius = MIN(cornerRadius, maxRadius);
    
    UIBezierPath *path = [UIBezierPath bezierPath];
    
    // 起始点:左上角圆角的结束位置
    CGPoint startPoint = CGPointMake(CGRectGetMinX(rect) + clampedRadius, CGRectGetMinY(rect));
    [path moveToPoint:startPoint];
    
    // 上边线 + 右上角圆角
    [path addLineToPoint:CGPointMake(CGRectGetMaxX(rect) - clampedRadius, CGRectGetMinY(rect))];
    [path addArcWithCenter:CGPointMake(CGRectGetMaxX(rect) - clampedRadius, CGRectGetMinY(rect) + clampedRadius)
                    radius:clampedRadius
                startAngle:-M_PI_2
                  endAngle:0
                 clockwise:YES];
    
    // 右边线 + 右下角圆角
    [path addLineToPoint:CGPointMake(CGRectGetMaxX(rect), CGRectGetMaxY(rect) - clampedRadius)];
    [path addArcWithCenter:CGPointMake(CGRectGetMaxX(rect) - clampedRadius, CGRectGetMaxY(rect) - clampedRadius)
                    radius:clampedRadius
                startAngle:0
                  endAngle:M_PI_2
                 clockwise:YES];
    
    // 下边线 + 左下角圆角
    [path addLineToPoint:CGPointMake(CGRectGetMinX(rect) + clampedRadius, CGRectGetMaxY(rect))];
    [path addArcWithCenter:CGPointMake(CGRectGetMinX(rect) + clampedRadius, CGRectGetMaxY(rect) - clampedRadius)
                    radius:clampedRadius
                startAngle:M_PI_2
                  endAngle:M_PI
                 clockwise:YES];
    
    // 左边线 + 左上角圆角
    [path addLineToPoint:CGPointMake(CGRectGetMinX(rect), CGRectGetMinY(rect) + clampedRadius)];
    [path addArcWithCenter:CGPointMake(CGRectGetMinX(rect) + clampedRadius, CGRectGetMinY(rect) + clampedRadius)
                    radius:clampedRadius
                startAngle:M_PI
                  endAngle:-M_PI_2
                 clockwise:YES];
    
    [path closePath];
    return path;
}

@end

Swift实现(UIBezierPath扩展)

extension UIBezierPath {
    static func legacyRoundedRect(_ rect: CGRect, cornerRadius: CGFloat) -> UIBezierPath {
        let maxRadius = min(rect.width, rect.height) / 2.0
        let clampedRadius = min(cornerRadius, maxRadius)
        let path = UIBezierPath()
        
        let startPoint = CGPoint(x: rect.minX + clampedRadius, y: rect.minY)
        path.move(to: startPoint)
        
        // 上边线 + 右上角圆角
        path.addLine(to: CGPoint(x: rect.maxX - clampedRadius, y: rect.minY))
        path.addArc(withCenter: CGPoint(x: rect.maxX - clampedRadius, y: rect.minY + clampedRadius),
                    radius: clampedRadius,
                    startAngle: -.pi / 2,
                    endAngle: 0,
                    clockwise: true)
        
        // 右边线 + 右下角圆角
        path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY - clampedRadius))
        path.addArc(withCenter: CGPoint(x: rect.maxX - clampedRadius, y: rect.maxY - clampedRadius),
                    radius: clampedRadius,
                    startAngle: 0,
                    endAngle: .pi / 2,
                    clockwise: true)
        
        // 下边线 + 左下角圆角
        path.addLine(to: CGPoint(x: rect.minX + clampedRadius, y: rect.maxY))
        path.addArc(withCenter: CGPoint(x: rect.minX + clampedRadius, y: rect.maxY - clampedRadius),
                    radius: clampedRadius,
                    startAngle: .pi / 2,
                    endAngle: .pi,
                    clockwise: true)
        
        // 左边线 + 左上角圆角
        path.addLine(to: CGPoint(x: rect.minX, y: rect.minY + clampedRadius))
        path.addArc(withCenter: CGPoint(x: rect.minX + clampedRadius, y: rect.minY + clampedRadius),
                    radius: clampedRadius,
                    startAngle: .pi,
                    endAngle: -.pi / 2,
                    clockwise: true)
        
        path.close()
        return path
    }
}

使用方式

直接调用上述自定义方法替代系统的bezierPathWithRoundedRect:cornerRadius:即可,圆角半径从0到最大半径的过程中会完全平滑过渡,不会出现跳变。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 08:00:55