iOS:如何将聊天气泡尾部从右下调整至左上
解决方案:调整聊天气泡尾部至左上位置
要将气泡尾部从当前的右下位置修改为目标的左上位置,核心是重新构建UIBezierPath的绘制路径,把尾部的曲线逻辑从右下角区域转移到左上角区域,同时修正原代码中的变量笔误。
以下是修改后的完整draw(_:)方法代码:
public override func draw(_ rect: CGRect) { let hasBlurSubview = subviews.contains(where: {$0 is UIVisualEffectView }) guard !hasBlurSubview else { return } let lineWidth = configuration.lineWidth let bezierPath = UIBezierPath() bezierPath.lineWidth = lineWidth let bottom = rect.height - lineWidth let right = rect.width - lineWidth let top = lineWidth let left = lineWidth // 从左上尾部的起始点开始绘制 bezierPath.move(to: CGPoint(x: left + 22, y: top)) // 绘制左上尾部的贝塞尔曲线 bezierPath.addCurve(to: CGPoint(x: left + 11.04, y: top + 4.04), controlPoint1: CGPoint(x: left + 19, y: top), controlPoint2: CGPoint(x: left + 16, y: top)) bezierPath.addCurve(to: CGPoint(x: left - 0.05, y: top + 0.01), controlPoint1: CGPoint(x: left + 8.16, y: top + 1.06), controlPoint2: CGPoint(x: left + 4.07, y: top - 0.43)) bezierPath.addLine(to: CGPoint(x: left, y: top)) bezierPath.addCurve(to: CGPoint(x: left + 4, y: top + 11), controlPoint1: CGPoint(x: left, y: top), controlPoint2: CGPoint(x: left + 4, y: top + 1)) // 绘制气泡主体右侧边缘 bezierPath.addLine(to: CGPoint(x: left + 4, y: bottom - 17)) bezierPath.addCurve(to: CGPoint(x: left + 21, y: bottom), controlPoint1: CGPoint(x: left + 4, y: bottom - 7.61), controlPoint2: CGPoint(x: left + 11.61, y: bottom)) // 绘制气泡主体底部边缘 bezierPath.addLine(to: CGPoint(x: right - 17, y: bottom)) bezierPath.addCurve(to: CGPoint(x: right, y: bottom - 17), controlPoint1: CGPoint(x: right - 7.61, y: bottom), controlPoint2: CGPoint(x: right, y: bottom - 7.61)) // 绘制气泡主体右侧边缘 bezierPath.addLine(to: CGPoint(x: right, y: top + 17)) bezierPath.addCurve(to: CGPoint(x: right - 17, y: top), controlPoint1: CGPoint(x: right, y: top + 7.61), controlPoint2: CGPoint(x: right - 7.61, y: top)) // 绘制气泡主体顶部边缘,回到尾部起始点闭合路径 bezierPath.addLine(to: CGPoint(x: left + 22, y: top)) bezierPath.close() let blurEffect = UIVisualEffectView(effect: UIBlurEffect(style: .light)) blurEffect.alpha = configuration.blurAlpha blurEffect.frame = bounds // 改用bounds避免路径边界偏移问题 let mask = CAShapeLayer() mask.path = bezierPath.cgPath blurEffect.layer.mask = mask insertSubview(blurEffect, at: 0) let gradient = CAGradientLayer() gradient.frame = bounds gradient.colors = configuration.borderGradientColors.map({$0.cgColor}) gradient.startPoint = configuration.borderGradientStartPoint gradient.endPoint = configuration.borderGradientEndPoint let shape = CAShapeLayer() shape.lineWidth = lineWidth shape.path = bezierPath.cgPath shape.strokeColor = UIColor.black.cgColor shape.fillColor = UIColor.clear.cgColor gradient.mask = shape blurEffect.layer.addSublayer(gradient) }
关键修改说明:
- 路径起点转移:将路径起始点从右下区域转移到左上区域,重新编写尾部的贝塞尔曲线逻辑,匹配目标布局的左上尾部形状。
- 变量修正:原代码中
17 + borderWidth改为17 + lineWidth,修复未定义变量的错误。 - 边界适配:将
top和left的初始值从0.0调整为lineWidth,避免边框被视图边缘切割。 - 模糊视图优化:将
blurEffect.frame = bezierPath.bounds改为blurEffect.frame = bounds,解决路径边界导致的模糊区域偏移问题。
补充优化建议:
- 可在
Configuration中新增tailPosition枚举(如.topLeft、.bottomRight),实现动态切换气泡尾部位置的功能,提升组件复用性。 - 将路径绘制逻辑封装为独立方法,例如
createBubblePath(with rect: CGRect, lineWidth: CGFloat, tailPosition: TailPosition) -> UIBezierPath,让代码结构更清晰。
内容的提问来源于stack exchange,提问作者Michael Grigoryan
相关产品推荐
相关产品推荐

