SwiftUI实现自定义宽度圆角矩形左右遮罩(保留圆角且不影响点击)
SwiftUI 实现带自定义左右遮罩且保留圆角的视图
你遇到的问题本质是用overlay添加的遮罩是直角矩形,覆盖了圆角矩形的边角区域,导致原本的圆角被遮挡。下面提供几种简单可行的实现方式,同时保证手势识别不受影响:
方案一:整体裁剪法(最简洁)
通过ZStack组合主体和遮罩,最后用clipShape统一裁剪成圆角,遮罩超出圆角的部分会被自动切掉,同时用contentShape确保整个圆角区域都能响应手势:
struct MaskedRoundedView: View { @State private var leftMaskWidth: CGFloat = 50 @State private var rightMaskWidth: CGFloat = 50 private let cornerRadius: CGFloat = 20 private let viewSize = CGSize(width: 300, height: 100) var body: some View { ZStack(alignment: .leading) { // 橙色圆角主体 RoundedRectangle(cornerRadius: cornerRadius) .fill(.orange) // 左侧白色遮罩 Color.white .frame(width: leftMaskWidth) // 右侧白色遮罩 Color.white .frame(width: rightMaskWidth) .alignmentGuide(.trailing) { $0[.trailing] } } .frame(size: viewSize) // 统一裁剪为圆角,遮罩超出边角的部分会被剪掉 .clipShape(RoundedRectangle(cornerRadius: cornerRadius)) // 确保整个圆角区域都能响应点击/手势 .contentShape(RoundedRectangle(cornerRadius: cornerRadius)) // 测试手势 .onTapGesture { print("整个视图区域都能响应点击") } } } // 扩展简化frame写法 extension View { func frame(size: CGSize) -> some View { self.frame(width: size.width, height: size.height) } }
方案二:背景覆盖法
用背景色作为遮罩,只显示中间的橙色圆角区域,视觉效果和遮罩一致,同时天然保留圆角:
struct MaskedRoundedView: View { @State private var leftMaskWidth: CGFloat = 50 @State private var rightMaskWidth: CGFloat = 50 private let cornerRadius: CGFloat = 20 private let totalWidth: CGFloat = 300 private let height: CGFloat = 100 var body: some View { ZStack { // 白色背景(替代遮罩) RoundedRectangle(cornerRadius: cornerRadius) .fill(.white) // 橙色主体,只显示中间未被遮罩的部分 RoundedRectangle(cornerRadius: cornerRadius) .fill(.orange) .frame(width: totalWidth - leftMaskWidth - rightMaskWidth, height: height) .offset(x: leftMaskWidth) } .frame(width: totalWidth, height: height) .contentShape(RoundedRectangle(cornerRadius: cornerRadius)) .onTapGesture { print("点击有效") } } }
方案三:遮罩匹配圆角法(精确控制遮罩圆角)
如果需要遮罩本身也带对应边角的圆角,可以单独给遮罩做裁剪,确保只覆盖需要的区域,同时设置allowsHitTesting(false)让遮罩不拦截手势:
struct MaskedRoundedView: View { @State private var leftMaskWidth: CGFloat = 50 @State private var rightMaskWidth: CGFloat = 50 private let cornerRadius: CGFloat = 20 private let viewSize = CGSize(width: 300, height: 100) var body: some View { RoundedRectangle(cornerRadius: cornerRadius) .fill(.orange) .frame(size: viewSize) .overlay(alignment: .leading) { // 左侧遮罩:只保留左上角和左下角的圆角 RoundedRectangle(cornerRadius: cornerRadius) .fill(.white) .frame(width: leftMaskWidth) .clipShape( RoundedRectangle(cornerRadius: cornerRadius) .offset(x: leftMaskWidth - cornerRadius) .frame(width: leftMaskWidth + cornerRadius) ) .allowsHitTesting(false) } .overlay(alignment: .trailing) { // 右侧遮罩:只保留右上角和右下角的圆角 RoundedRectangle(cornerRadius: cornerRadius) .fill(.white) .frame(width: rightMaskWidth) .clipShape( RoundedRectangle(cornerRadius: cornerRadius) .offset(x: -(rightMaskWidth - cornerRadius)) .frame(width: rightMaskWidth + cornerRadius) ) .allowsHitTesting(false) } .onTapGesture { print("点击有效") } } }
以上三种方案都能保留圆角矩形的原始形状,同时不会影响手势识别。其中方案一和方案二代码更简洁,推荐优先使用。
内容的提问来源于stack exchange,提问作者Deepak Sharma
相关产品推荐
相关产品推荐

