Swift字符转LED矩阵网格映射失败,求解决方案
字符转n×n LED矩阵网格实现问题排查
问题目标
将单个字符映射为n×n的LED矩阵样式网格,当前输出未正确显示目标字符,需排查修复。
现有实现步骤
- 使用
UIGraphicsBeginImageContextWithOptions()将字符串转换为图片 - 遍历图片每个像素,提取RGB颜色存储到二维数组
- 创建由UIView组成的网格,为每个坐标应用对应颜色
现有代码
// 1. 字符串转图片 func textToImage() -> UIImage? { let text = "A" let attributes = [ NSAttributedString.Key.foregroundColor: UIColor.white, NSAttributedString.Key.font: UIFont.systemFont(ofSize: 10) ] let textSize = text.size(withAttributes: attributes) UIGraphicsBeginImageContextWithOptions(textSize, false, 0) text.draw(at: CGPoint.zero, withAttributes: attributes) let image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return image } // 2. 图片像素转颜色矩阵 func imageToMapping(_ inputImage: UIImage) -> [[UIColor]] { guard let cgImage = inputImage.cgImage, let data = cgImage.dataProvider?.data, let pixelPointer = CFDataGetBytePtr(data) else { fatalError("Couldn't access image data") } let bytesPerPixel = cgImage.bitsPerPixel / cgImage.bitsPerComponent let byteCount = CFDataGetLength(data) /* let matrix = Pixel.createPixelMatrix(cgImage.width, cgImage.height) return matrix.compactMap { yPixels in return yPixels.compactMap { xPixels in return xPixels.colorByPixel(pixelPointer) } } */ // update with for loops let matrix = Pixel.createPixelMatrix(Int(cgImage.width), Int(cgImage.height)) var ledGrid = [[UIColor?]](repeating: [UIColor?](repeating: nil, count: 36), count: 21) for (indexY, yPixels) in matrix.enumerated() { for (indexX, xPixels) in yPixels.enumerated() { ledGrid[indexY][indexX] = xPixels.colorByPixel(pixelPointer) } } } // 3. 生成LED网格视图 func ledMap(colorMap: [[UIColor]], imageWidth: Int, imageHeight: Int) { let yIndex = colorMap.count let xIndex = colorMap[0].count // cgImage size 177 36 let ledHeight = Int(view.frame.height) / imageHeight let ledWidth = ledHeight var y = 0 var x = 0 while y < colorMap.count { while x < colorMap[y].count { let led = UIView() led.frame = CGRect(origin: CGPoint(x: 11 * x, y: 11 * y), size: CGSize(width: 11, height: 11)) led.backgroundColor = colorMap[y][x] view.addSubview(led) x = x + 1 } x = 0 y = y + 1 } } struct Pixel { /** The number of bytes a pixel occupies. 1 byte per channel (RGBA). */ static let bytesPerPixel = 4 fileprivate let offset: Int fileprivate init(_ offset: Int) { self.offset = offset } static func createPixelMatrix(_ width: Int, _ height: Int) -> [[Pixel]] { return (0..<height).map { row in (0..<width).map { col in let offset = (width * row + col) * Pixel.bytesPerPixel return Pixel(offset) } } } }
问题现象
输出的LED网格未正确映射出字母A,改用for循环实现后结果仍错误。
解决建议
1. 补全缺失的colorByPixel方法
Pixel结构体中调用了colorByPixel但未实现,这是核心问题之一,需添加:
extension Pixel { func colorByPixel(_ pixelPointer: UnsafePointer<UInt8>) -> UIColor { let r = CGFloat(pixelPointer[offset]) / 255.0 let g = CGFloat(pixelPointer[offset + 1]) / 255.0 let b = CGFloat(pixelPointer[offset + 2]) / 255.0 let a = CGFloat(pixelPointer[offset + 3]) / 255.0 return UIColor(red: r, green: g, blue: b, alpha: a) } }
2. 修复imageToMapping的返回值与尺寸问题
当前代码未返回颜色矩阵,且硬编码了网格尺寸,需改为根据图片实际尺寸创建矩阵,并处理可选值:
func imageToMapping(_ inputImage: UIImage) -> [[UIColor]] { guard let cgImage = inputImage.cgImage, let data = cgImage.dataProvider?.data, let pixelPointer = CFDataGetBytePtr(data) else { fatalError("Couldn't access image data") } let width = Int(cgImage.width) let height = Int(cgImage.height) let matrix = Pixel.createPixelMatrix(width, height) var ledGrid = [[UIColor?]](repeating: [UIColor?](repeating: nil, count: width), count: height) for (indexY, yPixels) in matrix.enumerated() { for (indexX, xPixels) in yPixels.enumerated() { ledGrid[indexY][indexX] = xPixels.colorByPixel(pixelPointer) } } // 处理nil值,返回非可选的颜色矩阵 return ledGrid.compactMap { row in row.compactMap { $0 ?? UIColor.clear } } }
3. 修正坐标系颠倒问题
CGImage的原点在左下角,而UIKit视图原点在左上角,需反转y轴保证字符方向正确,修改Pixel.createPixelMatrix:
static func createPixelMatrix(_ width: Int, _ height: Int) -> [[Pixel]] { return (0..<height).map { row in let flippedRow = height - 1 - row // 反转y轴 return (0..<width).map { col in let offset = (width * flippedRow + col) * Pixel.bytesPerPixel return Pixel(offset) } } }
4. 优化文本图片生成,添加对比背景
当前生成的图片背景透明,添加黑色背景可提升字符与背景的对比度,同时增大字体提升像素辨识度:
func textToImage() -> UIImage? { let text = "A" let attributes = [ NSAttributedString.Key.foregroundColor: UIColor.white, NSAttributedString.Key.font: UIFont.systemFont(ofSize: 60) ] let textSize = text.size(withAttributes: attributes) UIGraphicsBeginImageContextWithOptions(textSize, false, 0) UIColor.black.setFill() UIRectFill(CGRect(origin: .zero, size: textSize)) // 填充黑色背景 text.draw(at: CGPoint.zero, withAttributes: attributes) let image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return image }
5. 修复LED网格布局逻辑
当前布局硬编码了11×11的尺寸,应使用计算好的ledWidth和ledHeight,保证网格适配父视图:
func ledMap(colorMap: [[UIColor]], imageWidth: Int, imageHeight: Int) { guard !colorMap.isEmpty else { return } let ledHeight = Int(view.frame.height) / imageHeight let ledWidth = Int(view.frame.width) / imageWidth // 按宽高比例计算,避免拉伸 var y = 0 while y < colorMap.count { var x = 0 while x < colorMap[y].count { let led = UIView() led.frame = CGRect(origin: CGPoint(x: ledWidth * x, y: ledHeight * y), size: CGSize(width: ledWidth, height: ledHeight)) led.backgroundColor = colorMap[y][x] view.addSubview(led) x += 1 } y += 1 } }
6. 可选:固定n×n网格尺寸
如果需要固定n×n的网格,可在生成文本图片后添加缩放逻辑,比如缩放到36×36:
func scaleImage(_ image: UIImage, to size: CGSize) -> UIImage? { UIGraphicsBeginImageContextWithOptions(size, false, 0) image.draw(in: CGRect(origin: .zero, size: size)) let scaledImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return scaledImage } // 使用示例 if let textImage = textToImage(), let scaledImage = scaleImage(textImage, to: CGSize(width: 36, height: 36)) { let colorMap = imageToMapping(scaledImage) ledMap(colorMap: colorMap, imageWidth: 36, imageHeight: 36) }
内容的提问来源于stack exchange,提问作者Mark
相关产品推荐
相关产品推荐

