IBDesignable from External Framework?
Alright, so apparently if you want to include @IBDesignabl
e and @IBInspectable
in a framework
the framework
has to either be:
- Included WITHIN the consuming application, so the
Framework
will not be in it's own project. (i.e. adding a framework as a target by doing something like'file -> new.. -> target -> framework'
from consuming application). - Include the
external framework
as aCocoaPod
in your consuming application. This actually adds theframework
as a target rather than just linking theframework
against the application.- There is a way to include local
CocoaPods
in a project, so don't worry you don't have to deploy yourframework
to the public just to do this.
- There is a way to include local
UICircularProgressRing solves this by adding the Swift file containing the @IBDesignable
class in the framework Headers
folder. To do this, select the Xcode project of the framework, select the framework's target, then go to Build Phases tab and expand the Headers phase, and drop the Swift file into Public:
I have a workaround. With this workaround, you don't need to add the framework as target. So it works with Carthage.
@IBDesignable
class MyCustomView: CustomView {
@IBInspectable override var bgColor: NSColor {
get {
return super.bgColor
}
set {
super.bgColor = newValue
}
}
}
Create a sub class (MyCustomView
) of the custom class (CustomView
) in the destination project (not in the framework project), and mark the sub class as @IBDesignable. Use sub class in your app. This way makes @IBDesignable work.
Inside the sub class, override those @IBInspectable properties(bgColor
), this way makes @IBInspectable work.
You might encounter this issue: Loading code from a framework for a custom control in IBDesignable Following this guide to solve it: http://www.dribin.org/dave/blog/archives/2009/11/15/rpath/
And please make both the custom class and its inspectable properties public otherwise this method doesn't compile.
Please leave comments if you cannot get it work.
I have found a way to use designables and inspectables with Cocoa Touch frameworks. The instructions below are for an Objective-C project and Xcode 8 (I didn't test on older versions), and should be identical if Swift code is involved.
Since designables are not discovered by Interface Builder in frameworks, it is useless to mark classes as IB_DESIGNABLE
in the framework headers. Interface Builder will only discover designable classes when compiling project source files. The idea is therefore to supply this information as a framework companion source file, which clients can then compile with their project.
I discovered that you do not have to subclass to mark a framework class as designable in a project. You can namely simply annotate each class which must be designable through a category declared in the companion .m
source file, e.g.:
IB_DESIGNABLE
@interface MyCustomView (Designable)
@end
In fact, the code does not even have to compile, you could wrap it within an enclosing #if 0 ... #endif
and it would still work. All that is needed is that the class is somehow associated with the IB_DESIGNABLE
attribute.
With this information in mind, here is how to make designables work with Cocoa Touch frameworks:
If you are a framework vendor:
- If needed, have the component which must be designable implement
-prepareForInterfaceBuilder
- Add a Folder reference (blue folder) to your framework target, with the companion
.m
file in it. A possible naming convention would be to name the folderDesignables
and the file within itMyFrameworkNameDesignables.m
, but you can choose whatever you like most. - In the
.m
file, create a category like the one above for each view which must be designable. The file itself must be compilable by client projects, which means you either need to make the necessary imports (e.g. your framework global public header#import <MyFramework/MyFramework.h>
) or use the#if 0 ... #endif
trick above
By enclosing the file within a blue folder, we ensure the folder is copied as is in the final .framework
product, without the companion source file being compiled. Moreover, as the folder is part of the framework bundle, it is available for all clients of the framework, whether they integrate it directly or using Carthage.
If you have a demo project using the framework as target dependency, and if your framework depends on other frameworks, you will run into dlopen
issues when trying to render designable views in the demo project. This is because IB_DESIGNABLE
attributes are discovered in the framework target (since the Designables
folder has been added to it), which Xcode pre-builds in the Build/Intermediates/IBDesignables
derived data folder corresponding to your project. If you look at the content of this folder, framework dependencies are missing, leading to the dlopen
issues.
To fix rendering in your demo, simply add a Copy Files phase to the framework target, add each required framework dependency to the file list, and set Products directory as destination. Now, when Xcode builds your demo for rendering, it will include the dependencies as well.
If you are the user of a framework with designable support:
- Add the framework (and all its framework dependencies, if any) as embedded binary to your target
- Retrieve the companion source file from the framework bundle and copy it into your project, adding it to your target. Adding the file located within the framework or using a symbolic link sadly does not work, as Xcode does not seem to look within frameworks at all
- Add an instance of the designable view class (
MyCustomView
in our example above) to a storyboard. Interface Builder should build the project and render the view
This solution is not perfect since you still have to manually copy a supplied source file, which could change between framework versions. But it works pretty well, though, and provides everything required within the framework bundle itself.