如何为UIView添加上下端圆角的左侧绿色边框(适配动态高度Cell)
为UIView添加左侧自定义边框(Layer实现方案)
问题描述

需要为UIView添加如图所示的左侧绿色边框,尝试过用图片作为子视图的方案,但无法适配Cell的动态高度,且自身实现的图片拉伸代码未生效(代码如下),希望获取通过Layer实现的适配方案。
补充代码(失效的图片方案)
private func addBorderImage() { print("commit init") guard let img = UIImage(named: "BorderLeft") else { fatalError("Could not load image named \"stretch1\"!!!") } // create a resizable version let stretchImg = img.resizableImage(withCapInsets: .init(top: 12.0, left: 0.0, bottom: 12.0, right: 0.0)) let imgView = UIImageView() imgView.image = stretchImg.withRenderingMode(.alwaysTemplate) imgView.tintColor = UIColor(named: "AccentColor") imgView.translatesAutoresizingMaskIntoConstraints = false contentView.addSubview(imgView) let g = contentView.layoutMarginsGuide NSLayoutConstraint.activate([ imgView.topAnchor.constraint(equalTo: self.topAnchor, constant: 8.0), imgView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: -10.0), imgView.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -8.0), imgView.widthAnchor.constraint(equalToConstant: 25.0), ]) }
Layer实现方案(完美适配动态高度)
方案1:CAShapeLayer绘制纯色自定义边框
直接通过绘制路径实现,完全适配视图动态高度,无需依赖图片,可灵活调整样式参数。
封装扩展代码
extension UIView { func addLeftCustomBorder(color: UIColor, width: CGFloat = 4, topInset: CGFloat = 8, bottomInset: CGFloat = 8, cornerRadius: CGFloat = 6) { // 移除已存在的边框层,避免重复添加 layer.sublayers?.filter { $0.name == "LeftCustomBorder" }.forEach { $0.removeFromSuperlayer() } let borderLayer = CAShapeLayer() borderLayer.name = "LeftCustomBorder" borderLayer.fillColor = color.cgColor // 计算边框有效高度 let borderHeight = bounds.height - topInset - bottomInset // 绘制带圆角的路径 let path = UIBezierPath() path.move(to: CGPoint(x: 0, y: topInset)) path.addLine(to: CGPoint(x: width, y: topInset)) // 右上角圆角 path.addArc(withCenter: CGPoint(x: width, y: topInset + cornerRadius), radius: cornerRadius, startAngle: .pi * 3/2, endAngle: 0, clockwise: true) path.addLine(to: CGPoint(x: width, y: topInset + borderHeight - cornerRadius)) // 右下角圆角 path.addArc(withCenter: CGPoint(x: width, y: topInset + borderHeight), radius: cornerRadius, startAngle: 0, endAngle: .pi/2, clockwise: true) path.addLine(to: CGPoint(x: 0, y: topInset + borderHeight)) path.close() borderLayer.path = path.cgPath layer.addSublayer(borderLayer) // 监听视图尺寸变化,动态更新边框 addObserver(self, forKeyPath: "bounds", options: [.new], context: nil) } override open func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { if keyPath == "bounds" { // 重新绘制边框 if let color = layer.sublayers?.first(where: { $0.name == "LeftCustomBorder" })?.fillColor { addLeftCustomBorder(color: UIColor(cgColor: color)) } } else { super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) } } deinit { // 移除观察者,避免内存泄漏 removeObserver(self, forKeyPath: "bounds") } }
使用方式
在Cell的layoutSubviews或配置方法中调用:
override func layoutSubviews() { super.layoutSubviews() contentView.addLeftCustomBorder(color: UIColor(named: "AccentColor") ?? .systemGreen) }
方案2:CAGradientLayer实现渐变左侧边框
如果需要渐变效果(如图中可能的上下浅、中间深的绿色),可结合渐变层与遮罩实现:
封装扩展代码
extension UIView { func addLeftGradientBorder(colors: [UIColor], width: CGFloat = 4, topInset: CGFloat = 8, bottomInset: CGFloat = 8, cornerRadius: CGFloat = 6) { // 移除旧的渐变边框相关层 layer.sublayers?.filter { $0.name == "LeftGradientBorder" || $0.name == "LeftGradientBorderMask" }.forEach { $0.removeFromSuperlayer() } // 创建渐变层 let gradientLayer = CAGradientLayer() gradientLayer.name = "LeftGradientBorder" gradientLayer.colors = colors.map { $0.cgColor } gradientLayer.startPoint = CGPoint(x: 0.5, y: 0) gradientLayer.endPoint = CGPoint(x: 0.5, y: 1) gradientLayer.frame = CGRect(x: 0, y: topInset, width: width, height: bounds.height - topInset - bottomInset) // 创建遮罩层实现圆角 let maskLayer = CAShapeLayer() maskLayer.name = "LeftGradientBorderMask" let maskPath = UIBezierPath(roundedRect: gradientLayer.bounds, byRoundingCorners: [.topRight, .bottomRight], cornerRadii: CGSize(width: cornerRadius, height: cornerRadius)) maskLayer.path = maskPath.cgPath gradientLayer.mask = maskLayer layer.addSublayer(gradientLayer) // 监听尺寸变化 addObserver(self, forKeyPath: "bounds", options: [.new], context: nil) } override open func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { if keyPath == "bounds" { if let gradientLayer = layer.sublayers?.first(where: { $0.name == "LeftGradientBorder" }) as? CAGradientLayer { let colors = gradientLayer.colors?.map { UIColor(cgColor: $0 as! CGColor) } ?? [] addLeftGradientBorder(colors: colors) } } else { super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) } } }
使用方式
contentView.addLeftGradientBorder(colors: [.systemGreen.withAlphaComponent(0.6), .systemGreen, .systemGreen.withAlphaComponent(0.6)])
关于你之前图片方案失效的原因
resizableImage(withCapInsets:)的参数设置可能不符合图片结构:如果图片上下是圆角区域,需要确保top和bottom的capInsets值刚好覆盖圆角高度,否则拉伸后会变形。withRenderingMode(.alwaysTemplate)会忽略图片原有颜色,仅保留alpha通道,如果你的原图不是透明背景的模板图,会导致颜色显示异常。
内容的提问来源于stack exchange,提问作者Asha Gohel
相关产品推荐
相关产品推荐

