0

I want an easy way to load SFSymbol images with configuration, if possible. Else just load the image normally. So I have this extension and I have all SFSymbols I need in in my assetcatalog with the same name as the symbol (in this case I have "circle" and "circle.fill").

However, in iOS 12 this gives me "circle"!

I assume what's happening is that it counts the dot/period (i.e. circle . fill) as a file extension and assumes the extension is wrong so just grab the circle image with the 'other extension'. Even tho it's not. Is there an easy fix for this? Is this intended?

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        let imageView = UIImageView(frame: CGRect(x: 30, y: 30, width: 50, height: 50))
        imageView.image = UIImage.image(sfsymbolName: "circle.fill")
        view.addSubview(imageView)
    }
}

extension UIImage {
    static func image(sfsymbolName: String, config: Any? = nil) -> UIImage? {
        if #available(iOS 13.0, *), let sfImage = UIImage(systemName: sfsymbolName, withConfiguration: config as? UIImage.Configuration) {
            return sfImage
        } else {
            return UIImage(named: sfsymbolName)
        }
    }
}
thisIsTheFoxe
  • 1,584
  • 1
  • 9
  • 30
  • If you really think the dot is the problem, use a hyphen instead (in the asset catalog image set name) and substitute hyphen for dot in your extension. – matt Apr 06 '22 at 09:46
  • Yes that would work, but it's not very nice and also not easy when having hundreds of images.. is there any other way? :/ – thisIsTheFoxe Apr 06 '22 at 09:53
  • @thisIsTheFoxe - you may want to try some additional debugging... I just added two different images to an asset catalog: `test.png` and `test.fill.png`. When I run `let img1 = UIImage(named: "test")` and `let img2 = UIImage(named: "test.fill")` I get two different images. – DonMag Apr 06 '22 at 13:07
  • @DonMag are you sure were on iOS 12? It work as I would expect it on iOS 13+ afaik – thisIsTheFoxe Apr 06 '22 at 13:12
  • @thisIsTheFoxe - whoops... yeah, just tried it on iOS 12 and see the same results as you (do you **really** need to continue supporting 12?). Are your "fallback" image resources pngs? – DonMag Apr 06 '22 at 13:57
  • @thisIsTheFoxe As (falsely) attributed to Einstein, doing the same thing repeatedly and expecting a different result is lunacy. You've proved that having a dot in an image set name is not a good idea. The solution is Don't Do That. – matt Apr 06 '22 at 15:27
  • 1
    @thisIsTheFoxe - perhaps worth noting... using `.png` files as the "fallback" images but ***not*** adding them to the assist catalog, `let img1 = UIImage(named: "test") and let img2 = UIImage(named: "test.fill")` ***does*** load two different images... including correct loading of `@2x` / `@3x` variations. – DonMag Apr 06 '22 at 17:07
  • @DonMag if I could've, I would've. That's the reason I posted this. I was hoping for some other way to get the images from the asset catalog but I guess that's it. However, do you think this is intended behaviour or a bug in Xcode / UIKit? – thisIsTheFoxe Apr 07 '22 at 07:49
  • 1
    @thisIsTheFoxe - hmmm... running on iOS 12, if I have "test.a" and "test.b" images in the asset catalog - but NO "test" image - I can load both ".a" and ".b". As soon as I add "test" though, that's the image loaded regardless of `UIImage(named: "test.a")` or `UIImage(named: "test.b")`. iOS 13 and later (well, I only checked on 13.3) doesn't have that problem. So, it's probably safe to say it was a "bug" – DonMag Apr 07 '22 at 12:21

1 Answers1

0

As mentioned in @matt's comment the probably best solution is to just not have images in you asset catalog with a dot (.). Instead use a different character which you can replace at runtime. Just make sure that that character also also doesn't appear in any SFSymbol name (e.g. an underscore _). Then you can load the image like this:

let image = loadImage(named: "circle_fill")

func loadImage(named imageName: String) -> UIImage? {
    let sfSymbolName = imageName.replacingOccurrences(of: "_", with: ".")
    if #available(iOS 13.0, *), let img = UIImage(systemName: sfSymbolName)
        return img
    } else {
        return UIImage(named: imageName)
    }
}

With the catalog file structure looking like:

|Assets.xcassets
|--circle.imageset
|----...
|--circle_fill.imageset
|----...
thisIsTheFoxe
  • 1,584
  • 1
  • 9
  • 30