右舷

宇宙,人类最后的边疆

switch

在最近发布的 iTimeLog 4.8.5 中,我们终于支持快捷指令了。
快捷指令是 iOS 12 开始支持的效率工具,可以配合应用快速完成各种任务。这一次,我们让 iTimeLog 可以和快捷指令配合,使时间记录更加方便。这种配合的基本想法,就是在通过系统的快捷指令功能,在进行某些操作的时候,同时触发 iTimeLog 开始时间记录,但比较遗憾的事,并不是所有的结束记录工作都能触发。

这一版中, 我们先开放了五个指令:

  • 开始一个事件
    在 iTimeLog 中开始 事件名类型
  • 完成一个事件
    在 iTimeLog 中完成 事件名类型,如果这一事件并没有在记录,忽略它。
  • 完成正进行中事件
    完成进行中的事件
  • 添加或结束事件
    在 iTimeLog 中添加 事件名类型,如果相同事件正在进行,完成它
  • 添加或继续事件
    在 iTimeLog 中添加 事件名类型,如果相同事件正在进行,就继续记录

安装了 4.8.5 以上版本之后,你就会在快捷指定的 App 项目中看到 iTimeLog,里面包含了这五条指令。
下面我们通过几个例子看看 iTimeLog 如何与快捷指令配合。

上班和下班

自动化

快捷指令的自动化 tab 支持进入一个地点和离开一个地点时触发,就是说你可以把你的公司作为一个触发条件,粗略地记录你在公司上班的时间。

在快捷指令的自动化 tab 中,创建一条个人自动化,选行程中的到达项目,选取位置和时间,然后添加一条「添加或结束事件」就可以了。

当你在特定时间段进入公司时,快捷指定会通知你已进入公司,是否添加一个「工作」记录,你只需要点击运行就可以了,iTimeLog 会帮你记录一件开始上班的记录,而离开公司时,快捷指定也会询问你是否需要结束这条「工作」记录。

稍微麻烦一点的是,地点触发的自动化指定是无法设置成不询问直接记录的,可能是苹果不希望用户在不知情的情况下被执行了一些操作。

统计 app 使用时间

在 iOS 中,由于沙盒机制的存在,app 其实不知道有哪些其它的 app 开始运行的。但是在快捷指令的自动化中,有一项是 「打开 app」时,所以我们就可以设置在打开指定的 app 时,触发 iTimeLog 开始记录事件。

例如,我一般用 overcast 听 podcasts,所以我就设置了一条自动化指令,当打开 overcast 时,自动开始记录 「日谈公园:消遣」,在这里我用的是添加或继续事件,而不是开始一个事件指令,因为「打开 app」这一个触发项在 app 从后台到前台的时候也会触发,而我不想在这些时刻都直接开始一条新的记录,使我的日常记录无限变碎。

日谈公园

这是最初的使用方式,几天后,我发现快捷指令是可以放到主屏幕上的,所以最终我改成了同时运行两条指令这种方式,即,我编辑了一条指令,当它执行时,同时记录事件和打开对应的 app,然后我把它放到主屏幕上。
主屏
这样,当我想看最新一集星际迷航:航海家号时,我就直接点击星际迷航图标,系统帮我开始一条 iTimeLog 记录,并同时打开 nplayer。

跟 NFC 一起使用

贴了贴纸的 switch

上面说的是在 iPhone 中跟其它 app 协同的记录,这次触发我们做这个捷径功能的其实是 NFC, 新的 iPhone 是支持 NFC 的,最近我看了一些视频,发现 NFC 贴纸是一个非常便宜的东西,而快捷指令是支持 NFC 触发的。这就把 iTimeLog 跟现实世界联系了起来。

自动化

所以我在我的 Mac 上, kindle 上, switch 上和一把椅子上都贴上了 NFC 贴纸。当我的手机扫到 kindle 上时, iTimeLog 开始记录我正在看的电子书,扫椅子时则是最近的一本纸质书,而扫一下 mac,开始记录工作。

在这里,我使用的是「添加或完成事件」指令,当我放下书或者 switch 时,拿手机再扫一下贴纸,就可以完成这条记录了。

接下来

我们应该会继续探索使用快捷指令记录时间的方式,在后续版本中推出更多可用的指定,让时间记录这件事更加自动化。

需求

需求很简单,当一个内容超过 4 行时,默认显示 4 行,并显示一个展开按钮,占周完全展开,展开后显示一个收起按钮,点击收起到 4 行。

当然最直接的想法就是利用 YYLabeltruncationToken, truncationToken 天生的含义就是文本无法完整显示的时候接在文本后面作为提示的,默认的样式就是常见的,它是可以自定义的,比如说在这个需求出,就可以设置为展开和收起。

但有一个问题,如下图。


truncationToken是跟在文本后面的。它所跟随的文本行只到显示宽度的一半时,就会变成这样。这个应该是用 truncationToken 无法完成的设置。

于是考虑还是使用一个按钮来实现这个需求,那么问题两个,一个是按钮怎么定位,一个是怎么在文本中留出空间放按钮。

按钮定位很简单, 一直在 YYLabel 的右下角就好了,微调一下边距即可。关键的留空的问题,一开始想的是使用就用 truncationToken 来实现,把它设置为一个 N 个空格组成的字符串,比如 「 」,但是不生效, truncationToken 看起来忽略了纯的空字符串, 「 ab」就可以,空格后面跟实际的字符的话,空格也会作为 truncationToken 的一部分,但是只有空格就是被忽略。后来考虑用不可见字符 比如 \u000000 ,结果一样,被忽略了,CoreText 看起来相当智能。

后来发现了一个鸡贼的做法, truncationToken 是一个 NSAttributedString, 那就可以设置成实际的字符,但颜色设置为 clearColor,用这种方式实现不可见的占位,然后把按钮放在占位留出来的空间了,就实现了需求。

帕劳潜水轨迹图
轨迹

前段时间做了一些 fastlane 自动化脚本的工作,记一些搜索出来的东西。

修改 plist 中的一些普通属性

Xcode 一般用 Info.plist 来管理 project 的一些属性, 对 plist 的修改可以用 update_info_plist

1
2
3
4
update_info_plist(
plist_path: "./Runner/Info.plist",
display_name: 'iTimeLog'
)

改变 bundle id

如果用 update_Info_plist 来修改 bundle id , 会发现 plist 文件中 bundle id 变了,但是Build Setting 中的 product bundle identifier 没有变化。 在 Fastlane 中使用 update_app_identifier 来修改 bundle id, 同时 Info.plist 中不要显示地写 bundle id, 使用 $(PRODUCT_BUNDLE_IDENTIFIER) 引用 product bundle identifier 就好了。

1
2
3
4
5
update_app_identifier(
xcodeproj: "Runner.xcodeproj",
plist_path: "./Runner/Info.plist",
app_identifier: 'net.laihj.iTimeLog'
)

修改 url type, 遍历 plist 中的 array

对于 plist 中的 array,使用 block: proc do |plist|来遍历,找到相应的 key , 再修改。

1
2
3
4
5
6
7
update_info_plist( 
plist_path: "./Runner/Info.plist",
block: proc do |plist|
urlScheme = plist["CFBundleURLTypes"].find{|scheme| scheme["CFBundleURLName"] == "iTimeLog"}
urlScheme[:CFBundleURLSchemes] = [option[:scheme]]
end
)

修改 entitlements 的内容

项目文件中的 Associated Domains 写在 entitlements 文件中, 用文本打开后发现 entitlements 就是一个 XML 文件。 当成 plist 文件使用 update_plist 操作即可。

1
2
3
4
5
6
update_plist( 
plist_path: "./Runner/即燃.entitlements",
block: proc do |plist|
plist["com.apple.developer.associated-domains"] = [option[:domain]]
end
)

打包时指定 AppIcon

在 gym 打包时 通过 xcargs 参数修改 ASSETCATALOG_COMPILER_APPICON_NAME

1
2
3
gym(
xcargs: 'OTHER_SWIFT_FLAGS="-D PROFILE" ' + 'ASSETCATALOG_COMPILER_APPICON_NAME='+appicon,
)

2019年,我们终于支持 iPhone 的 today widget 了,today widget 就是 iPhone 的桌面小部件,在 iPhone X 系列中,滑到最左边就是 today widget的功能了,在非 X 系统 iPhone 中,需要下通知列表,再左滑才能看到。

iTimeLog 的 today widget 第一版,是一个快速输入系统。输入的简便是 iTimeLog录入设计 的最重要目标。

today

如图所示,iTimeLog 的 today widget 分为两个部分,用一条浅蓝色的线分割。上半部分第一条常驻按钮是添加一个新的事件,点击它将会调起 iTimeLog 的主程序,并且呼出键盘,接受你的输入。

第二条常驻元素是当前正在进行的事件,点击它将会完成这个事件的记录,如果当前没有记录事件,它的功能就和第一条常驻一样。

下半部分是常用输入事件备选项,一直以来, iTimeLog 都觉得将用户已输入的事件作为事件模板,自然形成最近一段时间的输入备选是一种合理的做法。这里也一样,这个桌面小部件的下半部分最多可以显示五个事件,这五个事件是 iTimeLog 中用户输入的最近五个事件列表,实测对于一个生活比较规律的人来说,可以覆盖 50% 以上的快速录入功能了。

在这个功能的开发过程中,我买了自己的第一台 X 系列 iPhone,一台红包的 iPhone Xr。实际使用中,我发现 today widget 的使用比想象中还要方便,在手机不解锁的情况下,你也可以在锁屏界面直接左滑使用这个功能。

图表是一种对数据的加工,目的是为了让你对你记录的数据有一种更直观的把握。

输入其实是为了输出数据进行检视,检视是为了改进工作流程。如果不做输出和检视,输入的数据就是死数据,输入所花费的时间也就白费了。

这一篇我们来介绍 iTimeLog 的图表,它们都是检视的辅助。

首页就是一张表

iTimeLog 的首页,是一个输入界面,同时它也是一张简单的基础时间消耗列表,这张列表中,以天为单位隔开,按时间顺序排列事件,并展示出每个事件的起止时间和时间消耗。

点击首页右上角进入第一个统计页面

(上图是以下两个界面的合并,app界面是分列表和饼图的)
第一个页面是事件的列表,简单直接但是不直观,点击右上角的按钮就进入了事件的统计列表。

在这张表中,我们把选定时间段内事件的消耗时间加起来,让你直接看到每天每周或每月的总体时间花费。

这张表中,我们还提供一张饼图,让你直接看到各个事件的时间使用比例,这样你就能看出这一天是生产的一天,还是休憩的一天。

列出事件详情

在统计界面中点击某个特定的事件,就进入了该事件的详细时间账单,这张账单显示出你使用 iTimeLog 以后在一件事件上付出的时间。

对于一些短期事件,比如读一本书之类的,也能非常方便地查看记录整体花费的时间。

单个事件的统计图表

柱状表显示每个时间段中,给事件付出的总使用时间。

同时多个时间段排列查看,也能看到这一事件的时间投出趋势,比如 iTimeLog 在最初比较密集开发的时候,每周大概要投入 20 个小时以上的业余时间,发布几版之后,主要功能稳定了,时间花费就降了下来。

这张事件的时间分布图,主要是显示你是否在正确的时间做重要的事情。

每个人都会有自己的高效时间和不高效的时间,在高效时间进行更有生产力的活动是一种更合理的选择。iTimeLog 其实并不希望给人压力,让人去尽力填满每天的 24 个小时,更希望通过统计数据,让人们对自己每天的 24 小时做出更合理的分配。

17年买了一个小房子,装修半年并搬了进去。
潜了一次水,见了长尾鲨。
年初去厦门跑了一场马拉松,年末去深圳踢了一个投资人组织的小杯赛,期间踢了很多友谊赛。
几乎完成了一个创业项目。
不记得读了多少书,Kindle 的磨损程度还一般,并且没有坏。
还保持着跑半个小时热身再踢两个小时球,最后十分钟才抽筋的体力。

工作

今年依然在做OpenPlay,上半年忙一个新版本,下半年比较懒散和盲目,可能做不了太久了。固然有市场上对于这种增值服务不认可的原因,我们团队本身自由散慢也确实不像很有生命力的样子。

个人项目


iTimeLog 去年赚了两只猫的生活费,今天情况好一些,跟一些大触没法比,可能赚出了四只猫的生活费,虽然也并没养四只猫。
工作时间上,花了 16 小时 47 分,发了 7 个版本,适配了 iPhone X。
年初处理一个在微信群里一直有反馈,我自己手机上无法复现的卡顿问题,折腾了近一个月。除此之外,都是一些功能上的小更新。
10月份左右,开始使用 swift 和 storyboard 构建一个新的 app ,累积了 41 小时 34 分钟的工作量,完成度 61.25% 左右,希望农历春节之前可以上线。

路飞艾斯

二位爷又老了一岁,已经是九年陈的老猫了。
路飞还是口臭,艾斯吃了罐头还是会吐。除此之外身体比较健康,体重没什么变动,也没去过医院。

Kindle

12月的时候,有一个周末,我跑到广州图书馆办了张卡,当时借了两本书,感觉实体书拿在手上真是压手,还是 Kindle 轻便好用。

Podcast

今天一直在听的中文谈话类 Podcast 是大内密谈,日谈公园和跟宇宙结婚。谈话类是一种陪伴,特别适合不太喜欢聊天又喜欢听的人。
英文的主要是 Accidental tech, undter the radar, core int 和 AppStories,没有全听,也没有全听懂。

BiliBili & youtube

要特别提一下 B站 和 油管,今年的视频消费主要是这两个站,在 B 站主要看一些老年电视剧,这一年看得最多的是 星际迷航:航海家号 ,可惜 B站 好像也没有版权,现在只剩下缓存里的几十集了。
youtube 上有万罗万象的短视频,没事看一个看外国人做木工特别生活。

17 年写了 7 篇 blog, 18 年写个 52 篇吧。


耐力:无伤、燃脂、轻松的MAF训练法
这本书我是在微博上看到有人说(跑步方面的书如果只看一本的话,这本就完全够了。 ​​​​),刚好 Kindle 又有电子版卖,就买了一本看看。
菲利普•马费通 博士在他这本耐力中,提出了一个我以前没有想过的概念,一个人运动能力强,不代表他身体健康。跑得快这个事,有可能是因为生理状态好,有可能是因为心理状态强,心理状态强便是“催谷”自己跑出长距离的极端结果就是马拉松上救护车。

我以前一直以为,只有职业运动员才会为了运动表现牺牲身体健康,但过度训练导致的健康受损和伤病还是挺普遍的。

这本书中提出了一个以心率为核心的健康训练方法,以跑步为例,它认为 (180 - 年龄)得到的数字大概就是你的最大有氧心率,用低于这个数字并接近这个数字的心率训练能发展你的有氧能力,用高于这个数字的心率训练能发展你的无氧能力。多数人在日常跑步的都会超过这个数字进行训练。这样在提高成绩的同时,其实也在累积疲劳,累积伤病,可能在赛季中途被迫离开赛场。

以我为例,我正常的配速是五分半左右(每公理要跑5分半钟),并且在5公里之内可以保持匀速巡航,心率在第1公里结束时就会达到150左右,然后会慢慢提高,到5公里结束时就会达到170以上,而且我习惯最后1公里左右开始加个速,那么最后的结束心率会达到180左右。也就是说,在1公里之后,我基本就是在无氧空间跑步了。

我现在的身体状况是,如果去跑全程马拉松的话,大概在20到25公里右左开始掉速。

我尝试实践一个书中的 MAF 训练方法,12.26日晚上我要去踢个球,球场离家有四公里多,我就带上心率器跑过去,在跑步过程中一直注意心率的变化,尽力把它稳定在 147 左右。配速如上图所示,从6:42 到 8:49,感觉上,早期心率还好控制,基本来还是要花点力气才能到147,到了最后一公里,带稳定住就比较难了,稍不注意就到150,而且过了150就飞快飚到160左右,最后一两百米的时候,为了保持住心率都开始用走的了。

不过,这个心率也许真的差不多就是我的有氧心率,因为跑完后基本没什么太大的疲劳感,直接踢了场两个小时的中年养生球,最后十分钟才开始出现抽筋现象。(踢球跟训练无关,按书中的指示,如果认真进行有氧训练,踢这场球是有害的,然而……

周一早上,我有进行了一次低心率的训练,这次数据上好看一点,可能是因为早上的气温比较低一些,跑了6公里,配速从6:14 掉到了7:33,(8:22是干扰项,当时我在进行跑后的散步,等心率掉到120以下才关掉 runkeeper。

我打算以这个数据为基点试着实践一下这个训练,每周135早上跑6公里左右,保持心率,不求速度,看看在相同心率下,配速会不会慢慢提高,每隔一段时间跟这个数据进行比较。

时间快捷修改

来自一个用户需求。

如方便的话,恳请作者在二个地方小调整一下,一是新建时间段能否增加一个“上一阶段结束时间为本阶段开始时间”的选项,而不是简单的直接调用当前时间

我试了一下,可能对大部分人来说,可能只有10%的时间需要在开始一个事件时间段时使用上一个阶段的结束时间,而为了这个频度的需求在每次添加时间的时候选一下,并不太方便。
所以做了一个折衷的方案,在事件编辑界面加了两个按钮,一个对开始时间进行设置,一个则针对结束时间。
开始时间的按钮,功能是把开始时间设置为上一个事件的结束时间。
结束时间的按钮,会查看是否存在下一个事件,如果存在,就设置下一个事件的开始时间,否则设置为当前的时间。

一个分享的bug

上一个版本,存储分享用图片时会 crash。因为苹果不同的 iOS 版本会对相册权限有不同的设置(基本上是越来越严格,越来越细致),iTimeLog 没有存储相册的权限。

iTimeLog 4.5.0

4.5.0 没有太多可说的,就是一个快 7 年的老 app 适配了 iPhone X。
作为开发者,不得不说,积年的代码,不管有没有 bug,都会慢慢腐化。

经常有人在第一次听说时间记录 app 的时候,问它是自动记录的吗?没有人喜欢做额外的工作。但是很遗憾的是,至少在 iOS 上,自动记录的方法是不存在的。iTimeLog能做的是尽量简化记录。
iTimeLog 的录入设计,其基本的原则想法有两个:

  1. 尽快开始,不要为了记录时间这一额外步骤打断做事的步骤和心情
  2. 使用已经录入过的事件作为材料,减少输入成本,对大多数人来说,每一天的生活其实是由很多可归类的事件组成的。比如 工作-休息-工作-进食-休息,再比如在工作之余读一本书,很少数人是一次性读完,总是要读几章,有事打断,之后再回来读的。

所以 ,iTimeLog 的录入方法有以下几种:

直接点加号


什么都不要输入,如果你开始一件事情时脑中已经开始考虑做事的步骤和方法了,不要打断它,点一下这个加号,iTimeLog 为你建立一个开始时间为当前时间的事件,然后计时。等你中途休息或完全事件时再回来编辑就好了。

像短信一样输入

所有事件都是文字,所以 iTimeLog 其实不鼓励预先设定事件,然后通过选择来进行录入或开始的方式。我们鼓励直接输入文字,输入完成之后点击键盘上的完成或者输入提示条上的加号就开始计时。

提示


当你在输入文字时,iTimeLog 为你在 app 中实时搜索,并在系统的输入提示上方展示 iTimeLog 的提示,这个提示中展示了你之前输入过的事件,按时间倒序排列。也就是说,排在最前面的事件并不是跟你当前的输入最匹配的事件,而是在所有可以匹配的事件中离现在最近的事件,因为这可能是你最想输入的事件。

简单的文本处理

在上面输入提示的图中,你可能注意到了,提示中的事件不单只是事件,而是“事件:类型” 的格式。这个格式是全 app 通用的,也就是说你在任何地方输入或设计这个格式的文本,都会被分解成 事件+类型 的数据型式。
在首页输入框可以这样输入,在模版设置中也可以设置这样的模版以供使用。

模版


上面提到了模版,模版这个功能做得比输入提示要早,但做了输入提示之后,我个人已经很少用模版功能的。
模版功能是指你可以在设定中预设你的事件模版或类型模版,这样在需要的时候,你可以选择模版,而不需要手动输入了。
这个功能因为预设事件比较麻烦,而且如果预设的事件比较多,滚动选择也不是很方便。但如果只设置五六个左右的模版,而且这些模版事件不是很常用的(比如天天都会记录),还是有一定的作用。

重新开始


那么,最常用的事件,天天都做的事,比如工作,吃饭,交通,(还有人记录睡觉时间 ),我们推荐使用“重新开始” 按钮,这个按钮是我觉得 iTimeLog 还算可以使用的原因。
对于大多数普通人来说,每天的生活主干就这上面说的几件事(工作,吃饭,交通,休息),所以在时间流中,iTimeLog的记录列表中,这几件事总是会出现在屏幕上。这个时候,就不要用输入或模版了,只要选中相应的事件,点击重新开始就可以,最快最简单。

0%