You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 02:15:41