Alexander Sergeev's blog

SVG on iOS

I could not find a good library for SVG for iOS:

  • SVGQuartzRenderer doesn’t compile.
  • SVGKit works good only with shipped samples and crahes on “real world” SVGs.
  • CKSVG looks good, but it doesn’t support iOS out from box as soon as it’s designed for Mac, and opens my files with some errors (anyway better than SVGKit)

The main problem is a lack of free time. I had no time to fix compilers’ error, crashes and port code for Mac to iOS. I needed a ready solution and I need it quick.

I was not alone in my research. Another guy had a same problem (and I made the same reserch as me). But I could not use unstable solution in hope that it will work in my case, so I started a little investigation and I found out, that SVG can handle touches and executes javascript. OMG it can be even animated! So I tried to put it into UIWebView and now, at 1:47 am looks like I succeed.

First, SVG can be loaded with a loadHTMLString: method; I had to use this code instead:

NSData * svg = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"africa" ofType:@"svg"]];

[webview loadData:svg MIMEType:@”image/svg+xml” textEncodingName:0 baseURL:[[NSBundle mainBundle] bundleURL]];

And SVG should looks something like that:

...
// var svgDocument; var svgDocument; var xmlns="http://www.w3.org/2000/svg" function startup(evt) { O=evt.target svgDocument=O.ownerDocument } function onclick(evt) { var t = evt.target; t.setAttribute("visibility","hidden"); i=t.getAttribute("id"); var url="map://id"; window.location = url.concat(i); } // ]]&gt;</script></p> <polygon fill-rule="evenodd" clip-rule="evenodd" points=" ... " id="egypt" onclick="onclick(evt)" /><br /> </code></p> <p>Now I can load this SVG into web view and handle an URL map://egypt in my</p> <p><code><br /> -(BOOL) webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType<br /> </code></p> <p>delegate&#8217;s method!<br /> I still had to get rid of nasty black rectangle (link highlighting) when I tapping on map, but anyway, this approach is much better than rasterization my SVG images or dealing with buggy third party code&#8230;</p> <div class="addtoany_share_save_container addtoany_content_bottom"><div class="a2a_kit addtoany_list a2a_target" id="wpa2a_1"><a class="a2a_button_facebook_like addtoany_special_service" data-href="http://alxsrg.com/?p=30"></a><a class="a2a_button_twitter_tweet addtoany_special_service" data-url="http://alxsrg.com/?p=30" data-text="SVG on iOS"></a><a class="a2a_button_google_plusone addtoany_special_service" data-href="http://alxsrg.com/?p=30"></a><a href="https://www.addtoany.com/share" onclick="_gaq.push(['_trackEvent', 'outbound-article', 'https://www.addtoany.com/share', '']);" class="a2a_dd addtoany_share_save"><img src="http://alxsrg.com/wp-content/plugins/add-to-any/share_save_171_16.png" width="171" height="16" alt="Share"/></a> <script type="text/javascript"><!-- if(wpa2a)wpa2a.script_load(); //--></script> </div></div> </div> <div class="entry meta"> <p><span class="highlight">Category:</span> <a href="http://alxsrg.com/?cat=1" rel="category">IT</a></p> <p><span class="highlight">Tagged:</span> <a href="http://alxsrg.com/?tag=ios" rel="tag">ios</a>, <a href="http://alxsrg.com/?tag=iphone" rel="tag">iphone</a>, <a href="http://alxsrg.com/?tag=svg" rel="tag">svg</a></p> </div> </div> <div id="commentsContainer"> <!-- You can start editing here. --> <!-- If comments are open, but there are no comments. --> <div id="respond"> <h3 id="respondTitle"><span class="hook">Leave a Reply <span class="cancel-comment-reply"><a rel="nofollow" id="cancel-comment-reply-link" href="/?p=30#respond" style="display:none;">&times;&nbsp;Cancel reply</a></span></span></h3> <p>You must be <a href="http://alxsrg.com/wp-login.php?redirect_to=http%3A%2F%2Falxsrg.com%2F%3Fp%3D30">logged in</a> to post a comment.</p> </div> </div> </div> <div class="secondaryColumn"> <div id="loginlogout-3" class="widgetContainer widget_loginlogout"> <form name='loginform' id='loginform' action='http://alxsrg.com/wp-login.php' method='post'> <label>User:</label><br /> <input type='text' name='log' id='user_login' class='input' tabindex='20' /><input type='submit' name='wp-submit' id='wp-submit' value='Login' tabindex='23' /><br /> <label>Pass:</label><br /> <input type='password' name='pwd' id='user_pass' class='input' tabindex='21' /> <span id="forgotText"><a href="http://alxsrg.com/wp-login.php?action=lostpassword" rel="nofollow" >Forgot?</a></span><br /> <a href="http://alxsrg.com/wp-login.php?action=register">Register</a> <input type='hidden' name='redirect_to' value='/?p=30' /> </form> <!-- WP-FB AutoConnect Button v4.2.1 --> <span class="fbLoginButton"><script type="text/javascript">//<!-- document.write('<fb:login-button scope="" v="2" size="small" onlogin="jfb_js_login_callback();">Login with Facebook</fb:login-button>'); //--></script></span></div></div> <div class="secondaryColumn"> <div id="text-3" class="widgetContainer widget_text"> <div class="textwidget"><a href="http://www.adfreeblog.org/" onclick="_gaq.push(['_trackEvent', 'outbound-widget', 'http://www.adfreeblog.org/', ' ']);" target="_blank"> <img src="http://www.adfreeblog.org/adfreebutton.jpg"></a></div> </div></div></div> <div class="navStripWrapper"> <ul class="nav fix"> <li><a href="http://alxsrg.com/" title="Return to the the frontpage">Frontpage<br /><span>Return home</span></a></li> <li><a id="triggerCatID2" href="#" title="Show categories">Browse<br /><span>By topic</span></a></li> <li class="last"><a href="http://alxsrg.com/?feed=rss2" title="Subscribe to the main feed via RSS">Subscribe<br /><span>RSS feed</span></a></li> <li id="searchBar2" class="searchField"> <div> <form method="get" id="searchForm2" action="http://alxsrg.com/"> <span><input type="text" value="Search the archives..." onfocus="if (this.value == 'Search the archives...') {this.value = '';}" onblur="if (this.value == '') {this.value = 'Search the archives...';}" name="s" id="s2" /></span> </form> </div> </li> </ul> <div id="footerStrip" class="toggleCategories fix" style="display: none;"> <ul class="fix"> <li class="cat-item cat-item-1"><a href="http://alxsrg.com/?cat=1" >IT</a> </li> </ul> </div> </div> <div id="footer" class="fix"> <p class="left"><a href="http://5thirtyone.com/grid-focus" title="Grid Focus by: Derek Punsalan">Grid Focus</a> by Derek Punsalan <a href="http://5thirtyone.com">5thirtyone.com</a>.</p> <p class="right">Whitespace</p> </div> </div> <script type="text/javascript"><!-- wpa2a.targets=[ {title:"SVG on iOS",url:"http://alxsrg.com/?p=30"}]; wpa2a.html_done=true;if(wpa2a.script_ready&&!wpa2a.done)wpa2a.init();wpa2a.script_load(); //--></script> <!-- WP-FB AutoConnect Init v4.2.1 (NEW API) --> <div id="fb-root"></div> <script type="text/javascript">//<!-- window.fbAsyncInit = function() { FB.init({ appId: '121357734644477', status: true, cookie: true, xfbml: true, oauth: true, channelUrl: 'http://alxsrg.com/wp-content/plugins/wp-fb-autoconnect/assets/channel.html' }); }; (function() { var e = document.createElement('script'); e.src = document.location.protocol + '//connect.facebook.net/en_US/all.js'; e.async = true; document.getElementById('fb-root').appendChild(e); }()); //--></script> <!-- WP-FB AutoConnect Callback v4.2.1 --> <form id="wp-fb-ac-fm" name="jfb_js_login_callback_form" method="post" action="http://alxsrg.com/wp-login.php" > <input type="hidden" name="redirectTo" value="/?p=30" /> <input type="hidden" name="access_token" id="jfb_access_token" value="0" /> <input type="hidden" name="fbuid" id="jfb_fbuid" value="0" /> <input type="hidden" id="autoconnect_nonce" name="autoconnect_nonce" value="1c3587d78d" /><input type="hidden" name="_wp_http_referer" value="/?p=30" /> </form> <script type="text/javascript">//<!-- function jfb_js_login_callback() { //wpfb_add_to_js: An action to allow the user to inject additional JS to be executed before the login takes place //wpfb_add_to_js: Finished //Make sure the user logged into Facebook (didn't click "cancel" in the login prompt) FB.getLoginStatus(function(response) { if (!response.authResponse) { //Note to self: if this is happening unexpectedly, it could be because third-party browser cookies are disabled. return; } //Set the uid & access token to be sent in to our login script jQuery('#jfb_access_token').val(response.authResponse.accessToken); jQuery("#jfb_fbuid").val(response.authResponse.userID); //Submit the login and close the FB.getLoginStatus call document.jfb_js_login_callback_form.submit(); }) } //--></script> Facebook login by <a href="http://www.justin-klein.com/projects/wp-fb-autoconnect">WP-FB-AutoConnect</a><script type='text/javascript' src='http://alxsrg.com/wp-includes/js/comment-reply.min.js?ver=4.4.32'></script> <script type='text/javascript' src='http://alxsrg.com/wp-content/plugins/social-sharing-toolkit/script_2.1.2.js?ver=4.4.32'></script> <script type='text/javascript' src='http://alxsrg.com/wp-includes/js/wp-embed.min.js?ver=4.4.32'></script> <script src="http://alxsrg.com/wp-content/themes/gridfocus/js/functions.js" type="text/javascript" charset="utf-8"></script> </body> </html>