技巧索引
按框架、源码项目、验证方式、章节和标签筛选全部 300 条技巧。
下面列出 15 份主题笔记中的全部条目。筛选条件会写入地址栏,所以可以直接分享筛选结果。例如,同时选中“AppKit”和“私有 API”标签,就能列出 macOS 端依赖未公开接口的条目。点击条目标题进入章节页,查看代码摘录和证据链。
显示 300 / 300 条
- #01-01UIKit运行时与私有 API精选 01-1借安全文本输入层给任意图层打上截图排除标记
代码找到
UITextField的内部TextLayoutCanvasView。代码临时用 KVC 把目标CALayer替换成该视图的layer。代码切换secureTextEntry后恢复原图层。秘密聊天的历史节点会调用此函数。UIKitUtils.m · L299–L328 - #01-02UIKit运行时与私有 API精选 01-2用 XOR 字节串隐藏私有拼写控制器,并用精确 ABI 调用它
代码将私有类和 selector 的 UTF-8 字节逐字节 XOR
0x5A。代码只在运行时解码并查找。代码把带BOOL的语法检查调用转换为准确的 C 函数类型。 混淆还原:我按byte ^ 0x5A解码源码数组。结果是UITextCheckingController、``…NativeTextChecking.swift · L4–L88 - #01-03UIKit运行时与私有 API精选 01-3伪造
Info.plist结果,让 App-Bound 域名可以动态增删代码交换
NSBundle.objectForInfoDictionaryKey:。当 WebKit 查询WKAppBoundDomains时,代码返回带引用计数的运行时域名列表。代码再经dlsym调用私有重载函数刷新指定数据存储。 混淆还原:@"WK" + @"Website…UIViewController+Navigation.m · L518–L543 - #01-04UIKit运行时与私有 API从私有键盘场景代理取得键盘窗口,并避开新版陷阱
代码为未声明接口声明最小协议。代码通过
UIWindowScene.keyboardSceneDelegate.keyboardWindow取得内部键盘窗口。每一步都先检查 selector 和结果类型。UIViewController+Navigation.m · L333–L350 - #01-05UIKit运行时与私有 API精选 01-4用私有
cornerRadii获得可动画的四角半径,避免离屏遮罩代码探测私有 setter
setCornerRadii:。代码按 QuartzCore 类型编码把四个CGSize装入NSValue。代码用 KVC 读写私有cornerRadii属性。CALayerCornerRadii.swift · L37–L124 - #01-06UIKit运行时与私有 API交换继承方法前先落地到目标类,避免污染父类
代码先尝试向目标类添加原 selector。添加成功说明原实现继承自父类。代码此时只在目标类安装交换后的入口,并把父类 IMP 放到替代 selector。只有原方法本来就在目标类时才直接交换。
RuntimeUtils.m · L21–L32 - #01-07UIKit运行时与私有 API把
UIMenuItem的文字渲染偷换成图标渲染代码给带图标的菜单标题追加
U+FEFF U+200B隐形后缀。代码交换NSString.sizeWithAttributes:与UILabel的绘制、布局和 frame setter。代码根据标题反查菜单项,抑制文字并嵌入居中的UIImageView。UIMenuItem+Icons.m · L30–L164 - #01-08UIKit运行时与私有 API从私有
_scrollHeartbeat抓显示链接,强制旧系统滚动使用满帧率代码在 iOS 15 上以 KVC 读取
UIScrollView的私有_scrollHeartbeat。代码标记该CADisplayLink,并请求屏幕最大帧率。另一个CADisplayLinkswizzle 会再次把带标记的范围固定到最大值。 混淆还原:@"_" +…UIViewController+Navigation.m · L291–L317 - #01-09UIKit运行时与私有 API精选 01-5先读私有方法签名,再选择
float或double的 IMP 调用约定代码动态查找
CASpringAnimation的私有求值 selector。代码读取第一个显式参数的 Objective-C 类型编码。代码按float或double选择不同函数指针。找不到时退化为线性值。 混淆还原:@"_" + @"solveForInput:"还原…UIKitUtils.m · L22–L49 - #01-10UIKit运行时与私有 API给弹簧动画写入私有高帧率原因码
代码组合公开的
preferredFrameRateRange和私有 KVC 键highFrameRateReason。代码只在 iOS 15 及以上设置这两项。UIKitUtils.m · L68–L83 - #01-11UIKit运行时与私有 API用
NSInvocation安全探测并配置私有模糊效果代码在 iOS 11 到 iOS 12 解析私有类,并经
NSInvocation调用存在的 setter。辅助函数先取得NSMethodSignature。不存在的 setter 会成为无操作。 混淆还原:类名是_UICustomBlurEffect。setter 依次是se…UIKitUtils.m · L110–L200 - #01-12UIKit运行时与私有 API将私有
_UIPortalView包装为最小协议,并关闭其命中测试转发代码拼接并解析私有门户视图类。代码以本地
UIKitPortalViewProtocol访问已知属性。代码把门户设为不接收触摸,并按需要同步源视图的位置和变换。 混淆还原:数组拼接结果是_UIPortalView。代码还通过同样的解析确认一个视图是否为该类,并读取其sourceVie…UIKitUtils.m · L211–L257 - #01-13UIKit运行时与私有 API精选 01-6检验私有方法的完整类型编码后才替换 IMP
代码仅在 iOS 26 及以上注册。代码解析 UIKit Swift mangled 类名,再找
backdropLayer:didChangeLuma:。代码要求完整编码正好是v32@0:8@16d24后,才保存原 IMP 并替换。 混淆还原:TGEncodeText的键为0…UIViewController+Navigation.m · L378–L420 - #01-14UIKit运行时与私有 API通过私有 selector 让悬浮窗口既不改状态栏,也不夺取键盘焦点
AboveStatusBarWindow处于状态栏之上。代码在类初始化时,给它动态添加两个私有 selector。两个 selector 都复用返回false的公开命名实现。 混淆还原:第一个 selector 是_canAffectStatusBarAppearance。第二个…UIViewController+Navigation.m · L890–L941 - #01-15UIKit运行时与私有 APIDebug 录制器直接替换 UIKit 基类 IMP,采集原始滚动物理数据
调试专用录制器把
UIPanGestureRecognizer的四个触摸入口替换为 block IMP。代码先执行原实现,再记录已经更新的速度和状态。代码还 hooksetContentOffset:,以及存在时的私有橡皮筋计算器。UIScrollViewPhysicsSwizzler.swift · L75–L133 - #01-16UIKit运行时与私有 API关闭
UITextView分块渲染,并识别 iOS 16 私有自动更正提示构造输入框时,调用存在才执行的私有
_disableTiledViews。iOS 17 前,代码加一个不延迟也不取消触摸的 tap recognizer。它会把@"UI" + @"AutocorrectInlinePrompt"解析为UIAutocorrectInlinePrompt…ChatInputTextViewImpl.m · L23–L70 - #01-17UIKit运行时与私有 API旧系统用 KVC 强制设备方向,新系统改走场景几何更新
代码将方向策略切为仅竖屏时,在 iOS 16 以上请求公开的
UIWindowScene.GeometryPreferences。旧系统则向UIDevice的 KVC 键orientation写入竖屏 raw value。NativeWindowHostView.swift · L103–L123 - #01-18UIKit运行时与私有 API重写私有
UIWindow方向回调,精确包住布局中的旋转状态NativeWindow覆盖私有_update(toInterfaceOrientation:duration:force:)。代码在super前后切换布局中标志。代码在结束后把转换后的方向回传给自定义窗口宿主。NativeWindowHostView.swift · L318–L339 - #01-19UIKit运行时与私有 API对运行时类表做哈希索引,避免私有类名进入二进制
Freedom枚举全部 Objective-C 类。代码用带固定 seed 的 MurmurHash3 把类名映射到uint32_t。调用点只保留哈希常量,再从映射获得运行时类。Freedom.mm · L124–L155 - #01-20UIKit运行时与私有 API自动生成装饰子类,再从
alloc时替换每个实例的 ISA代码动态创建
Decorated<hash>子类。代码从另一个类复制指定方法到装饰类。代码把原类元类的alloc替换为freedomAllocImpl,所以每个新实例都会在分配后被object_setClass成装饰类。Freedom.mm · L469–L524 - #01-21UIKit运行时与私有 API直接改写系统返回手势识别器的 ISA,增加全屏导航手势
代码把 UIKit 创建的
interactivePopGestureRecognizer改成自定义TGNavigationPanGestureRecognizer。代码保留系统原 delegate,并以代理注入自定义开始和同时识别判断。遇到QLPreviewController…TGNavigationController.m · L129–L182 - #01-22UIKit运行时与私有 API把
NSNotificationCenter的同步投递改造成可截获的中间件代码全局交换通知中心的同步投递方法。主线程处理器可先做工作、调用
passNotification决定投递时机,或返回true完全吞掉投递。方向处理模块用它在旋转通知前后维护状态。NotificationCenterUtils.m · L19–L68 - #01-23UIKit运行时与私有 API把截图重复通知去重状态放在每个 observer,避免覆盖层漏报
代码以每个
ScreenshotNotificationObserver自己的最后时间戳去重。相隔少于0.5秒的相同截图通知会被丢弃。ScreenCaptureDetection.swift · L53–L88 - #01-24UIKit运行时与私有 API只把
CAFilter当作运行时工厂,不引入私有头文件代码以本地
GraphicsFilterProtocol描述唯一需要的类方法。代码用NSClassFromString(@"CAFilter")解析私有 Core Animation 类。各工厂直接按滤镜名调用filterWithName:。UIKitUtils.m · L259–L295 - #02-01UIKit窗口、键盘与状态栏精选 02-1经私有键盘场景委托取得键盘窗口
它从
UIWindowScene的keyboardSceneDelegate取keyboardWindow。AppDelegate先走此路径,再回退扫描所有场景窗口。UIViewController+Navigation.m · L761–L782 - #02-02UIKit窗口、键盘与状态栏用类名形状兼容多代系统键盘视图
它按前后缀匹配
UIRemoteKeyboardWindow、UITextEffectsWindow、UIInputSetHostView和UIKeyboardItemContainerView。它也匹配容器UIInputSetContainerView与UITrac…AppDelegate.swift · L73–L106 - #02-03UIKit窗口、键盘与状态栏精选 02-2直接移动系统键盘层,而非只移动应用内容
它把键盘视图的
bounds.origin.y设为负偏移。它再对同一CALayer加加性位移动画。窗口布局同时把输入高度扣除该偏移。KeyboardManager.swift · L65–L79 - #02-04UIKit窗口、键盘与状态栏精选 02-3用多个坐标修正键盘通知的错误高度
它优先信任实时键盘视图的
frame.minY。iOS 16.1 以上,它把通知中的屏幕坐标转换到事件窗口。它还修正居中窗口的半个高度差、iPad 的近边界误差和子窗口的27pt偏差。WindowContent.swift · L650–L761 - #02-05UIKit窗口、键盘与状态栏全窗口手势用第一响应者和附件高度划定拖拽区
它把窗口级拖拽限制在键盘上方。它沿命中视图向上读取禁用标记。它把输入附件高度计入键盘上界,并以
3pt阈值开始更新布局。WindowContent.swift · L1489–L1513 - #02-06UIKit窗口、键盘与状态栏精选 02-4键盘收起手势显式取消同一手指的列表惯性
它在触摸结束期间记录“此手势已收起键盘”。列表稍后收到自己的
.ended时可读取该值并放弃惯性滚动。WindowContent.swift · L1547–L1573 - #02-07UIKit窗口、键盘与状态栏伪造选区变化以触发文本自动更正
它先取消 marked text。它把选区左移一个字符。它立即恢复原选区。
UIViewController+Navigation.m · L850–L859 - #02-08UIKit窗口、键盘与状态栏精选 02-5将系统键盘窗口横向平移到第一响应者所在表面
它根据第一响应者宿主的屏幕 X 坐标改
keyboardWindow.layer.sublayerTransform。它可限制只向前或向后平移。响应者消失时,它短暂隐藏窗口并递归取消position与bounds动画。KeyboardManager.swift · L99–L123 - #02-09UIKit窗口、键盘与状态栏抓取系统状态栏后按像素重着色
它把每个系统状态栏子视图渲染进
DrawingContext。它再按 alpha 把像素改为黑色或白色。最后用ASDisplayNode.contents在相同坐标复刻图像。StatusBarProxyNode.swift · L153–L194 - #02-10UIKit窗口、键盘与状态栏运行时拼接私有状态栏类名
它不在单个字面量中放完整私有类名。
CFAbsoluteTimeGetCurrent() > 0在正常运行时为真,因此代码会拼出完整名称后再传给NSClassFromString。StatusBarProxyNode.swift · L360–L414 - #02-11UIKit窗口、键盘与状态栏用公共 RunLoop 定期重抓状态栏的活动元素
代理显示时立即重抓条目。它随后每五秒在
.commonRunLoop 模式重抓。隐藏时它销毁定时器。StatusBarProxyNode.swift · L482–L499 - #02-12UIKit窗口、键盘与状态栏把多个节点树的状态栏决策汇聚到唯一根控制器
Window1依次让根导航、普通展示层和顶层展示层覆盖状态栏值。WindowRootViewController保存最终值,并调用setNeedsStatusBarAppearanceUpdate()。WindowContent.swift · L1188–L1208 - #02-13UIKit窗口、键盘与状态栏精选 02-6全局覆盖层在键盘可见时改挂到键盘窗口
键盘可见且有真实高度时,它把全局控制器的父视图改为
keyboardWindow。键盘不存在时,它改回普通窗口的parentView。GlobalOverlayPresentationContext.swift · L72–L105 - #02-14UIKit窗口、键盘与状态栏展示上下文不用 UIKit containment,而是手动生命周期
它等待
ContainableController.ready,最多两秒。它直接配置 view 和节点布局。随后代码直接插入子视图,并手动调用viewWillAppear和viewDidAppear。PresentationContext.swift · L157–L244 - #02-15UIKit窗口、键盘与状态栏自定义
UIWindow.hitTest特判系统编辑菜单自定义窗口先倒序测试 UIKit 加在窗口顶层的编辑菜单。只有未命中时,它才分发给 Telegram 的覆盖层和根内容。
WindowContent.swift · L957–L999 - #02-16UIKit窗口、键盘与状态栏透明伪滚动视图接管状态栏“回到顶部”手势
它在窗口上覆盖一个透明
UIScrollView。它伪造1000pt可滚动距离。状态栏点击触发代理回调,但 delegate 返回false,因此代理本身不滚动。ScrollToTopProxyView.swift · L4–L37 - #02-17UIKit窗口、键盘与状态栏仅全屏窗口套用按设备表测得的安全区
它把窗口与屏幕的纵向尺寸逐项比较。差超过
1pt时,它完全放弃设备刘海表。多显示屏设备则直接读窗口的系统安全区。WindowContent.swift · L104–L140 - #02-18UIKit窗口、键盘与状态栏用型号、缩放率和屏幕尺寸选择经验设备档案
它从
utsname.machine读取型号。它先按型号筛档案,再用 scale 与屏幕尺寸确认。它把高度加20pt后再比一次,以兼容状态栏尺寸差异。DeviceMetrics.swift · L57–L76 - #02-19UIKit窗口、键盘与状态栏把灵动岛与刘海绘制成一张按机型量出的几何表
它为每代设备指定缺口宽、高和顶部偏移。显示缩放模式不另存一份表,而是把标准机型几何按宽度比例缩放。
DeviceMetrics.swift · L487–L533 - #02-20UIKit窗口、键盘与状态栏双显示屏型号绕开整张设备指标表
对型号
iPhone19,4,它不信任启动时匹配出的设备档案。它改读当前窗口的系统安全区和状态栏。状态栏暂时消失时,它按屏幕尺寸缓存上一次可见高度。DeviceMetrics.swift · L71–L76 - #02-21UIKit窗口、键盘与状态栏方向锁定同时兼容现代 Scene API 与旧 KVC
iOS 16 以上用
requestGeometryUpdate锁竖屏。旧系统用 KVC 写UIDevice.orientation。其他方向变更则请求 UIKit 重试设备方向。NativeWindowHostView.swift · L103–L123 - #02-22UIKit窗口、键盘与状态栏在 UIKit 继承动画期间手工补一条窗口层帧动画
自定义
UIWindowsetter 读取UIView.inheritedAnimationDuration。有继承动画时,它在窗口层追加同持续时间的animateFrame。无尺寸变化时,它避免布局通知。NativeWindowHostView.swift · L263–L287 - #03-01UIKit布局与动画用私有
cornerRadii实现零掩膜的四角圆角它探测私有 setter
setCornerRadii:。它通过 KVC 读写私有cornerRadii。CornerRadii.boxedValue按CACornerRadii的四个CGSize布局封箱。调用方还能从展示层读取中断时的实际圆角。CALayerCornerRadii.swift · L95–L119 - #03-02UIKit布局与动画精选 03-1劫持 UIKit 事务内的弹簧动画
ComponentTransition.animateView先把覆盖参数压栈。它调用UIView.animate。+load再交换-[CALayer addAnimation:forKey:]。交换后的实现会复制 UIKit 生成的动画,并替换为指定刚度、阻尼或三次贝塞尔曲…UIViewController+Navigation.m · L191–L215 - #03-03UIKit布局与动画调用私有
_solveForInput:统一手工插值和系统弹簧ListViewAnimation不自行近似系统弹簧。运行时工具将字符串"_" + "solveForInput:"还原为_solveForInput:。它读取方法签名,并按float或double调用 IMP。找不到时它回退为线性t。ListViewAnimation.swift · L91–L110 - #03-04UIKit布局与动画精选 03-2借用未公开的 UIKit 曲线值
7 << 16代码把
UIView.AnimationOptions的曲线字段设为7。它把该值命名为.spring。ContainedViewLayoutTransition对.spring和.customSpring使用相同 raw value。Transition.swift · L81–L99 - #03-05UIKit布局与动画精选 03-3通过私有
_scrollHeartbeat强制旧系统滚动到高刷新率它将字符串拼接为
_scrollHeartbeat。它用 KVC 取出UIScrollView内部的CADisplayLink。它在 iOS 15 的高刷设备上,把该 link 的最小、最大和首选 FPS 都设为屏幕最大值。UIViewController+Navigation.m · L293–L317 - #03-06UIKit布局与动画为 iOS 26 弹簧写入私有高刷原因码
它为 iOS 26 分支换用一组不同弹簧参数。它禁止 overdamping。它通过 KVC 写入
highFrameRateReason = 1048619。它再请求 80–120 FPS,并首选 120 FPS。UIKitUtils.m · L68–L84 - #03-07UIKit布局与动画用存储几何量规避
CALayer.frame的浮点重启动画它不直接比较派生的
frame。在无变换时,它从目标 frame 推出position,并比较图层存储的position和bounds。有变换时才退回比较frame。ContainedViewLayoutTransition.swift · L30–L42 - #03-08UIKit布局与动画精选 03-4从展示层接力中断中的布局动画
新布局先写入节点模型层。若旧动画仍在运行,它从展示层的当前 frame 取起点。随后显式动画从屏幕当前位置过渡到新模型值。
ContainedViewLayoutTransition.swift · L234–L248 - #03-09UIKit布局与动画精选 03-5将 frame 拆成两个动画,并汇合完成信号
animateFrame用position和bounds两个 CA 动画表示一个 frame。它分别记录完成状态。任一动画被打断时,它将整个 frame 动画报告为失败。两个成功后,它才报告成功。CAAnimationUtils.swift · L510–L535 - #03-10UIKit布局与动画用 additive 位移追赶已更新的模型 frame
它先设置目标
frame。它再以 additiveposition动画,从旧坐标减新坐标的 offset 动到零。视觉上图层从旧位置移动到新位置。ContainedViewLayoutTransition.swift · L320–L336 - #03-11UIKit布局与动画精选 03-6分别以不同曲线过渡水平和垂直几何量
CombinedTransition保有两个独立ContainedViewLayoutTransition。它把 frame 拆成 x、y、宽和高四个 additive 动画。它等待四个回调后再完成。ContainedViewLayoutTransition.swift · L1975–L1999 - #03-12UIKit布局与动画在
action(forKey:)中同时禁用隐式动画和追踪挂载自定义
CAAction的run为空。图层对任何 action key 都返回该对象。它在kCAOnOrderIn和kCAOnOrderOut时维护层级状态并触发回调。SimpleLayer.swift · L10–L24 - #03-13UIKit布局与动画用一个共享 DisplayLink 服务多种帧率需求
单例仅保有一个
CADisplayLink。它从活动 request 中取最高 FPS。高刷设备仅在需求超过 60 FPS 时请求 120 FPS。iPad 无条件请求该范围。每个低 FPS request 再通过累计targetTimestamp - timestamp节流。DisplayLinkAnimator.swift · L89–L94 - #03-14UIKit布局与动画用稳定身份移动 UIKit 子视图,而不是重建
ChildMap将 map 对象地址和业务 key 组合为 child ID。更新时它按 ID 找既有 view。顺序变动时,它调整索引并用insertSubview(_:at:)重排,而不重新创建 view。CombinedComponent.swift · L686–L710 - #03-15UIKit布局与动画让移除中的子视图持续追踪运动 guide
子组件移除后,框架暂不
removeFromSuperview()。它把 view 放进disappearingChildViews。带 guide 的消失动画可在后续 body 更新中收到新的 guide 位置。它同时关闭用户交互。CombinedComponent.swift · L818–L836 - #03-16UIKit布局与动画用等价性、环境和尺寸组成渲染跳过条件
宿主缓存组件值、容器尺寸和结果尺寸。环境未变且值相等时,它直接返回缓存 size。局部状态更新则绕过相等性检查,强制重跑 update。
ComponentHostView.swift · L76–L97 - #03-17UIKit布局与动画将组件上下文附着到 UIView 来保留类型化状态
类型擦除组件把
ComponentContext作为关联对象附着到它的UIView。上下文保存组件、环境、布局结果和ComponentState。状态对象通过_updated回传 transition。Component.swift · L80–L101 - #03-18UIKit布局与动画将任意连续变换离散成按设备帧率采样的关键帧
setTransformAsKeyframes接受生成任意CATransform3D的闭包。它按曲线求进度,并预先生成 transform 数组。它把最终 transform 写入模型层,再提交关键帧动画。Transition.swift · L752–L774 - #03-19UIKit布局与动画预渲染并原子缓存圆角位图,再用 blit 合成
它以角方向和整数半径作为缓存 key。首次绘制一块黑色椭圆象限到
DrawingContext。后续调用通过context.blt粘贴该位图。缓存字典使用Atomic更新。ImageCorners.swift · L40–L64 - #03-20UIKit布局与动画用随机相位的 additive 微位移避免重排项同频摆动
它给位置和 Z 轴旋转各创建无限重复关键帧。每条动画的
beginTime随机落在 0–0.24 秒。两条动画都 additive,因此不会覆盖基础布局或变换。ShakeAnimation.swift · L67–L86 - #03-21UIKit布局与动画用无布局
CATransformLayer适配 AsyncDisplayKitASTransformLayer继承CATransformLayer,并禁止内容、背景色和布局回调。ASTransformLayerNode将它作为 AsyncDisplayKit 的 layer block 返回。ASTransformLayerNode.swift · L5–L27 - #03-22UIKit布局与动画让模拟器慢动画同时影响 UIKit 和显式 CA 动画
模拟器构建直接链接
UIAnimationDragCoefficient。显式动画工具读取该系数,并将 CAspeed设为其倒数。真机固定返回1.0。Transition.swift · L5–L18 - #04-01UIKit列表与滚动精选 04-1隐形
UIScrollView只提供滚动物理ListView隐藏ListViewScroller。它仍复用UIScrollView的拖拽、减速和回弹。它不把内容放进 scroll view。它在scrollViewDidScroll中按增量平移自己的ASDisplayNode。ListView.swift · L516–L534 - #04-02UIKit列表与滚动精选 04-2用 20,000pt 假内容高度承载半边未知数据
当已加载节点只碰到一个数据边界时,
ListView使用infiniteScrollSize * 2。infiniteScrollSize在初始化时为10000.0。当两端都已加载时,它切回真实completeHeight。ListView.swift · L1726–L1745 - #04-03UIKit列表与滚动精选 04-3倒序聊天用父子两次 π 旋转保持文字正向
历史列表的外层在
rotated时旋转 π。聊天条目也在rotated时旋转 π。两次旋转让条目文字恢复正向。外层旋转把索引0的视觉位置变成屏幕底部。ChatHistoryListNode.swift · L1025–L1045 - #04-04UIKit列表与滚动精选 04-4事务队列串行化异步布局完成顺序
队列只启动第一个事务。当前事务完成后,
endTransaction()才启动下一个事务。完成回调不在主线程时,它强制切回主线程。ListViewTransactionQueue.swift · L14–L35 - #04-05UIKit列表与滚动精选 04-5布局可异步,节点写入必须回到主队列
nodeForItem把条目更新的调度器传给ListViewItem。默认async(_:)使用.userInteractive全局队列。QueueLocalObject把节点访问绑定到主队列。最终apply也断言主队列。ListView.swift · L1820–L1838 - #04-06UIKit列表与滚动快速滚动时只挂一个可见窗口刷新事务
enqueuedUpdateVisibleItems合并多次滚动回调。事务期间,transactionOffset继续累计。只有偏移仍有变化时,它才补挂下一次刷新。ListView.swift · L4618–L4642 - #04-07UIKit列表与滚动精选 04-6用静止条目锚住插入和删除后的视口
事务可以提供
stationaryItemRange。中间状态记录其中可用条目的旧minY。新布局完成后,它整体平移所有 frame,使该条目回到旧屏幕位置。ListViewIntermediateState.swift · L409–L425 - #04-08UIKit列表与滚动删除动画用占位节点保住消失行的几何空间
动画删除时,中间布局先移除逻辑节点。它立即放入保留旧 frame 的
.Placeholder。稍后的 replay 阶段可继续让旧节点消失。若旧节点已被复用,它会改用ListViewTempItemNode。ListViewIntermediateState.swift · L823–L843 - #04-09UIKit列表与滚动用
apparentHeight分离真实布局高度和动画高度条目有最终
contentSize,也有逐帧变化的apparentHeight。动画每帧计算高度差。列表依据条目位于视口上方还是下方,向相反一侧偏移兄弟节点。ListViewItemNode.swift · L569–L580 - #04-10UIKit列表与滚动键盘和 inset 动画改为整体
sublayerTransform尺寸或 inset 改变后,代码先提交最终 frame。它再给列表 layer 的
sublayerTransform加一个反向平移动画。所有子节点一起从视觉旧位置过渡到新位置。ListView.swift · L3328–L3351 - #04-11UIKit列表与滚动以视口外 500pt 作为软预载窗口
preloadPages开启时,ListView将invisibleInset设为500.0。中间状态只在已加载总高度超过视口加两侧预载区时,移除窗口外节点。ListViewIntermediateState.swift · L631–L653 - #04-12UIKit列表与滚动浮动 header 同时处理顶部、边缘和底部吸附
header 不属于普通行。它根据逻辑分组的上下界计算独立 frame。它暴露吸附距离和归一化因子给 header 自己绘制。完整函数还处理
.bottom和同一stackingId的避让。ListView.swift · L4004–L4025 - #04-13UIKit列表与滚动不知道总行高时仍绘制近似滚动条
自绘滚动条以
44pt推算未加载行的高度。它再用首尾可见行的真实 frame 和真实高度校正当前视口边界。ListView.swift · L4525–L4549 - #04-14UIKit列表与滚动外部速度可注入一段像 UIKit 的减速
transferVelocity(_:)用ConstantDisplayLinkAnimator驱动指数衰减。它保存亚像素余数,只把整像素写入偏移。ListViewScroller.isDecelerating在forceDecelerating为真时报告减速中。ListView.swift · L951–L969 - #04-15UIKit列表与滚动
GridNode给UIScrollView换成不响应绘制失效的 layerGridNodeScrollerView指定GridNodeScrollerLayer为 backing layer。该 layer 覆盖setNeedsDisplay()且不执行任何操作。GridNodeScroller.swift · L4–L28 - #04-16UIKit列表与滚动
GridNode先切出 preload 窗口,再创建节点GridNode先从完整itemLayout中筛出显示区加preloadSize。后续applyPresentationLayoutTransition只对presentationItems创建或更新GridItemNode。GridNode.swift · L815–L849 - #04-17UIKit列表与滚动网格动画先固化屏幕坐标,再做 additive 位移
布局变更前,代码把项目 frame 减去旧
contentOffset。这得到屏幕坐标。新布局确定后,它计算幸存项目的共同偏差,并以 additiveposition动画补偿。GridNode.swift · L887–L899 - #04-18UIKit列表与滚动网格用线性分区做“相册式”均衡行
.balanced将每个项目的宽高比量化为权重。它按理想行高估算行数。它再用linearPartitionForWeights切分连续项目。GridNode.swift · L594–L606 - #04-19UIKit列表与滚动稀疏网格把未加载索引视为洞,并定向拉取
SparseItemGrid.Items持有总数与局部项目。可见索引没有项目时,代码查找最近HoleAnchor。它只为第一个洞发出一次.toLower加载请求。SparseItemGrid.swift · L1305–L1329 - #04-20UIKit列表与滚动稀疏网格按稳定 id 维护可见 layer,而不是 cell 池
visibleItems以业务稳定 id 保存VisibleItem。仍可见的 id 只更新。首次进入窗口的项目才创建 layer 或 view,并进入批量bindLayers。SparseItemGrid.swift · L1095–L1119 - #04-21UIKit列表与滚动用一张长渐变图和
contentsRect驱动全局 shimmerShimmer缓存一张1 × 320的渐变图。每个占位 layer 只裁剪不同的contentsRect。它动画化contentsRect.origin.y,而不是每帧重绘渐变。SparseItemGrid.swift · L131–L145 - #04-22UIKit列表与滚动
AsyncListComponent旋转 90° 复用纵向ListView横向模式不新建横向虚拟列表。它旋转整个
ListView负 π/2,交换宽高,再旋转内部contentContainer正 π/2。AsyncListComponent.swift · L547–L558 - #04-23UIKit列表与滚动CoreList 用 IMP swizzle 录制 UIKit 物理真值
CoreList 的 demo 在调试时替换
UIScrollView.setContentOffset:。它还替换私有 selector,以记录橡皮筋输入输出。录制数据供纯 Swift 的ScrollReplay回放。UIScrollViewPhysicsSwizzler.swift · L108–L132 - #05-01UIKitASDK 与文本精选 05-1不安装系统选择交互,完全自绘选择 UI
DocumentCanvasView实现UITextInput,但不创建UITextSelectionDisplayInteraction。它保留UIEditMenuInteraction,并自行绘制光标、选区和两个手柄。它只在一次放大镜拖动期间创建临时交互对象。DocumentCanvasView+Interaction.swift · L44–L68 - #05-02UIKitASDK 与文本精选 05-2向键盘投影“虚拟换行”,修复韩文和 CJK 跨段重组
结构化文档内部使用连续的全局 UTF-16 位置。它向
UITextInput.text(in:)投影文本时,才在跨顶层段落处插入\n。它把 emoji 和公式附件还原为可读的替代文本。DocumentCanvasView+UITextInput.swift · L40–L77 - #05-03UIKitASDK 与文本精选 05-3把 IME 组合文本和内联预测分成“提交”与“丢弃”两条路径
setMarkedText先保存一次组合开始快照。它把输入法文本作为临时文档编辑。光标在标记文本开头时,它把该文本标为预测。finalizeMarkedText会提交组合文本,却删除预测幽灵文本。DocumentCanvasView+MarkedText.swift · L109–L161 - #05-04UIKitASDK 与文本精选 05-4用不可见的 1×1
NSTextAttachment预留空间,再覆盖真实动态表情视图附件自身没有可见内容。共享的透明
spacerImage强制 TextKit 调用attachmentBounds。它按 run 字体的 ascender 和 descender 预留正方形字框。随后画布在该框上安装实际动画表情视图。EmojiTextAttachment.swift · L19–L62 - #05-05UIKitASDK 与文本精选 05-5旧文本节点用
CTRunDelegate把自定义 emoji 伪装成一个排版字形代码把一个可能多 UTF-16 单元的自定义 emoji 替换为
&。它为这个替身绑定CTRunDelegate。CoreText 因此得到指定宽度、ascent 和 descent。TextNode跳过该 run 的字形绘制,并在同一几何位置叠加InlineStickerIte…TextNodeWithEntities.swift · L176–L230 - #05-06UIKitASDK 与文本异步绘制用原子 sentinel 取消旧帧,并在主线程原子回填结果
每次异步显示递增
_displaySentinel。后台 block 捕获当时值。新的失效请求使旧 block 的检查失败。完成 block 只在未取消时设置layer.contents。异步任务加入父层事务,事务在主线程同步回填。ASDisplayNode+AsyncDisplay.mm · L333–L411 - #05-07UIKitASDK 与文本用“基础层清除 + 剪裁重绘层”实现 spoiler 文本的双层渲染
普通文本层先绘制整行,再在 spoiler 的逐词矩形上
clear。启用displaySpoilers的第二层则只剪裁并绘制这些矩形中的字形。两个层可被特效节点分别控制。TextNode.swift · L2567–L2640 - #05-08UIKitASDK 与文本TextKit 2 刻意使用
.selection几何来修正 RTL 光标冻结光标位置查询不使用 TextKit 2 的
.standardsegment。它使用.selectionsegment。空 RTL 段落还手动把光标放到容器右端。BlockLayout.swift · L138–L157 - #05-09UIKitASDK 与文本关闭
UITextView滚动时仍让它认为滚动已开启ASPanningOverriddenUITextView对外报告“不可滚动”,却总是把父类scrollEnabled设为YES。它阻断自身 pan、contentSize和contentOffset更新。ASEditableTextNode.mm · L69–L135 - #05-10UIKitASDK 与文本自定义截断 token 采用两次
CTLineCreateTruncatedLine,防止 token 被吞掉TextNode先测量 token 宽度。它首次按全宽请求 CoreText 截断。它检查生成 run 的源 range 是否已抵达原字符串末尾。若没有 token,它把可用宽度减去 token 宽度再截断一次。显式换行截断则把 token 保存为additionalTrailingLin…TextNode.swift · L1991–L2055 - #05-11UIKitASDK 与文本手写 CoreText 逐行“切口”排版,给头像或气泡角留出空间
布局器对每一行判断其垂直范围是否碰到左上或右上切口。命中的行缩窄可用宽度。左上切口还平移行 x。底部切口在布局完成后再决定扩宽最后一行还是增加高度。
TextNode.swift · L1803–L1855 - #05-12UIKitASDK 与文本layer-backed 节点用弱代理修复
CALayer.delegate悬空指针layer-backed 节点不把自身直接交给
CALayer.delegate。它创建弱目标代理,并用 associated object 强持有代理。layer 比 node 活得更久时,代理把回调安全地吞掉。ASDisplayNode.mm · L582–L609 - #05-13UIKitASDK 与文本以字符串拆分引用私有自动纠错提示类,并禁用私有替换菜单项
编辑文本视图阻止
_promptForReplace:和部分格式菜单。点击自动纠错提示时,它调用dropAutocorrection。该方法先unmarkText,再把选择区前移一个字符又还原,以刷新 UIKit 状态。ASEditableTextNode.mm · L141–L175 - #05-14UIKitASDK 与文本复用链接高亮轮廓作为 mask,实现只在文字形状中流动的 shimmer
TextLoadingEffectView把LinkHighlightingNode生成的跨行圆角轮廓设为 UIView mask。它移动一张窄的模板渐变图片。内容层和描边层各有一张渐变图。TextLoadingEffect.swift · L49–L59 - #05-15UIKitASDK 与文本RichText spoiler 用显示属性隐藏字形,并以稳定身份池化粒子层
画布按选择区决定 spoiler 是否显示。
BlockLayout用 TextKit 2 的 rendering attribute 把隐藏字形绘制为透明,而不改 storage。粒子视图的 key 由布局对象身份和段内 run 索引组成,不用易变的全局字符偏移。DocumentCanvasView+Spoilers.swift · L37–L55 - #05-16UIKitASDK 与文本Markdown 粘贴拆成两个 run-loop 撤销组,并抑制中间闪屏
当宿主把纯文本 Markdown 转为富文本文档时,编辑器先粘贴纯文本。下一次主 run-loop 再替换为富文本。第一阶段不通知宿主,因此用户看不到中间纯文本。
DocumentCanvasView+Clipboard.swift · L198–L224 - #05-17UIKitASDK 与文本链接高亮把相邻行矩形缝成一个连续的圆角轮廓
高亮绘制器先分离不相邻矩形。它把近似相同的左右边吸附。然后沿多行矩形的外周构造一个
CGPath,并在行宽变化处加入连接圆角。LinkHighlightingNode.swift · L59–L195 - #05-18UIKitASDK 与文本文本布局比较所有影响条件,命中后直接复用旧
TextNodeLayoutasyncLayout捕获当前 layout。它检查尺寸、最大行数、截断方式、切口、对齐、行距、背景色和 attributed string。完全相同时直接返回旧 layout。否则计算新布局,并只在变化时setNeedsDisplay。TextNode.swift · L2854–L2906 - #05-19UIKitASDK 与文本将可 tint 的字和不可 tint 的彩色 emoji 分离为两个重叠文本节点
每个状态保留两份同样的文本布局。一份只画普通文字。另一份只画彩色 emoji。它们重叠放置。
updateTintColor只改变文字层的 layer tint。MultiScaleTextNode.swift · L6–L25 - #05-20UIKitASDK 与文本AsyncDisplayKit 的 TextKit 分叉用自定义容器保留引文缺口,并跳过自定义 emoji 字形
分叉实现
ASCustomTextContainer,将引文首行缩短 100pt。它还实现ASCustomLayoutManager,遇到Attribute__CustomEmoji时不调用父类绘制该 glyph。ASTextKitComponents.mm · L26–L80 - #06-01UIKit导航、手势与上下文菜单精选 06-1用私有
_UIPortalView把同一内容跨层显示代码创建一个 Portal 视图。它镜像源视图的位置和变换。它不镜像 alpha,也不接收命中测试。
PortalView会把源视图赋给sourceView,并强制插回原层级索引。PortalView.swift · L4–L35 - #06-02UIKit导航、手势与上下文菜单精选 06-2用 Portal 暂存源内容,并在动画末尾明确归还
代码先把真实内容重挂到
PortalSourceView。它再把 Portal 镜像放入覆盖层。动画完成时,代码调用settle,把真实内容重挂回菜单偏移容器或原父视图。ContextControllerExtractedPresentationNode.swift · L175–L196 - #06-03UIKit导航、手势与上下文菜单精选 06-3给透明背景塞入 1×1 图片,修复“看得见但点不到”的菜单外部点击
菜单背景层即使颜色为
.clear,也会拥有完全透明的contents。这使背景层继续参与触摸投递。它因此可以接住菜单外的点击并关闭菜单。ContextSourceContainer.swift · L410–L423 - #06-04UIKit导航、手势与上下文菜单提取到未缩放覆盖层前,补回祖先缩放
代码把源视图的窗口可见宽度除以模型宽度。它把检测到的缩放施加到提取容器。它还保存该缩放,供退场补偿使用。
ContextControllerExtractedPresentationNode.swift · L776–L800 - #06-05UIKit导航、手势与上下文菜单在退场中用 presentation layer 重定向落点
源消息在菜单退场时改高度,代码不重新跑整套布局。它读取
presentation()与模型层的残差。它用稳定 key 替换一条bounds.origin.y的加法动画,并只使用剩余时长。ContextControllerExtractedPresentationNode.swift · L2271–L2295 - #06-06UIKit导航、手势与上下文菜单精选 06-4全屏右滑返回先查命中视图链,再用方向阈值裁决
识别器从
hitTest结果向上查找。子视图可声明“有横向手势”或“严格禁止”。普通横向内容会禁用屏幕中央返回。边缘返回仍可保留。移动超过 10 pt 后,识别器才固定横向主方向。InteractiveTransitionGestureRecognizer.swift · L106–L128 - #06-07UIKit导航、手势与上下文菜单精选 06-5手工驱动返回进度,并同步键盘和状态栏
右滑开始时,导航容器预先布局底层控制器。每个交互进度会移动键盘左边界。进度达到
0.3时,状态栏风格提前切到目标页。结束条件为速度超过1000,或进度超过0.2。NavigationContainer.swift · L236–L313 - #06-08UIKit导航、手势与上下文菜单用像素对齐的手工几何实现导航视差和阴影
协调器把顶部页、底部页、遮罩和阴影拆成独立节点。顶部页使用屏幕像素对齐。非平面容器让底部页只走 30% 距离,形成视差。初始化时它还持有最大帧率的
SharedDisplayLinkDriver链接。NavigationTransitionCoordinator.swift · L158–L187 - #06-09UIKit导航、手势与上下文菜单转场时临时重挂两个导航栏,再用裁剪容器切开
容器保存两个导航栏的原父节点。它在转场时把两个导航栏重挂进独立裁剪节点。
layout()根据进度移动顶部裁剪区域,并收窄底部可见宽度。完成后它再归还导航栏。NavigationBarTransitionContainer.swift · L22–L68 - #06-10UIKit导航、手势与上下文菜单上下文长按先避开系统左边缘,再主动取消整棵手势树
ContextGesture会拒绝窗口左侧 8 pt 内的按压。延迟后它用显示链接动画按压进度。真正激活时,它取消窗口中的同类手势,并沿父视图链把其他手势置为.failed。ContextGesture.swift · L130–L178 - #06-11UIKit导航、手势与上下文菜单一个识别器同时处理点击、双击、长按、右键和按住
该自定义识别器根据命中位置选择等待策略。它区分单击、双击、长按、二级点击和 hold。hold 分支会先
prepareTap()。长按触发时还会取消所有同类识别器,并结束祖先滚动视图的 pan。TapLongTapOrDoubleTapGestureRecognizer.swift · L181–L239 - #06-12UIKit导航、手势与上下文菜单Peek 从源矩形弹出,并按激活模式切换模糊策略
Peek 根据源矩形中心计算加法位置弹簧。已出现的内容会按源宽度与内容宽度的比例缩放进入。构造阶段中,拖动激活使用
UIVisualEffectView模糊。按压激活使用半透明纯色遮罩。构造阶段还预热了触感。PeekControllerNode.swift · L271–L300 - #06-13UIKit导航、手势与上下文菜单仅在最内层滚动视图位于顶部时才认可下滑关闭
识别器递归从触点寻找
UIScrollView。它只在滚动位置不低于顶部 inset 时保留资格。它超过 5 pt 的横向位移就失败。下移超过 2 pt 才开始。SwipeToDismissGestureRecognizer.swift · L48–L107 - #06-14UIKit导航、手势与上下文菜单最小化后延迟把活控制器换成快照
最小化卡片先承载真实控制器视图。0.45 秒后,它抓取内容快照。它移除真实视图,只留下快照和自有标题栏。
MinimizedContainer.swift · L125–L137 - #06-15UIKit导航、手势与上下文菜单用私有
CAFilter给快照补背景模糊卡片快照比例不匹配容器时,代码另抓一个快照。它给该快照的
layer.filters设置高斯模糊,再放到清晰快照下方填满空白区域。MinimizedContainer.swift · L272–L295 - #06-16UIKit导航、手势与上下文菜单用 3D 变换把最小化页面堆成可滚动卡片组
展开后,每张卡片按其滚动位置计算
final3dTransform。高亮卡片额外旋转0.04。关闭中的卡片单独接受横向偏移。折叠时,非顶卡按比例缩小并下沉。MinimizedContainer.swift · L935–L1017 - #06-17UIKit导航、手势与上下文菜单iPad 分栏只把真实屏幕边缘的安全区给对应窗格
分栏宽度被限制在屏宽的三分之一到二分之一之间。master 去掉右安全区。detail 去掉左安全区。它还为两个窗格分别生成状态栏高度的滚动到顶代理区域。
NavigationSplitContainer.swift · L73–L106 - #06-18UIKit导航、手势与上下文菜单触感生成器按样式缓存,并在手势开始时预热
封装延迟创建选择、通知和 impact 生成器。impact 生成器按样式缓存。长按 hold 和 Peek 构造期会先调用
prepareTap()。click05、click06和veryLight在 iOS 13 以后用低强度模拟。HapticFeedback.swift · L29–L39 - #06-19UIKit导航、手势与上下文菜单画廊退场用源快照接棒,并等待三个动画完成
画廊关闭时,代码把源节点提供的
copyView插到内容下方。快照从全屏内容的几何变换到源位置。真实全屏节点同时淡出并缩放。只有位置、缩放和快照动画都完成,代码才移除快照并回调。ChatAnimationGalleryItem.swift · L276–L312 - #06-20UIKit导航、手势与上下文菜单反应面板通过顶部过拉扩展,并主动取消滚动 pan
菜单内容不足一屏时,代码只允许顶部过拉。过拉距离先乘以
0.35做阻尼。超过 30 pt 时,它取消滚动视图自身的 pan,并把反应面板扩展为新状态。ContextControllerExtractedPresentationNode.swift · L590–L631 - #07-01UIKit视觉特效与玻璃精选 07-1直接驱动 iOS 26 的私有 Liquid Lens
代码按运行时类名创建
_UILiquidLensView。 它把抬起内容和挖空内容交给私有 Lens。 它把 Lens 放在背景和交互内容之间。LiquidLensView.swift · L198–L257 - #07-02UIKit视觉特效与玻璃精选 07-2为私有 Lens 补齐弹跳刷新和位置动画
代码先发起 bounds 动画。 它随后清空私有 Lens 的残留动画。 它再写入最终 bounds。 它还补一个随宽度变化的加法 position 动画。
LiquidLensView.swift · L375–L415 - #07-03UIKit视觉特效与玻璃精选 07-3私有 Lens 动画期间强制 120 Hz
代码为每个 morph 申请共享显示链路。 它把支持 ProMotion 的屏幕锁定为 120 Hz。 它使用引用计数合并并发 morph。 它还用一秒超时释放未回调的申请。
LiquidMorphTransition.swift · L75–L145 - #07-04UIKit视觉特效与玻璃没有私有 Lens 时,用亮度转 Alpha 模拟挖空玻璃
代码把白色容器作为内容 mask。 它给 mask 加私有
luminanceToAlpha滤镜。 它以可拉伸圆角胶囊图控制挖空区域。LiquidLensView.swift · L267–L296 - #07-05UIKit视觉特效与玻璃用
_UIPortalView复制远端视图,而非截图代码运行时解析并实例化
_UIPortalView。 它同步源视图的位置和变换。 它禁用命中测试转发和 portal 命中测试。UIKitUtils.m · L211–L235 - #07-06UIKit视觉特效与玻璃Portal 重绑后原位置重插,迫使渲染刷新
代码先重新设置
sourceView。 它再把 portal 重插回原来的下标。 它在 UIView 和纯 CALayer 两种宿主中都处理。PortalView.swift · L15–L28 - #07-07UIKit视觉特效与玻璃聊天气泡真正“透出”同一张动态壁纸
每种气泡类型都有各自的壁纸 portal 源。 气泡节点只显示相同源的
PortalView。 它不在每个气泡内重新绘制壁纸。WallpaperBackgroundNode.swift · L1988–L2023 - #07-08UIKit视觉特效与玻璃拆解
UIVisualEffectView内部滤镜,获得可控导航模糊代码从公开
UIVisualEffectView取得系统内部层。 它隐藏内部视觉子视图。 它只保留gaussianBlur和可选colorSaturate。NavigationBackgroundView.swift · L62–L108 - #07-09UIKit视觉特效与玻璃精选 07-4用
CABackdropLayer和颜色矩阵手搓旧版玻璃代码创建私有
CABackdropLayer。 它先应用 backdrop-aware 颜色矩阵。 它再应用小半径高斯模糊。LegacyGlassView.swift · L7–L13 - #07-10UIKit视觉特效与玻璃精选 07-5私有
CAMeshTransform让 backdrop 边缘像玻璃一样折射代码为圆角矩形生成位移网格。 它把网格写到 backdrop 的私有
meshTransform键。 尺寸变化时,它显式插值旧网格和新网格。LegacyGlassView.swift · L198–L230 - #07-11UIKit视觉特效与玻璃把可变模糊半径编码进一张一像素宽的 mask
代码生成一像素宽的黑白渐变图。 它拉伸该图覆盖任意宽度。 它把图作为私有
variableBlur的inputMaskImage。EdgeEffect.swift · L693–L698 - #07-12UIKit视觉特效与玻璃波纹优先用位移贴图,缺失时回退网格变形
代码优先生成低分辨率的位移贴图。 它根据性能自适应贴图分辨率。 没有位移层时,它回退私有 mesh 变形。
SpaceWarpView.swift · L888–L912 - #07-13UIKit视觉特效与玻璃自绘 Liquid Glass 用 Metal 输出私有位移图的 RG 编码
Shader 在每个形状边缘计算向内采样偏移。 它将 X 和 Y 偏移编码进红绿通道。 中性值
0.5表示不位移。LiquidGlassShapes.metal · L497–L524 - #07-14UIKit视觉特效与玻璃每帧自绘玻璃,但离屏后及时释放昂贵 Surface
代码为内容、遮罩和位移图分别渲染专属 Surface。 源码注释说明录音 blob 的 3x Surface 约为 5.5 MB。 离开窗口时,策略延迟到当前 run loop 末尾检查。
LiquidGlassShapesLayer.swift · L358–L389 - #07-15UIKit视觉特效与玻璃Spoiler 用私有
CAEmitterBehavior实现闪烁和吸引力代码通过 IMP 调用私有 emitter 工厂。 调用方可以取得闪烁和吸引器行为对象。 公开
CAEmitterLayer仍负责粒子发射。SpoilerDustView.swift · L239–L250 - #07-16UIKit视觉特效与玻璃点按揭示 spoiler 时,反向径向 mask 让粒子从触点爆开
代码生成中心透明、外部不透明的 mask。 半径同时受 10 pt 和文本区域尺寸限制。 该 mask 作为粒子揭示区域的触点孔洞。
SpoilerDustView.swift · L183–L199 - #07-17UIKit视觉特效与玻璃动态渐变不是每帧算 shader,而是预烘 8 个阶段并动画
contents代码先生成一串渐变位图。 它用
CAKeyframeAnimation动画 layer 的contents。 它把同一关键帧同步给原色和降饱和 clone。SoftwareGradientBackground.swift · L637–L675 - #07-18UIKit视觉特效与玻璃多个动态壁纸用共享阶段保持相位一致
代码把阶段循环限制在
0...7。 它把发起者之外的背景同步到同一phase。 它用管道广播而不是各自用时间戳计算。SoftwareGradientBackground.swift · L770–L794 - #07-19UIKit视觉特效与玻璃Shimmer 用超宽 mask 和加法 position 无限滚动
mask 比实际内容宽两段完整行程。 动画只对
position.x做加法位移。 布局变化时,它先更新静态基准位置再重启动画。ShimmeringMaskView.swift · L61–L81 - #07-20UIKit视觉特效与玻璃气泡撕裂边缘用“过冲一像素”消除平铺接缝
黑色撕裂带超出气泡边界。 白色边缘图却避开 overhang 和尾巴预留列。 上边图刻意少一个屏幕像素高。
ChatMessageBackgroundTear.swift · L289–L314 - #08-01UIKit渲染与性能精选 08-1用一张
IOSurface图集承载多个独立图层引擎把多个
MetalEngineSubjectLayer分配到同一IOSurface。每个图层只用contentsRect展示自己的子矩形。引擎只在图集变更时重设layer.contents。MetalEngine.swift · L920–L976 - #08-02UIKit渲染与性能为 EDR 大层放弃固定图集,避免 32 MB 空耗
普通小层优先放入固定表面。尺寸达到 1024 的层改用贴合尺寸的表面。
rgba16Float也使用对齐后的专用尺寸。MetalEngine.swift · L920–L975 - #08-03UIKit渲染与性能精选 08-2只在首个脏 subject 时请求一次绘制
引擎为每个 subject 赋稳定内部编号。它在一个显示周期内去重脏 subject。只有队列从空变为非空时才调用根层的
setNeedsDisplay()。MetalEngine.swift · L1043–L1071 - #08-04UIKit渲染与性能精选 08-3三缓冲私有纹理,并在 GPU 完成后归还
PooledTexture预建三个 GPU 私有纹理。每次渲染取一个空闲纹理。引擎在 command buffer 完成回调中再调用free()。MetalEngine.swift · L217–L268 - #08-05UIKit渲染与性能精选 08-4版本差异用 Objective-C 方法替换规避
MTLBinaryArchive崩溃保存 Metal 二进制归档前,代码临时登记目标路径。它替换
FileManager的公开 Objective-C 方法。失败时,它把NSError保活到serialize(to:)返回。MetalBinaryArchiveSerialization.swift · L22–L90 - #08-06UIKit渲染与性能用保存标记和
rename(2)防止缓存崩溃循环管线突发创建后延迟一秒合并保存。保存前创建
.saving标记。它先写随机临时文件,再用原子rename(2)替换旧归档。MetalPipelineCache.swift · L233–L309 - #08-07UIKit渲染与性能一个定时器驱动整组 DCT 动画,并按电量降到 30 FPS
一个
DCTMultiAnimationRendererImpl用一个 timer 驱动所有活动项。没有活动项时立刻销毁 timer。低电量或 CPU 核数不超过 2 时,frameSkip为 2。DCTMultiAnimationRendererImpl.swift · L893–L934 - #08-08UIKit渲染与性能相同贴纸只解码一次,再扇出到多个弱目标
默认
uniqueId为 0。相同资源和尺寸命中同一个ItemAnimationContext。该上下文把同一帧分发给多个弱引用目标。调用者可用unique强制独立时间线。DCTMultiAnimationRendererImpl.swift · L686–L743 - #08-09UIKit渲染与性能用两条固定后台队列并行解码,再批量回主线程提交
渲染器按稳定的
queueAffinity将帧任务分到两条固定队列。两个分区完成后,代码再把所有图层更新闭包投到主线程。DCTMultiAnimationRendererImpl.swift · L1026–L1077 - #08-10UIKit渲染与性能DCT 缓存以 YUVA 平面存储关键帧和差分帧
缓存把帧转为 Y、U、V、Alpha 四个 DCT 系数平面。非关键帧存储相对前帧的系数差。每帧带关键帧标记和四个平面长度。
DCTAnimationCacheImpl.swift · L366–L445 - #08-11UIKit渲染与性能以“单飞”请求共享缓存构建,并在无人订阅时中止
缓存键相同的读取者挂到同一个
ItemContext。只有首个读取者启动抓取和压缩。后续读取者订阅结果。最后一个订阅者离开时,代码 dispose 抓取并把 writer 标为取消。DCTAnimationCacheImpl.swift · L1343–L1423 - #08-12UIKit渲染与性能从更高分辨率缓存即时派生较小缓存
请求尺寸缺失时,缓存可读取更高分辨率版本。它直接缩放四个 YUVA 平面,并写回所需尺寸的 DCT 缓存。
DCTAnimationCacheImpl.swift · L1139–L1186 - #08-13UIKit渲染与性能
DrawingContext将位图缓冲的所有权直接交给CGImageDrawingContext采用统一的设备行对齐。它让CGContext直接画入ASCGImageBuffer。生成CGImage时,createDataProviderAndInvalidate()转移缓冲所有权。GenerateImage.swift · L619–L700 - #08-14UIKit渲染与性能用原位字节创建带 IOSurface 属性的
CVPixelBuffer代码不复制
DrawingContext的位图。它将同一地址传给CVPixelBufferCreateWithBytes,并以 release callback 管理ASCGImageBuffer生命周期。options 请求 IOSurface 属性。GenerateImage.swift · L702–L724 - #08-15UIKit渲染与性能GIF 网格使用一个显示链路,并只预读两帧
多个 GIF 视频目标共用
SharedDisplayLinkDriver。每个目标最多补到两帧。所有预读都在sharedQueue完成。isRendering阻止同一时间重复发起解码批次。BatchVideoRenderingContext.swift · L268–L372 - #08-16UIKit渲染与性能用墙钟重放
CMSampleBuffer,而不信任各层播放时钟批处理器用
CACurrentMediaTime()作为共同墙钟。它比较已显示帧的 PTS 和真实经过时间。PTS 回退时按上一帧 duration 判断循环边界。BatchVideoRenderingContext.swift · L53–L87 - #08-17UIKit渲染与性能小尺寸 GIF 禁用去块滤波,并给不可解码文件熔断
网格 GIF 约以 93 pt 显示时,解码器跳过去块滤波。若整个文件未产出有效帧,或出现不应发生的等待状态,代码永久标记失败。
BatchVideoRenderingContext.swift · L90–L162 - #08-18UIKit渲染与性能Lottie 缓存固定每隔一帧栅格化一次
Lottie 缓存无条件使用
frameSkip = 2。它把两帧的时间合并进一张位图。代码也在后台 writer 队列生成,并随时响应取消。LottieAnimationCache.swift · L10–L60 - #08-19UIKit渲染与性能Subcodec 把多个精灵合为一条编码流,再切回各自区域
相同尺寸的贴纸进入
SurfaceGroup。每个贴纸占一个 slot。任一 slot 前进后,surface 只发出一条合成 NAL 流。代码解码最近帧,再按 color/alpha 区域切回每个目标。SubcodecMultiAnimationRendererImpl.swift · L206–L288 - #08-20UIKit渲染与性能尘埃效果把粒子状态更新和绘制都留在 GPU
每个尘埃 item 在 GPU buffer 中保存粒子。compute shader 初始化并更新状态。渲染阶段用六顶点四边形做实例绘制,不把粒子位置读回 CPU。
DustEffectLayer.swift · L295–L362 - #08-21UIKit渲染与性能JPEG 解码选择 TurboJPEG 快速路径,并让 vImage 不分块
代码把行宽对齐到 32 字节。它要求 TurboJPEG 使用快速 DCT 和快速上采样。随后用两次 vImage 操作转为应用所需的通道顺序。
MozjpegBinding.mm · L735–L811 - #08-22UIKit渲染与性能复用
AVSampleBufferDisplayLayer,并禁用隐式动作takeSampleBufferLayer()从全局原子池取图层。归还时在主线程 flush、清图并重置 transform。子类对所有 layer action 返回空CAAction。SampleBufferPool.swift · L6–L65 - #09-01UIKit聊天界面精选 09-1先交接第一响应者,再释放旧输入框
代码先让新输入节点成为第一响应者。代码随后才让旧节点辞去第一响应者。
ChatTextInputPanelNode.swift · L1124–L1141 - #09-02UIKit聊天界面文本发送动画分离横纵轴曲线
代码把横向和纵向位移拆成两条动画。代码再把输入框背景和内容交给目标气泡。
ChatMessageTransitionNode.swift · L689–L713 - #09-03UIKit聊天界面精选 09-2媒体快照以反向缩放交接给气泡
代码把媒体输入快照放进过渡容器。代码同时缩放目标内容和反向缩放快照层。
ChatMessageTransitionNode.swift · L986–L1010 - #09-04UIKit聊天界面精选 09-3录音圆球自己飞入语音气泡
代码不截取录音圆球的快照。代码让圆球本身飞向消息内的播放按钮位置。
ChatMessageTransitionNode.swift · L904–L928 - #09-05UIKit聊天界面精选 09-4媒体拼图按四个角独立决定合并形状
代码为拼图的四个角分别计算邻接状态。代码还区分普通合并和气泡合并。
ChatMessageBubbleItemNode.swift · L3353–L3375 - #09-06UIKit聊天界面反应区会改变气泡的底部合并边
代码在有反应且不是预览时覆盖底部合并状态。入站消息取左边,出站消息取右边。
ChatMessageBubbleItemNode.swift · L2395–L2415 - #09-07UIKit聊天界面输入框以文本测量值增长,但最多十二行
代码用可用宽度测量富文本高度。代码把高度限制在 1 到 12 行,并返回溢出标记。
ChatTextInputPanelNode.swift · L1410–L1429 - #09-08UIKit聊天界面精选 09-5快速点按录音键改模式,长按才开始录音
代码延迟 0.19 秒才调用
beginRecording。用户在定时器存活时松手,代码切换录音模式。ChatTextInputMediaRecordingButton.swift · L519–L550 - #09-09UIKit聊天界面录音手势把横移、取消位移和锁定拆开上报
代码把控制条横向位移和取消提示位移分别保存。代码在锁定事件中上报锁定,并允许锁定。
ChatTextInputMediaRecordingButton.swift · L553–L575 - #09-10UIKit聊天界面媒体面板只在允许时自动切回文字输入
输入面板请求文字模式时先询问当前实体键盘。代码只把
.media状态改成.text。ChatControllerNode.swift · L1035–L1051 - #09-11UIKit聊天界面实体键盘把隐藏输入框高度当作组件输入
代码缓存完整布局状态。代码把被隐藏的文字输入面板高度传给实体键盘组件。
ChatEntityKeyboardInputNode.swift · L1814–L1836 - #09-12UIKit聊天界面表情搜索覆盖层只在首次出现时立即建树
代码把搜索放在现有键盘上的独立
searchView。首次出现时淡入并按顶部面板位移,退出后淡出再移除。EntityKeyboard.swift · L813–L842 - #09-13UIKit聊天界面精选 09-6列表更新前只采样一次已稳定的偏移
代码在准备列表事务时读取一次
settledContentOffsets。代码用同一份样本判断是否贴近最新消息和是否填满屏幕。ChatHistoryListNode.swift · L2515–L2534 - #09-14UIKit聊天界面未读分割条离屏时强制重新聚焦
代码遍历现有项目节点。它找到
ChatUnreadItemNode后立即确保该节点可见。ChatHistoryListNode.swift · L2957–L2975 - #09-15UIKit聊天界面精选 09-7回复跳转不能伪装成新消息到达
代码在创建内容节点前记录真正的新到达消息。代码明确排除加载另一段历史,包括回复跳转。
ChatHistoryListNode.swift · L4095–L4115 - #09-16UIKit聊天界面跳转请求同时携带高亮、引用和回复设置
代码把高亮、引用片段、回复主题和滚动锚点封装进一次
.Scroll请求。每次请求还生成新的位置标识。ChatHistoryListNode.swift · L3856–L3879 - #09-17UIKit聊天界面精选 09-8用标识符替换编辑器格式菜单,保留系统菜单其余项
代码移除编辑器自带的格式子菜单。代码按固定位置插入聊天输入框自己的格式菜单。
ChatTextInputPanelNode.swift · L5201–L5220 - #09-18UIKit聊天界面录音期间禁用交互式返回手势
代码注册窗口平移识别器后,提供交互式页面转场的动态禁用回调。录音状态会令回调返回
true。ChatControllerNode.swift · L1098–L1120 - #10-01UIKit媒体、Stories 与个人页用运行时调用私有
CAEmitterBehavior代码动态组装类名
CAEmitterBehavior。代码动态组装 selectorbehaviorWithType:。随后它经 KVC 为粒子设置colorOverLife和simpleAttractor行为。UIKitUtils.swift · L935–L943 - #10-02UIKit媒体、Stories 与个人页精选 10-1个人页用私有
_verticalVelocity把第二次下拉变成头像画廊代码把
"_"和"verticalVelocity"拼成_verticalVelocity。它通过 KVC 读取UIScrollView的私有纵向速度。首次下拉到-32时展开头像。展开后再次下拉会触发画廊。PeerInfoScreen.swift · L5984–L6021 - #10-03UIKit媒体、Stories 与个人页精选 10-2圆视频把 NV12 的两个平面零拷贝映射到 Metal
相机输入的 NV12 数据不经过
CGImage。Y 平面映射为.r8Unorm。CbCr 平面映射为.rg8Unorm。计算着色器随后完成颜色转换、镜像和裁切。RoundVideoFrameProcessor.swift · L467–L516 - #10-04UIKit媒体、Stories 与个人页精选 10-3圆视频先低分辨率模糊,再在同一命令缓冲区合成
代码以 30 fps 离散选择装饰图集帧。代码先写入小尺寸纹理。代码对小纹理执行模糊。后续合成器读取模糊纹理、原始 YUV、图集和水印。
RoundVideoFrameProcessor.swift · L332–L385 - #10-05UIKit媒体、Stories 与个人页精选 10-4圆视频用有上限的
CVPixelBufferPool施加背压输出池最少保留四个缓冲。代码主动申请四个缓冲后立即释放引用。池的分配阈值为六。
isRendering还保证同一时刻只处理一帧。RoundVideoFrameProcessor.swift · L218–L272 - #10-06UIKit媒体、Stories 与个人页精选 10-5处理完成前显式保活像素缓冲和纹理
代码保留输入、输出
CVPixelBuffer和CVMetalTexture。它直到 GPU 命令完成后才结束这些对象的生命周期。回调也切回相机的mediaQueue。RoundVideoFrameProcessor.swift · L406–L434 - #10-07UIKit媒体、Stories 与个人页媒体编辑器用分块直方图生成局部增强 LUT
代码为每个网格块计算
MPSImageHistogram。代码将每块直方图写进共享私有缓冲。计算着色器再生成一组 LUT。EnhanceRenderPass.swift · L154–L188 - #10-08UIKit媒体、Stories 与个人页增强滤镜复用私有输出纹理,并跳过无用清屏
滤镜只在首次执行时创建输出纹理。纹理使用
.private存储。渲染通道使用.dontCare作为加载动作。EnhanceRenderPass.swift · L199–L245 - #10-09UIKit媒体、Stories 与个人页切换嵌入式相机时冻结旧预览并叠加圆形毛玻璃
切相机前先取
snapshotView。代码把快照留在原位。代码再以翻转动画显示圆形深色模糊层。切换完成后分别淡出毛玻璃和快照。DrawingStickerEntityView.swift · L558–L595 - #10-10UIKit媒体、Stories 与个人页GalleryUI 用双过渡视图同步缩略图与全屏圆角
画廊创建两个过渡视图。一个加入上层 transition surface。一个留在项目自身。回调收到源尺寸、目标尺寸、屏幕圆角和
progress。UniversalVideoGalleryItem.swift · L2565–L2589 - #10-11UIKit媒体、Stories 与个人页Stories 退场时把克隆层递归设回屏幕光栅倍率
fixScale会将层及所有子层的rasterizationScale设为UIScreenScale。退场时故事内容和目标头像各有一份过渡克隆。代码在跨父视图搬移主克隆前换算全局坐标。StoryItemSetContainerComponent.swift · L2382–L2407 - #10-12UIKit媒体、Stories 与个人页Stories 把全部暂停原因收口为播放模式函数
播放模式由缩放、下拉、菜单、输入、录音、贴纸和隐私页共同决定。上下文菜单或文本选择会返回
.blurred。其余干扰状态通常返回.pause。StoryItemSetContainerComponent.swift · L1430–L1479 - #10-13UIKit媒体、Stories 与个人页Stories 明确规定点击让位于平移,并只和滚动平移共存
点击识别器会等待平移失败。故事主平移只与滚动视图或当前观看列表的平移同时识别。其他组合被拒绝。
StoryItemSetContainerComponent.swift · L910–L937 - #10-14UIKit媒体、Stories 与个人页头像画廊只预加载当前项前后两张
头像轮播窗口固定为当前项前后各两项。代码以资源 ID 复用节点。只有中心项在要求时同步加载。首项还可限制为全尺寸资源。
PeerInfoAvatarListNode.swift · L1649–L1673 - #10-15UIKit媒体、Stories 与个人页切前后摄时暂存首帧,并校正录制时间线
录制器用
videoTimelineOffset重映射展示时间。切相机后的首帧先被heldFrame暂存。解析后,首帧可越过正常的停止边界检查。VideoRecorder.swift · L277–L317 - #11-01非 UI非 UI 内核精选 11-1共享模块间接证据用双重虚拟映射消除环形缓冲区回绕
它先预留两倍页对齐地址空间。它再把第一段物理页映射到紧邻的第二段。任何跨越尾部的逻辑连续区间都变成连续指针。
RingBuffer.m · L33–L105 - #11-02非 UI非 UI 内核精选 11-2用“所有权栅栏”和三次阀门保护共享 SQLite
SqliteValueBox只让主应用的读写连接删除数据库。它将连续可计数打开失败写入原子标记文件。它达到三次后,才同时删除主库、-shm和-wal。SqliteValueBox.swift · L254–L303 - #11-03非 UI非 UI 内核精选 11-3采样 CPU 时手工解 PAC 并限制栈遍历
CpuProfiler直接读取挂起线程的 arm64 寄存器和帧指针链。它先去掉 arm64e 返回地址的 PAC。它再用栈上下界、8 字节对齐和单调帧指针终止遍历。CpuProfiler.mm · L42–L99 - #11-04非 UI非 UI 内核精选 11-4通知扩展选择崩溃,而不是带着半事务继续
通知扩展在启动处理器时开启
setValueBoxStrictErrorHandling(true)。SQLite 的BEGIN、COMMIT或step失败会使扩展进程失败。系统随后展示未修改的推送。NotificationService.swift · L766–L778 - #11-05非 UI非 UI 内核将硬链接的多个路径合并为一个缓存淘汰单元
清理器用
(st_dev, st_ino)识别相同物理文件。第一个路径记录大小。额外硬链接只附加到同一临时数据库行。淘汰时会一起取消链接全部路径。TimeBasedCleanup.swift · L255–L284 - #11-06非 UI非 UI 内核用无行 ID 表和持久语句实现紧凑 ValueBox
ValueBox按逻辑表 ID 动态创建t<ID>。二进制主键表可使用WITHOUT ROWID。它按表缓存查询语句,并以SQLITE_PREPARE_PERSISTENT编译。SqliteValueBox.swift · L898–L952 - #11-07非 UI非 UI 内核显式关闭 SQLite mmap,并分拆持久化取舍
禁用缓存的连接把 SQLite 页缓存限制为 32。它显式关闭 SQLite mmap。它组合 WAL、
synchronous=NORMAL和内存临时表。检查点仍要求fullfsync。SqliteValueBox.swift · L590–L628 - #11-08非 UI非 UI 内核MTProxy 与 Web 代理只探测一个地址
对 MTProxy 或 Web 代理,发现逻辑选择单个地址。它优先选择 IPv4。只要配置了代理,它也禁用 80 和 5222 的备用端口探测。
MTDiscoverConnectionSignals.m · L194–L225 - #11-09非 UI非 UI 内核精选 11-5用可取消的延迟递归实现指数退避
每轮完成后才从计时器回调发起下一轮。间隔按两倍增长,并封顶。
MTMetaDisposable覆盖当前轮。外部取消会设置原子标记并处置当前订阅。MTDiscoverConnectionSignals.m · L228–L257 - #11-10非 UI非 UI 内核到期时靠释放队列本地对象取消整个通知管线
serviceExtensionTimeWillExpire()不逐项停止网络任务。它将QueueLocalObject置空。处理器析构时会释放MetaDisposable,从而处置轮询并断开保活连接。它再提交最新内容或原始内容。NotificationService.swift · L2748–L2765 - #11-11非 UI非 UI 内核将名为
async的队列调用改为同队列内联Queue为串行队列设置DispatchSpecificKey。在目标队列内,它让async和sync都直接执行闭包。主队列则用Thread.isMainThread识别。Queue.swift · L42–L81 - #11-12非 UI非 UI 内核用启动时间加运行时长实现不可回拨的自动锁
锁状态保存启动时间和开机运行秒数。若设备已重启,它立即锁定。未重启时,它使用
uptime比较超时,不使用可被用户调整的墙上时间。AppLock.swift · L18–L39 - #11-13非 UI非 UI 内核先累积消息索引,再批量更新未读状态
每个 peer 的操作循环复用一个键、写缓冲和编码器。它将新增入站消息索引放进
Set。它只在遇到次序敏感操作或循环末尾时批量调用addIncomingMessages。MessageHistoryTable.swift · L234–L260 - #11-14非 UI非 UI 内核在新启动 VM 中提前挂载 Metal 工具链
Tart 构建脚本在 Bazel 前串行挂载 Xcode 26 的 Metal cryptex。它检测
xcrun metal失败后,用软链接覆盖 Xcode 默认工具链内的同名 stub。TartBuild.py · L968–L998 - #11-15非 UI非 UI 内核从 Bazel 图生成 SwiftPM 的影子源码树
脚本读取 Bazel 输出的模块清单。它在
spm-files建立模块目录,但普通源码和bazel-out生成源码都只创建相对软链接。它拒绝不在声明模块目录中的输入。generate_spm.py · L333–L378 - #12-01AppKitTGUIKit 核心精选 12-1用无键窗口自绘全功能 AppMenu
它没有调用
NSMenu的原生弹出流程。它为每层菜单创建透明、无键、非主窗口。它把窗口级别设为.screenSaver。菜单内容由MenuView和表格绘制。AppMenuController.swift · L607–L681 - #12-02AppKitTGUIKit 核心精选 12-2已发布 · 仍可用删除毛玻璃内部图层和滤镜
VisualEffect在每次updateLayer()后检查毛玻璃子树。它移除名称不为backdrop的内部子层。它只保留饱和度和高斯模糊滤镜。最后它清空所有内部层背景。VisualEffectView.swift · L47–L79 - #12-03AppKitTGUIKit 核心精选 12-3用优先级重写 Window 键盘分发
Window.sendEvent(_:)先自动选择 responder。然后它先调度.All全局处理器。它只让优先级更高的全局处理器抢占具体按键。.supreme可以跨过修饰键判定。处理器可消费、拒绝或交给 AppKit。Window.swift · L781–L824 - #12-04AppKitTGUIKit 核心精选 12-4以条件
updateLayer模拟 UIKit 变换属性View把锚点、变换和子层变换包装为存储属性。属性变更时,它主动调用updateLayer()。它只在至少一个属性被使用时请求 AppKit 的updateLayer路径。View.swift · L109–L153 - #12-05AppKitTGUIKit 核心精选 12-5在基础 View 上统一翻转坐标并关闭隐式动画
所有
View默认 layer-backed。flip默认值为true。初始化关闭图层隐式动画,关闭自动子视图缩放,并按需重绘 layer。View.swift · L356–L397 - #12-06AppKitTGUIKit 核心精选 12-6用空 CAAction 兼做层级进入通知
SimpleShapeLayer拦截 CA 的onOrderIn和onOrderOut动作查询。它把这两个动作转换为层级生命周期回调。无论事件是什么,它都返回无操作CAAction。初始化时设置 Retina 比例。SimpleLayer.swift · L19–L42 - #12-07AppKitTGUIKit 核心已发布 · 仍可用用未公开 KVC 键给 Layer 内容着色
扩展用 KVC 读写
contentsMultiplyColor。它把该值包装为layerTintColor。同一扩展还把大量 layer action 映射为NSNull()。Extensions.swift · L560–L580 - #12-08AppKitTGUIKit 核心让装饰 View 穿透命中测试
isEventLess为真时,视图仍参与显示。它不会作为命中结果返回。它还跳过同类、ImageView和LayerBackedView的装饰层。View.swift · L421–L443 - #12-09AppKitTGUIKit 核心用真实 hit-test 修正 hover 判断
_mouseInside()不只测试坐标是否在 bounds。它先拿内容视图的真实命中结果。它把 eventless 装饰层和VisualEffect视为可穿透。后续代码还沿 superview 链检查,并对NSTableView的行视图再做一次 hit-test。Extensions.swift · L724–L775 - #12-10AppKitTGUIKit 核心用
NSTrackingArea处理非激活窗口 hover控件在进入窗口或 superview 变更后重建 tracking area。它使用
.inVisibleRect,因此不依赖过期 bounds。它使用.activeInActiveApp和.assumeInside。Control.swift · L220–L245 - #12-11AppKitTGUIKit 核心已发布 · 仍可用将 CVDisplayLink 回调安全转送到主队列
CVDisplayLink 的实时线程只向
DispatchSourceUserDataAdd累加数据。主队列上的 source handler 再调用 Swift 闭包。C 回调通过passUnretained传递 source 指针。DisplayLinkAnimator.swift · L22–L64 - #12-12AppKitTGUIKit 核心已发布 · 仍可用共享一个 DisplayLink 并按请求降帧
SharedDisplayLinkDriver统一维护请求。低帧率请求累积显示器真实周期。累计到目标周期 95% 时才回调。失效 link 在本轮后倒序清理。DisplayLinkAnimator.swift · L228–L280 - #12-13AppKitTGUIKit 核心手工求解 CAMediaTimingFunction 的三次贝塞尔
DisplayLinkAnimator不把数值交给 CA。它从CAMediaTimingFunction提取控制点。它在每个显示刷新时求解贝塞尔曲线,再回调插值结果。DisplayLinkAnimator.swift · L448–L469 - #12-14AppKitTGUIKit 核心导航 push 前冻结动态子内容
push 前,导航控制器递归锁住新页面的动态内容。它把新页面从右侧放入。旧页面向左移三分之一宽度。它只为不透明页面添加边缘阴影。转场结束后会调用
restoreHierarchyDynamicContent()。NavigationViewController.swift · L770–L794 - #12-15AppKitTGUIKit 核心把滚轮横向 delta 变成可取消的交互式导航
横向滚轮手势会连续直接改两个页面的位置。当前页按完整 delta 移动。前页只移动三分之一。阴影、边界和导航栏也同步更新。返回值把已消费 delta 交给
Window状态机。NavigationViewController.swift · L1225–L1249 - #12-16AppKitTGUIKit 核心以 contentView 覆盖层替代 AppKit sheet
Modal是被加到window.contentView的ModalBackground,不是beginSheet。它注册最高层 responder、全局按键、Escape 和 Return。背景控件还负责外点关闭或把点击转交给下层。Modal.swift · L654–L700 - #12-17AppKitTGUIKit 核心光栅化模态容器以压低阴影开销
模态外容器允许阴影越界。内层容器裁剪圆角内容。外层开启 rasterization,并把比例设为当前屏幕比例。边框独立成一层。
Modal.swift · L604–L631 - #12-18AppKitTGUIKit 核心从触发控件位置缩放自定义 Popover
Popover先把控件中心转到屏幕坐标。它再转换到背景坐标作为缩放锚点。翻转背景时补偿 y。它提供缩放和 reveal 两种动画,完成后才设置 first responder。Popover.swift · L243–L274 - #12-19AppKitTGUIKit 核心Tooltip 反转坐标并进行像素对齐
Tooltip 从目标视图转换位置。父内容翻转时,它手动反转 y。x 坐标按 backing scale 向下对齐。它把提示限定在左右 10 pt 内。箭头可自动指向控件中心或鼠标位置。
TooltipController.swift · L192–L231 - #12-20AppKitTGUIKit 核心复制 SimpleLayer 实现桌布横向铺砖
BackgroundView.TileControl根据宽度计算图片块。它只增删不足的SimpleLayer。每个块复用主层的内容、混合模式、背景和透明度。后续代码再更新每层 frame 并插入目标层。ViewController.swift · L94–L150 - #12-21AppKitTGUIKit 核心以全局鼠标移动轮询保持 Popover 打开
鼠标首次离开控件和 popover 后,代码启动 200 ms 的循环信号。循环检查鼠标是否回到任一方。未按下鼠标且没有上下文菜单时,它关闭 popover。
Popover.swift · L282–L338 - #13-01AppKit列表、滚动与文本精选 13-1已发布 · 仍可用用
CAEmitterLayer私有行为做可交互隐形墨水代码动画
emitterBehaviors.randomAttractor0的私有属性。它让粒子吸引力和位置持续随机变化。InvisibleInkDustView.swift · L340–L369 - #13-02AppKit列表、滚动与文本精选 13-2用 Core Text 反查空偏移,修正跨行选区尾部
如果选区结束索引的 Core Text 横向偏移为零,代码向前逐字符重试。它随后用修正后的两端偏移绘制高亮矩形。
TextView.swift · L2482–L2499 - #13-03AppKit列表、滚动与文本精选 13-3按稳定标识恢复滚动锚点,而不是恢复裸 offset
更新前保存可见行的
stableId和屏幕相对位置。更新后代码查找同一行,再重算NSClipView的 y 坐标。TableView.swift · L1882–L1914 - #13-04AppKit列表、滚动与文本精选 13-4平滑滚动保存目标点,并可同步结算中途动画
TGClipView为每次动画滚动保存唯一目标。它在完成时按真实bounds.origin验证结果。TGClipView.swift · L158–L203 - #13-05AppKit列表、滚动与文本精选 13-5把粘性标题移到滚动视图同级,并适配翻转列表
代码在可见行中按坐标方向寻找最近的
TableStickItem。TableView随后把TableStickView放在滚动内容之外。TableView.swift · L1424–L1445 - #13-06AppKit列表、滚动与文本精选 13-6粘性标题压住下一标题时,手算 y 轴推挤和层级
代码以候选标题的实际 row rect 计算覆盖标题的 y 偏移。它为第二个粘性标题预留
secondaryOffset。TableView.swift · L1503–L1553 - #13-07AppKit列表、滚动与文本精选 13-7用 presentation layer 接住行位移,再人工补 Core Animation
列表插入时读取
presentation()层的当前位置。它从屏幕上的实际位置继续动画,而不是从 model layer 跳变后的值开始。TableAnimationInterface.swift · L95–L110 - #13-08AppKit列表、滚动与文本为倒序消息流写独立的增量 diff
mergeListsStableWithUpdatesReversed以反向比较规则生成删除、插入和更新索引。它还保留更新项的旧索引。MergeLists.swift · L288–L312 - #13-09AppKit列表、滚动与文本只重排可见行,并用手工行高通知驱动
NSTableView窗口改宽时,代码只对可见 range 调用
makeSize。只有高度变化的行才 reload 并通知NSTableView重算高度。TableView.swift · L1083–L1101 - #13-10AppKit列表、滚动与文本用
TextNode复用相同输入的 Core Text layoutlayoutText把约束尺寸、截断策略、镂空、选中态和 attributed string 都作为缓存键。全部相等时直接复用旧 layout。TextNode.swift · L342–L369 - #13-11AppKit列表、滚动与文本绕过
NSTextView,直接逐 glyph run 绘制消息文本TextNode自己翻转 Core Graphics 坐标系。它逐行、逐CTRun绘制文本,并计算居中 pen offset。TextNode.swift · L305–L335 - #13-12AppKit列表、滚动与文本将内嵌内容留在文本流中,再交给独立图层渲染
代码从
CTLine精确得到内嵌范围的左右边界。它使用 floor/ceil 和备用偏移,减少非整数 glyph 坐标的缝隙。TextView.swift · L1014–L1049 - #13-13AppKit列表、滚动与文本spoiler 遮罩时同步隐藏浮在文字上的嵌入层
代码以嵌入层中心点和 spoiler rect 相交为条件,把对应 layer 的 opacity 置零。
TextView.swift · L3383–L3397 - #13-14AppKit列表、滚动与文本输入框用第二套 TextKit 栈测量高度,避免污染显示布局
InputTextView用独立的 storage、layout manager 和无限高 container 测量高度。它仅在文本或宽度改变时使测量布局失效。ChatInputTextView.swift · L695–L721 - #13-15AppKit列表、滚动与文本重写
NSTextContainer的行片段,为引用块预留不规则边距输入框在 TextKit 请求每个 line fragment 时检查 quote 属性。引用块行会被动态缩窄并右移。
ChatInputTextView.swift · L467–L503 - #13-16AppKit列表、滚动与文本自定义表情不是
NSTextAttachment,而是 glyph rect 上的叠加层代码用
customEmoji属性找字符范围,再转换为 glyph range 和 bounding rect。外层的emojiContent接收这些 rect 并渲染表情。ChatInputTextView.swift · L743–L769 - #13-17AppKit列表、滚动与文本发送键先检查
hasMarkedText(),避免截断中文等输入法候选Enter 只有在没有 marked text 时才进入发送委托。输入法组合文本存在时,事件继续交给
NSTextView。ChatInputTextView.swift · L1163–L1184 - #13-18AppKit列表、滚动与文本为输入控件单独持久化拼写和语法检查开关
InputTextView覆写 AppKit 拼写和语法开关。它以TGGrowingTextView后缀写入UserDefaults。ChatInputTextView.swift · L1292–L1309 - #13-19AppKit列表、滚动与文本横向滚动直接把纵向滚轮 delta 映射到 x,并在边界冒泡
横向容器把触控板的垂直 delta 转为 x 方向。它把 x 对齐到屏幕像素,并在无可滚动内容时交给父视图。
HorizontalTableView.swift · L124–L150 - #13-20AppKit列表、滚动与文本数字计数器把字符串拆成多个
TextView,分别滚入和滚出DynamicCounterTextView不直接替换整段文本。它让离场和入场的 token 各自沿 y 轴移动,并叠加透明度动画。DynamicCounterTextView.swift · L171–L201 - #13-21AppKit列表、滚动与文本强制关闭自动行高和默认背景,完全接管
NSTableView的行几何TGFlipableTableView关闭交替背景、自动行高和图层重绘策略。外层TableView负责尺寸、焦点和辅助元素。TableView.swift · L510–L541 - #13-22AppKit列表、滚动与文本滚动列表更新时按可视范围筛选动画对象
TableAnimationInterface先把插入和删除限制在可见行范围。它再以这些行的高度决定是否需要补偿 content offset。TableAnimationInterface.swift · L41–L79 - #14-01AppKit私有系统集成已发布 · 仍可用拼接私有类名,并用函数指针调用
CAEmitterBehavior代码运行时构造私有类
CAEmitterBehavior。代码也构造真实 selectorbehaviorWithType:。代码把method(for:)的 IMP 强转为 C 函数指针。随后,代码通过emitterBehaviors的 KVC 键装配吸附和生命周期行为。InvisibleInkDustView.swift · L12–L18 - #14-02AppKit私有系统集成精选 14-1已发布 · 仍可用用监听型
CGEventtap 实现全局按住说话代码监听 HID 级键盘、修饰键和其他鼠标键。它把
CGEvent转回NSEvent。它选择.listenOnly,所以监听器不拦截原始输入。权限不足时,代码退化为应用内NSEventmonitor。PushToTalk.swift · L119–L169 - #14-03AppKit私有系统集成精选 14-2已发布 · 仍可用用 FinderInfo 位和资源叉永久改写 Dock 图标
代码先让
sips -i生成图标资源叉。代码把资源叉复制到主.app包内名为Icon\r的文件。代码设置com.apple.FinderInfo第 8 字节的第 2 位。最后,代码删除 Dock 图标缓存,并执行killall Dock。Dock.swift · L64–L93 - #14-04AppKit私有系统集成精选 14-3版本差异深挖
AVMovableView,再点击隐藏的全屏按钮代码按类名找到 AVKit 内部的
AVMovableView。代码假设六层子视图链的末端是NSButton。代码通过performClick触发那个按钮的全屏动作。MGalleryExternalVideoItem.swift · L257–L267 - #14-05AppKit私有系统集成精选 14-4版本差异重排
NSTitlebarView内部子视图,腾出 50 pt 标题栏代码改写
window.titleView的 frame。代码递归按字符串找到私有NSTitlebarView。代码遍历前三个内部子视图。代码隐藏它们,并按 Retina 像素取整后重设坐标。PassportWindowController.swift · L89–L104 - #14-06AppKit私有系统集成精选 14-5系统结构已变化用私有文本菜单 selector 删除系统子菜单
代码检查 AppKit 生成的文本右键菜单。代码通过
_shareServiceSelected:识别系统分享服务项。代码发现分享、字体或替换项时,删除整个父菜单。代码再插入 Telegram 的文本转换项。ChatInputTextView.swift · L987–L1013 - #14-07AppKit私有系统集成精选 14-6已发布 · 仍可用直接调用未公开的对角缩放光标 selector
代码向
NSCursor类发送两个下划线 selector。它取得两种对角窗口缩放光标。调用点把它们用于图像编辑窗口和通话窗口的角落拖拽。ObjcUtils.m · L30–L43 - #14-08AppKit私有系统集成精选 14-7系统结构已变化按私有
MKZoomSegmentedControl类名移动地图缩放控件代码在每次 layout 后扫描
MKMapView的内部树。代码找到MKZoomSegmentedControl。代码把它固定到(20, 20)。代码在调整 map frame 时暂时置空 delegate。LocationModalController.swift · L231–L246 - #14-09AppKit私有系统集成用
CATransaction包裹系统全屏切换,并延迟发布状态Window在调用super.toggleFullScreen前后显式开关 Core Animation 事务。它允许processFullScreen先介入。它把保存状态写在实际切换后。无介入器时,它下一轮主队列才发布信号。Window.swift · L936–L958 - #14-10AppKit私有系统集成已发布 · 仍可用混用 IOPS 摘要和
AppleSmartBattery注册表私有键代码先用公开 IOPS 获取充满或耗尽剩余时间。代码再连接 IOKit 的
AppleSmartBattery服务。代码直接读取容量、循环次数、温度和设计循环数等键。它把电流与电压换算为带正负号的瓦数。InternalFinder.swift · L32–L53 - #14-11AppKit私有系统集成已发布 · 仍可用用 Carbon 键盘布局翻译物理键,并重试非 Unicode 布局
代码先映射功能键和修饰键。普通物理键则从当前 ASCII 键盘布局取得
UCKeyboardLayout。代码把 Cocoa 修饰键转成 Carbon 标志。代码用UCKeyTranslate得到用户当前布局下的快捷键文案。HotKeyUtilities.m · L89–L129 - #14-12AppKit私有系统集成把可能阻塞的输入监控授权移出主线程
无提示检查同步返回授权状态。需要提示时,代码在用户发起队列调用
IOHIDRequestAccess。代码立即返回NO。10.15 以前,它回退到辅助功能授权。另一方法还拼接系统设置深链。HotKeyUtilities.m · L150–L176 - #14-13AppKit私有系统集成直接用 POSIX 扩展属性存取 Finder 元数据
代码先以空缓冲区查询属性长度。代码再分配精确长度的缓冲区并读取原始字节。它把 POSIX errno 包装成 Swift 错误。Dock 包用它读写
com.apple.FinderInfo和com.apple.ResourceFork。XAttr.swift · L4–L29 - #14-14AppKit私有系统集成已发布 · 仍可用从
IOHIDSystem读取原始空闲纳秒数代码遍历
IOHIDSystem。代码读取注册表键HIDIdleTime。代码把读出的数右移 30 位。通知锁定逻辑用该值与超时比较。在线状态还要求该值小于 30 秒。ObjcUtils.m · L1235–L1257 - #14-15AppKit私有系统集成已发布 · 仍可用为 M2 后的泛化
hw.model启动system_profiler代码不只读
sysctlbyname("hw.model")。它启动 Apple 的system_profiler。代码请求 JSON 硬件摘要。代码从machine_name和chip_type合成展示用型号。外部命令失败后才回退到sysctl。TelegramSystem.swift · L4–L52 - #14-16AppKit私有系统集成用双时间戳文件做无信号处理器的崩溃哨兵
启动时,代码检查名为
crashhandler的文件。代码每次中间保存更新时间戳。下次启动时,文件只含一个时间戳,或两个时间戳相差小于 10 秒,都会被视为异常退出。应用代理启动后立即写入一次,并在终止时删除文件。CrashHandler.swift · L11–L50 - #14-17AppKit私有系统集成对 Spotlight 做稳定 ID 差分,而非全量重建索引
每个 Spotlight 条目 ID 带帐户 ID 和 peer ID。代码维护前一份条目列表。代码计算删除、插入和更新集合。它只把插入和更新交给
indexSearchableItems。它只删除消失的标识符。应用收到CSSearchableItemActionType后,再按帐户路由…Spotlight.swift · L104–L157 - #14-18AppKit私有系统集成把 Dock 徽标字符串当成状态栏图标的输入
未读数先写入
NSDockTile.badgeLabel。状态栏图标更新函数随后再读取这个字符串。代码在资源队列生成图像。代码回主队列替换状态栏图片。它也按 macOS 版本和外观选择黑白颜色。GlobalBadgeNode.swift · L199–L201 - #15-01AppKit渲染与应用层精选 15-1已发布 · 仍可用用 XOR 帧差分再用 LZFSE 压缩贴纸像素缓存
它把连续帧按 64 位字进行 XOR。它再把差分结果写为 LZFSE 压缩帧。读取时,它解压后再次 XOR 前帧,得到原始 BGRA 帧。
LottieBufferCompressor.swift · L308–L350 - #15-02AppKit渲染与应用层精选 15-2让相同内联贴纸共享一个解码和播放上下文
MultiTargetContextCache用动画键和唯一值索引上下文。第二个同键图层只注册回调。最后一个订阅者移除时,它才销毁上下文。InlineStickerItemLayer.swift · L208–L250 - #15-03AppKit渲染与应用层精选 15-3按运行循环把多个贴纸绘制合入一个 Metal 命令缓冲
同一
LottieRunLoop的待绘制项进入list。定时器每帧只创建一个MTLCommandBuffer。各项目编码后统一commit和present。LottiePlayer.swift · L1548–L1584 - #15-04AppKit渲染与应用层精选 15-4GIF 用
AVSampleBufferDisplayLayer自行供帧,而非逐帧NSImage动画它让
AVAssetReader只在显示层可接收时取样并enqueue。它关闭样本复制。它把显示层控制时钟绑定到主机时钟,并在读完后重建 reader。GIFPlayerView.swift · L245–L301 - #15-05AppKit渲染与应用层精选 15-5通话帧池达到五个缓冲就丢帧,优先控制实时延迟
它按分辨率复用
CVPixelBufferPool。池占用超过五张时,它立即丢弃新通话帧。它随后把 I420 拷入 NV12 缓冲。MetalVideoRenderView.swift · L286–L334 - #15-06AppKit渲染与应用层精选 15-6通话背景先统一颜色格式,再降采样做两次计算模糊
它先把 BGRA、双平面 YUV 或三平面 YUV 统一转成 RGBA。仅当背景层可见时,它降采样并依次执行横向和纵向计算模糊。
PrivateCallVideoLayer.swift · L190–L257 - #15-07AppKit渲染与应用层Thanos 效果把视图快照变成 GPU 像素粒子,并只在 M2 以上启用
它截取目标视图的像素快照。它把快照和转换后的坐标交给覆盖整个窗口的
DustLayer。粒子结束后,该无交互覆盖视图自动移除。DustLayer.swift · L369–L418 - #15-08AppKit渲染与应用层精选 15-7Thanos 粒子只在有项目且可见时订阅全局显示链接
层仅在有粒子且已进入层级时订阅共享显示链接。每个项目只初始化一次粒子状态。每帧计算着色器更新状态,再按
particleCount实例化绘制。DustLayer.swift · L170–L229 - #15-09AppKit渲染与应用层CPU 模糊把三个颜色通道塞入 64 位累积值,并用滑动二阶差分
实现用固定半径
7做横向和纵向两遍模糊。它把 RGB 通道打包在uint64_t。它通过二阶差分递推滑动窗口,并以首尾像素延拓边缘。FastBlur.m · L120–L215 - #15-10AppKit渲染与应用层软件渐变先渲染很小的位图,再动画
contents代码按 16×16 量级的适配尺寸生成软件渐变帧。它在 CPU 插值颜色锚点。它把预制
CGImage列表交给 Core Animation 的contents关键帧动画。SoftwareGradientBackground.swift · L287–L372 - #15-11AppKit渲染与应用层Shimmer 将压缩路径缩略图解码为异步位图掩码
它把自定义紧凑字节编码还原成路径字符串。它在并发队列将路径栅格化为
CGImage。主线程只把结果设给mask或前景层。ShimmerLayer.swift · L158–L175 - #15-12AppKit渲染与应用层声音波纹把原始音量做指数平滑,再生成闭合贝塞尔曲线
视图将输入音量归一化,并给三层 Blob 同时调速。显示链接用
0.9/0.1的指数低通生成展示音量。路径以相邻点导出的控制点闭合为贝塞尔曲线。VoiceBlobView.swift · L78–L112 - #15-13AppKit渲染与应用层聊天贴纸按尺寸降低帧率,并用缩略图无闪接力
它用映射读取载入完整动画数据。小于 200 pt 的非表情贴纸限定 30 fps。缩略图一直显示到播放器真正进入
.playing,再切换图层可见性。ChatMediaAnimatedStickerView.swift · L188–L246 - #15-14AppKit渲染与应用层内联贴纸延迟冷启动,却让缓存命中立即开始
新上下文冷启动时等待 100 ms。命中共享缓存、强制播放或指定无延迟时立即启动。回调发现图层离开窗口后会异步撤销自身。
InlineStickerItemLayer.swift · L576–L627 - #15-15AppKit渲染与应用层内联贴纸仅在窗口活跃且滚动区域可见时播放
播放条件同时要求可播放、窗口活跃和
visibleRect非零。它监听窗口焦点、滚动裁剪区边界和文档视图 frame 变化,立即重新计算条件。InlineStickerItemLayer.swift · L280–L322 - #15-16AppKit渲染与应用层PiP 不依赖系统 PiP 控制器,而用跨 Space 的非激活面板模拟
它用
NSPanel自建浮窗。面板不抢键盘焦点,并加入所有 Space 和全屏辅助窗口集合。它保留一个移到(3000, 3000)的Window,以让PictureInpictureView.window返回该窗口。PIPVideoWindow.swift · L69–L150 - #15-17AppKit渲染与应用层GIF 转码器修正极短延时,并直接绘入
CVPixelBuffer它优先使用 GIF 的未钳制延时。低于 11 ms 的延时统一修正为 100 ms。它从像素池取 ARGB 缓冲,并让 Core Graphics 直接写入该缓冲。
TGGifConverter.m · L185–L255 - #15-18AppKit渲染与应用层已发布 · 仍可用软件视频预览锁定
CVPixelBuffer后用 vImage 一次缩放预览路径直接锁定解码帧的像素内存。它用
vImageScale_ARGB8888缩放到目标 backing scale。最后才包装为CGImage。SoftwareVideoSource.swift · L26–L50