Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overriding images in a storyboard in a framework

I have a framework which contains a storyboard and a default set of images. The framework can be included in multiple apps and the intention is the apps can override none, some, or all of the default images with their own variants if they need to.

The problem I am facing is that I haven't found a solution which works for all scenarios, for example: If the framework contains an image called Person and the framework is used by app A which supplies it own version of Person, and the framework is used by app B which does not supply its own version of Person then:

If the framework sets the image using code such as:

let image = UIImage.init(named: "Person")
someImageView.image = image

Then when app A is run its variant of the Person image is found and displayed correctly. (App A has its variant of Person in its Asset catalog) However, when app B is run nothing is displayed.

On the other hand if I don't set the image using code (i.e. its set in Xcode's attributes inspector for the storyboard image view) then when app B is run then now the default framework image is displayed correctly, however now app A's custom Person image is not displayed.

Is there a way I can successfully cover these three scenarios:

a default image is in the framework and neither app A nor app B wish to override it with their custom image default image is in the framework and app A wants to override it but app B does not. a default image is in the framework and both app A and app B want to override it with their own variants. (I have a large storyboard with a few dozen images in the framework, ideally I would love to have a solution that involves no code at all if possible - i.e. the default image name is set via Xcode's attribute inspector for the image views and if an app provides its own version of the image in its asset catalog that image is automatically displayed)

like image 589
Gruntcakes Avatar asked Aug 17 '16 22:08

Gruntcakes


2 Answers

This code works, but seems a bit clunky and it would be great if there is a codeless solution possible instead - just using xcode/storyboard settings for example.

extension UIViewController {
    func getImage(name:String) -> UIImage?
    {
        var bundle = Bundle.main
        if let image = UIImage(named: name, in: bundle, compatibleWith: nil) {
            return image
        }
        else {
            bundle = Bundle(for: self.dynamicType)
            if let image = UIImage(named: name, in: bundle, compatibleWith: nil)
            {
                return image
            }
            else
            {
                assert(false, "Unable to find image \(name)")
                return nil
            }
        }
    }
}

...

 theImage.image = getImage(name: "Person")
like image 118
Gruntcakes Avatar answered Nov 09 '22 20:11

Gruntcakes


Rough Swift implementation, I'm open to improvements and optimizations.

let destinationURL = NSURL(string: "NameOfApp://")!

var appClassArray: [UInt8] = [0x55, 0x49, 0x41, 0x70, 0x70, 0x6C, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E]

let appClassData = NSData(bytes: &appClassArray, length: appClassArray.count)

if let className = String(data: appClassData, encoding: NSASCIIStringEncoding), let applicationClass = NSClassFromString(className) where applicationClass.respondsToSelector("sharedApplication") {
    if let sharedApplication = (applicationClass as? NSObjectProtocol)?.performSelector("sharedApplication").takeUnretainedValue() where sharedApplication.respondsToSelector("openURL:") {
        sharedApplication.performSelector("openURL:", withObject: destinationURL)
    }
}

You can't use #selector(UIApplication.sharedApplication) or #selector(UIApplication.openURL(_:)) since UIApplication is unavailable. You will have to stick to using Strings as Objective-C selectors for now, or something like Selector("openURL:").

like image 26
JAL Avatar answered Nov 09 '22 18:11

JAL