SwiftUI自定义Shape路径拐角圆角实现求助
我在SwiftUI中创建了自定义BannerShape,目前已实现部分路径,但圆形附近的两个拐角呈现尖角,无法设置为圆角。尝试使用addArc方法修改后效果不符合预期,附上相关代码,请求解决方案。
原代码
struct BannerShape: Shape { func path(in rect: CGRect) -> Path { return Path { path in path.move(to: .zero) path.addLine(to: CGPoint(x: 0, y: 100)) path.addLine(to: CGPoint(x: UIScreen.main.bounds.width, y: 100)) path.addLine(to: CGPoint(x: UIScreen.main.bounds.width, y: 0)) path.addLine(to: CGPoint(x: UIScreen.main.bounds.width / 2 + 48, y: 0)) path.addCurve(to: CGPoint(x: UIScreen.main.bounds.width / 2 - 48, y: 0), control1: CGPoint(x: UIScreen.main.bounds.width / 2 + 80, y: 105), control2: CGPoint(x: UIScreen.main.bounds.width / 2 - 80, y: 105)) path.addLine(to: .zero) path.closeSubpath() } } }
预览代码
struct TabBarBackgroundView_Previews: PreviewProvider { // @State static var selectedTabPreview: Int = 0 static var previews: some View { // TabBarBackgroundView(selectedTab: $selectedTabPreview) BannerShape() .cornerRadius(15, corners: [.topLeft, .topRight]) .previewLayout(.sizeThatFits) .frame(width: UIScreen.main.bounds.width, height: 100) .padding() } }
尝试修改后的代码
struct MyShape : Shape { func path(in rect: CGRect) -> Path { return Path { path in path.move(to: .zero) path.addLine(to: CGPoint(x: UIScreen.main.bounds.width / 2 - 48, y: 0)) path.addArc(center: CGPoint(x: UIScreen.main.bounds.width / 2 - 48, y: 10), radius: 10, startAngle: .degrees(-90), endAngle: .degrees(0), clockwise: false) path.addCurve(to: CGPoint(x: UIScreen.main.bounds.width / 2 + 48, y: 0), control1: CGPoint(x: UIScreen.main.bounds.width / 2 - 80, y: 105), control2: CGPoint(x: UIScreen.main.bounds.width / 2 + 80, y: 105)) path.addArc(center: CGPoint(x: UIScreen.main.bounds.width / 2 + 48, y: 10), radius: 10, startAngle: .degrees(-90), endAngle: .degrees(-180), clockwise: true) path.addLine(to: CGPoint(x: UIScreen.main.bounds.width, y: 0)) path.addLine(to: CGPoint(x: UIScreen.main.bounds.width, y: 100)) path.addLine(to: CGPoint(x: 0, y: 100)) path.addLine(to: .zero) path.closeSubpath() } } }
问题修复方案
核心问题
- 直接依赖
UIScreen.main.bounds导致Shape无法自适应布局,路径坐标计算脱离传入的rect上下文 - 原路径中曲线与直线的衔接处无平滑过渡,
addArc的角度和中心坐标设置错误,导致圆角无法正确贴合拐角
修正后的代码
struct BannerShape: Shape { // 可配置参数,方便调整圆角大小和凹陷宽度 let cornerRadius: CGFloat = 10 let indentWidth: CGFloat = 48 func path(in rect: CGRect) -> Path { Path { path in let centerX = rect.midX let bottomY = rect.maxY let topY = rect.minY // 从左上角开始绘制路径 path.move(to: CGPoint(x: rect.minX, y: topY)) // 左侧竖线 -> 左下角 path.addLine(to: CGPoint(x: rect.minX, y: bottomY)) // 底部横线 -> 右下角 path.addLine(to: CGPoint(x: rect.maxX, y: bottomY)) // 右侧竖线 -> 右上角 path.addLine(to: CGPoint(x: rect.maxX, y: topY)) // 右上角到右侧凹陷点:添加圆角过渡 let rightIndentStart = CGPoint(x: centerX + indentWidth, y: topY) path.addArc(center: CGPoint(x: rightIndentStart.x, y: topY + cornerRadius), radius: cornerRadius, startAngle: .degrees(0), endAngle: .degrees(-90), clockwise: true) // 绘制中间凹陷的曲线 path.addCurve(to: CGPoint(x: centerX - indentWidth, y: topY), control1: CGPoint(x: centerX + 80, y: bottomY + 5), control2: CGPoint(x: centerX - 80, y: bottomY + 5)) // 左侧凹陷点到左上角:添加圆角过渡 let leftIndentEnd = CGPoint(x: centerX - indentWidth, y: topY) path.addArc(center: CGPoint(x: leftIndentEnd.x, y: topY + cornerRadius), radius: cornerRadius, startAngle: .degrees(-90), endAngle: .degrees(180), clockwise: true) // 闭合路径 path.closeSubpath() } } }
适配后的预览代码
struct BannerShape_Previews: PreviewProvider { static var previews: some View { BannerShape() .fill(Color.blue) .frame(height: 100) .padding() .previewLayout(.sizeThatFits) } }
修改说明
- 完全基于传入的
rect计算坐标,保证Shape在任意布局下都能正确显示 - 调整
addArc的中心坐标、角度参数,让圆角完美贴合曲线与顶部直线的拐角 - 将圆角半径、凹陷宽度设为可配置参数,方便后续调整样式
内容的提问来源于stack exchange,提问作者Theo Sementa
相关产品推荐
相关产品推荐

