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

Swift实现无模糊多尺寸用户首字母头像(带波浪效果)

解决方案

问题分析

你的代码核心存在两个问题:

  1. 生成的头像模糊:使用UIGraphicsBeginImageContext未考虑Retina屏幕缩放比例,导致图片分辨率不足。
  2. 缺少波浪背景:未在绘图流程中添加波浪形状的绘制逻辑。

修复步骤

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 01:24:29