最近,接到了一个需求,关于iOS中加载SVG图片的实现,就我了解苹果是没有提供这种API直接加载SVG图片的,但是发现网上有各种资料和相关库的实现,于是为了实现这个需求专门了解了一下,虽然最后没有用到牛逼的技术,或者没有话太多时间在SVG的解析上,但是这一路也折腾的挺累的,于是就有了这篇文章
SVG简介
首先我们来看看上面是SVG
svg 可缩放的矢量图形
SVG 可伸缩矢量图形 (Scalable Vector Graphics)
SVG 文件是纯粹的 XML
SVG 图像在放大或改变尺寸的情况下其图形质量不会有所损失
简单的说就是svg格式的图不会随着图片的缩放出现模糊、svg图实际是xml格式的,可以通过代码获取各种element
参考资料:http://www.ibm.com/developerworks/cn/web/wa-scalable/
SVG相关库
由于在ios中源生的api没有提供相关的操作,但是可喜的是有第三方库提供了操作:SVGKit
使用SVGKit库显示svg图:
1、使用SVGKit库显示svg图
2、实现svg图的缩放操作
3、点击svg图的某个部分,获取svg图的某个部分
SVGKit的地址:https://github.com/SVGKit/SVGKit
代码中集成SVGKit参考:http://stackoverflow.com/questions/3520977/build-fat-static-library-device-simulator-using-xcode-and-sdk-4/3647187#3647187
SVGKit是一个非常强大的,可以快速渲染SVG文件的框架,由Matt Rajca开发。你可以直接把SVG文件加载至app中,并且SVG中的每个图形会变成一个CAShapeLayer,可以方便地进行缩放和动画你的图形。SVGKit包含iOS和Mac示例,不过不支持渐变。可以从github上查看使用说明、示例以及下载相关的类。如果你想渲染app中的矢量图形,SVGKit是个不错的解决办法。
SVG图片加载
加载网络文件
目前来说,苹果相册并不支持SVG的存储,但是由于公司考虑到用户可能在PC端上传对应的SVG文件到服务器,
当在客户端操作相册或者需要上传SVG图片的时候,就需要将用户上传到服务器的SVG获取并显示到界面,
以供用户上传SVG的初衷。
一路来其实考虑了各种方案,但是由于目前还没有找到特别符合项目,特别好,而且是Swift版的库,就选择的使用WebView来直接加载SVG
上代码
if (isSVG) {
self.webView = [[WebView alloc] initWithConfiguration:nil];
self.webView.frame = CGRectMake(index * CGRectGetWidth(visibleBounds), 0, CGRectGetWidth(visibleBounds), CGRectGetHeight(visibleBounds));
self.webView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
self.webView.contentMode = UIViewContentModeRedraw;
self.webView.opaque = YES;
self.webView.allowsBackForwardNavigationGestures = NO;
// 解决本地文件svg模拟器能正常打开,真机无法打开白屏问题
if ([originURL isFileURL]) {
SEL sel = NSSelectorFromString(@"loadFileURL:allowingReadAccessToURL:");
if ([self.webView respondsToSelector:sel]) {
NSString* directory = [originURL.absoluteString stringByDeletingLastPathComponent];
SEL myMethod = @selector(loadFileURL:allowingReadAccessToURL:);
// 返回一个方法 如果那个方法找不到则返回nil
NSMethodSignature *signature = [[WKWebView class] instanceMethodSignatureForSelector:myMethod];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setSelector:myMethod];
NSURL *loadFileURL = originURL;
NSURL *allowingReadAccessToURL = [NSURL URLWithString:directory];
//注意:1、这里设置参数的Index 需要从2开始,因为前两个被selector和target占用。下面这样写也没有任何
[invocation setArgument:&loadFileURL atIndex:2];
[invocation setArgument:&allowingReadAccessToURL atIndex:3];
invocation.target = self.webView;
[invocation invoke];
} else {
// load the passed in URL
[self.webView loadRequest:[NSURLRequest requestWithURL:originURL]];
}
} else {
[self.webView loadRequest:[NSURLRequest requestWithURL:originURL]];
}
[view addSubview:self.webView];
self.photoCurrentModel = ((UDPhotoModel *)obj);
if (index == 0) {
self.webView.tag = 10000;
} else {
self.webView.tag = 0;
}
// 长安弹出手势
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPressWebImage:)];
longPress.minimumPressDuration = 1;
longPress.delegate = self;
[self.webView addGestureRecognizer:longPress];
// 单击手势
UITapGestureRecognizer *tapWeb = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapWebImage:)];
tapWeb.delegate = self;
[self.webView addGestureRecognizer:tapWeb];
// 轻扫手势
UIPanGestureRecognizer *panWebImageBack = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panWebImageBackAction:)];
panWebImageBack.delegate = self;
[self.webView addGestureRecognizer:panWebImageBack];
}
交互操作的实现
由于根据项目的需求,我们需要对SVG图片进行一个操作,但是本身使用WebView加载的SVG,因此只能使用手势的方式,增加对他的交互
/**
* 要想让点击手势起作用需要实现
* 如果第一个手势是点击第二个是长按就返回NO 不支持同时响应长按和点击手势
*/
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
if ([otherGestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]] && [gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]) {
return NO;
} else {
return YES;
}
}
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{
// 不执行前段界面弹出列表的JS代码
[self.webView evaluateJavaScript:@"document.documentElement.style.webkitTouchCallout='none';" completionHandler:nil];
}
-(void)panWebImageBackAction:(UIPanGestureRecognizer *)gest{
if (gest.state == UIGestureRecognizerStateChanged) {
[self commitTranslation:[gest translationInView:gest.view] tagIndex:gest.view.tag];
}
}
/**
* 判断手势方向
*
* @param translation translation description
*/
- (void)commitTranslation:(CGPoint)translation tagIndex:(NSInteger)tagIndex
{
CGFloat absX = fabs(translation.x);
CGFloat absY = fabs(translation.y);
// 设置滑动有效距离
if (UIUserInterfaceIdiomIsPad()) {
if (MAX(absX, absY) < 300)
return;
} else {
if (MAX(absX, absY) < 150)
return;
}
if (absX > absY ) {
if (translation.x<0) {
//向左滑动
}else{
//向右滑动
}
}
else if (absY > absX) {
if (translation.y<0) {
//向上滑动
}else{
//向下滑动
}
}
}
// 单击
- (void)handleTapWebImage:(UITapGestureRecognizer *)sender {
}
// 长按
- (void)handleLongPressWebImage:(UILongPressGestureRecognizer *)sender{
}
加载本地文件
加载本地SVG的时候,其实和加载本地的html,pdf等式一样的,打开之后完全是一个网页
具体如下:
NSString *svgName = @"svg名称";
NSString *svgPath = [[NSBundle mainBundle] pathForResource:svgName ofType:nil];
NSData *svgData = [NSData dataWithContentsOfFile:svgPath];
NSString *reasourcePath = [[NSBundle mainBundle] resourcePath];
NSURL *baseUrl = [[NSURL alloc] initFileURLWithPath:reasourcePath isDirectory:true];
UIWebView *webView = [[UIWebView alloc] init];
webView.frame = CGRectMake(0, 0, width, height);
[webView loadData:svgData MIMEType:@"image/svg+xml" textEncodingName:@"UTF-8" baseURL:baseUrl];
总结:如果想要交互并且放大缩小,难度就大了,然后我就百度了下,发现了个好东西SVGKit
由于项目的原因,暂时只能考虑使用WebView来实现了,后期有机会专门研究一下SVG的加载过程或者将它替换成使用库的方式
当然,我也希望苹果能够增加对SVG的支持,哈哈!
误操作
考虑到WebView默认的特性是,所有内容会直接放在左上角,如果你没有对她进行一些适应的话
于是我想到了,之前用WebView加载HTML的方式,去执行JS代码将它居中,以控制内部内容居中
[webView evaluateJavaScript:@"Math.max(document.body.scrollHeight, document.body.offsetHeight, document.documentElement.clientHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight)"
completionHandler:^(id _Nullable result, NSError * _Nullable error) {
if (!error) {
}
}];
不执行前段界面弹出列表的JS代码
[webView evaluateJavaScript:@"document.documentElement.style.webkitTouchCallout='none';" completionHandler:nil];
// 执行JS,是WebView内容剧中,边距自适应:这里无效,具体原因不明,
// 预览同张svg图片两端预览效果不一致(https://yun.115.com/5/T339573.html#)
[webView evaluateJavaScript:@"document.getElementsByTagName('body')[0].style.verticalAlign = 'middle';" completionHandler:nil];
[webView evaluateJavaScript:@"document.getElementsByTagName('body')[0].style.textAlign = 'center';" completionHandler:nil];
[webView evaluateJavaScript:@"document.getElementById('mapid').style.margin = 'auto';" completionHandler:nil];
但是发现并没有什么卵用,试了好久才发现自己,傻逼了,SVG是XML格式的,并不能像HTML一样,使用JS去执行对应的DOC操作
于是,就停下了。
直到现在暂时还没有找到对应的方案,由于Android能够控制SVG居中适应,导致我这边有一个区配合Android的Bug
如果您知道怎么处理或者有什么好的方案,欢迎联系我,或者有什么好的建议和简介,我们可以一起交交流与学习。