Swift+UIKit实现猫眼风格选座:座位渲染到订单提交的完整闭环

简介:一份iOS电影选座功能实现源码包,面向需要开发票务类或影院App选座模块的开发者,以类似猫眼App的选座流程为蓝本,完整覆盖界面布局、数据管理、座位交互与网络同步等环节。项目使用自定义滚动视图承载座位图,通过座位数据模型管理可选、已选、禁选状态;自定义座位视图负责点选手势、连坐限制和选中高亮等反馈。资源共110个文件,压缩包仅约906KB,包含28张界面图片、13个m源码、9个头文件、12个工程配置方案、9个属性列表及5个json数据文件,另有依赖管理、编译配置、脚本等辅助内容。已有555人前来学习。深入阅读这套代码,可重点理解滚动座位图的绘制与适配、多场次座位占用冲突处理,以及通过Core Graphics自绘座位样式的实现技巧,同时学习工程结构组织方式,对提升自定义控件与交互设计能力很有帮助。

1. 类似猫眼电影选座(iOS):把一张座位图做成完整的闭环

做过票务类 App 的人都知道,选座页是全链路里最容易翻车、也最影响转化的页面。用户从场次点进来,真正关心的其实只有三件事:哪些座位能选、能不能连座、价格怎么算,任何一个环节卡住都可能丢单。这个项目就是一套参考猫眼选座交互的 iOS 完整实现,从座位图渲染、点击选座、价格联动、限选规则到生成下单参数,整条链路都真实跑得通。它适合两类人:一类是想搞懂自定义视图绘制、手势命中与状态管理的 iOS 开发者;另一类是产品原型期需要先交出一个可点可选选座 Demo、不等后端接口就能给业务方演示的开发者。工程不依赖任何第三方库,纯 Swift + UIKit 手写,拿到后直接在 Xcode 里编译运行,没有任何中间环节。

2. 技术选型与工程结构:为什么是纯 UIKit,而不是 SwiftUI

2.1 Swift + UIKit:选座场景对视图层的要求

先回答一个大概率会被问的问题:都做到这个年头了,为什么不用 SwiftUI?我的看法是,选座页本质上是一张「可以缩放、可以平移、上面散落着几百个可独立变化状态的小部件」的画布。SwiftUI 做列表和表单确实顺手,但一旦涉及 UIScrollView 缩放、精确命中区域、以及高频的选中态切换,UIKit 的显式视图树反而更好控制——每个座位就是一个独立的 UIControl,点谁谁响应,系统帮你把命中测试都处理完了。

这个 Demo 的架构是轻量 MVVM,没有引入 RxSwift 之类的响应式框架,而是用「ViewModel 暴露方法、View 通过闭包回调事件」的经典写法。这样做的原因有三个:第一,座位状态变化是典型的多视图联动——点一个座位,顶部提示文案、底部价格条、座位图本身要同时刷新,状态集中到 ViewModel 里可以避免控制器里塞满 if;第二,后续接真实接口时,只需要把 SeatService 的 mock 数据源换掉,View 层一行都不用动;第三,ViewModel 不依赖 UIKit,可以直接写单元测试验证选座规则,这在票务这种规则三天两头变的业务里非常值钱。

2.2 工程目录:每个文件负责一件事

工程按功能拆分,不是按层拆分,找代码的时候顺着页面走就行:

SeatDemo/ ├── App/ │ ├── AppDelegate.swift # 启动入口,创建窗口 │ └── SceneDelegate.swift ├── Models/ │ ├── Movie.swift # 影片信息 │ ├── Showtime.swift # 场次与影厅维度 │ ├── Hall.swift # 影厅座位矩阵描述 │ ├── Seat.swift # 座位模型与状态枚举 │ └── Order.swift # 订单草稿与下单参数 ├── ViewModels/ │ └── SeatSelectionViewModel.swift # 选座规则、选中集合、金额计算 ├── Views/ │ ├── SeatMapView.swift # 座位图画布,负责铺座与缩放 │ ├── SeatButton.swift # 单个座位按钮,渲染与命中 │ ├── SeatLegendView.swift # 图例:可选/已售/选中/过道 │ ├── PriceBarView.swift # 底部价格条与确认按钮 │ └── FilmStripView.swift # 顶部影片与场次切换条 ├── ViewControllers/ │ └── SeatSelectionViewController.swift # 页面装配与回调绑定 ├── Services/ │ └── SeatService.swift # mock 数据源,模拟接口延迟 └── Resources/ └── seat_theme.json # 配色与价格分区配置

简单过一遍职责:SeatService 是数据源的黑匣子边界,内部用 DispatchQueue.asyncAfter 模拟 300ms 网络延迟,返回场次、影厅和座位数组;SeatMapView 只做两件事——根据座位矩阵生成按钮、把点击事件通过闭包抛出去,自己不保存任何业务状态;SeatSelectionViewModel 持有座位矩阵和选中集合,所有规则都在这里;ViewController 只负责把三者串起来,同时处理弹窗、loading 这类 UI 细节。这种分层方式最直接的好处是出问题时能快速定位:按钮显示不对查 View,规则不对查 ViewModel,数据不对查 Service。

关键类职责对外接口
SeatMapView渲染座位矩阵、缩放平移容器configure(with:)、onSeatTap 闭包
SeatButton单座按钮皮肤与命中外抛seat 属性、isSelected 刷新
SeatSelectionViewModel限选、取消、金额、快照toggle(_:)、selectedSeatIds、totalAmount
SeatServicemock 影厅与座位数据fetchSeats(showtimeId:completion:)
SeatSelectionViewController视图装配、提交订单confirmTapped()、showOrderConfirm(_:)

这个表基本就是二次开发的索引:想改选座规则,直接看 ViewModel;想改座位长什么样,进 Views 目录;想接真实数据,只动 SeatService。

3. 核心模块拆解:座位模型、渲染与命中测试

3.1 座位数据模型:状态为什么不用一个 Bool

座位模型是整个页面的地基,先看它长什么样:

enum SeatStatus: Int, Codable { case sold = 0 // 已售出,灰色不可点 case available = 1 // 可售,可点击 case aisle = 2 // 过道,排内占位,不渲染 case locked = 3 // 他人锁定,几分钟内未支付 } struct Seat: Codable, Equatable { let seatId: String // 唯一标识,规则:排号_列号,如 "A_12" let rowNo: Int // 排号索引,从 1 开始 let colNo: Int // 列号索引,从 1 开始 let zone: Int // 票价分区:1 普通区 / 2 黄金区 / 3 情侣座区 let price: Int // 单座价格,单位是分,避免浮点误差 let status: SeatStatus // 服务端返回的实时状态 }

逻辑说明:状态用枚举而不是 Bool 的原因很实际——「不能选」至少分三种情况:已售完、过道、别人正在锁定。如果只用一个 isAvailable: Bool,过道座和已售座就没法区分,渲染时要么错位,要么会多画出一排空洞。价格用 Int 分而不是 Double 元,是票务项目最常规的做法,金额计算只做整数加减,不会出现 0.1 + 0.2 这种经典型的精度坑。

参数说明:seatId 用排号加列号拼接,主要是为了排查问题方便——用户反馈「第 6 排最左边点不动」,看日志里打印的 seatId 立刻能对上位置。zone 字段是给后续做分区配色预留的,当前版本只在图例和价格条里体现,后面要做「点击某分区高亮所有同价座位」,这个字段就是现成的索引。

3.2 渲染:375 设计稿铺座,用比例缩放适配机型

SeatMapView 用 UIScrollView 包一个容器视图,所有座位都铺在容器上,布局核心就一段:

/// 座位尺寸与间距,按 375pt 宽设计稿定稿 private let seatSize = CGSize(width: 36, height: 36) private let hGap: CGFloat = 8 private let vGap: CGFloat = 14 private let designBaseWidth: CGFloat = 375 private func layoutSeats(on container: UIView) { var contentHeight: CGFloat = 0 for (rowIndex, row) in matrix.enumerated() { let y = CGFloat(rowIndex) * (seatSize.height + vGap) + topPadding for (colIndex, seat) in row.enumerated() { guard seat.status != .aisle else { continue } // 过道不落按钮 let zoneOffset = CGFloat(seat.zone - 1) * 6 // 分区错位 6pt let x = CGFloat(colIndex) * (seatSize.width + hGap) + zoneOffset let button = SeatButton(type: .custom) button.frame = CGRect(x: x, y: y, width: seatSize.width, height: seatSize.height) button.seat = seat button.addTarget(self, action: #selector(seatTapped(_:)), for: .touchUpInside) container.addSubview(button) } contentHeight = y + seatSize.height } container.frame.size = CGSize(width: designBaseWidth, height: contentHeight) } /// 不同屏幕用等比缩放,避免小屏重叠、大屏被拉伸成椭圆 func applyScreenScale(to container: UIView) { let scale = UIScreen.main.bounds.width / designBaseWidth container.transform = CGAffineTransform(scaleX: scale, y: scale) scrollView.contentSize = CGSize(width: designBaseWidth * scale, height: container.frame.height * scale) }

逻辑说明:布局用「行索引 ×(座位高 + 行距)」算 y,用「列索引 ×(座位宽 + 列距)」算 x,是最朴素的网格铺法。zoneOffset 让不同价位的分区在视觉上错开一点,模拟真实影厅里分区之间有走道的感觉。按钮统一用 .custom 类型,因为系统默认按钮自带高亮背景和点击态,会影响自定义座位皮肤。

参数说明:36pt 的座位在 375pt 宽屏幕上大约可以放 8 个带间距,竖屏场景下影厅宽度刚好够;vGap 14pt 比水平间距大一圈,是为了在视觉上明确区分「排」和「列」。整体 scale 用的是「屏幕宽 / 设计稿宽」,等比缩放而不是逐点拉伸,避免在 6.7 寸大屏上座位被拉成椭圆,这是选座页最常见的变形翻车原因。

3.3 命中测试:点到的到底是哪个座位

每个座位都是独立的 UIControl,命中测试直接交给系统,这是 UIKit 做选座页一个隐性好处。按钮实现:

final class SeatButton: UIButton { var seat: Seat? // 绑定的座位数据,点击时回抛给外部 override var isSelected: Bool { didSet { refreshAppearance() } } private func refreshAppearance() { guard let seat else { return } switch (seat.status, isSelected) { case (.available, false): setBackgroundImage(UIImage(named: "seat_normal"), for: .normal) case (.available, true), (.locked, true): setBackgroundImage(UIImage(named: "seat_selected"), for: .normal) case (.sold, _): setBackgroundImage(UIImage(named: "seat_sold"), for: .normal) isUserInteractionEnabled = false default: break } accessibilityLabel = "\(seat.rowNo)排\(seat.colNo)号座位" } }

逻辑说明:SeatButton 持有 seat 数据,外观由「服务端状态 + 本地选中态」共同决定。isSelected 是 UIButton 自带的属性,选中变化时触发 didSet 刷新图片,ViewModel 只要切换选中集合,按钮外观自动跟着变,不需要手动 setImage,这也是把状态收敛到 ViewModel 带来的连锁收益。

参数说明:isUserInteractionEnabled 置 false 的座位会直接从响应链里摘出去,连高亮动画都不会触发,这是处理已售座位的正确姿势,而不是在回调里加 if 判断。顺手把 accessibilityLabel 做了,VoiceOver 用户能听到「第几排第几号」,这类辅助功能字段看起来不起眼,等 App 走审核或合规检查时就是硬指标。

4. 选座状态流转:ViewModel 怎么管住几百个座位

4.1 选座规则:限选、重复点击与金额联动

选座交互的规则不多,但每一条都直接影响用户体感。核心逻辑全部在 ViewModel 里:

struct SeatSelectionViewModel { let matrix: [[Seat]] // 服务端返回的原始座位矩阵 let maxSeats: Int // 单笔订单最多可选座位数 private(set) var selected: [Seat] = [] // 本地选中集合,按点击顺序 mutating func toggle(_ seat: Seat) -> Result<[Seat], SeatError> { guard seat.status == .available else { return .failure(.notAvailable) } if let index = selected.firstIndex(where: { $0.seatId == seat.seatId }) { selected.remove(at: index) // 再点一次取消选中 return .success(selected) } guard selected.count < maxSeats else { return .failure(.exceedLimit) } selected.append(seat) return .success(selected) } var totalAmount: Int { selected.reduce(0) { $0 + $1.price } } var selectedSeatIds: [String] { selected.map { $0.seatId } } } enum SeatError: Error { case notAvailable // 已售或锁定,不可选 case exceedLimit // 超过限选数量 }

逻辑说明:toggle 是整套选座逻辑的唯一入口,成功返回新的选中数组,失败返回错误类型。注意「取消选中」的判断在「限选数量」之前,所以当用户已经点了 6 个座位后想换一个,必须先点掉旧座再点新的——实际产品里也可以改成「超限时自动顶掉最早选的那个」,两种策略见仁见智,Demo 保留了最直观、用户最能理解的写法。

参数说明:selected 用数组而不是 Set,是为了保留点击顺序,后续做连坐判定和自动推荐都要依赖这个顺序。totalAmount 每次调用都重新 reduce,而不是维护一个增量变量,是刻意为之——选座操作虽然高频,但座位最多也就几十个,全量重算的开销可以忽略,却换来「永远不可能出现金额状态不同步」的确定性。

4.2 下单:座位快照与重复提交防护

点击底部「确认选座」后,页面要把当前选中座位打包成下单参数:

struct OrderDraft: Encodable { let showtimeId: String // 场次 ID let hallId: String // 影厅 ID let seatIds: [String] // 座位 ID 快照,如 ["A_5", "A_6"] let totalAmount: Int // 总金额,单位分 let orderSource: String // 渠道标识,用于后端埋点 } final class SeatSelectionViewController: UIViewController { private var isSubmitting = false // 提交锁,防止连点 @objc private func confirmTapped() { guard !isSubmitting else { return } isSubmitting = true let draft = OrderDraft( showtimeId: viewModel.showtime.id, hallId: viewModel.showtime.hall.id, seatIds: viewModel.selectedSeatIds, totalAmount: viewModel.totalAmount, orderSource: "ios_seat_demo" ) orderService.submit(draft) { [weak self] result in self?.isSubmitting = false // 成功跳转支付占位页 / 失败弹出重试 } } }

逻辑说明:提交时做三件事——用 isSubmitting 锁住入口、把选中座位拍成快照、走 orderService 提交。快照的意义在于,提交过程中用户仍然可以操作页面,但传给后端的永远是点击确认那一瞬间的座位列表,不会出现「界面显示选了 A5、实际提交了 A6」的错位。

参数说明:isSubmitting 是防重提交最简单的手段,比「按钮 disable 再恢复」更稳,因为异步回调回来时页面状态可能已经变了。orderSource 这种字段看起来不起眼,但线上排查订单来源、区分 App 端和后续的 H5 渠道全靠它,属于一开始就该留好的字段,后面补会非常痛。

5. 跑起来与避坑指南:从 Xcode 到真机调试

5.1 环境要求与启动步骤

工程是纯 Swift 工程,没有任何第三方依赖,因此省掉了 CocoaPods 和 SwiftPM 这一层,解压就能编。环境要求按当前主流 Xcode 版本即可:Xcode 14 以上、iOS 15 以上模拟器、Swift 5.7 以上。启动流程分四步:

  1. 解压工程后,双击 SeatDemo.xcodeproj 打开 Xcode。如果提示签名问题,在 Signing 里选一个自己的开发团队即可,模拟器运行不要求真机签名。
  2. 在 Xcode 顶部 Scheme 里把运行目标选成一个 iPhone 模拟器,然后 Command + R 编译运行。
  3. 进 App 后先出现场次列表,这是 SeatService 用 mock 数据生成的,点击任意场次进入选座页,座位图会模拟 300ms 网络延迟后渲染出来。
  4. 想从 mock 切换到真机联调时,只需要替换 SeatService 内部实现,接口路径和参数都写在文件注释里,照着改就行。

模拟器上有一个小技巧:座位图支持双指缩放,模拟器里按住 Option 键就能模拟双指手势,调试大影厅座席时非常方便,不用非得找真机。

5.2 避坑排查:五个真实踩过的坑

下面五条是从开发到联调阶段反复遇到的问题,每一条都按「现象 → 原因 → 解决」整理。

坑一:点击座位,选中的却是旁边一个

现象:用户点第 5 排 3 号,高亮却落在 4 号上,小屏机型尤其明显。

原因:座位容器包在 UIScrollView 里,容器又设置了 transform 缩放。点击坐标要经过 scrollView 的内容偏移和 transform 两层折算,任何一层没处理对,命中就偏。

解决:把命中交给按钮自身,不要自己用坐标换算。现在每个座位是独立的 UIControl,事件由系统分发。如果遇到必须手写命中测试的场景,记得先把 point 用convert(_:from:)转换到按钮父视图坐标系,再判断frame.contains(point)。从那以后我检查所有带缩放的容器,只要涉及触摸一律先做坐标转换,这类错位问题就基本绝迹了。

坑二:快速连点两次,产生了两个订单

现象:提交后页面跳转有延迟,用户手快又点了一次,后端收到两笔相同订单。

原因:按钮 disable 的生效是异步的,第一次点击的回调还没回来,第二次点击已经进来了。

解决:用 isSubmitting 布尔锁而不是 disable 按钮,它跟 UI 状态无关,天然防重。另外可以在 OrderDraft 里补一个 clientRequestId(UUID 字符串),后端拿它做幂等,同一个请求号只创建一笔订单,这是从服务端层面兜底的做法,前后端各守一道。

坑三:6.7 寸大屏上,座位被拉成了椭圆

现象:大屏真机上座位横向间距明显比设计稿大,整排座位看起来被横向抻开。

原因:页面用了 Auto Layout 约束,把座位宽度跟着屏幕宽度撑开了,相当于每个座位横向等比变大,视觉上就成了椭圆。

解决:固定 375pt 设计稿宽度,整体 transform 等比缩放,也就是 3.2 节 applyScreenScale 的思路。缩放之后要记得同步更新 scrollView 的 contentSize,否则滚动范围算错,最边上的座位滚不到。

坑四:刚进页面时已售座位没问题,过一会儿却能选中

现象:进入页面后已售座位是灰的,但停留十几秒后再点,有的座又能选中了。

原因:前端只在首次加载时拉了一次座位状态,这段时间别人买走或被锁定的座位,本地完全没有感知。

解决:选座页通常不主动刷新——刷新会把用户正在选的座位状态打掉,体验更差。正确做法是进页面时拉一次全量状态,提交订单时后端做最终校验,前端收到座位冲突错误码就提示「座位已售出」并刷新本地状态。把最终一致性放在提交那一跳,是所有票务项目的通用妥协方案。

坑五:VoiceOver 读不出座位排号

现象:开启旁白后,座位只朗读「按钮」,用户根本分不清自己在选第几排。

原因:自定义按钮没有设置 accessibilityLabel,系统只能读出控件类型。

解决:在 SeatButton 里给每个座位设置 accessibilityLabel,内容就是「第几排第几号」,顺手把已售座位的 accessibilityTraits 标记为 .notEnabled,旁白用户就不会点到已售座位上。

6. 改造思路:加「智能推荐选座」和分区高亮

6.1 改造一:一键选中间排连座

产品上有个高频需求:帮用户「选最好的位置」。实现思路是定位影厅中线排,从中线向两侧扩展,找连续可售座位:

func suggestSeats(in matrix: [[Seat]], count: Int) -> [Seat] { guard !matrix.isEmpty, count > 0 else { return [] } let centerRow = matrix.count / 2 for rowOffset in 0...3 { // 从中间排向外扩散最多 3 排 for rowIndex in [centerRow + rowOffset, centerRow - rowOffset] { guard matrix.indices.contains(rowIndex) else { continue } if let seats = findContinuous(in: matrix[rowIndex], count: count) { return seats } } } return [] } private func findContinuous(in row: [Seat], count: Int) -> [Seat]? { var streak = 0 var startIndex = 0 for (index, seat) in row.enumerated() { if seat.status == .available { if streak == 0 { startIndex = index } streak += 1 if streak == count { return Array(row[startIndex...index]) } } else { streak = 0 } } return nil }

逻辑说明:找连座本质是一个滑动窗口问题,在单排里数连续 available 的座位,攒够 count 个就返回。外层从中间排向上下扩展,优先满足「位置居中」的直觉。这个函数是纯计算,不碰 UI,可以直接塞进 SeatSelectionViewModel 里,配合单测覆盖各种边界。

参数说明:rowOffset 上限 3 表示最多向外找 3 排,再远就失去「推荐好位置」的意义了。返回数组按排内顺序排列,调用方拿来替换 selected 集合即可,再让座位图刷新。改造时确认一个边界:如果用户已经手动选了 2 个座,再点推荐是追加还是替换?建议做成替换,并弹确认提示,不然会把用户已有的选择静默冲掉。

6.2 改造二:按价格分区高亮整片座位

另一个常见交互是「点击价格条某档位,同价座位全部高亮」。这个改动不需要碰渲染层,只需在 SeatSelectionViewModel 里加一个highlightZone: Int?属性,SeatButton 的 refreshAppearance 在读图之前判断当前座位的 zone 是否匹配,匹配就叠加一层半透明遮罩。业务状态与视觉状态彻底解耦,分区高亮和选中态互不覆盖。

验证方法分两层:逻辑层用单元测试直接调 ViewModel 的 toggle 和 suggestSeats,断言选中集合、限选次数、金额分项;渲染层用 XCUITest 跑一条完整用例——进选座页、点两个座位、断言底部价格条文本等于两个座位价格之和、点确认、断言跳转到支付占位页。选座这种页面,UI 自动化前期投入不高,但后续每改一次规则都能自动回归,非常值钱。

最后说一个我自己的习惯:凡是带手势和状态联动的页面,每次改动后我都会强制走一遍完整流程——小屏、大屏、横竖屏各点一遍,再开着 VoiceOver 过一遍选座和取消。因为选座页的 bug 从来不藏在代码编译错误里,而是藏在坐标换算、状态时序和辅助功能这些最容易忽略的角落。这个工程把这些典型坑都提前趟了一遍,拿到手先按第 5 章的步骤跑通,再动手改,希望帮到你。

本文还有配套的精品资源,点击获取

相关新闻

自愈合防水材料销售平台:选型、交易与施工全流程指南

自愈合防水材料销售平台:选型、交易与施工全流程指南

树千晶自愈合防水材料销售平台&#xff0c;乍一听像是普通的建材电商&#xff0c;但做久了建材这行的人都清楚&#xff0c;防水材料不是快消品&#xff0c;客户不敢闭眼买&#xff0c;卖家也没法简单标个价就完事。尤其是自愈合防水材料这类偏功能型的产品&#xff0c;买家需要…

2026/10/20 11:48:45 阅读更多 →
Python学数学源码报错排查:环境配置与NumPy/SymPy避坑指南

Python学数学源码报错排查:环境配置与NumPy/SymPy避坑指南

简介&#xff1a;配套《程序员数学》一书的完整源码程序包&#xff0c;面向需要借助编程理解线性代数与微积分的学习者&#xff0c;尤其适合初中级程序员与数据科学入门人群&#xff0c;用可运行的代码化解抽象概念。压缩包共一百零四个文件&#xff0c;总体积约十三点五五兆字…

2026/10/20 11:48:45 阅读更多 →
开源了一个 Java 多智能体 LLM 框架 Baton:一个依赖,把 Agent 循环、工具调用、记忆、编排全配齐

开源了一个 Java 多智能体 LLM 框架 Baton:一个依赖,把 Agent 循环、工具调用、记忆、编排全配齐

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 11:48:45 阅读更多 →

最新新闻

水风光互补调度与净现值计算:MATLAB资源包实战指南

水风光互补调度与净现值计算:MATLAB资源包实战指南

简介&#xff1a;本资源面向计算机、电子信息工程、数学等专业的大学生及新能源调度方向研究者&#xff0c;提供水风光互补调度系统的净现值计算与不同网格装机容量、出力系数影响分析的完整MATLAB实现方案&#xff0c;可用于课程设计、期末大作业与毕业设计。压缩包共18个文件…

2026/10/20 12:26:14 阅读更多 →
情感化无感防刷与手势阻尼反馈:基于用户注意力疲劳度动态微调按钮交互阈值

情感化无感防刷与手势阻尼反馈:基于用户注意力疲劳度动态微调按钮交互阈值

在电商狂欢秒杀、限量潮流球鞋发售或火热赛事抢票的关键瞬间&#xff0c;前端页面承受着来自全球数百万用户的极速疯狂连击。 面对这种狂热的并发点击&#xff0c;传统的防刷防重复提交方案往往充斥着一种“居高临下的冰冷傲慢”&#xff1a; 方案一&#xff1a;粗暴弹窗报错—…

2026/10/20 12:26:14 阅读更多 →
推理引擎底层逻辑与优化实践:从计算图到量化部署

推理引擎底层逻辑与优化实践:从计算图到量化部署

做推理引擎方向有段时间了&#xff0c;经常有人拿着模型部署的报错来问我&#xff0c;聊到最后发现卡点往往不在算子实现上&#xff0c;而是对“推理”这件事本身的底层逻辑没吃透。这篇文章就是想把这些基础的东西掰开揉碎讲清楚&#xff0c;从模型是什么、推理时计算机到底在…

2026/10/20 12:26:14 阅读更多 →
AI智能体架构图绘制指南:四层框架与实战排查

AI智能体架构图绘制指南:四层框架与实战排查

画AI智能体架构图这件事&#xff0c;看着不难&#xff0c;翻车率却很高。前阵子帮一个小团队评审方案&#xff0c;对方贴出一张图&#xff1a;中间一个“LLM”的大框&#xff0c;左边拖进来几个接口&#xff0c;右边连着一个数据库&#xff0c;就敢叫“AI智能体架构图”。我说这…

2026/10/20 12:26:14 阅读更多 →
动效运行时弹簧物理引擎:纯 TypeScript 实现 RK4 龙格-库塔高精度数值积分

动效运行时弹簧物理引擎:纯 TypeScript 实现 RK4 龙格-库塔高精度数值积分

在现代自然交互动效中&#xff0c;传统的基于固定时间持续与三次贝塞尔曲线&#xff08;Cubic-bezier&#xff09;的过渡动画&#xff0c;正日益暴露出其致命的机械感与割裂感。 假设一个用户正在用手指拖拽一个浮动卡片&#xff1a;当手指松开时&#xff0c;卡片已经具有了初速…

2026/10/20 12:26:14 阅读更多 →
Flink SQL与DataStream本质差异:从API选择到生产就绪

Flink SQL与DataStream本质差异:从API选择到生产就绪

1. 为什么这节课是Flink学习者真正的分水岭我带过不少刚接触流处理的开发者&#xff0c;他们往往在学完DataStream API后会陷入一种“我已经会了”的错觉——能写个WordCount、能接Kafka、能做简单窗口聚合&#xff0c;就以为掌握了Flink。直到某天被业务方甩来一份需求&#x…

2026/10/20 12:25:14 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 10:02:08 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:45 阅读更多 →