iOS 显示SVG文件的方法

1. Swift iOS : 显示SVG文件的方法

SVG文件是矢量图标准之一,特点是可以缩放,并且可以用可以阅读的源代码的方式(而不是二进制)来存储图形信息。比如如下文件就是一个svg文件:<svgxmlns=“http://www.w3.org/2000/svg"viewBox="00350100”> <markerid="arrowhead"markerWidth="10"markerHeight=&q

SVG文件是矢量图标准之一,特点是可以缩放,并且可以用可以阅读的源代码的方式(而不是二进制)来存储图形信息。比如如下文件就是一个svg文件:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 350 100"> 
<defs> 
<marker id="arrowhead" markerWidth="10" markerHeight="7" 
refX="0" refY="3.5" orient="auto"> 
<polygon points="0 0, 10 3.5, 0 7" /> 
</marker> 
</defs> 
<line x1="0" y1="50" x2="250" y2="50" stroke="#000" 
stroke-width="8" marker-end="url(#arrowhead)" /> 
</svg>

它是一个箭头图。可以使用UIWebView视图加载此文件并显示。首先把SVG文件作为资源文件加入工程,命名为1.svg。

其实,使用如下代码加载此文件:

var path: String = Bundle.main.path(forResource: "1", ofType: "svg")! 
var url: NSURL = NSURL.fileURL(withPath: path) as NSURL 
var request: NSURLRequest = NSURLRequest(url: url as URL) 
webview?.loadRequest(request as URLRequest)
完整代码如下:

import UIKit 
@UIApplicationMain 
class AppDelegate: UIResponder, UIApplicationDelegate { 
var window: UIWindow? 
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 
self.window = UIWindow(frame: UIScreen.main.bounds) 
let page = Page() 
page.view.backgroundColor = .blue 
self.window!.rootViewController = page 
self.window?.makeKeyAndVisible() 
return true 
} 
} 
class Page: UIViewController { 
var count = 0 
var webview : UIWebView! 
var webview1 : UIWebView! 
override func viewDidLoad() { 
super.viewDidLoad() 
webview = UIWebView() 
webview?.frame = CGRect(x: 0, y: 100, width: 100, height: 100) 
view.addSubview(webview!) 
webview1 = UIWebView() 
webview1.frame = CGRect(x: 0, y: 200, width: 50, height: 50) 
view.addSubview(webview1!) 
let path: String = Bundle.main.path(forResource: "1", ofType: "svg")! 
let url: NSURL = NSURL.fileURL(withPath: path) as NSURL 
let request: NSURLRequest = NSURLRequest(url: url as URL) 
webview?.loadRequest(request as URLRequest) 
webview1.loadRequest(request as URLRequest) 
} 
}

2. 使用工具SVGKit解析SVG文件

SVGKit下载源码地址:点击这里下载SVGKit源码

2.1 SVGKit使用

我们按照正常的swift创建我们的项目,然后我们按照我们的GitHub的要求(Build the static library),这里我就不翻译了其实很简单的,创建了我们的静态库.a文件以及usr文件夹,这里注意一定要按照GitHub上的选择“Debug-universal”的文件夹里面的文件,否则会出错,选择iphone或者模拟器的文件都不能够完整的运作。

  1. 新建链接文件OCHeader.h然后 我们到 build setting里面进行设置,注意路径,如果建在某个文件夹下面的,要把文件夹名称带上,xxx/xxx.h否则会报错
  2. 然后,我们在这个OCHeader.h设置我们要导入的一些类
#ifndef svgtest_OCHeader_h
#define svgtest_OCHeader_h
#import "SVGKit.h"             //这就是我要添加的SVGKit的类,方便我的swift直接调用
#import "SVGKImage.h"  //同上

#endif
  1. 一次添加全局的swift都可以直接使用,

然后我在我的ViewControler中写我们的代码把

我先设置了一个button让其点击后加载我的svg格式的文件,svg格式的文件事先导入到了我的项目。

import UIKit

//cnblog里面没有swift 将就一下用了c的模板 代码里面对比了oc的例子指导代码,我们可以看到很多不同点。
class ViewController: UIViewController {
    
    @IBOutlet var firstview: UIView!
    
    @IBAction func showsvgbuttonclic(sender: AnyObject) {
        var svgim:SVGKImage=SVGKImage(named: "Rsimplified.svg")
        //SVGKImage* newImage = [SVGKImage imageNamed:@"myImage.svg"];
        
        
        var svgimview=SVGKLayeredImageView(SVGKImage: svgim)
        //SVGKImageView* imageView = [[SVGKLayeredImageView alloc] initWithImage:newImage];
        
        firstview.addSubview(svgimview)
        //[self.view addSubView:  [[SVGKLayeredImageView alloc] initWithImage:newImage];
        NSLog("showsvgbuttonclick")
       
    }

2.2举例

新建一个 Single View Application ,把二维码拖进项目里去,在 ViewController 里添加一个 UIView 作为二维码的容器:

class ViewController: UIViewController {

    let qrView = UIView()

    override func viewDidLoad() {
        super.viewDidLoad()
        
        qrView.center = view.center
        view.addSubview(qrView)
    }
    
    ...
}

初始化一个 NSXMLParser 去解析 svg ,同时让 ViewController 实现 NSXMLParserDelegate 的 parser(_:didStartElement:namespaceURI:qualifiedName:attributes:) 和 parserDidEndDocument(_:) 两个方法用于处理解析结果:

class ViewController: UIViewController, NSXMLParserDelegate {
    
    ...
    
    override func viewDidLoad() {
        ...
        
        let qrPath = NSBundle.mainBundle().pathForResource("zcfan_qrcode", ofType: "svg")!
        let qrData = NSData(contentsOfFile: qrPath)
        let xmlParser = NSXMLParser(data: qrData)
        xmlParser.delegate = self
        xmlParser.parse()
    }
    
    func parser(parser: NSXMLParser!, didStartElement elementName: String!, namespaceURI: String!, qualifiedName qName: String!, attributes attributeDict: [NSObject : AnyObject]!) {
        // 每当解析到一个新标签,这里就会被调用
    }
    
    func parserDidEndDocument(parser: NSXMLParser!) {
        // 整个 svg 文件解析完毕后,这里就会被调用
    }
    
    ...
}

接下来我们会在 parser(_:didStartElement:namespaceURI:qualifiedName:attributes:) 中把遇到的每一个形如:

<rect … x=“0” y=“0” width=“12” height=“12” fill=“black”/>
的标签转换成 CGRect 保存在数组中,并在 parserDidEndDocument(_? 中把他们转换为 CAShapeLayer 并添加动画。

先来看看 parser(_:didStartElement:namespaceURI:qualifiedName:attributes:) 的内容

var rects = [CGRect]()  // 用于存储二维码

func parser(parser: NSXMLParser!, didStartElement elementName: String!, namespaceURI: String!, qualifiedName qName: String!, attributes attributeDict: [NSObject : AnyObject]!) {

    // 只转换 “黑色” 的二维码 “方块”
    if elementName == "rect" && (attributeDict["fill"] as String) == "black" {
        let x = (attributeDict["x"] as NSString).doubleValue
        let y = (attributeDict["y"] as NSString).doubleValue
        let w = (attributeDict["width"] as NSString).doubleValue
        let h = (attributeDict["height"] as NSString).doubleValue
        
        let rect = CGRect(x: x, y: y, width: w, height: h)
        rects.append(rect)
        
    // 设置 qrView 的尺寸为 svg 图像的大小
    } else if elementName == "svg" {
        let w = (attributeDict["width"] as NSString).doubleValue
        let h = (attributeDict["height"] as NSString).doubleValue
        
        qrView.bounds = CGRect(x: 0, y: 0, width: w, height: h)
    }
}

...

接下来是 parserDidEndDocument(_? ,在这里我们要把二维码显示出来:

func parserDidEndDocument(parser: NSXMLParser!) {
    for r in rects {
        let rectLayer = CAShapeLayer()
        
        rectLayer.fillColor = UIColor.darkGrayColor().CGColor
        rectLayer.strokeColor = nil
        rectLayer.path = UIBezierPath(rect: CGRect(origin: CGPointZero, size: r.size)).CGPath  // #1
        rectLayer.frame = r  // #2
        
        qrView.layer.addSublayer(rectLayer)
    }
}
func parserDidEndDocument(parser: NSXMLParser!) {
    
    qrView.layer.shadowColor = UIColor.grayColor().CGColor
    qrView.layer.shadowRadius = 4
    qrView.layer.shadowOpacity = 1
    qrView.layer.shadowOffset = CGSizeZero
    
    for r in rects {
        let rectLayer = CAShapeLayer()
        
        rectLayer.fillColor = UIColor.darkGrayColor().CGColor
        rectLayer.strokeColor = nil
        rectLayer.path = UIBezierPath(rect: CGRect(origin: CGPointZero, size: r.size)).CGPath
        rectLayer.frame = r
        
        var startTransform = CATransform3DIdentity
        startTransform.m34 = 1.0 / -20  // 透视
        startTransform = CATransform3DRotate(startTransform, CGFloat(M_PI)*0.5, 0, 1, 0)  // 沿 y 轴旋转 π/2 圈,待会再动画转回来
        
        // transform 动画
        let transAnim = CABasicAnimation(keyPath: "transform")
        transAnim.duration = drand48() * 4  // 随机一个持续时间
        transAnim.fromValue = NSValue(CATransform3D: startTransform)
        transAnim.toValue = NSValue(CATransform3D: CATransform3DIdentity)
        rectLayer.addAnimation(transAnim, forKey: "transAnim")
        
        // 透明度动画
        let alphaAnim = CABasicAnimation(keyPath: "opacity")
        alphaAnim.duration = transAnim.duration
        alphaAnim.fromValue = 0
        alphaAnim.toValue = 1
        rectLayer.addAnimation(alphaAnim, forKey: "alphaAnim")
        
        qrView.layer.addSublayer(rectLayer)
    }
}