logo

iOS蓝牙打印实战:小票与发票二维码的指令指南

作者:c4t2025.09.19 12:56浏览量:0

简介:本文聚焦iOS蓝牙打印技术,详解如何通过蓝牙连接打印机打印小票及发票二维码,提供从环境搭建到指令发送的完整流程,助力开发者高效实现移动端打印功能。

一、引言:移动端打印的场景与需求

在零售、餐饮、物流等行业中,移动端打印小票和发票二维码已成为刚需。例如,收银员通过iPad快速打印购物小票,或外卖骑手使用手机打印配送单,均依赖蓝牙打印技术。iOS设备凭借其稳定的蓝牙协议栈和丰富的开发接口,成为实现这一功能的理想平台。本文将详细介绍如何在iOS应用中通过蓝牙连接打印机,并发送包含发票二维码的打印指令。

二、技术准备:环境搭建与依赖配置

1. 项目设置与权限申请

在Xcode中创建iOS项目时,需在Info.plist中添加蓝牙权限声明:

  1. <key>NSBluetoothAlwaysUsageDescription</key>
  2. <string>需要蓝牙权限以连接打印机</string>
  3. <key>NSBluetoothPeripheralUsageDescription</key>
  4. <string>需要蓝牙权限以搜索周边设备</string>

同时,在Signin & Capabilities中启用Background Modes并勾选Bluetooth central,确保应用在后台仍能维持蓝牙连接。

2. 核心框架与第三方库

iOS原生蓝牙开发依赖CoreBluetooth框架,但直接操作底层协议较为复杂。推荐使用封装良好的第三方库,如:

  • StarIO10:星徽打印机官方SDK,支持ESC/POS指令集。
  • Zebra SDK:斑马打印机专用库,提供图像转码功能。
  • BluetoothPrinter:开源库,简化蓝牙设备发现与连接流程。

以StarIO10为例,通过CocoaPods集成:

  1. pod 'StarIO10'

三、蓝牙打印机连接流程

1. 扫描周边设备

使用CBCentralManager扫描支持打印服务的蓝牙设备:

  1. import CoreBluetooth
  2. class BluetoothManager: NSObject, CBCentralManagerDelegate {
  3. private var centralManager: CBCentralManager!
  4. private var discoveredPrinters: [CBPeripheral] = []
  5. override init() {
  6. super.init()
  7. centralManager = CBCentralManager(delegate: self, queue: nil)
  8. }
  9. func startScan() {
  10. centralManager.scanForPeripherals(withServices: [CBUUID(string: "000018F0-0000-1000-8000-00805F9B34FB")], // 通用打印服务UUID
  11. options: nil)
  12. }
  13. func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
  14. if !discoveredPrinters.contains(where: { $0.identifier == peripheral.identifier }) {
  15. discoveredPrinters.append(peripheral)
  16. print("发现打印机: \(peripheral.name ?? "未知设备")")
  17. }
  18. }
  19. }

2. 连接与验证

选择目标设备后,建立连接并验证服务:

  1. func connect(to printer: CBPeripheral) {
  2. centralManager.stopScan()
  3. centralManager.connect(printer, options: nil)
  4. }
  5. func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
  6. peripheral.delegate = self
  7. peripheral.discoverServices([CBUUID(string: "000018F0-0000-1000-8000-00805F9B34FB")])
  8. }
  9. func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
  10. guard let services = peripheral.services else { return }
  11. for service in services {
  12. if service.uuid == CBUUID(string: "000018F0-0000-1000-8000-00805F9B34FB") {
  13. peripheral.discoverCharacteristics(nil, for: service)
  14. }
  15. }
  16. }

四、打印指令构建与发送

1. 小票基础指令格式

ESC/POS指令集是行业通用标准,典型小票打印流程如下:

  1. 1B 40 // 初始化打印机
  2. 1B 61 00 // 居中对齐
  3. 48 65 6C 6C 6F 0D 0A // 打印"Hello"并换行
  4. 1D 21 11 // 加大字号
  5. 1B 61 01 // 右对齐
  6. 1D 76 30 00 50 00 // 打印二维码(50x50模块)

2. 发票二维码生成与嵌入

使用CoreImage生成QR码,并转换为打印机可识别的位图指令:

  1. func generateQRCode(from string: String, size: CGSize) -> UIImage? {
  2. let data = string.data(using: .utf8)
  3. let filter = CIFilter(name: "CIQRCodeGenerator")
  4. filter?.setValue(data, forKey: "inputMessage")
  5. filter?.setValue("H", forKey: "inputCorrectionLevel") // 容错率
  6. let output = filter?.outputImage
  7. let transform = CGAffineTransform(scaleX: size.width / 21, y: size.height / 21) // 21是QR码默认模块数
  8. let scaledImage = output?.transformed(by: transform)
  9. let context = CIContext()
  10. guard let cgImage = context.createCGImage(scaledImage!, from: scaledImage!.extent) else { return nil }
  11. return UIImage(cgImage: cgImage)
  12. }
  13. // 转换为打印机指令(示例为简化版,实际需按厂商文档实现)
  14. func convertQRToPrinterCommand(_ image: UIImage) -> [UInt8] {
  15. guard let cgImage = image.cgImage else { return [] }
  16. let width = Int(cgImage.width)
  17. let height = Int(cgImage.height)
  18. var command: [UInt8] = [0x1D, 0x76, 0x30, 0x00] // QR码指令头
  19. // 添加尺寸参数(示例为50x50)
  20. command.append(0x32) // 50的十六进制
  21. command.append(0x00)
  22. command.append(0x32)
  23. command.append(0x00)
  24. // 添加图像数据(此处简化,实际需按行转换)
  25. for y in 0..<height {
  26. for x in 0..<width {
  27. let pixel = cgImage.dataProvider?.data?.withUnsafeBytes { $0[y * width + x] }
  28. let gray = pixel?.pointee.red ?? 0 // 简化为灰度处理
  29. command.append(gray > 128 ? 0x00 : 0x01) // 二值化
  30. }
  31. }
  32. return command
  33. }

3. 完整指令发送示例

  1. func printReceiptWithQRCode() {
  2. guard let printer = selectedPrinter else { return }
  3. let initCommand: [UInt8] = [0x1B, 0x40] // 初始化
  4. let centerAlign: [UInt8] = [0x1B, 0x61, 0x00] // 居中
  5. let textCommand = "订单号: 123456\n金额: ¥99.99\n".data(using: .ascii)?.bytes ?? []
  6. let qrContent = "https://example.com/invoice/123456"
  7. let qrImage = generateQRCode(from: qrContent, size: CGSize(width: 200, height: 200))!
  8. let qrCommand = convertQRToPrinterCommand(qrImage)
  9. let fullCommand = initCommand + centerAlign + textCommand + qrCommand
  10. // 通过Characteristic发送(需根据实际打印机协议调整)
  11. let characteristic = // 获取之前发现的打印Characteristic
  12. let data = Data(bytes: fullCommand, count: fullCommand.count)
  13. printer.writeValue(data, for: characteristic, type: .withResponse)
  14. }

五、常见问题与优化建议

1. 连接稳定性优化

  • 重连机制:监听centralManager(_:didDisconnectPeripheral:error:),自动触发重连。
  • 超时处理:使用DispatchQueue设置10秒未响应则断开。

2. 指令兼容性处理

不同厂商指令差异示例:
| 指令 | 星徽打印机 | 佳博打印机 |
|——————|——————|——————|
| 初始化 | 0x1B 0x40 | 0x1B 0x40 |
| 二维码模式 | 0x1D 0x28 | 0x1B 0x6B |

建议封装协议适配器层,通过工厂模式返回对应指令生成器。

3. 性能优化技巧

  • 指令缓存:对静态内容(如店招LOGO)预生成指令并缓存。
  • 异步处理:使用OperationQueue将打印任务与UI线程分离。
  • 压缩传输:对大尺寸二维码采用RLE压缩算法。

六、总结与扩展

本文详细阐述了iOS蓝牙打印的核心流程,包括设备发现、连接管理、指令构建等关键环节。实际开发中需注意:

  1. 严格测试不同厂商设备的兼容性。
  2. 实现完善的错误处理与用户反馈机制。
  3. 考虑添加打印预览功能,减少试错成本。

扩展方向可探索:

  • 结合Vision框架实现OCR票据识别。
  • 通过Network.framework实现云打印中继服务。
  • 集成Apple Pay验证,实现支付与开票一体化。

通过掌握这些技能,开发者能够高效实现稳定的移动端打印功能,为业务场景提供可靠的技术支持。

相关文章推荐

发表评论