我该如何在Swift中从字符串生成条形码?

39

我是一名新手iOS开发者。我想知道如何使用Swift生成条形码。

我已经有了代码,有很多资源可以学习如何读取条形码,但我没有找到任何关于如何从字符串生成条形码的资料。

非常感谢!

P.S. 我知道有一个类似的问题,但它是针对Objective-C的。我不懂Obj-C,而且从.NET转过来也很困难。

5个回答

89
你可以使用 CoreImage (import CoreImage) 滤镜来实现这个!
    class Barcode {
        class func fromString(string : String) -> UIImage? {
             let data = string.data(using: .ascii)
             if let filter = CIFilter(name: "CICode128BarcodeGenerator") {
                  filter.setValue(data, forKey: "inputMessage")
                  if let outputCIImage = filter.outputImage {
                       return UIImage(ciImage: outputCIImage)
                  }
             }
             return nil
        }
    }

    let img = Barcode.fromString("whateva")

一种新版本,带有guard和可失败的初始化程序:

extension UIImage {

    convenience init?(barcode: String) {
        let data = barcode.data(using: .ascii)
        guard let filter = CIFilter(name: "CICode128BarcodeGenerator") else {
            return nil
        }
        filter.setValue(data, forKey: "inputMessage")
        guard let ciImage = filter.outputImage else {
            return nil
        }
        self.init(ciImage: ciImage)
    }

}

使用方法:

let barcode = UIImage(barcode: "some text") // yields UIImage?

根据文档

根据ISO/IEC 15417:2007标准生成代表输入数据的输出图像。输出图像中每个模块(垂直线)的宽度为一个像素。条形码的高度为32个像素。要从字符串或URL创建条形码,请使用NSASCIIStringEncoding字符串编码将其转换为NSData对象。


2
太棒了!!但是,我想知道为什么人们创建更或者更少复杂的库来处理条形码生成。这和库之间有什么区别? - PAD
1
@PAD 嗯,这些核心图像滤镜相当新(iOS >= 8.0)。在iOS 8之前,生成条形码可能是通过这些库完成的。 - Matteo Pacini
需要注意的是,Code128有三种不同的类型。此实现使用子类型C。如果您想生成Code 128 A或B类型,则可能需要查看ZXing库。 - AtomicBoolean
太棒了!!谢谢,如果我想创建其他带有HRI值的条形码,比如UPCA、UPCE,这是可能的吗? - Dhaval
条形码 Code28、Code39 和 CODABAR 的 CIFilter 名称是什么? - JkAlombro
显示剩余2条评论

21

改进后的代码:

  • 条形码缩放
  • 设置条形码图像边距
  • 将UIImage转换为NSData(以上述代码不可行的某种原因)。
  • 分享条形码图像时不会失败(可能是由于相同的错误)

Swift 3

func generateBarcode(from string: String) -> UIImage? {

    let data = string.data(using: String.Encoding.ascii)

    if let filter = CIFilter(name: "CICode128BarcodeGenerator") {
        filter.setDefaults()
        //Margin
        filter.setValue(7.00, forKey: "inputQuietSpace")
        filter.setValue(data, forKey: "inputMessage")
        //Scaling
        let transform = CGAffineTransform(scaleX: 3, y: 3)

        if let output = filter.outputImage?.applying(transform) {
            let context:CIContext = CIContext.init(options: nil)
            let cgImage:CGImage = context.createCGImage(output, from: output.extent)!
            let rawImage:UIImage = UIImage.init(cgImage: cgImage)

            //Refinement code to allow conversion to NSData or share UIImage. Code here:
            //https://dev59.com/A3E95IYBdhLWcg3wmvGh
            let cgimage: CGImage = (rawImage.cgImage)!
            let cropZone = CGRect(x: 0, y: 0, width: Int(rawImage.size.width), height: Int(rawImage.size.height))
            let cWidth: size_t  = size_t(cropZone.size.width)
            let cHeight: size_t  = size_t(cropZone.size.height)
            let bitsPerComponent: size_t = cgimage.bitsPerComponent
            //THE OPERATIONS ORDER COULD BE FLIPPED, ALTHOUGH, IT DOESN'T AFFECT THE RESULT
            let bytesPerRow = (cgimage.bytesPerRow) / (cgimage.width  * cWidth)

            let context2: CGContext = CGContext(data: nil, width: cWidth, height: cHeight, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: CGColorSpaceCreateDeviceRGB(), bitmapInfo: cgimage.bitmapInfo.rawValue)!

            context2.draw(cgimage, in: cropZone)

            let result: CGImage  = context2.makeImage()!
            let finalImage = UIImage(cgImage: result)

            return finalImage

        }
    }

    return nil
}

@Rushi,你是通过扫描仪还是应用程序来读取条形码的? - castillejoale
工作得很完美。但是当时我的图像尺寸太小了,所以无法阅读。 - Rushi trivedi
工作解决方案,但代码不安全,有很多强制解包。 为什么首先创建cgImage,然后从中创建rawImage:UIImage,然后再从UIImage获取cgimage?这有什么区别吗? - Andreas777
有没有一种设置背景透明的方法? - grep

12

如果您的部署目标至少为iOS 8,则可以使用Core Image。这是我的BarcodeGenerator类(您需要import CoreImage):

class BarcodeGenerator {
    enum Descriptor: String {
        case code128 = "CICode128BarcodeGenerator"
        case pdf417 = "CIPDF417BarcodeGenerator"
        case aztec = "CIAztecCodeGenerator"
        case qr = "CIQRCodeGenerator"
    }

    class func generate(from string: String, 
                         descriptor: Descriptor, 
                               size: CGSize) -> CIImage? {
        let filterName = descriptor.rawValue

        guard let data = string.data(using: .ascii),
            let filter = CIFilter(name: filterName) else {
                return nil
        }

        filter.setValue(data, forKey: "inputMessage")

        guard let image = filter.outputImage else {
            return nil
        }

        let imageSize = image.extent.size

        let transform = CGAffineTransform(scaleX: size.width / imageSize.width,
                                               y: size.height / imageSize.height)
        let scaledImage = image.transformed(by: transform)

        return scaledImage
    }
}

它可以像这样使用

BarcodeGenerator.generate(from: "barcode-string", 
                     descriptor: .code128, 
                          size: CGSize(width: 800, height: 300))

1
条形码Code28、Code39和CODABAR的CIFilter名称是什么? - JkAlombro
只有以下4种描述符受支持,如您所见:https://developer.apple.com/documentation/coreimage/cibarcodedescriptor - Luca Torella
除非我漏掉了什么,他们似乎已经在1月19日将Code128替换为DataMatrix。 - ChrisH
如果您访问我上面发布的链接,我仍然看到Code128,而没有看到DataMatrix。 - Luca Torella
这是Code128 https://developer.apple.com/library/archive/documentation/GraphicsImaging/Reference/CoreImageFilterReference/index.html#//apple_ref/doc/filter/ci/CICode128BarcodeGenerator - Luca Torella
显示剩余2条评论

1
使用方法如下:
func createBarcodeFromString(barcode:String)->UIImage?{

    let data = self.data(using: .isoLatin1)
    guard let filter = CIFilter(name: "CICode128BarcodeGenerator") else {
        return nil
    }
    filter.setValue(data, forKey: "inputMessage")
    filter.setValue(7.0, forKey:"inputQuietSpace")
    guard var ciImage = filter.outputImage else {
        return nil
    }

    let imageSize = ciImage.extent.integral
    let outputSize = CGSize(width:320, height: 60)
    ciImage = ciImage.transformed(by:CGAffineTransform(scaleX: outputSize.width/imageSize.width, y: outputSize.height/imageSize.height))

    let image = convertCIImageToUIImage(ciimage: ciImage)
    return image
}

func convertCIImageToUIImage(ciimage:CIImage)->UIImage{
    let context:CIContext = CIContext.init(options: nil)
    let cgImage:CGImage = context.createCGImage(ciimage, from: ciimage.extent)!
    let image:UIImage = UIImage.init(cgImage: cgImage)
    return image
}

0
类似于 Matteo Pacini的答案,这是使用较新的类型安全CIFilter实例的版本(iOS 13.0+)。
import UIKit
import CoreImage.CIFilterBuiltins // Type-safe CIFilter instances, iOS 13.0+

extension UIImage {
    static func barcode(
        string: String,
        height: Float,
        quietSpace: Float
    ) -> UIImage? {
        guard let message = string.data(using: String.Encoding.ascii) else {
            return nil
        }
        let filter = CIFilter.code128BarcodeGenerator()
        filter.message = message
        filter.barcodeHeight = height
        filter.quietSpace = quietSpace
        guard let outputImage = filter.outputImage else {
            return nil
        }
        return UIImage(ciImage: outputImage)
    }
}

使用方法:

imageView.image = .barcode(
    string: "0123456789",
    height: 64,
    quietSpace: 10
)

相关文档:

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接