前言
最近公司產(chǎn)品有個(gè)需求衰伯,為我們app中每個(gè)H5應(yīng)用實(shí)現(xiàn)添加桌面快捷方式的功能,與支付寶健康碼添加到桌面一樣张吉。所以特地研究了下相關(guān)產(chǎn)品中該功能的實(shí)現(xiàn)方式,如哈羅單車和支付寶烹植。
原理
在 app 里面通過 OpenURL 方式 跳轉(zhuǎn)到Safari 瀏覽器,打開一個(gè)引導(dǎo)頁面愕贡,然后點(diǎn)擊添加到主屏幕草雕,如下圖:
哈羅單車的實(shí)現(xiàn)
哈羅單車在Safari中展示的是一整張引導(dǎo)圖片,為啥這里說一整張圖呢固以,因?yàn)橹Ц秾氄故镜囊龑?dǎo)頁是由多張圖片拼接而成墩虹。
哈羅單車不同的業(yè)務(wù)使用快捷方式,需要UI每次做一張引導(dǎo)圖憨琳,或者做一張通用圖片當(dāng)引導(dǎo)圖诫钓。
探索支付寶添加桌面快捷方式的實(shí)現(xiàn)
- 在關(guān)閉網(wǎng)絡(luò)的情況下,在支付寶健康碼中點(diǎn)擊"添加到桌面",跳轉(zhuǎn)到Safari瀏覽器篙螟,得到url
https://render.alipay.com/p/s/shortcut/index?appId=20000067&appName=杭州健康碼&appIcon=https://gw.alicdn.com/tfs/TB1nUmbzUT1gK0jSZFhXXaAtVXa-1024-1024.png&appUrl=https%3A%2F%2Fh5.dingtalk.com%2FhealthAct%2Findex.html
可以看出url為https://render.alipay.com/p/s/shortcut/index菌湃,后面參數(shù)有appId,appName遍略,appIcon惧所,appUrl
- 支付寶小程序也同樣有添加到桌面的功能,同樣的方式獲取到小程序跳轉(zhuǎn)到Safari瀏覽器中的url绪杏,這里以"體育服務(wù)"小程序?yàn)槔?/li>
https://render.alipay.com/p/s/shortcut/index?appId=2018073060792690&appName=體育服務(wù)&appIcon=https://appstoreisvpic.alipayobjects.com/prod/40ad067a-899a-4d31-89ba-c8758c405d36.png@120w.png
-
我們將支付寶健康碼頁面的url在mac上的Safari 瀏覽器打開下愈,查看元素
截屏2020-07-23 下午6.49.10.png
可以看到頁面主要有index.html,index.js蕾久,還有幾張圖片
從這張圖我們可以猜到势似,支付寶是通過html和js將appIcon動(dòng)態(tài)嵌入到上圖相應(yīng)的位置,以此來實(shí)現(xiàn)safari中的引導(dǎo)頁面腔彰。
比較
支付寶的實(shí)現(xiàn)方式更加高明叫编,采用動(dòng)態(tài)拼接圖片的方式辖佣,業(yè)務(wù)方只需要傳入appIcon霹抛。
但是哈羅單車主要是靠app客戶端實(shí)現(xiàn)的,支付寶則是靠Web端來實(shí)現(xiàn)卷谈。
支付寶總體實(shí)現(xiàn)
通過對(duì)index.html和index.js的分析杯拐,得出支付寶實(shí)現(xiàn)方式如下:
app客戶端請(qǐng)求站點(diǎn)https://render.alipay.com/p/s/shortcut/index,并將appId世蔗,appName端逼,appIcon,appUrl等傳遞過去污淋。站點(diǎn)下index.html中接收url中攜帶的參數(shù)值顶滩,生成對(duì)應(yīng)的base64。
這樣app客戶端實(shí)現(xiàn)起來很簡(jiǎn)單寸爆,只需要打開url并將參數(shù)傳遞過去即可礁鲁。剩下的交由Web端開發(fā)來做盐欺。
//支付寶實(shí)現(xiàn)應(yīng)用桌面快捷方式
//url中不含中文時(shí),可以不對(duì)url進(jìn)行unicode轉(zhuǎn)碼仅醇,使用https://render.alipay.com/p/s/shortcut/index?appId=20000067&appName=123&appIcon=https://gw.alicdn.com/tfs/TB1nUmbzUT1gK0jSZFhXXaAtVXa-1024-1024.png&appUrl=https://Fh5.dingtalk.com/FhealthAct/Findex.html也可以
NSURL *URL = [NSURL URLWithString:@"https://render.alipay.com/p/s/shortcut/index?appId=20000067&appName=123&appIcon=https://gw.alicdn.com/tfs/TB1nUmbzUT1gK0jSZFhXXaAtVXa-1024-1024.png&appUrl=https%3A%2F%2Fh5.dingtalk.com%2FhealthAct%2Findex.html"];
UIApplication *application = [UIApplication sharedApplication];
NSURL *URL = [NSURL URLWithString:urlString];
if (@available(iOS 10.0, *)) {
[application openURL:URL options:@{}
completionHandler:^(BOOL success) {
}];
} else {
[application openURL:URL];
}
但是我身為一個(gè)ios開發(fā)冗美,可不可以不借助Web端開發(fā),自己?jiǎn)为?dú)來實(shí)現(xiàn)呢?
如果自己?jiǎn)为?dú)實(shí)現(xiàn)析二,需要解決哪些問題呢?
- 首先我從mac端瀏覽器中可以獲取到index.html和index.js和圖片等資源文件
- 我們需要將支付寶的index.html文件中原本通過url中獲取參數(shù)的形式改為app本地替換
步驟
1. 開發(fā)引導(dǎo)頁面
我們首先在app項(xiàng)目工程的根目錄下創(chuàng)建一個(gè)文件夾Web粉洼,文件夾下存放引導(dǎo)頁面
content.html
content.html是拷貝的是支付寶index.html文件,對(duì)其做了修改叶摄。
- 支付寶的index.html文件通過從瀏覽器的url中獲取參數(shù)属韧,通過js函數(shù)getQueryParams實(shí)現(xiàn)。因?yàn)槲业膮?shù)沒有放在URL中准谚,所以通過getQueryParams函數(shù)獲取到的參數(shù)都是空的挫剑。可以給appId等參數(shù)賦個(gè)默認(rèn)值柱衔,這樣就實(shí)現(xiàn)了本地參數(shù)替換樊破。
var query = getQueryParams(location.href);
var { appId = "TransitCodeAppId", appName= "TransitCodeAppTitle", appIcon = "TransitCodeAppIcon", appUrl = "ezvizsaas://123", scheme = "TransitCodeAppScheme" } = query;
修改過后的content.html代碼如下:
將其中的"企業(yè)螢石云"改成自己的主app名稱即可,也可使用字符串進(jìn)行動(dòng)態(tài)替換唆铐。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title id="J_desktopTitle">企業(yè)螢石云</title>
<meta http-equiv="cache-control" content="no-cache" />
<meta content="yes" name="apple-touch-fullscreen" />
<meta content="yes" name="apple-mobile-web-app-capable" />
<meta content="white" name="apple-mobile-web-app-status-bar-style" />
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1.0, maximum-scale=1.0, minimal-ui" />
<meta name="data-aspm" content="a192" />
<link id="J_desktopIcon" sizes="114x114" rel="apple-touch-icon-precomposed" />
<a href="TransitCodeAppScheme" id="qbt" style="display:none"></a>
<script>
var Base64 = function() {
var _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
var _utf8_encode = function (string) {
string = string.replace(/\r\n/g,"\n");
var utftext = "";
for (var n = 0; n < string.length; n++) {
var c = string.charCodeAt(n);
if (c < 128) {
utftext += String.fromCharCode(c);
} else if((c > 127) && (c < 2048)) {
utftext += String.fromCharCode((c >> 6) | 192);
utftext += String.fromCharCode((c & 63) | 128);
} else {
utftext += String.fromCharCode((c >> 12) | 224);
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
utftext += String.fromCharCode((c & 63) | 128);
}
}
return utftext;
};
this.encode = function (input) {
var output = "";
var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
var i = 0;
input = _utf8_encode(input);
while (i < input.length) {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output = output +
_keyStr.charAt(enc1) + _keyStr.charAt(enc2) +
_keyStr.charAt(enc3) + _keyStr.charAt(enc4);
}
return output;
};
};
var getQuery = function(url) {
var query = {};
var hashParts = url.split('#');
var urlParts = hashParts[0].split('?');
if (urlParts.length > 1) {
// 有query
var queryParts = urlParts[1].split('&');
for (var i = 0, len = queryParts.length; i < len; i++) {
var items = queryParts[i].split('=');
query[items[0]] = decodeURIComponent(items[1]);
}
}
return query;
};
var htmlEncode = function(myStr) {
myStr = myStr || '';
//myStr = myStr.replace(/&/g, "&");
myStr = myStr.replace(/'/g, "'");
myStr = myStr.replace(/"/g, """);
myStr = myStr.replace(/</g, "<");
myStr = myStr.replace(/>/g, ">");
myStr = myStr.replace(/\s/g, ' ');
return myStr;
};
var iOSversion = function() {
if (/iP(hone|od|ad)/.test(navigator.platform)) {
var v = (navigator.appVersion).match(/OS (\d+)_(\d+)_?(\d+)?/);
return [parseInt(v[1], 10), parseInt(v[2], 10), parseInt(v[3] || 0, 10)];
}
}
var base64 = new Base64();
var query = getQuery(location.href);
var { appId = "TransitCodeAppId", appName= "TransitCodeAppTitle", appIcon = "TransitCodeAppIcon", appUrl = "ezvizsaas://123", scheme = "TransitCodeAppScheme" } = query;
<!-- var { appId, appName, appIcon, appUrl, scheme } = query;-->
var sysVer = iOSversion();
appName = htmlEncode(appName);
appIcon = htmlEncode(appIcon);
if ((appId || scheme) && appName && appIcon) {
if (sysVer[0] < 14) {
// 14 以下的版本走離線版
var htmlTpl = "<!DOCTYPE html><html><head><meta charset=\"UTF-8\"><title id=\"J_desktopTitle\">\u652F\u4ED8\u5B9D<\/title><meta content=\"yes\" name=\"apple-touch-fullscreen\"><meta content=\"yes\" name=\"apple-mobile-web-app-capable\"><meta content=\"white\" name=\"apple-mobile-web-app-status-bar-style\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,minimal-ui\"><meta name=\"data-aspm\" content=\"a192\"><link id=\"J_desktopIcon\" sizes=\"114x114\" rel=\"apple-touch-icon-precomposed\"><a href=\"TransitCodeAppScheme" id=\"qbt\" style=\"display:none\"><\/a>
<\/head><script>window._to={autoExpo:!0},document.documentElement.style.fontSize=100*document.documentElement.clientWidth\/375+\"px\"<\/script><script>!function(r){function n(t){if(a[t])return a[t].exports;var e=a[t]={exports:{},id:t,loaded:!1};return r[t].call(e.exports,e,e.exports,n),e.loaded=!0,e.exports}var a={};n.m=r,n.c=a,n.p=\"\",n(0)}([function(t,e){\"use strict\";!function(){if(!window.Tracert){for(var o={_isInit:!0,_readyToRun:[],_guid:function(){return\"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(\/[xy]\/g,function(t){var e=16*Math.random()|0;return(\"x\"===t?e:3&e|8).toString(16)})},get:function(t){if(\"pageId\"!==t)return this[t];if(window._tracert_loader_cfg=window._tracert_loader_cfg||{},window._tracert_loader_cfg.pageId)return window._tracert_loader_cfg.pageId;var e=document.querySelectorAll(\"meta[name=data-aspm]\"),r=e&&e[0].getAttribute(\"content\"),n=document.body&&document.body.getAttribute(\"data-aspm\"),a=r&&n?r+\".\"+n+\"_\"+o._guid()+\"_\"+Date.now():\"-_\"+o._guid()+\"_\"+Date.now();return window._tracert_loader_cfg.pageId=a},call:function(){var e,r=arguments;try{e=[].slice.call(r,0)}catch(t){var n=r.length;e=[];for(var a=0;a<n;a++)e.push(r[a])}o.addToRun(function(){o.call.apply(o,e)})},addToRun:function(t){var e=t;\"function\"==typeof e&&(e._logTimer=new Date-0,o._readyToRun.push(e))}},t=[\"config\",\"logPv\",\"info\",\"err\",\"click\",\"expo\",\"pageName\",\"pageState\",\"time\",\"timeEnd\",\"parse\",\"checkExpo\",\"stringify\",\"report\",\"set\",\"before\"],e=0;e<t.length;e++){!function(t){o[t]=function(){var e,r=arguments;try{e=[].slice.call(r,0)}catch(t){var n=r.length;e=[];for(var a=0;a<n;a++)e.push(r[a])}e.unshift(t),o.addToRun(function(){o.call.apply(o,e)})}}(t[e])}window.Tracert=o}}()}])<\/script><script src=\"index.js\"><\/script><style>*{margin:0;padding:0}body,html{height:100%;width:100%;overflow:hidden;background:#f3f2f2;text-align:center}.backguide{height:100%;width:100%}.backguide .enter{position:absolute;height:.44rem;width:3.43rem;background:#108ee9;border:0;font-size:.18rem;color:#fff;bottom:.52rem;left:.16rem;border-radius:.02rem}.backguide .tips{margin-top:1.29rem;font-size:.14rem;line-height:.2rem;color:#999}.backguide .icon{height:.71rem;width:.71rem;border-radius:.14rem}.backguide .appname{font-size:.18rem;line-height:.25rem;color:#333;margin-top:-.14rem}.main{color:#333;text-align:center}.subject{margin-top:.3rem;font-size:.2rem}.preview{margin-top:.36rem;position:relative}.preview .content{position:relative;margin:0 .55rem}.preview .bg{width:2.5rem;margin-left:.05rem;position:relative}.preview .icon{position:absolute;display:block;border-radius:21%}.preview .title{position:absolute;text-align:center;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.preview .icon.large{width:1.25rem;height:1.25rem;left:1.22rem;top:.42rem}.preview .title.large{width:1.5rem;left:1.13rem;top:1.7rem;font-size:.17rem}.preview .icon.small{width:.14rem;height:.14rem;left:.8rem;top:1.125rem}.preview .title.small{width:.16rem;left:.78rem;top:1.266rem;font-size:.025rem}.guide{width:100%;position:absolute;left:0;bottom:.3rem}.guide .content{position:relative;z-index:20;width:3.5rem;padding-top:.16rem;padding-bottom:.06rem;margin:0 auto;border-radius:.04rem;box-shadow:0 6px 15px rgba(0,0,0,.13);background:#fff;font-size:.14rem}.guide .tips{position:relative;z-index:20}.guide .icon{width:.2rem;height:.24rem;margin:0 .035rem .02rem;vertical-align:bottom}.guide .toolbar{width:100%;height:auto;margin-top:-.12rem;position:relative;z-index:10}.guide .arrow{width:.27rem;height:auto;position:absolute;left:50%;bottom:-.26rem;margin-left:-.135rem;z-index:10}<\/style><body data-aspm=\"b15502\"><div id=\"B_container\" class=\"backguide\" style=\"display:none\"><div class=\"tips\">\u4F60\u5373\u5C06\u8FDB\u5165<\/div><img class=\"icon\" src=\"" + appIcon + "\"><div class=\"appname\">" + appName + "<\/div><button class=\"enter\" onclick=\"jumpSchema()\" data-aspm=\"c37816.d76317\" data-aspm-expo data-aspm-param=\"appId=" + appId + "\">\u7ACB\u5373\u8FDB\u5165<\/button><\/div><div class=\"main\" id=\"J_container\" style=\"display:none\"><div class=\"subject\">\u6DFB\u52A0\u670D\u52A1\u5230\u684C\u9762<\/div><div class=\"preview\"><div class=\"content\"><img class=\"bg\" src=\"https:\/\/gw.alipayobjects.com\/zos\/rmsportal\/ceOiTFtaubdHkhSrNfPj.jpg\"> <img class=\"icon small\" src=\"" + appIcon + "\"> <img class=\"icon large\" src=\"" + appIcon + "\"><div class=\"title large\">" + appName + "<\/div><\/div><\/div><div class=\"guide\"><div class=\"content\"><p class=\"tips\">\u70B9\u51FB\u4E0B\u65B9\u5DE5\u5177\u680F\u4E0A\u7684<img class=\"icon\" src=\"https:\/\/gw.alipayobjects.com\/zos\/rmsportal\/XEbFrgamEdvSxVFOBeuZ.png\"><\/p><p class=\"tips\">\u5E76\u9009\u62E9<img class=\"icon\" src=\"https:\/\/gw.alipayobjects.com\/zos\/rmsportal\/IkKEhyTLQpYtqXMZBYtQ.png\">\u201C<strong>\u6DFB\u52A0\u5230\u4E3B\u5C4F\u5E55<\/strong>\u201D<\/p><img class=\"toolbar\" src=\"https:\/\/gw.alipayobjects.com\/zos\/rmsportal\/oFNuXVhPJYvBDJPXJTmt.jpg\"><\/div><img class=\"arrow\" src=\"https:\/\/gw.alipayobjects.com\/zos\/rmsportal\/FlBEnTRnlhMyLyVhlfZT.png\"><\/div><\/div><script>function jumpSchema(){window.location.href=window.AlipaySchema;window.Tracert.click(\"c37816.d76317\",{appId:'" + (appId || "") + "'})}function getIOSversion(){if(\/iP(hone|od|ad)\/.test(navigator.platform)){var e=navigator.appVersion.match(\/OS (\\d+)_(\\d+)_?(\\d+)?\/);return[parseInt(e[1],10),parseInt(e[2],10),parseInt(e[3]||0,10)]}}var appId='" + (appId || "") + "',appUrl='" + (appUrl || "") + "',scheme='" + (scheme || "") + "',href=\"\";if(href=scheme||\"TransitCodeAppScheme:\/\/platformapi\/startapp?appId=" + appId + "&chInfo=ch_desktop\"+(appUrl=appUrl?\"&url=\"+encodeURIComponent(appUrl):\"\"),window.AlipaySchema=href,window.navigator.standalone){var v=getIOSversion();13<=v[0]&&(document.getElementById(\"B_container\").style.display=\"block\",window.Tracert.call(\"expoCheck\")),window.location.href=window.AlipaySchema}else document.getElementById(\"J_container\").style.display=\"block\",document.getElementById(\"J_desktopTitle\").textContent=\"" + appName + "\",document.getElementById(\"J_desktopIcon\").setAttribute(\"href\",\"" + appIcon + "\")<\/script><\/body><\/html>";;
var base64Tpl = base64.encode(htmlTpl);
location.href = 'data:text/html;base64,' + base64Tpl;
}
//} else { // iOS 13 以上用在線頁面當(dāng)快捷方式哲戚,解決 -1 屏搜索點(diǎn)擊不動(dòng)的兼容性問題
// location.href = '' + '?appId=' + appId + '&scheme=' + scheme + '&appUrl=' + appUrl + '&appName=' + appName + '&appIcon=' + appIcon;
//}
} else {
alert('參數(shù)錯(cuò)誤');
}
</script>
<script>
window._to = { autoExpo: true };
document.documentElement.style.fontSize = document.documentElement.clientWidth * 100 / 375 + 'px';
</script>
<script>!function(modules){function __webpack_require__(moduleId){if(installedModules[moduleId])return installedModules[moduleId].exports;var module=installedModules[moduleId]={exports:{},id:moduleId,loaded:!1};return modules[moduleId].call(module.exports,module,module.exports,__webpack_require__),module.loaded=!0,module.exports}var installedModules={};return __webpack_require__.m=modules,__webpack_require__.c=installedModules,__webpack_require__.p="",__webpack_require__(0)}([function(module,exports){"use strict";!function(){if(!window.Tracert){for(var Tracert={_isInit:!0,_readyToRun:[],_guid:function(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(c){var r=16*Math.random()|0,v="x"===c?r:3&r|8;return v.toString(16)})},get:function(key){if("pageId"===key){if(window._tracert_loader_cfg=window._tracert_loader_cfg||{},window._tracert_loader_cfg.pageId)return window._tracert_loader_cfg.pageId;var metaa=document.querySelectorAll("meta[name=data-aspm]"),spma=metaa&&metaa[0].getAttribute("content"),spmb=document.body&&document.body.getAttribute("data-aspm"),pageId=spma&&spmb?spma+"."+spmb+"_"+Tracert._guid()+"_"+Date.now():"-_"+Tracert._guid()+"_"+Date.now();return window._tracert_loader_cfg.pageId=pageId,pageId}return this[key]},call:function(){var argsList,args=arguments;try{argsList=[].slice.call(args,0)}catch(ex){var argsLen=args.length;argsList=[];for(var i=0;i<argsLen;i++)argsList.push(args[i])}Tracert.addToRun(function(){Tracert.call.apply(Tracert,argsList)})},addToRun:function(_fn){var fn=_fn;"function"==typeof fn&&(fn._logTimer=new Date-0,Tracert._readyToRun.push(fn))}},fnlist=["config","logPv","info","err","click","expo","pageName","pageState","time","timeEnd","parse","checkExpo","stringify","report","set","before"],i=0;i<fnlist.length;i++){var fn=fnlist[i];!function(fn){Tracert[fn]=function(){var argsList,args=arguments;try{argsList=[].slice.call(args,0)}catch(ex){var argsLen=args.length;argsList=[];for(var i=0;i<argsLen;i++)argsList.push(args[i])}argsList.unshift(fn),Tracert.addToRun(function(){Tracert.call.apply(Tracert,argsList)})}}(fn)}window.Tracert=Tracert}}()}]);</script>
<script src="index.js"></script>
<style>
* {
margin: 0;
padding: 0;
}
html,body{
height: 100%;
width: 100%;
overflow: hidden;
background: #F3F2F2;
text-align: center;
}
.backguide {
height: 100%;
width: 100%;
}
.backguide .enter {
position: absolute;
height: 0.44rem;
width: 3.43rem;
background: #108EE9;
border: 0;
font-size: 0.18rem;
color: white;
bottom: 0.52rem;
left: 0.16rem;
border-radius: 0.02rem;
}
.backguide .tips {
margin-top: 1.29rem;
font-size: 0.14rem;
line-height: 0.2rem;
color: #999999;
}
.backguide .icon {
height: 0.71rem;
width: 0.71rem;
border-radius: 0.14rem;
}
.backguide .appname {
font-size: 0.18rem;
line-height: 0.25rem;
color: #333333;
margin-top: -0.14rem;
}
.main {
color: #333;
text-align: center;
}
.subject {
margin-top: .3rem;
font-size: 0.2rem;
}
.preview {
margin-top:0.36rem;
position: relative;
}
.preview .content {
position: relative;
margin: 0 0.55rem;
}
.preview .bg {
width: 2.5rem;
margin-left: 0.05rem;
position: relative;
}
.preview .icon {
position: absolute;
display: block;
border-radius: 21%;
}
.preview .title {
position: absolute;
text-align: center;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.preview .icon.large {
width: 1.25rem;
height: 1.25rem;
left: 1.22rem;
top: .42rem
}
.preview .title.large {
width: 1.5rem;
left: 1.13rem;
top: 1.70rem;
font-size: 0.17rem;
}
.preview .icon.small {
width: 0.14rem;
height: 0.14rem;
left: 0.8rem;
top: 1.125rem;
}
.preview .title.small {
width: 0.16rem;
left: 0.78rem;
top: 1.266rem;
font-size: 0.025rem;
}
.guide {
width: 100%;
position: absolute;
left: 0;
bottom: .30rem;
}
.guide .content {
position: relative;
z-index: 20;
width: 3.50rem;
padding-top: 0.16rem;
padding-bottom: 0.06rem;
margin: 0 auto;
border-radius: 0.04rem;
box-shadow: 0 6px 15px rgba(0, 0, 0, 0.13);
background: #fff;
font-size: .14rem;
}
.guide .tips {
position: relative;
z-index: 20;
}
.guide .icon {
width: 0.20rem;
height: 0.24rem;
margin: 0 0.035rem 0.02rem;
vertical-align: bottom;
}
.guide .toolbar {
width: 100%;
height: auto;
margin-top: -0.12rem;
position: relative;
z-index: 10;
}
.guide .arrow {
width: 0.27rem;
height: auto;
position: absolute;
left: 50%;
bottom: -0.26rem;
margin-left: -0.135rem;
z-index: 10;
}
</style>
</head>
<body data-aspm="b15502">
<div id="B_container" class="backguide" style="display: none;">
<div class="tips">你即將進(jìn)入</div>
<img id="B_icon" class="icon" src=""></img>
<div id="B_appname" class="appname">企業(yè)螢石云</div>
<button class="enter" onclick="jumpSchema()" data-aspm="c37816.d76317" data-aspm-expo data-aspm-param="appId=${appId}">立即進(jìn)入</button>
</div>
<div class="main" id="J_container" style="display: none;">
<div class="subject">添加服務(wù)到桌面</div>
<div class="preview">
<div class="content">
<img class="bg" src="https://gw.alipayobjects.com/zos/rmsportal/ceOiTFtaubdHkhSrNfPj.jpg">
<img id="J_iconsmall" class="icon small" src="">
<img id="J_iconlarge" class="icon large" src="">
<div id="J_appname" class="title large">企業(yè)螢石云</div>
</div>
</div>
<div class="guide">
<div class="content">
<p class="tips">點(diǎn)擊下方工具欄上的<img class="icon" src="https://gw.alipayobjects.com/zos/rmsportal/XEbFrgamEdvSxVFOBeuZ.png"></p>
<p class="tips">并選擇<img class="icon" src="https://gw.alipayobjects.com/zos/rmsportal/IkKEhyTLQpYtqXMZBYtQ.png">“<strong>添加到主屏幕</strong>”</p>
<img class="toolbar" src="https://gw.alipayobjects.com/zos/rmsportal/oFNuXVhPJYvBDJPXJTmt.jpg">
</div>
<img class="arrow" src="https://gw.alipayobjects.com/zos/rmsportal/FlBEnTRnlhMyLyVhlfZT.png">
</div>
</div>
<script>
function jumpSchema() {
var lnk = document.getElementById("qbt").click();
<!-- window.location.href = window.AlipaySchema;-->
<!-- var appId = window.AlipayShortCutAppId;-->
<!-- window.Tracert.click('c37816.d76317', {appId: appId});-->
}
function getQueryParams(url) {
var query = {};
var hashParts = url.split('#');
var urlParts = hashParts[0].split('?');
if (urlParts.length > 1) {
// 有query
var queryParts = urlParts[1].split('&');
for (var i = 0, len = queryParts.length; i < len; i++) {
var items = queryParts[i].split('=');
query[items[0]] = decodeURIComponent(items[1]);
}
}
return query;
};
function getIOSversion() {
if (/iP(hone|od|ad)/.test(navigator.platform)) {
var v = navigator.appVersion.match(/OS (\d+)_(\d+)_?(\d+)?/);
return [parseInt(v[1], 10), parseInt(v[2], 10), parseInt(v[3] || 0, 10)];
}
}
var query = getQueryParams(location.href);
var { appId = "TransitCodeAppId", appName= "TransitCodeAppTitle", appIcon = "TransitCodeAppIcon", appUrl = "ezvizsaas://123", scheme = "TransitCodeAppScheme" } = query;
window.AlipayShortCutAppId = appId;
var href = '';
if (scheme) {
href = scheme;
} else {
appUrl = appUrl ? '&url=' + encodeURIComponent(appUrl) : '';
href = `TransitCodeAppScheme`;
}
window.AlipaySchema = href;
if (window.navigator.standalone) {
var v = getIOSversion();
if (v[0] >= 13) {
document.getElementById('B_container').style.display = 'block';
document.getElementById('B_icon').setAttribute('src', appIcon);
document.getElementById('B_appname').textContent = appName;
window.Tracert.call('expoCheck');
}
var lnk = document.getElementById("qbt").click();
<!-- window.location.href = window.AlipaySchema;-->
} else {
document.getElementById('J_container').style.display = 'block';
document.getElementById('J_desktopTitle').textContent = appName;
document.getElementById('J_desktopIcon').setAttribute('href', appIcon);
document.getElementById('J_iconsmall').setAttribute('src', appIcon);
document.getElementById('J_iconlarge').setAttribute('src', appIcon);
document.getElementById('J_appname').textContent = appName;
}
</script>
</body>
</html>
代碼中包含的一些字符串含義如下:
TransitCodeAppIcon:快捷方式在桌面的圖標(biāo)
TransitCodeAppTitle:快捷方式的名稱
TransitCodeAppScheme:跳轉(zhuǎn)頁面對(duì)應(yīng)的 scheme,比如 ezvizsaas://web/app?appId=1
通過這幾個(gè)字符串代替實(shí)際內(nèi)容艾岂,就可以做到動(dòng)態(tài)替換顺少。"企業(yè)螢石云"替換為自己的主app名稱即可。
這里通過 window.navigator.standalone 可以知道引導(dǎo)頁是否是全屏展示王浴,如果是全屏那么跳轉(zhuǎn)到 app 對(duì)應(yīng)頁面脆炎,非全屏則插入具體 HTML 內(nèi)容,展示引導(dǎo)內(nèi)容氓辣。
index.js
index.js中代碼過長(zhǎng)秒裕,由于簡(jiǎn)書對(duì)內(nèi)容長(zhǎng)度的限制,會(huì)導(dǎo)致無法發(fā)布钞啸,這里就不貼出來了几蜻。
index.js用的是支付寶的代碼,其中許多代碼是無用的体斩,不過不影響我們的業(yè)務(wù)梭稚,可以不用刪減。js中的一些關(guān)于"支付寶"的文本可以替換成我們的絮吵。
2. 使用 Data URI Scheme 技術(shù)將引導(dǎo)頁轉(zhuǎn)為一個(gè)字符串
代碼如下
//iconUrl:桌面圖標(biāo) appTitle:默認(rèn)app名稱
- (NSString *)oppcreateDesktopWithPreUrl:(NSString *)preUrl iconUrl:(NSString *)iconUrl appTitle:(NSString *)title scheme:(NSString *)scheme {
if ([preUrl length] == 0) {
return nil;
}
NSString *contentHtmlString = [self contentHtmlWithIconImageString:iconUrl title:title appScheme:scheme];
NSData *data = [contentHtmlString dataUsingEncoding:NSUTF8StringEncoding];
contentHtmlString = [data base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed]; // base64格式的字符串;
NSString *DataURIString = [NSString stringWithFormat:@"data:text/html;charset=utf-8;base64,%@",contentHtmlString];
NSString *urlString = [NSString stringWithFormat:@"%@%@", preUrl, DataURIString];
return urlString;
}
-(NSString *)contentHtmlWithIconImageString:(NSString *)iconImageString title:(NSString *)title appScheme:(NSString *)scheme{
NSString *contentHtmlPath = [self getcontentHTMLTempletPath];
NSString *contentHtmlString = [NSString stringWithContentsOfFile:contentHtmlPath encoding:NSUTF8StringEncoding error:nil];
/*替換字符串*/
contentHtmlString = [contentHtmlString stringByReplacingOccurrencesOfString:@"TransitCodeAppIcon" withString:iconImageString];
contentHtmlString = [contentHtmlString stringByReplacingOccurrencesOfString:@"TransitCodeAppTitle" withString:title];
contentHtmlString = [contentHtmlString stringByReplacingOccurrencesOfString:@"TransitCodeAppScheme" withString:scheme];
return contentHtmlString;
}
- (NSString *)getcontentHTMLTempletPath{
NSString * path = [[NSBundle mainBundle] pathForResource:@"content" ofType:@"html"];
return path;
}
Data URI Scheme 技術(shù)就是將一個(gè)文件弧烤,圖片或者HTML文件等等,通過 Base64 加密等轉(zhuǎn)為字符串放到 HTML 里面直接加載蹬敲,這樣就不用請(qǐng)求服務(wù)器了暇昂。這里我們需要將上面介紹的本地的引導(dǎo) HTML 頁面轉(zhuǎn)為一個(gè)字符串想幻,后面要將這個(gè)字符串拼到服務(wù)器HTML url 后面。
看代碼可以知道话浇,通過 getcontentHTMLTempletPath 方法拿到本地 HTML 文件路徑脏毯,然后將里面內(nèi)容進(jìn)行替換為我們應(yīng)用的快捷方式相關(guān)內(nèi)容。然后將 HTML 內(nèi)容轉(zhuǎn)為 base64 編碼幔崖。最后通過將 base64 編碼按照特定格式拼接即可食店,這里是
[NSString stringWithFormat:@"data:text/html;charset=utf-8;base64,%@",contentHtmlString];以 data:text/html;charset=utf-8;base64, 開頭赏寇,后面我們可以看到存儲(chǔ)的時(shí)候也是保存的這個(gè)吉嫩。
3. 打開服務(wù)器 HTML 頁,用本地引導(dǎo)頁替換
用上面生成的字符串拼到服務(wù)器 HTML 頁面url (這里我是直接將 HTML 文件發(fā)給Web端同事嗅定,放到 Web網(wǎng)站上面然后拿到鏈接)后面自娩,代碼如下
//下面是添加快捷方式到桌面的相關(guān)代碼
- (void)addShortcutToDesk {
NSString *baseUrl = @"https://www.baidu.com"; //這里將https://www.baidu.com替換為你自己的shortcut-middle.html所在的Web服務(wù)器地址
NSString *preUrl = [NSString stringWithFormat:@"%@/shortcut-middle.html",baseUrl];
preUrl = [NSString stringWithFormat:@"%@?dataurl=", preUrl];
NSString *title = [self.webAppParams objectForKey:@"appName"];
NSString *scheme = [NSString stringWithFormat:@"ezvizsaas://web/app?appId=%ld",self.webAppId];
NSString *iconUrl = [self.webAppParams objectForKey:@"appLogo"];
NSString *urlString = [self oppcreateDesktopWithPreUrl:preUrl iconUrl:iconUrl appTitle:title scheme:scheme];
UIApplication *application = [UIApplication sharedApplication];
NSURL *URL = [NSURL URLWithString:urlString];
if (@available(iOS 10.0, *)) {
[application openURL:URL options:@{}
completionHandler:^(BOOL success) {
}];
} else {
[application openURL:URL];
}
}
這里服務(wù)器保存的 HTML頁面shortcut-middle.html很簡(jiǎn)單,加載后主要就是通過上面拼接的鏈接獲取本地引導(dǎo)頁內(nèi)容渠退,然后加載引導(dǎo)頁忙迁,shortcut-middle.html代碼如下
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
</body>
<script type="text/javascript">
const dataurl = '';
var url = location.search;
if (url && url.length > 1 && url.indexOf('?dataurl=') !== -1) {
url = url.replace("?dataurl=", "");
window.location.replace(url);
} else {
window.location.replace(dataurl);
}
</script>
</html>
通過 location.search 方法可以獲取到拼接好的url內(nèi)容,然后拿到我們拼上去的內(nèi)容碎乃,使用 window.location.replace 方法來重新進(jìn)行內(nèi)容替換姊扔,就可以展示我們本地的引導(dǎo)頁了。此時(shí)展示就是非全屏展示梅誓,跟從快捷鍵打開不一樣恰梢。
可能遇到的問題
- 在Safari中出現(xiàn)如下錯(cuò)誤提示 414 Request-URI Too Large
這是因?yàn)槲覀兊膗rl超過了nginx 服務(wù)器的限制,找Web端開發(fā)人員設(shè)置下nginx 服務(wù)器的配置即可梗掰。
參考https://blog.csdn.net/qq_41198398/article/details/83618204
- 添加到桌面的圖標(biāo)有黑邊
這是因?yàn)槲覀儌魅氲腶ppIcon圖標(biāo)有圓角嵌言,appIcon用大圖512x512或1024x1024,不要圓角
參考資料
- 哈羅單車乘車碼添加桌面快捷方式
http://www.reibang.com/p/15e36a3bd22c - 支付寶健康碼和小程序添加桌面快捷方式