如何解决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
相关产品推荐
相关产品推荐

