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

SwiftUI自定义Shape路径拐角圆角实现求助

SwiftUI自定义BannerShape圆角问题解决方法

我在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()
        }
    }
}

问题修复方案

核心问题

  1. 直接依赖UIScreen.main.bounds导致Shape无法自适应布局,路径坐标计算脱离传入的rect上下文
  2. 原路径中曲线与直线的衔接处无平滑过渡,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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 09:32:08