iOS开发——SVG图展示方案

最近,接到了一个需求,关于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
如果您知道怎么处理或者有什么好的方案,欢迎联系我,或者有什么好的建议和简介,我们可以一起交交流与学习。

参考

http://www.cocoachina.com/ios/20161115/18087.html

https://segmentfault.com/a/1190000002580541

坚持原创技术分享,您的支持将鼓励我继续创作!