Categories
programming

SVGKit 2013 – Recipes

SVG is an awesome image format thats widely used, works in all browsers. SVG graphics make better apps and better games – and automatically “upgrade” themselves for future devices.

This post gives some simple 1-line / few lines of code recipes for using some of the main features of SVGKit – SVG implementation for iOS/OS X.

NOTE: this post refers to the 1.0.x version of SVGKit

Basic usage / installation

Install instructions are on the main GitHub page.

Basic usage / first-time usage info is on SVGKit 2013 – Usage.

Recipes

Load an SVG file like loading a PNG file

[objc]
SVGKImage* newImage = [SVGKImage imageNamed:@"imagename"];
[/objc]

Display an SVG file on-screen using an ImageView

[objc]
[self.view addSubView: [[SVGKFastImageView alloc] initWithImage:newImage];
[/objc]

…or an ImageView that supports CoreAnimation for every element

NB: the “Fast” imageview above saves everything as a single layer. Good for rendering, but bad for interaction. The “Layered” imageview here allows you to tap on any layer, rotate/animate/fade/drop-shadow/glow any element, etc.

[objc]
[self.view addSubView: [[SVGKLayeredImageView alloc] initWithImage:newImage];
[/objc]

Use a URL to load an SVG (open an SVG file *directly* from the web)

[objc]
// Splitting URL to multiple lines to make blogpost
// easier to read…
NSString* longURL = [NSString stringWithFormat:@"%@%@%@",
@"http://upload.wikimedia.org/",
@"wikipedia/commons/f/fd/",
@"Ghostscript_Tiger.svg";
NSURL* url = [NSURL urlWithString:longURL];
SVGKImage* newImage = [SVGKImage imageWithContentsOfURL:url];
[/objc]

Open an SVG from a custom source (maybe an in-memory SVG creation method)

First, create a class that conforms to SVGKSourceReader, i.e.:

[objc]
@interface MyNewClass : NSObject <SVGKSourceReader>

[/objc]

…then subclass SVGKSource, and over-ride the method:

[objc]
-(NSObject<SVGKSourceReader>*) newReader:(NSError**) error
[/objc]

Finally, use your customized SVGKSource to load your SVG:

[objc]
SVGKSource* myCustomSource = [[[MyCustomClass alloc] init] newReader:nil];

/** Other examples, using the default SVGKSource class:
SVGKSource* urlSource = [SVGKSource sourceFromURL: [NSURL …];
SVGKSource* fileSource = [SVGKSource sourceFromFilename: @"monkey.svg"];
*/

SVGKImage* newImage = [SVGKImage imageWithSource:myCustomSource];
[/objc]

Search an SVG file for particular tags / nodes / elements

SVG is an XML file, containing XML tags/nodes such as: “<svg>”, “<g>”, “<path>”, “<linearGradient>”, etc.

[objc]
NSString* tagToFind = @"linearGradient";
NodeList* result = [svgImage.DOMDocument getElementsByTagName:tagToFind];

for( Element* domElement in result )
{
// You can use the Element object directly:
domElement.tagName;

// …or, if it was parsed by the SVG parser, you can convert it to an SVGElement:
SVGElement* svgElement = (SVGElement*) domElement;

}
[/objc]

Search for sub-tags / sub-nodes of a particular tag/node

If you already have an SVGElement reference, you can search all its descendants:

[objc]
// To search the entire document, use:

// SVGElement* startPoint = newImage.DOMTree;
SVGElement* startPoint = … // from your code

NSString* tagToFind = @"linearGradient";
NodeList* result = [startPoint getElementsByTagName:tagToFind];

for( Element* domElement in result )
{
SVGElement* svgElement = (SVGElement*) domElement; // if your tags are all supported by SVGKit, they will be converted to SVGElement instances

}
[/objc]

Get a list of ALL descendant tags (from a particular node down)

[objc]
SVGElement* startPoint = … // e.g. for whole SVG doc, use: newImage.DOMDocument;
NodeList* allElements = [startPoint getElementsByTagName:@"*"];
[/objc]

Render one small part of the SVG file on its own

E.g. if you want to display a subset of the SVG, or want to export a single element:
[objc]
NSString* idInSVGFile = … // assuming your SVG file has an "id" attribute for this node
CALayer* absoluteLayer = [newImage newCopyPositionedAbsoluteLayerWithIdentifier:isInSVGFile];

// NB: "absoluteLayer" is now positioned in absolute space;
// if you add it to your window using e.g.:
[self.view.layer addSublayer: absoluteLayer];
// …it will appear in the same place as it appeared before,
// keeping all the offsets, rotations, etc
[/objc]

NOTE: there are several “newCopy…” methods, and each has different effects and outcomes. Read the method docs for each to decide which one you want to use. In a future version of SVGKit, we’d like to move these methods into a class of their own, and make it easier to see which one does what

Customise the parsing, using your own parser extensions

Create your custom class that adheres to SVGKParserExtension:
[objc]
@interface MyCustomSVGParserExtension : NSObject <SVGKParserExtension>

[/objc]
Then create a parser, INCLUDE THE DEFAULT extensions, and add your one on the end:
[objc]
MyCustomSVGParserExtension* myCustomExtension = [[MyCustomSVGParserExtension alloc] init];

SVGKParser* parser = [[SVGKParser alloc] init];
[parser addDefaultSVGParserExtensions]; // HIGHLY RECOMMENDED
[parser addParserExtension:myCustomExtension];

SVGKParseResult* result = [parser parseSynchronously];

SVGKImage* newImage = [[SVGKImage alloc] initWithParsedSVG:result];
[/objc]

Get help on why parsing failed (and warnings and line numbers!)

[objc]
SVGKImage* newImage = … // use methods above

// EITHER: parse using default parser:
SVGKParseResult* parseResult1 = newImage.parseErrorsAndWarnings; // this is a convenience pointer to (SVGKParser*).currentParseRun

// OR: use a custom parser:
SVGKParser* parser = … // use methods above
SVGKParseResult* parseResult2 = parser.currentParseRun;
[/objc]

And then you have the following info:
[objc]
/* array of NSError objects, each one a "WARNING" from the parser */
parseResult.warnings;

/* array of NSError objects, each one a "FATAL ERROR" from the parser – if your SVG didn’t render at all, this is why! */
parseResult.errorsFatal;

/* array of NSError objects, each one a "RECOVERABLE ERROR" from the parser – if your SVG didn’t render correctly, this is why! (although you probably still got to see something) */
parseResult.errorsRecoverable;
[/objc]

UPDATE: more Recipes!

For more recipes, see SVGKit Recipes part 2

(this covers answers to some of the common questions asked in the Comments below)