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

iOS Swift加载SVG图片遇混合格式问题及优化需求

处理SVG内嵌Base64 PNG的iOS渲染问题

开发Swift iOS项目时,从API获取的图片URL指向SVG文件,但iOS原生不支持直接渲染SVG。尝试过SDWebImage、SDWebImageSVGCoder、SDWebImageSVGKitPlugin、SVGKit等第三方库后,仍有部分图片显示空白。排查发现,能正常渲染的是纯SVG,无法显示的SVG内嵌了Base64编码的PNG资源。

现有代码实现

ViewController代码

import SDWebImageSVGCoder

class ViewController: UIViewController {
    
    var banks: [Bank] = [Bank(logo:"https://identity.moneyhub.co.uk/bank-icons/default",name:"Dummy"),
        Bank(logo: "https://identity.moneyhub.co.uk/bank-icons/accord", name: "Accord Mortgages"),
                           Bank(logo: "https://identity.moneyhub.co.uk/bank-icons/ajBell", name: "AJ Bell"),
                           Bank(logo: "https://identity.moneyhub.co.uk/bank-icons/aldermore", name: "Aldermore"),
                           Bank(logo: "https://identity.moneyhub.co.uk/bank-icons/amex", name: "American Express"),
                           Bank(logo: "https://identity.moneyhub.co.uk/bank-icons/brewinDolphin", name: "Brewin Dolphin"),
                           Bank(logo: "https://identity.moneyhub.co.uk/bank-icons/caxton", name: "Caxton"),]
    
    
    @IBOutlet weak var tableView: UITableView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupTableView()
    }
    
    func setupTableView() {
        self.tableView.delegate = self
        self.tableView.dataSource = self
    }

}

//MARK: - TABLEVIEW DELEGATE DATASOURCE
extension ViewController: UITableViewDelegate, UITableViewDataSource {
    
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return banks.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: BankTableViewCell.identifier, for: indexPath) as! BankTableViewCell
        cell.configure(self.banks[indexPath.row])
        return cell
    }
    
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 62
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        
    }
    
}

初始TableViewCell代码

class BankTableViewCell: UITableViewCell {
    
    @IBOutlet weak var bankNameLabel: UILabel!
    @IBOutlet weak var bankLogoImageView: UIImageView!
    
    class var identifier: String {
        return "BankTableViewCell"
    }
    
    func configure(_ bank: Bank) {
        self.bankNameLabel.text = bank.name
        guard let url = URL(string: bank.logo) else {return}
        self.bankLogoImageView.sd_setImage(fromURL: url)
    }
    
}

SVG类型差异

可正常渲染的纯SVG

<svg viewBox="-25 -25 200 200" xmlns="http://www.w3.org/2000/svg">
    <path d="M138.299 130.707H10.644a6.386 6.386 0 00-6.384 6.391 6.384 6.384 0 006.384 6.384h127.652a6.384 6.384 0 006.384-6.384c-.002-3.532-2.86-6.391-6.381-6.391zM18.621 114.101c-3.526 0-6.384 2.859-6.384 6.388s2.858 6.391 6.384 6.391h111.697c3.526 0 6.384-2.861 6.384-6.391s-2.858-6.388-6.384-6.388h-1.593V56.625h1.593a3.19 3.19 0 000-6.38H18.621a3.19 3.19 0 000 6.38h1.597v57.472h-1.597v.004zm97.336-57.476v57.472H96.81V56.625h19.147zm-31.917 0v57.472H64.893V56.625H84.04zm-51.06 0h19.147v57.472H32.98V56.625zM10.644 44.503H138.34a6.387 6.387 0 006.402-6.388 6.392 6.392 0 00-4.312-6.044L77.094 3.558a6.4 6.4 0 00-5.237 0L8.026 32.293a6.385 6.385 0 00-3.622 7.165 6.38 6.38 0 006.24 5.045z"/>
</svg>

无法渲染的内嵌Base64 PNG的SVG

<svg width="500" height="500" viewBox="0 0 500 500" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect x="20" y="135" width="460" height="230" fill="url(#pattern0)"/>
<defs>
<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0" transform="scale(0.005 0.01)"/>
</pattern>
<image id="image0" width="200" height="100" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAABkCAYAAADDhn8LAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAACBRJREF......UwT2kAAAAASUVORK5CYII="/>
</defs>
</svg>

自定义尝试的代码

尝试通过判断SVG类型选择渲染方式:纯SVG用SDWebImage,内嵌PNG则提取Base64解码,但存在性能问题,异步加载导致图片显示不稳定。

class BankTableViewCell: UITableViewCell {
    
    @IBOutlet weak var bankNameLabel: UILabel!
    @IBOutlet weak var bankLogoImageView: SVGImageView!
    
    class var identifier: String {
        return "BankTableViewCell"
    }
    
    func configure(_ bank: Bank) {
        self.bankNameLabel.text = bank.name
        guard let url = URL(string: bank.logo) else {return}
        self.bankLogoImageView.loadImage(fromURL: url, placeHolderImage: "")
    }
    
}

class SVGImageView: UIImageView {
    
    private let imageCache = NSCache<AnyObject, UIImage>()
    
    func loadImage(fromURL imageURL: URL, placeHolderImage: String)
    {
        self.image = UIImage(named: placeHolderImage)

        if let cachedImage = self.imageCache.object(forKey: imageURL as AnyObject)
        {
            debugPrint("image loaded from cache for =\(imageURL)")
            self.image = cachedImage
            return
        }

        DispatchQueue.global().async {
            [weak self] in
            
            if let imageData = try? Data(contentsOf: imageURL) {
                guard let xmlStr = String(data: imageData, encoding: .utf8) else {
                    DispatchQueue.main.async {
                        self?.sd_setImage(with: URL(string: "https://identity.moneyhub.co.uk/bank-icons/default")!)
                    }
                    return
                }
                print(xmlStr)
                if let range = xmlStr.range(of: "data:image/png;base64,") {
                    let base64StringWithoutfilter = xmlStr.suffix(from: range.upperBound)
                    let base64StringWithoutfilterRange = base64StringWithoutfilter.range(of: "/>")
                    
                    let lowerRange = range.upperBound
                    if let upperRange = base64StringWithoutfilterRange?.upperBound {
                        let base64ImageString = xmlStr[lowerRange..<upperRange].dropLast(3)
                        if let imageData = NSData(base64Encoded: String(describing:base64ImageString)) {
                            if let image = UIImage(data: imageData as Data) {
                                DispatchQueue.main.async {
                                    self?.image = image
                                    return
                                }
                            }
                        }
                    }
                } else {
                    self?.sd_setImage(with: imageURL,completed: { image, error,cache, url in
                        if let image = image {
                            DispatchQueue.main.async {
                                self?.image = image
                                return
                            }
                        }
                    })
                }
            }
        }
    }
}

解决方案建议

1. 优化自定义代码,复用SDWebImage能力

放弃手动网络请求和缓存,基于SDWebImage的现有机制处理,提升稳定性:

import SDWebImage

class SVGImageView: UIImageView {
    func loadImage(from url: URL) {
        sd_setImage(with: url, placeholderImage: nil) { [weak self] (image, _, _, url) in
            guard let self = self, let url = url else { return }
            
            // SDWebImage无法解析时,尝试提取内嵌Base64
            if image == nil {
                SDWebImageManager.shared.downloadImage(with: url) { [weak self] (_, _, _, _, _, data) in
                    guard let self = self, let data = data, let xmlStr = String(data: data, encoding: .utf8) else {
                        self?.sd_setImage(with: URL(string: "https://identity.moneyhub.co.uk/bank-icons/default")!)
                        return
                    }
                    
                    // 正则提取Base64字符串
                    guard let regex = try? NSRegularExpression(pattern: #"data:image/png;base64,([^"]+)"#, options: .caseInsensitive),
                          let match = regex.firstMatch(in: xmlStr, range: NSRange(xmlStr.startIndex..., in: xmlStr)),
                          let base64Range = Range(match.range(at: 1), in: xmlStr),
                          let imageData = Data(base64Encoded: String(xmlStr[base64Range])),
                          let pngImage = UIImage(data: imageData) else {
                        self?.sd_setImage(with: URL(string: "https://identity.moneyhub.co.uk/bank-icons/default")!)
                        return
                    }
                    
                    // 存入SDWebImage缓存
                    SDImageCache.shared.store(pngImage, forKey: url.absoluteString)
                    DispatchQueue.main.async { self.image = pngImage }
                }
            }
        }
    }
}

2. 使用支持内嵌资源的纯Swift SVG库

尝试SwiftSVG(纯Swift实现,支持解析内嵌Base64资源):

  1. Podfile添加:pod 'SwiftSVG'
  2. 修改Cell渲染逻辑:
import SwiftSVG

class BankTableViewCell: UITableViewCell {
    @IBOutlet weak var bankLogoContainer: UIView!
    @IBOutlet weak var bankNameLabel: UILabel!
    
    class var identifier: String { return "BankTableViewCell" }
    
    func configure(_ bank: Bank) {
        bankNameLabel.text = bank.name
        guard let url = URL(string: bank.logo) else { return }
        
        // 清空复用的视图
        bankLogoContainer.subviews.forEach { $0.removeFromSuperview() }
        
        URLSession.shared.dataTask(with: url) { [weak self] data, _, _ in
            guard let self = self, let data = data else { return }
            DispatchQueue.main.async {
                let svgView = UIView(SVGData: data)
                svgView.frame = self.bankLogoContainer.bounds
                svgView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
                self.bankLogoContainer.addSubview(svgView)
            }
        }.resume()
    }
}

可结合SDWebImage缓存SVG数据,减少重复请求。

3. 后端转换(最优解)

协调后端将内嵌PNG的SVG直接转换为PNG/JPG格式返回,或统一返回纯SVG,彻底避免前端处理格式差异的问题,性能和稳定性最优。

内容的提问来源于stack exchange,提问作者Murtaza Mehmood

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 11:45:30