GenerallyHelpfulSoftware / SVGgh

A framework for using SVG artwork in iOS Apps. Includes a UIView and a button class, printing and PDF export.
MIT License
141 stars 38 forks source link

SVGgh an SVG Rendering Framework for iOS

CocoaPods Carthage compatible

Author Glenn R. Howes, owner Generally Helpful Software

A Note on the Future

At this point (2021), I'd strongly suggest looking into using SFSymbols for much of what I've used SVGgh for in the past. Still SVGs are a bit less complicated to make than a fully weighted SFSymbol.

Introduction

In my own apps, I've often wished to avoid using bitmapped images for my interface elements. Often, I'll need to add PNG files for Retina, and non-retina, iPhone and iPad, and find myself confined to what I can do with an interface in terms of stretching elements. And all this artwork made my app bulky. So, I decided to implement an SVG renderer which could use standard Scalable Vector Graphics documents to draw button icons, background art or whatever my art needs were. I have Apps in the App Store like SVG Paths whose only PNG files are the required icons.

Features

Handles shapes quite well such as paths, ellipses, circles, rectangles, polygons, polylines, and arcs with all the standard style attributes. Implements basic text and font handling, including rough text along a path. Implements both linear and radial gradients, including applying gradients to strokes and text. Implements scale invariant line widths. Provides a static UIView subclass, a UIControl button, and a segmented control. All are configurable from either nib or storyboards. Supports embedded bitmap images in standard formats.

Can export to PDF, create UIImages, and print via the UIPrintInteractionController mechanism.

Can be integrated into SwiftUI Views. See the card example in the Debugging App.

In the Xcode debugger, you can use the QuickLook button (the eye icon) to see the contents of an SVGRenderer.

Limitations

The entire SVG specification is not implemented. At present, it only implements the portions of the specification I needed or thought I might need. In particular, it doesn't support SVG fonts, animation, Javascript, css, or effects. Also, some attributes remain unimplemented or partially implemented, for example the width attribute of an svg entity cannot be expressed as a percentage. I hope users of this library will contribute back implementations of at least some of these.

There are undoubtably bugs but I've used this library in all 8 apps I have in the App Store without issue so it is reasonably stable. Also, I would not label this a high performance renderer although I've never had cause to complain about it in the way I use it.

The included library assumes ARC style memory management. It's also been set to support iOS 9 and up. I've moved to using code annotations such as nullable so it requires a recent version of Xcode to compile. Supports both traditional and module based framework includes.

Originally, this was distributed as a static library, but that is not a modern way to use it. So the enclosed project will build a framework, and most developers will probably find the use of CocoaPods more enjoyable. Requires CocoaPods 0.39 or above.

I've enabled IB_DESIGNABLE for the view classes for environments with a minimum OS version of iOS 8 and above. This will allow artwork and widgets to be visible in Interface Builder. CocoaPods users should put use_frameworks! in their Podfile.

The segmented control is not ready for use in tvOS apps.

As a Black Box Library

If you just want to use the code in your app and are uninterested in the underlying engine, the included Xcode project generates a framework (SVGgh) with the following public headers. By the way, the reason that classes tend to have a GH (Generally Helpful, or Glenn Howes) prefix is not narcissism, but an attempt of getting around the lack of a namespace in plain Objective-C.

If you are familiar with using CocoaPods and using it in your project

If you are familiar with using Carthage and usin it in your project

For more information follow https://github.com/Carthage/Carthage

If you are not using CocoaPods

To compile the framework.

To use, you'll want to follow the following steps:

Once you have installed the library

# import <SVGgh/SVGgh.h>

@implementation YourAppDelegate

+(void) initialize
{
    [super initialize];
    MakeSureSVGghLinks(); // classes only used in Storyboards might not link otherwise
    [GHControlFactory setDefaultScheme:kColorSchemeClear];
    [GHControlFactory setDefaultTextColor:[UIColor greenColor]];
}
...
    import SVGgh

    override class func initialize()
    {
        super.initialize()        
        MakeSureSVGghLinks()
        let tintColor = UIColorFromSVGColorString("#5D6")!
        GHControlFactory.setDefaultButtonTint(tintColor)
    }

To add a button to a .xib file or storyboard:

Key Path Type Value
Artwork Path String Artwork/MenuButton (assumes you have such an asset)
Scheme Number Number 3
Key Path Type Value
Title Localized String My Label
Scheme Number Number 3
Constant Enumeration Description
0 kColorSchemeiOS Round solid buttons with a thin inset ring
1 kColorSchemeMachine Grey top to bottom gradient with inset ring
2 kColorSchemeKeyboard Gray gradient, light on top, no ring
3 kColorSchemeClear Gray gradient, light on top, ring
4 kColorSchemeEmpty No chrome. Just the artwork or label
5 kColorSchemeHomeTheatre Garish gold gradient, ring
6 kColorSchemeiOSVersionAppropriate kColorSchemeEmpty on iOS, kColorSchemeTVOS on AppleTV
7 kColorSchemeFlatAndBoxy Solid fill color with square corners.
8 kColorSchemeTVOS Attempt to mimic the appearance and behavior of an AppleTV button.

To add a static view to a .xib file or storyboard:

Key Path Type Value
Artwork Path String Artwork/MyBackground

Hints

<switch>
    <g systemLanguage ="zh">
        <text x="24" y="20" font-family="Helvetica" font-size="22"  fill="grey">绝对直线</text>
        <text x="218" y="95" font-family="Helvetica" font-style="italic" font-size="20" text-anchor="end" fill="blue">终点 x, y</text>
    </g>
    <g>
        <text x="24" y="20" font-family="Helvetica" font-size="22"  fill="grey">line to</text>
        <text x="218" y="95" font-family="Helvetica" font-style="italic" font-size="20" text-anchor="end" fill="blue">end x, y</text>
    </g>
</switch>

Under the Hood

If you are inclined to fix bugs or add features, and please do, then you'll be interested in the general mechanism by which an SVG document is converted to an onscreen image.

The starting point is the SVGRenderer, which as a subclass of SVGParser is capable of loading in the XML of an SVG document and being used to render into a Core Graphics context (a CGContextRef). The parser takes the XML and converts it to a tree composed of NSDictionaries and NSArrays of NSDictionaries. The NSDictionary at the root of this tree is used to create an GHShapeGroup which starts the process of building up a tree of SVGAttributedObjects, each of which knows how to render themselves. In general, when sending a message to an SVGAttributedObject, an object which implements the SVGContext protocol is provided so that certain state information is available to the SVGAttributedObject such as the currentColor or a method to look up the document tree for either a named object or an attribute defined by a parent.

I've gone through and added Doxygen style comments to all the header files, so there is some hope of finding your way.

Attribution

While the vast majority of the code in this release was written by me. There are a couple of classes or categories that were found online but have a flexible enough license for me to include here.