WKWebView Native & JS Stuff

 • 

目前在写一个半 web 半 native app。业务实现里网页带的 App.js 会在某些事件触发(比如点击元素跳转页面)时执行 window.jsInterface 这个 object 的某些 function,要求 iOS 监听 function 的触发并作出响应(比如 push 一个 ViewController)。
问题是网页本身没有 window.jsInterface,而且 WKWebView 里只能监听 window.webkit.messageHandlers 的事件,所以还需要注入一个 js Object 来转发这些事件。

具体实现写在了 WKWebView 的一个 subclass 里。


@interface YHWebView ()<WKScriptMessageHandler>

实现 WKScriptMessageHandler 协议,只有这么一个函数:

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message

用此实现来监听 window.webkit.messageHandlers


在此基础上需要使用 WKWebViewConfiguration 来自定义接口(比如 jumpToNative )。

- (id)initWithFrame:(CGRect)frame
{
    self.webViewConfig = [[WKWebViewConfiguration alloc] init];
    self.webViewConfig.userContentController = [[WKUserContentController alloc] init];
    [self.webViewConfig.userContentController addScriptMessageHandler:self name:@"jumpToNative"];
    
    self = [super initWithFrame:frame configuration:self.webViewConfig];

#if DEBUG
// 开启 Safari Debug  
    [self.webViewConfig.preferences setValue:@YES forKey:@"developerExtrasEnabled"];
#endif
    
    return self;
}

这个时候如果 js 调用

window.webkit.messageHandlers.jumpToNative.postMessage()
``` 就可以在 `WKScriptMessageHandler` 的实现里面收到了,比如 js 发送:

window.webkit.messageHandlers.jumpToNative.postMessage({body: dict}); //dict 为一个 JSON String

则代理实现里可以读取:
  • (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
    {
    if ([message.name isEqualToString:@"jumpToNative"])
    {
    NSString *body = [message.body objectForKey:@"body"];
    NSData *bodyData = [body dataUsingEncoding:NSUTF8StringEncoding];
    NSError *error;
    NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:bodyData options:0 error:&error];
    // 处理 body 内的 key value
    }
    }

---
现在就差最后一步,注入 js 对象,把所有关于 `window.jsInterface` 的东西都发给 `window.webkit.messageHandlers`。使用 WKUserScript 可以在页面加载前注入这么一个对象:

NSString *jsCode = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource: @"YHWebView" ofType: @"js"] encoding:NSUTF8StringEncoding error:NULL];
self.jsInterfaceUserScript = [[WKUserScript alloc] initWithSource:jsCode injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];
[self.webViewConfig.userContentController addUserScript:self.jsInterfaceUserScript];

YHWebView.js:
```js
window.jsInterface =
{
    jumpToNative: function(dict)
    {
        window.webkit.messageHandlers.jumpToNative.postMessage({body: dict});
    }
};

Done.

2016.10.31

 • 

最近主要的两个感受就是 困和累。


大约两周前,一个非常好的挚友自己非常忙,所以给我介绍了一份外包。原本的计划是一到两周写完整个 iOS 的部分,结果写的时候遇到了一些常见的问题,比如没有需求文稿,需求方要求按照设计稿做;但设计稿有一些逻辑页面没有做,需要问;设计稿没切图,需要我手动切;一些没有考虑到的图比如 UIImageView 通过网络加载时的 placeholder 没有做,需要再去要;以及设计稿过几天就会做一些改动,不是很大但对于我这样用 xib 写界面习惯加一堆约束的人来说改起来还是有些麻烦(这个其实应该是我自己的问题)。
说这么多其实不是吐槽,这些问题对我的困扰也不大,因为这种事情谁都不能保证完美,自己能理解也有准备。只是说我两周前计划的是花两周写完,然后验收拿钱,然后我继续捣鼓 Unity 去,就这么简单。


然后乱七八糟的事情就出现了,比如学校里一堆课设,提前写完了还不能走。比如没去上课,多了一份 5000 字检讨。
然后上周初的时候,室友看到武汉有 hackathon,想去。当时看到奖项只说“实物奖品”也不说清楚具体,感觉不是很靠谱。而且我上上周刚从上海两场并行 hackathon 回来还没恢复正常,上海那场的奖金还没发给我呢 ... 不过舍友非常想参加,而且都拉好其他人了,那就尽力去参加吧。
周末以 NodeX 的身份去参加。是目前我见到的最烂的 hackathon。全程网络百度都打不开,更不用提需要代理的网站了。人数近三百人,又没有复旦那样的大厅,孵化器写字楼肯定装不下了。但我还是第一次见安置在厂房里的。总之,最烂的 hackathon,这场荣膺。


比赛的时候做了一个软件,叫 Café GO。大致就是 awesome-cn-cafe + MapzenGo,是一个 Unity 软件。就不开源了,因为包含了一部分 Asset Store 的收费内容,license 是 pre-seat 的。


主要还是觉得自己被活生生浪费了很多时间,所以很累。
我非常不喜欢原有计划被打乱的状态,特别是被不负责任的人和事物所打乱。课设写完了不检查又不给走,举办 hackathon 又没有准备好网络,这都是他人不负责任的事情,然而失去时间的是我自己。我有很多计划想要去实现,如果能够远离这些不负责任浪费他人生命的人,我花多少钱都愿意。

替换 iPhone 5S Home Button

 • 

最近一直被闲置的 5S 被拿来用做小屏幕适配测试机,home 按键无响应挺恼人的。遂某宝上买了 home 按键总成,买的时候店家在网页上强调不能刷机什么的,但搜索后发现苹果貌似已经给了 Error 53 的解决办法,所以还是买了。
按照 ifixit 教程修的,这个过程让我想到了一年前 hACKbUSTER 在 Segment Fault 1024 Beijing Hackathon 做的 Fix++,一个 AR 版的 ifixit。现在看来当时用的高通 AR 引擎精度还是不够达到维修 Home 按键这种程度,但可以动态地展示零部件动画
1
这个还应该蛮有用的。
拆的时候不小心把后置摄像头也带下来了,没想到比 Macbook 一个键帽都小。

总结:

1.亲测替换按键后可以刷机(OTA 10.0.2 -> 10.1)。(前提是系统要在 9.3 以上,否则先去用 iTunes 升级吧)
2.维修中间可以顺带清灰(比如下图的很多地方),特别是边框。
3.万能某宝啥都能买。

  1. 维修时,哪些零件需要从哪个方向撬开之类的问题,以网页为载体用图片或文字都很无力,不过在 Unity 里面这个就好办多了。

2016.10.23

 • 


题图是一个计划要做的 app,还在打草稿。
最近课程设计扎堆地来,虽然大部分我没去是了,不过还是挺烦的,因为 Unity 项目需要大段不受干扰的时间。
买了 Script Inspector 3,安利一发,比 MonoDevelop 和 JetBrains Rider 都好用些。
仍然在打 Kevin 的 PSV,Persona 4 Golden。在小熊之影的部分卡了两天,终于过去了。
写了些 iOS 的业务代码。
觉得还是要有些耐心。很多东西不是 quick & dirty 就能解决的,着急只会产生不必要的拖延,不如慢慢来。

2016.10.17

 • 

2016.10.14
周五。从长沙坐动车去上海。在虹桥火车站和 Cee陈叔思宇 汇合。去复旦。吃晚饭,Cee 在汉堡王用了一个非常有趣的 bug 来解决吃饭问题 😂。
晚上,HACKxFDU 开幕式。和 王鹏翰冬瓜鞠春利 见面,认识了 BlueCocoa 和其他人。
讨论了会这个 hackathon 要做什么,王鹏翰提起了一套方案,于是做 Delight。半夜,回宾馆,睡。
2016.10.15
跑去云赛空间,参加 赛客·创享未来 开幕式。这次仍然是“不会骑自行车”的 烧碱 大人主持。讨论了下这个 hackathon 要做什么,结果是做 Connect++。我开始做 Connect++ 的 UI,思宇开始接微软 API,陈叔开始搭建 iOS Project,Cee 和王鹏翰一起在写 Delight 的网页,以及写 Connect++。
下午回到复旦赛场,完成了 Connect++ 的 UI
夜间从复旦光华楼出,去云赛空间。王鹏翰在复旦会场继续写 Delight。我们在云赛空间开始写 Connect++。半夜陈叔和 Cee 骑着摩拜单车出去买东西吃。
2016.10.16
困。Connect++ 的基本功能在上午十点左右基本完成,于是我在会场打了两个小时的 PSV。中间大家调试了下微软的 API,但网络太差总是失败。Cee 和王鹏翰在给 Delight 打包成 Mac App。
下午陈叔,Cee,思宇在云赛空间开始展示 Demo,同时王鹏翰一人坚守复旦 Demo 位(...)。看完云赛所有队伍大家觉得有望卫冕(但是并没有,于是拿了三等奖(1W 块和一些外设)。知道名次后借口赶飞机立马退场,去复旦等王鹏翰,这个时候他已经进入前十,准备上台展示。
等待的时间里在复旦的食堂里面趴着睡了一觉。复旦的结果好像是没结果。去 吃饭,味道不错。大家一起吐槽两个 hackathon。
2016.10.17
今天。
早上八点半出门,去浦东机场。距离飞机起飞还有二十分钟的时候赶到机场,结果被告知不能打印登机牌了。
遂坐回虹桥,定动车票。下午我坐上了车,北京三人都已到站,而王鹏翰已经开始上课了(。
夜间到达长沙。

GET

  1. Octocat 一只 from Cee & Xhacker。心念念很久的玩偶,还是八月份在北京的时候说的,后来九月回学校就一直没有拿到。
  2. 这算是我第一次在 hackathon 里作为设计师完成项目,个人认为做的还不错(当然是以快速 hackathon 的水准来看):
  3. 团队两天来回参加并行 hackathon 做两个项目的感觉。估计国内也没几个像 hACKbUSTER 这么丧心病狂地干了吧。但以后再也不能这么干了,爆肝。

吐槽
1.复旦的管理工作太乱了,中间遇到了去 IBM 问一下 API 回来位子就被占也无人解决的事情。此外,复旦没大奖啊,就是因为这个所以我们才动了一下子去俩 hackathon 的念头。
2.云赛的评委里感觉有并不是很懂技术的。团队里面只有 PPT 没有真机演示的太多,静态页面拿来当 Demo 的也太多。
3.云赛的奖品外设很尴尬 —— 华硕的游戏外设,价格和 hhkb 差不多,但在程序员眼里价值和流通度就不一定了。

关于两个 hackathon 项目
Connect++ 是一个 iOS app,可以在这个软件内拍照,图片会自动通过微软的 CV 图像识别处理,然后自动标记 tag,并有类似于 Google Now On Tap 的内容推荐。可以在地图界面 Peek&Pop 不同 tag 类目的兴趣点。
Delight 是一个 Atom Shell App,用于教师和学生或幼年儿童的互动教学。代码可以同步显示,同步编辑,并且有自带的 console,test case 和文档教学面板。

All Code and Design files Hosted On hACKbUSTER

2016.10.18 Update : Cee 写了一篇 文章,比我写的更翔实。

2016.10.12

 • 

最近突然多了很多课程设计和上机安排,于是花了些时间处理课程设计,包括写了一个 Android 软件,配套的 Rails 服务器端,以及些有的没的。写 Android 软件的首要感受是慢。即使是开了 Instant Run 也会觉得太慢了,这个是相对于使用 Objective-C 的 iOS 项目来说。以及个人感觉系统库里有很多东西功能重复但又没有标清楚,有时候都不知道要用哪一个,用了后又会发现有的已经被标为不再支持了,挺困惑的。
理所当然的,个人项目一行都没写,不过倒是花了几个小时尝试用 MODO 建模一辆车,目前是这个样子:


目前的问题是涉及到一些曲面工具的使用时就会没有办法达到想要的效果,此外总是控制不好面数,有些地方扭曲在一起,显得很乱。自己捣鼓估计是没希望了,还是要找教程。


看到 Tisoga 推荐了 Apple Music,于是开了国区。我听歌非常杂,而想把我所有歌曲都一个个配对为 CDN 里的文件,对于每个音乐服务提供商估计都挺困难的。不过 Spotify 港区在我目前感兴趣的歌手/制作人上表现更好一些,比如 FKJ
Apple Music 国区(2 首歌,而且是在选集中)

Spotify 港区(专辑,歌曲,信息齐全)

以及 Apple Music 目前在 Android 上的逻辑和界面仍然是 iOS 9 的风格,这个就有些尴尬了。总而言之,如果日常使用设备全都是苹果公司产品,并且有多年 iTunes 购买记录的话,Apple Music 应该还是不错的。当然满足这个条件的人应该也不会像我一样开国区了。

2016.10.9

 • 

刚刚把 Ghost 升级到 0.11.1,文章全都没了,吓我一跳。后来发现数据库里面还有,于是删了 node_modules 重新安装了一遍。


十一期间主要是呆在家发呆,然后花了些时间写另一个 Unity 项目,Epoch 暂时延后。说到这里又该说我挖坑不填完的问题了,不过 Epoch 在 Ray-Marching 这块折腾了一周,毕竟是在手机上运行,一旦 Ray Step 和 Noise FBM 次数增加 Shader 运行时间就要超过 40 ms,我也实在是想先放着不管了,等到 iPhone 8 出来了应该会好很多吧。然后说起来挖了坑还没填完的还有 Cetacea 的 Mac 和 iOS 版,其实完成度已经相当高了但想到要重构 Cetacea 的 Theme Manager 就觉得蛋疼。
算起来九月份一直在学校呆着,公司的事情只帮忙写了一点点,结果九月的工资还是打了过来,倒是让我感觉挺不好意思的。


花了些时间把本地歌曲迁移到 Spotify 上。想着反正开了 Premium 不如直接把音乐库用 Spotify 做缓存,但发现其实(至少香港区)歌曲数量也挺少的。在这一点上最全的算是 2012 年左右的虾米,那个时候虾米更多的是一个未授权音乐分享平台,而不是一个音乐流媒体平台。但虾米现在已经没救到我不用的程度了。


一个认识的朋友最近创业,拉了一个群。于是我见识了各色少年。之前大二的时候会被一些人说我年轻,现在我也见到了很多不仅更年轻而技术远远超过我的人了。
我总是感觉着急。我把我见识的一些厉害的人和我周围的人说,他们只是耸耸肩 —— 你还能指望什么反应呢。

海边的卡夫卡

 • 

火车上坐对面的人带了本海边的卡夫卡看,虽然翻了不到几页就趴在上面睡着了 —— 这话只是在叙述,不过听起来像是有些自以为是的原教旨主义者嘲笑他人的意味。不过我倒也没有嘲笑他人的资格,火车里晃来晃去,我看了会关于 Rails 的电子书也昏昏欲睡。
海边的卡夫卡是在高三读完的。坐在班级最后面连看三四天,靠窗边的耳朵带着耳机没人管也没人问,实在是现在想来都神奇的体验。现在看来,海边的卡夫卡与其说是一个自我蜕变的故事,倒不如说是一个自我修复的故事。卡夫卡是有着自己意识的一个人,并不需要外界压力来促使自己改变,外界也改变不了什么。
对于我来说,读这本书具体来说最大的收获就是:所有“后悔”大都是一种借口。若是足够清醒又有执行力之人,明白事情走向不对后定会及时改变,如此事态总不至于落到无可挽回的地步。若是什么也不做,眼睁睁看着事态说着自己后悔当初的决定,那大概也是做什么都无可救药。无论成功和失败,都总是人和人自己较劲,正如卡夫卡和乌鸦一样,没有什么要说后悔的,也不应该说。

2016.9.25

 • 


Fun Fact: 刚才用了下 Unity Cloud Build,Epoch 目前生成的安卓 .apk 文件只有 38.4 MB。
而这个版本的 Epoch 在理论上保守估计就已经有实时随机出 32768 个类地星球的能力了(指地形/半径/大气参数不一样的绿色/灰色为主的有海洋覆盖的星球)。考虑到 Unity 新工程什么也不做都有 7-8 MB,我觉得这个还是蛮有趣的。


一篇有趣的文章:Chris Roberts defends CRYENGINE评论里有人说:

Also, it is kind of hard to even call what they use the same Cryengine anymore. It has been so heavily modified for their specific needs, as well as so many of the Cryengine team now works at CiG that i would call it more the Star Citizen engine at this point.

想起来两天前因为 Unity 在 iOS 上缺少 Deferred Rendering 以及一些难以查找源头的问题和 KevinZhow 吐槽,然后 Kevin 说转虚幻引擎吧,我说要是再这么下去我估计真的要把 Epoch Core 重写到 C++ 上了。不过这话当然是有一些开玩笑的意味,重写的话我估计真的会再也不想做太空游戏了。以及幸好最后解决了大部分问题,最新的截图见 FinGameWorks


22 号去听了 Keren Ann 在长沙的演唱会。说是演唱会但到场的人数只有整个厅的一半,到了最后所有人都围在了前面,倒挺像是在 Live House。记得第一次听 Keren Ann 还是在虾米上听的 La Biographie de Luka Philipsen


不在公司的生活基本是就是写自己的游戏,吃饭,睡觉。没有上课一说。
也不怎么发推了,因为在寝室的每天都是一样的,不知道要说啥。
时常我会有这种感觉:看朋友圈里这几个人在参加什么技术大会,那个人作为大会的讲师,还有几个人今天面基,几个人今天出去玩。大家都过的很正常,日常生活罢了,但很丰富。但他们都在北京,或者杭州,或者什么地方。而我哪也去不了。浏览完朋友圈,继续呆在寝室里面,写着 Epoch。

Epoch Dev Blog 8 - Depth Texture Precision on iOS

 • 

这篇 Update 会很短,因为这个问题的解决只有一句话。
Unity 很多海水 Shader 在 iOS 上都有一个类似的问题:A7 及以前的设备(iPhone 5S / iPad mini 2)上一切正常,但是在 之后的所有设备上都会出现 banding 的效果:


(typo in pic : bonding --> banding)


这个问题是相机的 Depth Buffer 精度不足导致的。当然前提是相机打开了 depthTextureMode:

GetComponent<Camera>().depthTextureMode |= DepthTextureMode.Depth;

解决这个问题需要修改 Shader 中的 sampler2D 精度。这个非常奇怪,因为理论上 sampler2D 的精度就应该和 sampler2D_float 一样 而不是 sampler2d_half,但改了后在 A9 设备上就正常了,谁知道呢。

uniform sampler2D _CameraDepthTexture;

改为

uniform sampler2D_float _CameraDepthTexture;