V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
andyJado
V2EX  ›  程序员

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

  •  
  •   andyJado ·
    AndyJado · 2022-06-26 18:16:38 +08:00 · 866 次点击
    这是一个创建于 909 天前的主题,其中的信息可能已经有所发展或是发生改变。

    前言

    全身心写几个月 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 个链接.

    😮‍💨

    agagega
        1
    agagega  
       2022-06-26 21:23:11 +08:00
    Combine 是 SwiftUI 的核心呀,为什么会被弃用呢
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2725 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 08:03 · PVG 16:03 · LAX 00:03 · JFK 03:03
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.