1
1
mirror of https://github.com/exyte/Macaw.git synced 2024-11-13 05:07:24 +03:00

Fix #67. Implement SVGView

This commit is contained in:
shipinev 2016-11-24 17:45:16 +07:00
parent 5dd06ea894
commit 606f733fa0
4 changed files with 178 additions and 13 deletions

View File

@ -109,6 +109,8 @@
57CAB1361D7832E000FD8E47 /* triangle.svg in Resources */ = {isa = PBXBuildFile; fileRef = 57CAB12D1D7832E000FD8E47 /* triangle.svg */; };
57FCD2771D76EA4600CC0FB6 /* Macaw.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 57FCD26C1D76EA4600CC0FB6 /* Macaw.framework */; };
57FCD27C1D76EA4600CC0FB6 /* MacawTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57FCD27B1D76EA4600CC0FB6 /* MacawTests.swift */; };
662808C41DE6BC9900A61B95 /* CGFloat+Double.swift in Sources */ = {isa = PBXBuildFile; fileRef = 662808C31DE6BC9900A61B95 /* CGFloat+Double.swift */; };
66EA4AE41DE5AE030011818C /* SVGView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66EA4AE31DE5AE030011818C /* SVGView.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -239,6 +241,8 @@
57FCD2761D76EA4600CC0FB6 /* MacawTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MacawTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
57FCD27B1D76EA4600CC0FB6 /* MacawTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacawTests.swift; sourceTree = "<group>"; };
57FCD27D1D76EA4600CC0FB6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
662808C31DE6BC9900A61B95 /* CGFloat+Double.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CGFloat+Double.swift"; sourceTree = "<group>"; };
66EA4AE31DE5AE030011818C /* SVGView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SVGView.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -461,6 +465,7 @@
children = (
38A997E41D91888700AC4545 /* SVGParser.swift */,
38A997E51D91888700AC4545 /* SVGParserRegexHelper.swift */,
66EA4AE31DE5AE030011818C /* SVGView.swift */,
);
path = svg;
sourceTree = "<group>";
@ -470,6 +475,7 @@
children = (
38A997E71D91888700AC4545 /* CAAnimationClosure.swift */,
38A997E81D91888700AC4545 /* NSTimer+Closure.swift */,
662808C31DE6BC9900A61B95 /* CGFloat+Double.swift */,
);
path = thirdparty;
sourceTree = "<group>";
@ -719,6 +725,7 @@
38A997ED1D91888700AC4545 /* AnimatableVariable.swift in Sources */,
38A9982F1D91888700AC4545 /* Size.swift in Sources */,
38A997FE1D91888700AC4545 /* TransformHashable.swift in Sources */,
662808C41DE6BC9900A61B95 /* CGFloat+Double.swift in Sources */,
38A997FF1D91888700AC4545 /* OpacityGenerator.swift in Sources */,
38A998321D91888700AC4545 /* Image.swift in Sources */,
38A998181D91888700AC4545 /* LinearGradient.swift in Sources */,
@ -744,6 +751,7 @@
38A9983B1D91888700AC4545 /* ShapeRenderer.swift in Sources */,
38A998211D91888700AC4545 /* GeomUtils.swift in Sources */,
38A998131D91888700AC4545 /* Effect.swift in Sources */,
66EA4AE41DE5AE030011818C /* SVGView.swift in Sources */,
38A998141D91888700AC4545 /* Fill.swift in Sources */,
38A9983A1D91888700AC4545 /* RenderUtils.swift in Sources */,
38A998201D91888700AC4545 /* Ellipse.swift in Sources */,

View File

@ -7,19 +7,25 @@ import CoreGraphics
///
open class SVGParser {
/// Parse an SVG file identified by the specified name and file extension.
/// - returns: Root node of the corresponding Macaw scene.
open class func parse(path: String, ofType: String = "svg") -> Node {
let path = Bundle.main.path(forResource: path, ofType: ofType)
let text = try! String(contentsOfFile: path!, encoding: String.Encoding.utf8)
return SVGParser.parse(text: text)
}
/// Parse the specified content of an SVG file.
/// - returns: Root node of the corresponding Macaw scene.
open class func parse(text: String) -> Node {
return SVGParser(text).parse()
}
/// Parse an SVG file identified by the specified bundle, name and file extension.
/// - returns: Root node of the corresponding Macaw scene.
open class func parse(bundle: Bundle, path: String, ofType: String = "svg") -> Node {
let path = bundle.path(forResource: path, ofType: ofType)
let text = try! String(contentsOfFile: path!, encoding: String.Encoding.utf8)
return SVGParser.parse(text: text)
}
/// Parse an SVG file identified by the specified name and file extension.
/// - returns: Root node of the corresponding Macaw scene.
open class func parse(path: String, ofType: String = "svg") -> Node {
return SVGParser.parse(bundle: Bundle.main, path: path, ofType: ofType)
}
/// Parse the specified content of an SVG file.
/// - returns: Root node of the corresponding Macaw scene.
open class func parse(text: String) -> Node {
return SVGParser(text).parse()
}
let moveToAbsolute = Character("M")
let moveToRelative = Character("m")

142
Source/svg/SVGView.swift Normal file
View File

@ -0,0 +1,142 @@
import UIKit
open class SVGView: MacawView {
@IBInspectable var fileName: String? {
didSet {
render()
}
}
private func render() {
let viewBounds = self.bounds
let svgNode = SVGParser.parse(
path: fileName ?? ""
)
if let nodeBounds = svgNode.bounds()?.cgRect() {
let svgWidth = nodeBounds.origin.x + nodeBounds.width
let svgHeight = nodeBounds.origin.y + nodeBounds.height
let viewAspectRatio = viewBounds.width / viewBounds.height
let svgAspectRatio = svgWidth / svgHeight
let scaleX = viewBounds.width / svgWidth
let scaleY = viewBounds.height / svgHeight
switch self.contentMode {
case .scaleToFill:
svgNode.place = Transform.scale(
sx: Double(scaleX),
sy: Double(scaleY)
)
case .scaleAspectFill:
let scaleX, scaleY: CGFloat
if viewAspectRatio > svgAspectRatio {
scaleX = viewBounds.width / svgWidth
scaleY = viewBounds.width / (svgWidth / svgAspectRatio)
} else {
scaleX = viewBounds.height / (svgHeight / svgAspectRatio)
scaleY = viewBounds.height / svgHeight
}
let calculatedWidth = svgWidth * scaleX
let calculatedHeight = svgHeight * scaleY
svgNode.place = Transform.move(
dx: (viewBounds.width / 2 - calculatedWidth / 2).doubleValue,
dy: (viewBounds.height / 2 - calculatedHeight / 2).doubleValue
).scale(
sx: scaleX.doubleValue,
sy: scaleX.doubleValue
)
case .scaleAspectFit:
let calculatedXWidth = scaleX * svgWidth
let calculatedXHeight = scaleX * svgHeight
let calculatedYWidth = scaleY * svgWidth
let calculatedYHeight = scaleY * svgHeight
if calculatedXWidth <= viewBounds.width && calculatedXHeight <= viewBounds.height {
svgNode.place = Transform.move(
dx: (viewBounds.midX - calculatedXWidth / 2).doubleValue,
dy: (viewBounds.midY - calculatedXHeight / 2).doubleValue
).scale(
sx: scaleX.doubleValue,
sy: scaleX.doubleValue
)
} else if calculatedYWidth <= viewBounds.width && calculatedYHeight <= viewBounds.height {
svgNode.place = Transform.move(
dx: (viewBounds.midX - calculatedYWidth / 2).doubleValue,
dy: (viewBounds.midY - calculatedYHeight / 2).doubleValue
).scale(
sx: scaleY.doubleValue,
sy: scaleY.doubleValue
)
}
case .center:
svgNode.place = Transform.move(
dx: getMidX(viewBounds, nodeBounds).doubleValue,
dy: getMidY(viewBounds, nodeBounds).doubleValue
)
case .top:
svgNode.place = Transform.move(
dx: getMidX(viewBounds, nodeBounds).doubleValue,
dy: 0
)
case .bottom:
svgNode.place = Transform.move(
dx: getMidX(viewBounds, nodeBounds).doubleValue,
dy: getBottom(viewBounds, nodeBounds).doubleValue
)
case .left:
svgNode.place = Transform.move(
dx: 0,
dy: getMidY(viewBounds, nodeBounds).doubleValue
)
case .right:
svgNode.place = Transform.move(
dx: getRight(viewBounds, nodeBounds).doubleValue,
dy: getMidY(viewBounds, nodeBounds).doubleValue
)
case .topLeft:
break
case .topRight:
svgNode.place = Transform.move(
dx: getRight(viewBounds, nodeBounds).doubleValue,
dy: 0
)
case .bottomLeft:
svgNode.place = Transform.move(
dx: 0,
dy: getBottom(viewBounds, nodeBounds).doubleValue
)
case .bottomRight:
svgNode.place = Transform.move(
dx: getRight(viewBounds, nodeBounds).doubleValue,
dy: getBottom(viewBounds, nodeBounds).doubleValue
)
case .redraw:
break
}
}
self.node = svgNode
}
private func getMidX(_ viewBounds: CGRect, _ nodeBounds: CGRect) -> CGFloat {
let viewMidX = viewBounds.midX
let nodeMidX = nodeBounds.midX + nodeBounds.origin.x
return viewMidX - nodeMidX
}
private func getMidY(_ viewBounds: CGRect, _ nodeBounds: CGRect) -> CGFloat {
let viewMidY = viewBounds.midY
let nodeMidY = nodeBounds.midY + nodeBounds.origin.y
return viewMidY - nodeMidY
}
private func getBottom(_ viewBounds: CGRect, _ nodeBounds: CGRect) -> CGFloat {
return viewBounds.maxY - nodeBounds.maxY + nodeBounds.origin.y
}
private func getRight(_ viewBounds: CGRect, _ nodeBounds: CGRect) -> CGFloat {
return viewBounds.maxX - nodeBounds.maxX + nodeBounds.origin.x
}
}

View File

@ -0,0 +1,9 @@
import Foundation
internal extension CGFloat {
var doubleValue: Double {
return Double(self)
}
}