记 Swift async 替换 Combine 实现用户行为计时.

2022-06-26 18:16:38 +08:00
 andyJado

前言

全身心写几个月 swift 了, 第一次不依赖谷歌完全用自己的思路解决的一个小问题, 和大家分享一下, 希望大家能说些难听的话.🙇‍♀️

背景:

聊天软件几乎都有输入框, 配合键盘的唤出, 用于区分编辑状态, 十分符合人类直觉.

微信等聊天软件的通用逻辑是:

  1. 轻点唤醒键盘.
  2. 等键盘完全唤醒, 轻点(或下拉) 撤走键盘.

可是. 等待键盘的过程, 是一个动画, 用户只能看, 无法干预. 有那么半秒钟, 我的手机不听我的了, 自己玩了.

简单来说: “不够丝滑”.

「推敲」是允许你这样做的:

https://user-images.githubusercontent.com/101876416/175808976-cd1a4760-609a-497c-afa9-c12c14b1a1f1.mov

解耦出来看这并不难, 但「推敲」的一个核心功能是精准记录单个话语的编辑时长, 如何在暴力驾驶中依然完成精确的编辑计时呢?

聚焦:

丝滑的极致是一切不更新到视图上的进程都不上主线程. 丝滑的代价是一切视图上的进程随时能被干预

最初的实现采用的 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 个链接.

😮‍💨

752 次点击
所在节点    程序员
1 条回复
agagega
2022-06-26 21:23:11 +08:00
Combine 是 SwiftUI 的核心呀,为什么会被弃用呢

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/862309

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX