前言
全身心写几个月 swift 了, 第一次不依赖谷歌完全用自己的思路解决的一个小问题, 和大家分享一下, 希望大家能说些难听的话.♀
背景:
聊天软件几乎都有输入框, 配合键盘的唤出, 用于区分编辑状态, 十分符合人类直觉.
微信等聊天软件的通用逻辑是:
- 轻点唤醒键盘.
- 等键盘完全唤醒, 轻点(或下拉) 撤走键盘.
可是. 等待键盘的过程, 是一个动画, 用户只能看, 无法干预. 有那么半秒钟, 我的手机不听我的了, 自己玩了.
简单来说: “不够丝滑”.
「推敲」是允许你这样做的:
解耦出来看这并不难, 但「推敲」的一个核心功能是精准记录单个话语的编辑时长, 如何在暴力驾驶中依然完成精确的编辑计时呢?
聚焦:
丝滑的极致是一切不更新到视图上的进程都不上主线程. 丝滑的代价是一切视图上的进程随时能被干预
最初的实现采用的 Combine 框架, 但我的嗅觉告诉我 Combine 迟早会被弃用, 借这个机会替换掉,只 import swiftUI 和 os.log.
看见了钉子, 选好锤子, 下面就是砸.
思路:
划拉一块内存, 登记为 actor, 叫计时工. 予左手右手各一把章. 用户唤起键盘时左手戳一下, 键盘时他右手戳一下, 用户提交话语的时候双手一拍, 返回总时长.
实现:
计时工入职培训:
**actor** TimingManager { **private** **var** timeStack:[(Date,Date)] = [] **private** **var** aStart: Date? **func** onFocus() { aStart = Date() logger.debug("onFocus()") } **func** endFocus() { **if** **let** left = aStart { timeStack.append((left,Date())) aStart = **nil** logger.debug("endFocus!") } } **func** handClose() -> Int { **guard** !timeStack.isEmpty **else** {**return** 0} logger.debug("\(**self**.timeStack.debugDescription)") **var** sec: Double = 0 **for** hand **in** timeStack { sec += hand.1.timeIntervalSince(hand.0) } clearHand() **return** Int(sec) } **func** clearHand() { timeStack.removeAll() } } 计时工的日常工作, 追踪键盘是否抬起.
.onChange(of: focuing) { focuing **in** **switch** focuing { **case** **true**: Task { **await** viewModel.timeAcotr.onFocus() typerOffset = -340 } **case** **false**: Task { **await** viewModel.timeAcotr.endFocus() typerOffset = 0 } } } 计时工的汇报工作:
**func** submitted() { **if** aword.text == "" { Task { **await** timeAcotr.clearHand() } aword = Aword() } **else** { Task {@MainActor **in** **await** timeAcotr.endFocus() aword.secondSpent += **await** timeAcotr.handClose() withAnimation { wordsPool.append(aword) } aword = Aword) } } } 最后
我觉得作为一个产品「推敲」已经挺棒了的, 至少已经改善我生活了.
但我甚至不敢在这里 po 个链接.
