I'm working on a project which I'm getting data from API and in that API I'm getting url for image. but these images are not in PNG,JPEG or JPG formats. They are SVG(Scalable Vector Graphic) images. So as I know there is no direct method, function or maybe iOS project Framework library exist which is used to render SVG images. So we have bunch of Pods which is actually use for rendering these image like(SDWebImage,SDWebImageSVGCoder,SDWebImageSVGKitPlugin,SVGKit). I used them all but all these Pods didn't solve my problem. So let discuss what problem that I'm facing right now.
Here's my ViewController class code
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) {
}
}
My TableViewCell class
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)
}
}
But when I run this code only 3 images rendered first one is dummy or default case, but rest of three are not rendered. Then I try to locate the issue and then I found that not all images are true SVG images actually some of them are Raster image. here's I'm sharing data of both image type of images.
Those Images who's rendered successfully has data like this
<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>
But those image who's not rendered but display white or null images has this type of data
<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>
So my Screen look like this after running the project.
I found solution for this myself to just put condition and first check every image response. If image is pure written in SVG then I'm calling Pod function otherwise I'm using my own custom code to extract base64Encoded string and then render image. But it creating some performance issue. As you know library also working in Async calling and I'm also using that to images are sometime load and sometime not show correct image.
my custom code is here
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
}
}
})
}
}
}
}
}
I just want to solve my issue to put my custom code into library because I don't know about Objective C and SDWebImageSVGCoder pod is written in Objective C. If this is not possible then kindly give me a suggestion, or any other library pod anything which I use and solve my problem and display my list look like this.
SDImageCoder
, which will do you custom decoding, and if it's not custom one, will use the default SVG decoder works. Setting it in thecontext: [.imageCoder : YourCustomDecoder]
parameter. If not, as suggested, prefer callingsd_setImage()
, and in the completion, if there is an error, test you method. – Hobnailed