swift语言编写一个简单的条形码扫描APP

原文地址:appcoda
在处理职员在杂货店的收银台排了很长的队伍,在机场帮助检查背包和旅客,或者在主要的食品供应商,协助处理乏味的存货清单过程,条形码扫描是很简单的处理工具。实际上,他们已经用了这个办法来解决消费者在智能购物,图书分类,等其他目的。因此,让我们来制作一个iPhone版本的条形码扫描工具吧!
对我们来说幸运的是,苹果已经制作了条形码扫描的程序,实现它是一件很简单的事情。我们将要研究进入AV Foundation框架的世界,组建APP,用来读取光盘上的条形码,获取相册上的一些重要信息,以及输出这些信息在APP上查看。读取条形码是很酷和重要的,所以要进一步读取我们收到的条形码号码。不言而喻,但是条形码扫描APP只有在带有相机的设备上才能使用。基于这一点,让我们拿起带有摄像头的IOS设备开始我们的教程吧! ========================== (全都是废话……)

关于 CDBarcodes

今天我们将要制作的APP的名字是CDBarcodes,当我们的设备发现了一个条形码,我们将扫描它并且发送到discogs音乐数据库获取相册名字、在最近一年的受关注的艺术家。discogs有一个很庞大的记录音乐的数据库,因此我们可以很好的获取我们所需要的信息。现在就开始下载 CDBarcodes,进行我们的教程吧!

令人印象深刻的数据库,discogs提供有用的API供开发者调用查询。我们只能接触到一小部分discogs提供给开发者的功能,但是这对于我们来说,足够开发一些有趣的APP了。

discogs

开始浏览产看discogs内容,首先,我们需要登录和注册一个discogs账号。在登录之后,滚动到屏幕底部。在底部部分,左边的列,点击API链接。如下图:

在**discogs**API选项中,左边的面板,Database部分,点击 Search。如下图:

我们将要从这里获取专辑的音乐信息,通过titleyear这两个参数。现在我们趁机要记录一个URL地址,在 Constants.swift中,添加https://api.discogs.com/database/search?q=作为常量disCOGS_AUTH_URL的值。

let disCOGS_KEY = "your-discogs-key"

现在,在整个APP中用方便的disCOGS_AUTH_URL来访问我们的搜索RUL,回退discogs API,创建一个新的APP以及配置一些东西,在顶部页面的导航栏中,点击创建APP。让后点击穿件一个应用程序按钮。如下图:

这里应用名称就输入CDBarcodes,当然你也可以自己适当改下。关于描述,如下:
“This is an iOS app that reads barcodes from CDs and displays information about the albums.”
然后,点击创建应用按钮。
默认生成的屏幕会显示用户条形码的数字证书编号,复制这个唯一的数字号码,粘贴到Constants.swift文件里面的disCOGS_KEY常量值,然后复制用户秘钥到Constants.swift文件里面的disCOGS_SECRET值,正如这个URL,我们方便在CDBarodes里面访问这些数值。如图:

CocoaPods

为了使用discogs API,那么我们通过CocoaPods这个工具,关于更多discogs的信息,可以去看CocoaPods’ site.网站查看。

通过 CocoaPods集成了AlamofireSwiftyJSON用户访问网络和解析获取到的JSON数据。让我们来开始写这个简单的APP吧。
通过,CocoaPods建立第三方库,需要打开一个控制台,cd到CDBarcodes项目,初始化CocoaPodsXcode项目如下:

cd <your-xcode-project-directory>  
pod init

这时候,会在项目内生成Podfile文件,控制台输入:

open -a Xcode Podfile

然后粘贴以下代码:

source 'https://github.com/CocoaPods/Specs.git'
platform :ios,'8.0'
use_frameworks!

pod 'Alamofire','~> 3.0'

target ‘CDBarcodesdo
pod 'SwiftyJSON',:git => 'https://github.com/SwiftyJSON/SwiftyJSON.git'
end

实际上可以直接在Xcode中编辑Podfile文件,添加以上代码也可以。

最后执行:

pod install

完成建立AlamofireSwiftyJSON第三方库,是不是很简单呢?此时我们要做的就是回到XCode中,确保从CDBarcodes.xcworkspace打开项目。

The Barcode Reader

AV Foundation 框架提供了工具用于我们组建 barcode reader工具,这里有些说明:
* AVCaptureSession管理相机捕获的数据
* AVCaptureDevice代表物理设备是AV Foundation框架的一个属性,AVCaptureSession接受从AVCaptureDevice获取的输入数据。
* AVCaptureDeviceInput从输入设备中捕获数据。
* AVCaptureMetadataOutput通过一个Delegate对象暴露获取的原始数据,也就是二进制数据。

BarcodeReaderViewController.swift,第一步导入 AVFoundation。

import UIKit  
import AVFoundation

实现 AVCaptureMetadataOutputObjectsDelegate代理方法.在viewDidLoad(),获取读取barcode对象。
首先,创建AVCaptureSession对象,并且设置AVCaptureDevice,生成了一个输入对象并添加到AVCaptureSession对象中。

class BarcodeReaderViewController: UIViewController,AVCaptureMetadataOutputObjectsDelegate {

var session: AVCaptureSession!
var previewLayer: AVCaptureVideoPreviewLayer!

override func viewDidLoad() {
    super.viewDidLoad()

    // Create a session object.
    session = AVCaptureSession()

    // Set the captureDevice.
    let videoCaptureDevice = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)

    // Create input object.
    let videoInput: AVCaptureDeviceInput?

    do {
        videoInput = try AVCaptureDeviceInput(device: videoCaptureDevice)
    } catch {
        return
    }

    // Add input to the session.
    if (session.canAddInput(videoInput)) {
        session.addInput(videoInput)
    } else {
        scanningNotPossible()
    }

有可能,设备没有照相机,扫描将不能进行。因此需要判断扫描失败的情况,看,我们可以提示用户说,扫描的设备必须要有照相机。
这部分代码如下:

func scanningNotPossible() {
    // Let the user kNow that scanning isn't possible with the current device.
    let alert = UIAlertController(title: "Can't Scan.",message: "Let's try a device equipped with a camera.",preferredStyle: .Alert)
    alert.addAction(UIAlertAction(title: "OK",style: .Default,handler: nil))
    presentViewController(alert,animated: true,completion: nil)
    session = nil
}

回到 viewDidLoad()方法中,添加了一个输入的会话后,需要创建一个AVCaptureMetadataOutput输出对象,并要添加到该会话中,我们发送捕获到的数据到代理方法中。
下一步就是设置条形码扫描类型,这里我们需要扫描的条形码是EAN-13类型的,因此并不是要扫描所有的条形码,比如有些UPC-A类型的条形码就会存在一些问题。
苹果转换UPC-A条形码到EAN-13类型是在最前面添加0,UPC-A条形码只有12位数,但EAN-13有13位,好的事情是可以通过MetadataObjectTypesAVMetadataObjectTypeEAN13Code设置,来自动转换,但是这种转换会给 discogs照成一些问题,不用担心,很快我们会开始处理这个问题,再次,如果相机有问题,还将要发送scanningNotPossible()消息给用户。

// Create output object. let MetadataOutput = AVCaptureMetadataOutput() // Add output to the session. if (session.canAddOutput(MetadataOutput)) { session.addOutput(MetadataOutput) // Send captured data to the delegate object via a serial queue. MetadataOutput.setMetadataObjectsDelegate(self,queue: dispatch_get_main_queue()) // Set barcode type for which to scan: EAN-13. MetadataOutput.MetadataObjectTypes = [AVMetadataObjectTypeEAN13Code] } else { scanningNotPossible() }

好了,现在需要AVCaptureVideoPreviewLayer类来全屏展示捕获到的视频内容,最后,我们开始捕获数据:

// 添加预览图层,用来展示捕获到的视频内容,就是条形码了.
​        
    previewLayer = AVCaptureVideoPreviewLayer(session: session);
    previewLayer.frame = view.layer.bounds;
    previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
    view.layer.addSublayer(previewLayer);

    // 开始捕获

    session.startRunning()

captureOutput:didOutputMetadataObjects:fromConnection中,可以发现扫描到的条形码信息。
首先需要获取MetadataObjects数组里面的第一个元素并转换为机器可读的代码,然后发送readableCode字符串到barcodeDetected(),接着需要提示用户会话是否成功,可以通过震动手机,最后不要忘了停止我们的会话。

func captureOutput(captureOutput: AVCaptureOutput!,didOutputMetadataObjects MetadataObjects: [AnyObject]!,fromConnection connection: AVCaptureConnection!) {

    // Get the first object from the MetadataObjects array.
    if let barcodeData = MetadataObjects.first {
        // Turn it into machine readable code
        let barcodeReadable = barcodeData as? AVMetadataMachineReadableCodeObject;
        if let readableCode = barcodeReadable {
            // Send the barcode as a string to barcodeDetected()
            barcodeDetected(readableCode.stringValue);
        }

        // Vibrate the device to give the user some Feedback.
        AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))

        // Avoid a very buzzy device.
        session.stopRunning()
    }
}

barcodeDetected()方法中还有些工作需要做,获取到的代码必须要移除空格符号,去掉空格符好之后,确认条形码是EAN-13还是UPC-A,如果是后者,还需要转换为EAN-13类型格式,并返还到最初的状态,也就是没去空格的时候。之前讨论的,苹果处理转换时直接最前面添加0,因此想0是不是开头呢,如果移除掉,没有这一步,discogs将不会识别出号码,并获取不到数据呢?
获得最终的字符串后,将它发送到BarcodeReaderViewController.swift文件的DataService.searchAPI()方法处理。

func barcodeDetected(code: String) {

    // Let the user kNow we've found something.
    let alert = UIAlertController(title: "Found a Barcode!",message: code,preferredStyle: UIAlertControllerStyle.Alert)
    alert.addAction(UIAlertAction(title: "Search",style: UIAlertActionStyle.Destructive,handler: { action in

        // Remove the spaces.
        let trimmedCode = code.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet())

        // EAN or UPC?
        // Check for added "0" at beginning of code.

        let trimmedCodeString = "\(trimmedCode)"
        var trimmedCodeNoZero: String

        if trimmedCodeString.hasPrefix("0") && trimmedCodeString.characters.count > 1 {
            trimmedCodeNoZero = String(trimmedCodeString.characters.dropFirst())

            // Send the doctored UPC to DataService.searchAPI()
            DataService.searchAPI(trimmedCodeNoZero)
        } else {

            // Send the doctored EAN to DataService.searchAPI()
            DataService.searchAPI(trimmedCodeString)
        }

        self.navigationController?.popViewControllerAnimated(true)
    }))

    self.presentViewController(alert,animated: true,completion: nil)
}

在这之前,必须在viewDidLoad()下面,继续添加viewWillAppear()viewWilldisappear()两个方法,viewWillAppear()方法中开始捕获会话的行为,相反viewWilldisappear()就是结束该会话。

override func viewWillAppear(animated: Bool) {

    super.viewWillAppear(animated)
    if (session?.running == false) {
        session.startRunning()
    }
}

override func viewWilldisappear(animated: Bool) {
    super.viewWilldisappear(animated)

    if (session?.running == true) {
        session.stopRunning()
    }
}

The Data Service

DataService.swift中,导入AlamofireSwiftyJSON模块。然后,声明一些变量用来存储discogs返回的数据,
感谢 Bionik6建议,设置private(set)用来防止别的类更改这个值。之后,获取网络数据,解析JSON格式,并将获取到的专辑标题和
年份赋值给 ALBUM_FROM_disCOGSYEAR_FROM_disCOGS变量。好了,现在我们已经可以获取到数据了,需要将这个消息通知到AlbumDetailsViewController.swift类去,所以需要发送通知到这个类。

import Foundation
import Alamofire
import SwiftyJSON

class DataService {

static let dataService = DataService()

private(set) var ALBUM_FROM_disCOGS = ""
private(set) var YEAR_FROM_disCOGS = ""

static func searchAPI(codeNumber: String) {

    // The URL we will use to get out album data from discogs
    let discogsURL = "\(disCOGS_AUTH_URL)\(codeNumber)&?barcode&key=\(disCOGS_KEY)&secret=\(disCOGS_SECRET)"

    Alamofire.request(.GET,discogsURL)
        .responseJSON { response in

            var json = JSON(response.result.value!)

            let albumartistTitle = "\(json["results"][0]["title"])"
            let albumYear = "\(json["results"][0]["year"])"

            self.dataService.ALBUM_FROM_disCOGS = albumartistTitle
            self.dataService.YEAR_FROM_disCOGS = albumYear

            // Post a notification to let AlbumDetailsViewController kNow we have some data.
            NSNotificationCenter.defaultCenter().postNotificationName("AlbumNotification",object: nil)
    }
}

}

The Album Model

Album.swift模型中,共有两个属性分别是,albumyear,用artistAlbumalbumYear两个参数来初始化对象的两个数值,代码如下:

import Foundation

class Album {        

private(set) var album: String!
private(set) var year: String!

init(artistAlbum: String,albumYear: String) {

    // Add a little extra text to the album information
    self.album = "Album: \n\(artistAlbum)"
    self.year = "Released in: \(albumYear)"
}

}

Time to Show Album Data!

viewDidLoad()中,需要处理获取到的通知,添加观察者用于接收通知,代码如下:

deinit {
    NSNotificationCenter.defaultCenter().removeObserver(self)
}

override func viewDidLoad() {
    super.viewDidLoad()

    artistAlbumLabel.text = "Let's scan an album!"
    yearLabel.text = ""

    NSNotificationCenter.defaultCenter().addobserver(self,selector: #selector(setLabels(_:)),name: "AlbumNotification",object: nil)
}

当通知接收到的时候,setLabels()会被调用,就会初始化Album专辑信息通过DataService.swift数据的改变。

func setLabels(notification: NSNotification){

    // Use the data from DataService.swift to initialize the Album.
    let albumInfo = Album(artistAlbum: DataService.dataService.ALBUM_FROM_disCOGS,albumYear: DataService.dataService.YEAR_FROM_disCOGS)
    artistAlbumLabel.text = "\(albumInfo.album)"
    yearLabel.text = "\(albumInfo.year)"
}

Testing CDBarcodes

好了,我们已经完成了所有的工作,可以开始测试App了,完整代码下载地址:CDBarcodes,总的来说,原始文章废话有点多,但写的是特别的基础,这点对于新手还是比较不错的。下面还有一些说明,就没翻译了,和技术实现没什么关系!

Swift语言编写一个简单的条形码扫描APP的更多相关文章

  1. HTML5 Web缓存和运用程序缓存(cookie,session)

    这篇文章主要介绍了HTML5 Web缓存和运用程序缓存(cookie,session),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  2. iOS Swift上弃用后Twitter.sharedInstance().session()?. userName的替代方案

    解决方法如果您仍在寻找解决方案,请参阅以下内容:

  3. 使用Fabric SDK iOS访问Twitter用户时间线

    我试图在这个问题上挣扎两天.我正在使用FabricSDK和Rest工具包,试图为Twitter使用不同的RestAPIWeb服务.我可以使用具有authTokenSecret,authToken和其他值的会话对象的TWTRLogInButton成功登录.当我尝试获取用户时间线时,我总是得到失败的响应,作为:{“errors”:[{“code”:215,“message”:“BadAuthentic

  4. ios – 如何从Apple Watch调用iPhone上定义的方法

    有没有办法从Watchkit扩展中调用iPhone上的类中定义的方法?根据我的理解,目前在Watchkit和iPhone之间进行本地通信的方法之一是使用NSUserDefaults,但还有其他方法吗?

  5. ios – 无法识别的选择器发送到实例NSTimer Swift

    解决方法让updateTime成为一个类方法.如果它是在一个纯粹的Swift类中,你需要在@objc前面说明该方法的声明,如:

  6. cocoapods – 命令/ bin / sh失败,退出代码23

    适用于所有豆荚,无需豆荚但仍然是同样的错误.有任何想法吗?

  7. iOS编译库/框架链接器标记问题是设备编译而不是模拟器

    背景:我正在尝试使用带有cocoapods的Phonegap.Phonegap已经存在从命令行编译的问题所以我只是将Xcode与生成的项目文件一起使用(并且在使用cocoapods时我无论如何都必须这样做).我用我的问题向Github推送了一个复制的空项目:https://github.com/Dan2552/phonegap-reproducable-issue您可以在repo的根目录中的bui

  8. ios – CocoaPods CorePlot安装显示缺少的框架和丢失的文件

    我是CocoaPods的新手,我正在尝试在我的Podfile中使用此行安装CorePlot:当我在安装后打开.xcworkspace文件时,CorePlot会显示缺少的框架.我将这些链接的二进制文件安装到我的Xcode项目中,但这并没有解决问题.在CorePlot目标中,似乎没有单独的方法来安装框架.这不是唯一的问题.当我尝试在CorePlot库中引用文件时,即使我可以看到CorePlot目标中的文件,我也会收到文件未找到错误:我尝试了以下import语句:都导致同样的错误.我不知道这些问题是否相关.我应

  9. 通用iOS应用程序在TestFlight中的iPad Air 2上不兼容

    您应该为项目和目标设置“有效架构”:armv7,armv7s和arm64.

  10. ios – 如何在不创建新目标的情况下阻止`pod install`将libPods.a添加到“Link with Libraries”构建阶段?

    解决方法要防止集成,您可以使用:

随机推荐

  1. Swift UITextField,UITextView,UISegmentedControl,UISwitch

    下面我们通过一个demo来简单的实现下这些控件的功能.首先,我们拖将这几个控件拖到storyboard,并关联上相应的属性和动作.如图:关联上属性和动作后,看看实现的代码:

  2. swift UISlider,UIStepper

    我们用两个label来显示slider和stepper的值.再用张图片来显示改变stepper值的效果.首先,这三个控件需要全局变量声明如下然后,我们对所有的控件做个简单的布局:最后,当slider的值改变时,我们用一个label来显示值的变化,同样,用另一个label来显示stepper值的变化,并改变图片的大小:实现效果如下:

  3. preferredFontForTextStyle字体设置之更改

    即:

  4. Swift没有异常处理,遇到功能性错误怎么办?

    本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请发送邮件至dio@foxmail.com举报,一经查实,本站将立刻删除。

  5. 字典实战和UIKit初探

    ios中数组和字典的应用Applicationschedule类别子项类别名称优先级数据包contactsentertainment接触UIKit学习用Swift调用CocoaTouchimportUIKitletcolors=[]varbackView=UIView(frame:CGRectMake(0.0,0.0,320.0,CGFloat(colors.count*50)))backView

  6. swift语言IOS8开发战记21 Core Data2

    上一话中我们简单地介绍了一些coredata的基本知识,这一话我们通过编程来实现coredata的使用。还记得我们在coredata中定义的那个Model么,上面这段代码会加载这个Model。定义完方法之后,我们对coredata的准备都已经完成了。最后强调一点,coredata并不是数据库,它只是一个框架,协助我们进行数据库操作,它并不关心我们把数据存到哪里。

  7. swift语言IOS8开发战记22 Core Data3

    上一话我们定义了与coredata有关的变量和方法,做足了准备工作,这一话我们来试试能不能成功。首先打开上一话中生成的Info类,在其中引用头文件的地方添加一个@objc,不然后面会报错,我也不知道为什么。

  8. swift实战小程序1天气预报

    在有一定swift基础的情况下,让我们来做一些小程序练练手,今天来试试做一个简单地天气预报。然后在btnpressed方法中依旧增加loadWeather方法.在loadWeather方法中加上信息的显示语句:运行一下看看效果,如图:虽然显示出来了,但是我们的text是可编辑状态的,在storyboard中勾选Editable,再次运行:大功告成,而且现在每次单击按钮,就会重新请求天气情况,大家也来试试吧。

  9. 【iOS学习01】swift ? and !  的学习

    如果不初始化就会报错。

  10. swift语言IOS8开发战记23 Core Data4

    接着我们需要把我们的Rest类变成一个被coredata管理的类,点开Rest类,作如下修改:关键字@NSManaged的作用是与实体中对应的属性通信,BinaryData对应的类型是NSData,CoreData没有布尔属性,只能用0和1来区分。进行如下操作,输入类名:建立好之后因为我们之前写的代码有些地方并不适用于coredata,所以编译器会报错,现在来一一解决。

返回
顶部