Swift实现无模糊多尺寸用户首字母头像(带波浪效果)
解决方案
问题分析
你的代码核心存在两个问题:
- 生成的头像模糊:使用
UIGraphicsBeginImageContext未考虑Retina屏幕缩放比例,导致图片分辨率不足。 - 缺少波浪背景:未在绘图流程中添加波浪形状的绘制逻辑。
修复步骤
1. 解决模糊问题
替换UIGraphicsBeginImageContext(frame.size)为UIGraphicsBeginImageContextWithOptions(frame.size, false, UIScreen.main.scale),第三个参数传入屏幕原生缩放比例,生成适配Retina屏幕的高清图片。
2. 添加波浪背景
通过UIBezierPath绘制波浪路径,在渲染姓名Label前先将波浪背景绘制到绘图上下文,可自定义波浪振幅、波长参数适配不同尺寸头像。
3. 支持动态尺寸
重构头像生成函数,让它接受size参数替代固定的50x50尺寸,以便根据UITableViewCell的需求生成对应大小的头像。
完整代码
import UIKit class ViewController: UIViewController { @IBOutlet weak var imgName: UIImageView! override func viewDidLoad() { super.viewDidLoad() // 传入与imageView匹配的尺寸 let avatarSize = imgName.bounds.size imgName.image = generateUserAvatar(name: "B L", size: avatarSize) } func generateUserAvatar(name: String?, size: CGSize) -> UIImage? { guard let name = name, !name.isEmpty else { return nil } // 初始化Retina适配的绘图上下文 UIGraphicsBeginImageContextWithOptions(size, false, UIScreen.main.scale) guard let context = UIGraphicsGetCurrentContext() else { return nil } let frame = CGRect(origin: .zero, size: size) let baseColor = UIColor(red: 60/255, green: 160/255, blue:89/255, alpha: 1) // 绘制波浪背景 drawWaveBackground(in: frame, context: context, baseColor: baseColor) // 初始化姓名首字母Label let nameLabel = UILabel(frame: frame) nameLabel.textAlignment = .center nameLabel.backgroundColor = .clear // 透明背景,显示下方波浪 nameLabel.textColor = .white // 根据头像尺寸动态调整字体大小 let fontSize = min(size.width, size.height) * 0.4 nameLabel.font = UIFont.boldSystemFont(ofSize: fontSize) // 提取姓名首字母 var initials = "" let nameComponents = name.components(separatedBy: " ") if let firstWord = nameComponents.first, let firstLetter = firstWord.first { initials += String(firstLetter).capitalized } if nameComponents.count > 1, let lastWord = nameComponents.last, let lastLetter = lastWord.first { initials += String(lastLetter).capitalized } nameLabel.text = initials // 将Label渲染到上下文 nameLabel.layer.render(in: context) // 生成并返回图片 let avatarImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return avatarImage } private func drawWaveBackground(in frame: CGRect, context: CGContext, baseColor: UIColor) { // 第一层波浪 let wavePath = UIBezierPath() let waveAmplitude = frame.height * 0.1 let waveLength = frame.width * 0.8 let startY = frame.height * 0.7 wavePath.move(to: CGPoint(x: 0, y: startY)) for x in stride(from: 0, through: frame.width + waveLength, by: 1) { let relativeX = x / waveLength let y = startY + waveAmplitude * sin(relativeX * CGFloat.pi * 2) wavePath.addLine(to: CGPoint(x: x, y: y)) } wavePath.addLine(to: CGPoint(x: frame.width, y: frame.height)) wavePath.addLine(to: CGPoint(x: 0, y: frame.height)) wavePath.close() baseColor.setFill() wavePath.fill() // 第二层浅色波浪,增强层次感 let secondWavePath = UIBezierPath() let secondStartY = startY + waveAmplitude * 0.5 for x in stride(from: 0, through: frame.width + waveLength, by: 1) { let relativeX = x / waveLength + 0.5 // 偏移半个波长,形成交错效果 let y = secondStartY + waveAmplitude * 0.6 * sin(relativeX * CGFloat.pi * 2) secondWavePath.addLine(to: CGPoint(x: x, y: y)) } secondWavePath.addLine(to: CGPoint(x: frame.width, y: frame.height)) secondWavePath.addLine(to: CGPoint(x: 0, y: frame.height)) secondWavePath.close() baseColor.withAlphaComponent(0.3).setFill() secondWavePath.fill() } }
UITableView中的使用示例
在cellForRowAt方法中,根据cell内ImageView尺寸生成头像:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "UserCell", for: indexPath) as! UserCell let user = users[indexPath.row] // 假设users为你的数据源数组 let avatarSize = cell.avatarImageView.bounds.size cell.avatarImageView.image = generateUserAvatar(name: user.name, size: avatarSize) return cell }
自定义说明
- 可调整
drawWaveBackground中的波浪振幅、波长参数,匹配目标截图的视觉效果。 - 字体大小随头像尺寸动态计算,避免不同尺寸下文字溢出。
- 生成的图片为高清无模糊版本,完全适配Retina屏幕。
内容的提问来源于stack exchange,提问作者Sham Dhiman
相关产品推荐
相关产品推荐

