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资源):
- Podfile添加:
pod 'SwiftSVG' - 修改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
相关产品推荐
相关产品推荐

