Skip to content

Commit

Permalink
More complete documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
mchoe committed Jul 27, 2017
1 parent 4a81fc8 commit ce6b19f
Show file tree
Hide file tree
Showing 172 changed files with 9,166 additions and 26,128 deletions.
23 changes: 4 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,13 @@ SwiftSVG

A simple single pass SVG parser written in Swift.

I also wanted to make it possible to use SwiftSVG without installing the full Breakfast Framework, so feel free to use only SwiftSVG in your next project. It was meant to be as lightweight and modular as possible.


Development Priorities
======================

Developer Joy really encapsulates a few different concepts:
1. Clarity and an ability to easily reason about the code
2. Code length and DRY principles
3. Maintainability and tech debt
4. API Stability
4. Stability and Predicatability

To me, code at its best seamlessly marries performance with Developer Joy where both goals are equally being advanced. The reality however, means that compromises may have to made to maximize another goal.


Features
========

- Multiple interface options (String, UIBezierPath, CAShapeLayer, UIView, and IBDesignable Interface Builder subclass)
- Strives to be performant. Takes only one pass through path string.
- Low memory usage
- Parsing performance that meets or beats other popular SVG Frameworks
- Optimized for extension, flexibility and developer joy
- Multiple interface options (UIBezierPath, CAShapeLayer, UIView, and IBDesignable Interface Builder subclass)
- Tested and documented

Table of Contents
-----------------
Expand Down
12 changes: 7 additions & 5 deletions SwiftSVG.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@
38048F471F24520200179C95 /* CALayer+Sublayers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38048F461F24520200179C95 /* CALayer+Sublayers.swift */; };
38048F481F24527A00179C95 /* CALayer+Sublayers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38048F461F24520200179C95 /* CALayer+Sublayers.swift */; };
38048F4E1F24710100179C95 /* ukulele.svg in Resources */ = {isa = PBXBuildFile; fileRef = 38048F4A1F24705800179C95 /* ukulele.svg */; };
38048F4F1F24710100179C95 /* washington.svg in Resources */ = {isa = PBXBuildFile; fileRef = 38048F4B1F24705800179C95 /* washington.svg */; };
3807CCEA1F2866D900E78314 /* DelaysApplyingAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3807CCE91F2866D900E78314 /* DelaysApplyingAttributes.swift */; };
3807CCEB1F286B1B00E78314 /* DelaysApplyingAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3807CCE91F2866D900E78314 /* DelaysApplyingAttributes.swift */; };
380FECF81F000AA000EDB255 /* FloatingPoint+DegreesRadians.swift in Sources */ = {isa = PBXBuildFile; fileRef = 380FECF71F000AA000EDB255 /* FloatingPoint+DegreesRadians.swift */; };
380FED101F002C0E00EDB255 /* Stylable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 380FED0F1F002C0E00EDB255 /* Stylable.swift */; };
380FED331F00FEF000EDB255 /* PerformanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 380FED321F00FEF000EDB255 /* PerformanceTests.swift */; };
Expand Down Expand Up @@ -179,7 +180,7 @@
38048F371F24210F00179C95 /* UIView+SVG.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIView+SVG.swift"; path = "SwiftSVG/SVGExtensions/UIView+SVG.swift"; sourceTree = SOURCE_ROOT; };
38048F461F24520200179C95 /* CALayer+Sublayers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CALayer+Sublayers.swift"; sourceTree = "<group>"; };
38048F4A1F24705800179C95 /* ukulele.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; name = ukulele.svg; path = svgs/ukulele.svg; sourceTree = "<group>"; };
38048F4B1F24705800179C95 /* washington.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; name = washington.svg; path = svgs/washington.svg; sourceTree = "<group>"; };
3807CCE91F2866D900E78314 /* DelaysApplyingAttributes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DelaysApplyingAttributes.swift; path = Attributes/DelaysApplyingAttributes.swift; sourceTree = "<group>"; };
380FECF71F000AA000EDB255 /* FloatingPoint+DegreesRadians.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "FloatingPoint+DegreesRadians.swift"; sourceTree = "<group>"; };
380FED0F1F002C0E00EDB255 /* Stylable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Stylable.swift; sourceTree = "<group>"; };
380FED321F00FEF000EDB255 /* PerformanceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PerformanceTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -361,18 +362,18 @@
isa = PBXGroup;
children = (
38048F4A1F24705800179C95 /* ukulele.svg */,
38048F4B1F24705800179C95 /* washington.svg */,
);
name = Resources;
sourceTree = "<group>";
};
3813ED571EFC6FCF0054ECBD /* Attributes */ = {
isa = PBXGroup;
children = (
3807CCE91F2866D900E78314 /* DelaysApplyingAttributes.swift */,
3876E9601EFCBABB00F8C04E /* Fillable.swift */,
3876E9621EFCBAC500F8C04E /* Strokable.swift */,
3813ED581EFC703E0054ECBD /* Transformable.swift */,
380FED0F1F002C0E00EDB255 /* Stylable.swift */,
3813ED581EFC703E0054ECBD /* Transformable.swift */,
);
name = Attributes;
path = SVG;
Expand Down Expand Up @@ -567,7 +568,6 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
38048F4F1F24710100179C95 /* washington.svg in Resources */,
38048F4E1F24710100179C95 /* ukulele.svg in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down Expand Up @@ -620,6 +620,7 @@
177D98F51F01852E00A668B6 /* Stylable.swift in Sources */,
38048F431F24214400179C95 /* UIView+SVG.swift in Sources */,
381E99BD1EF9D05800839918 /* HorizontalLineToTests.swift in Sources */,
3807CCEB1F286B1B00E78314 /* DelaysApplyingAttributes.swift in Sources */,
38048F1E1F23DD1E00179C95 /* SVGEllipseTests.swift in Sources */,
38048F241F23DD7D00179C95 /* SVGPathTests.swift in Sources */,
176BC9E11EE51AE500F7B54C /* Dictionary+Add.swift in Sources */,
Expand Down Expand Up @@ -690,6 +691,7 @@
3876E9611EFCBABB00F8C04E /* Fillable.swift in Sources */,
176BC9F21EE62AF300F7B54C /* CoordinateLexer.swift in Sources */,
8AC4DBEA1CB37EA700137DC9 /* CrossPlatform.swift in Sources */,
3807CCEA1F2866D900E78314 /* DelaysApplyingAttributes.swift in Sources */,
38048F471F24520200179C95 /* CALayer+Sublayers.swift in Sources */,
176BC9D01EE513F400F7B54C /* SVGPolyline.swift in Sources */,
175093C11E6C669F00EF1853 /* SVGParserSupportedElements.swift in Sources */,
Expand Down
20 changes: 14 additions & 6 deletions SwiftSVG/CrossPlatform.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,22 @@ extension UIView {
#if os(iOS) || os(tvOS)
return self.layer
#elseif os(OSX)
if let l = self.layer {
return l
if let thisLayer = self.layer {
let transform = CGAffineTransform(
a: 1.0, b: 0.0,
c: 0.0, d: -1.0,
tx: 0.0, ty: self.bounds.size.height
)
thisLayer.setAffineTransform(transform)
return thisLayer
} else {
self.layer = CALayer()
self.layer?.frame = self.bounds
let flip = CATransform3DMakeScale(1.0, -1.0, 1.0)
let translate = CATransform3DMakeTranslation(0.0, self.bounds.size.height, 1.0)
self.layer?.sublayerTransform = CATransform3DConcat(flip, translate)
let transform = CGAffineTransform(
a: 1.0, b: 0.0,
c: 0.0, d: -1.0,
tx: 0.0, ty: self.bounds.size.height
)
self.layer?.setAffineTransform(transform)
self.wantsLayer = true
return self.layer!
}
Expand Down
58 changes: 58 additions & 0 deletions SwiftSVG/SVG/Attributes/DelaysApplyingAttributes.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//
// DelaysApplyingAttributes.swift
// SwiftSVG
//
//
// Copyright (c) 2017 Michael Choe
// http://www.github.com/mchoe
// http://www.straussmade.com/
// http://www.twitter.com/_mchoe
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.



#if os(iOS) || os(tvOS)
import UIKit
#elseif os(OSX)
import AppKit
#endif

/**
A protocol that describes an instance that will delay processing attributes, usually until in `didProcessElement(in container: SVGContainerElement?)` because either all path information isn't available or when the element needs to apply an attribute to all subelements.
*/
public protocol DelaysApplyingAttributes {

/// The attributes to apply to all sublayers after all subelements have been processed.
/// - parameter Key: The name of an element's attribute such as `d`, `fill`, and `rx`.
/// - parameter Value: The string value of the attribute passed from the parser, such as `"#ff00ee"`
var delayedAttributes: [String : String] { get set }
}

extension DelaysApplyingAttributes where Self : SVGElement {

func applyDelayedAttributes() {
for (attribute, value) in self.delayedAttributes {
guard let closure = self.supportedAttributes[attribute] else {
continue
}
closure(value)
}
}
}
1 change: 0 additions & 1 deletion SwiftSVG/SVG/Cache/SVGCache.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
/**
A minimal in-memory cache class for caching `SVGLayer`s. The `default` singleton is the default cache used and you can optionally create your own static singleton through an extension.
*/

open class SVGCache {

/// A singleton object that is the default store for `SVGlayer`s
Expand Down
6 changes: 6 additions & 0 deletions SwiftSVG/SVG/Elements/ParsesAsynchronously.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,18 @@
A protocol describing an instance that can manage elements that can parse asynchronously. In the `NSXMLSVGParser` implementation, the parser maintains a simple count of pending asynchronous tasks and decrements the count when an element has finished parsing. When the count has reached zero, a completion block is called
*/
protocol CanManageAsychronousParsing {
/**
The callback called when an `ParsesAsynchronously` element has finished parsing
*/
func finishedProcessing(_ shapeLayer: CAShapeLayer)
}

/**
A protocol describing an instance that parses asynchronously
*/
protocol ParsesAsynchronously {
/**
The delegate instance that can manage asynchronous parsing
*/
var asyncParseManager: CanManageAsychronousParsing? { get set }
}
13 changes: 12 additions & 1 deletion SwiftSVG/SVG/Elements/SVGCircle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,21 @@ final class SVGCircle: SVGShapeElement {
/// :nodoc:
static let elementName = "circle"

/**
The circle's center point. Defaults to `CGRect.zero`
*/
internal var circleCenter = CGPoint.zero

/**
The circle's radius. Defaults to `0`
*/
internal var circleRadius: CGFloat = 0

/// :nodoc:
internal var svgLayer = CAShapeLayer()
internal var supportedAttributes: [String : ((String) -> ())?] = [:]

/// :nodoc:
internal var supportedAttributes: [String : (String) -> ()] = [:]

/**
Function that parses the number string and sets this instance's radius
Expand Down
11 changes: 4 additions & 7 deletions SwiftSVG/SVG/Elements/SVGContainerElement.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,11 @@
A protocol that describes an instance that can store SVG sublayers and can apply a single attributes to all sublayers.
*/

public protocol SVGContainerElement: SVGElement, Fillable, Strokable, Transformable, Stylable {
public protocol SVGContainerElement: SVGElement, DelaysApplyingAttributes, Fillable, Strokable, Transformable, Stylable {

/// The layer that stores all the SVG sublayers
/**
The layer that stores all the SVG sublayers
*/
var containerLayer: CALayer { get set }

/// The attributes to apply to all sublayers after all subelements have been processed.
/// - parameter Key: The name of an element's attribute such as `d`, `fill`, and `rx`.
/// - parameter Value: The string value of the attribute passed from the parser, such as `"#ff00ee"`
var attributesToApply: [String : String] { get set }
}

6 changes: 4 additions & 2 deletions SwiftSVG/SVG/Elements/SVGElement.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,10 @@ public protocol SVGElement {
*/
static var elementName: String { get }

/// Dictionary of attributes of a given element that are supported by the `SVGParser`. Keys are the name of an element's attribute such as `d`, `fill`, and `rx`. Values are a closure that is used to process the given attribute.
var supportedAttributes: [String : ((String) -> ())?] { get set }
/**
Dictionary of attributes of a given element that are supported by the `SVGParser`. Keys are the name of an element's attribute such as `d`, `fill`, and `rx`. Values are a closure that is used to process the given attribute.
*/
var supportedAttributes: [String : (String) -> ()] { get set }


/**
Expand Down
18 changes: 16 additions & 2 deletions SwiftSVG/SVG/Elements/SVGEllipse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,31 @@
/**
Concrete implementation that creates a `CAShapeLayer` from a `<ellipse>` element and its attributes
*/

final class SVGEllipse: SVGShapeElement {

/// :nodoc:
static let elementName = "ellipse"

/**
The ellipse's center point. Defaults to `CGRect.zero`
*/
internal var ellipseCenter = CGPoint.zero

/**
The ellipse's x radius. Defaults to `CGRect.zero`
*/
internal var xRadius: CGFloat = 0

/**
The ellipse's x radius. Defaults to `CGRect.zero`
*/
internal var yRadius: CGFloat = 0

/// :nodoc:
internal var svgLayer = CAShapeLayer()
internal var supportedAttributes: [String : ((String) -> ())?] = [:]

/// :nodoc:
internal var supportedAttributes: [String : (String) -> ()] = [:]

/**
Function that parses the number string and sets this instance's x radius
Expand Down
Loading

0 comments on commit ce6b19f

Please sign in to comment.