目的只是要做一个能随时随地进行条形码扫描,并和某单号配对的小工具。
在淘宝上看了一下扫描枪的价格,便宜的,只能插在电脑上使用,这个不符合随时随地的条件。贵的,有显示屏,可编程,但是目前投入回报比太小,不值得付出。
怎么办呢?手上有一台 Mac Mini ,有一台 iPhone, 看过两个月的 ObjC 的基本语法和 ios 开发教程,不如就写个 app 吧。
一,越狱
首先,iphone 要越狱,网上大把,就不说了。
二,免证书真机调试
对于我这种初学者,花几百RMB去弄个开发者账号,然后真机调试,实在是浪费。但是条形码扫描又必须真机调试(摄像头无法模拟),所以,破解一下 Xcode ,弄免证书真机调式是必经之路:
http://kqwd.blog.163.com/blog/static/4122344820117191351263/
这遍博文写的很详细,各个主流版本的 Xcode 的都有讲,仔细对着做就成。当然iphone 必须先越狱。
三,ZBar
目前 ios 上有开源的扫描库 ZXing 和 ZBar, 我没有比较,对着网上搜到的博文就用了 ZBar,过程还算顺利。
但是如果有一堆条形码摆在那里,ZBar 的对焦框会飘来飘去。
ZBar 有个 scanCrop 属性,默认值是 (0,0,1,1)代表全屏,
the region of the image that will be scanned. normalized coordinates.
这句注释看起来很费解。normalized coordinates 我不明白到底是什么意思。
在屏幕上画一个框,这个框代表可扫描区域,然后把这个区域的 CGRect 传给 scanCrop 显然是不能工作的。
几经周折,找到了如下解决方法:
1 -(CGRect)getScanCrop:(CGRect)rect readerViewBounds:(CGRect)rvBounds{ 2 CGFloat x,y,width,height; 3 x = rect.origin.y / rvBounds.size.height; 4 y = 1 - (rect.origin.x + rect.size.width) / rvBounds.size.width; 5 width = (rect.origin.y + rect.size.height) / rvBounds.size.height; 6 height = 1 - rect.origin.x / rvBounds.size.width; 7 return CGRectMake(x, y, width, height); 8 }
很奇怪,x,y,width,height 好像都是翻的,不过,这个确实是可用的。
self.readerView.scanCrop = [self getScanCrop:maskView.frame readerViewBounds:self.readerView.bounds];
四,对二维码加、解密
除了条形码,还有另外一个码需要扫描,并和条形码对应。这个码里还要附加一些另外信息,所以,我就用了二维码。
生成二维码,我用的是 ZXing.NET, 在 NuGet 里可以找到,这个没什么难度。
因为这要生成二维码的内容,涉及到隐私,不是随便拿个手机就可以扫扫的。所以,我用了AES加密。
在.NET里做AES加密是很简单的事情,但是在 ios 里做解密,对我来说,有些复杂。
因为加密后的内容可能为不能显示的字符,所以需要用 Base64 转一下。
网上提供的一些 Base64 代码不是缺胳膊就是少腿,用着非常虐人。用了GTM后,赶脚爽多了
http://google-toolbox-for-mac.googlecode.com/svn/trunk/
需要FQ,FQ我用GoAgent
提供一下 ios 下的AES加解密代码:
1 #import <CommonCrypto/CommonCryptor.h> 2 #import "AESCrypt.h" 3 #import "GTMDefines.h" 4 #import "GTMBase64.h" 5 6 @implementation AESCrypt 7 8 9 10 +(NSString*) dencryptData:(NSString*)str key:(NSString *)key{ 11 NSString *newKey = [key stringByPaddingToLength:32 withString:@"0" startingAtIndex:0 ]; 12 NSString *iv = [key stringByPaddingToLength:16 withString:@"0" startingAtIndex:0]; 13 14 NSData *data = [GTMBase64 decodeString:str]; 15 NSData *keyData = [newKey dataUsingEncoding:NSUTF8StringEncoding]; 16 NSData *ivData = [iv dataUsingEncoding:NSUTF8StringEncoding]; 17 18 NSData *result = [self dencryptData:data :keyData :ivData]; 19 20 return [[NSString alloc ] initWithData:result encoding:NSUTF8StringEncoding ]; 21 } 22 23 + (NSData*)dencryptData:(NSData*)data :(NSData*)key :(NSData*)iv 24 { 25 size_t bufferSize = [data length] + kCCBlockSizeAES128; 26 void *buffer = malloc(bufferSize); 27 size_t encryptedSize = 0; 28 CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt, 29 kCCAlgorithmAES128, 30 kCCOptionPKCS7Padding, 31 [key bytes], 32 [key length], 33 [iv bytes], 34 [data bytes], 35 [data length], 36 buffer, 37 bufferSize, 38 &encryptedSize); 39 if (cryptStatus == kCCSuccess) 40 return [NSData dataWithBytesNoCopy:buffer length:encryptedSize]; 41 else 42 free(buffer); 43 44 return NULL; 45 } 46 47 48 49 50 51 +(NSString *)encrypt:(NSString *)str key:(NSString *)key{ 52 NSString *newKey = [key stringByPaddingToLength:32 withString:@"0" startingAtIndex:0 ]; 53 NSString *iv = [key stringByPaddingToLength:16 withString:@"0" startingAtIndex:0]; 54 55 NSData *data = [str dataUsingEncoding:NSUTF8StringEncoding]; 56 NSData *keyData = [newKey dataUsingEncoding:NSUTF8StringEncoding]; 57 NSData *ivData = [iv dataUsingEncoding:NSUTF8StringEncoding]; 58 59 NSData *result = [self encryptData:data key:keyData iv:ivData]; 60 result = [GTMBase64 encodeData:result]; 61 62 return [[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding ]; 63 } 64 65 + (NSData*)encryptData:(NSData*)data key:(NSData*)key iv:(NSData*)iv 66 { 67 size_t bufferSize = [data length] + kCCBlockSizeAES128; 68 void *buffer = malloc(bufferSize); 69 size_t encryptedSize = 0; 70 CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, 71 kCCAlgorithmAES128, 72 kCCOptionPKCS7Padding, 73 [key bytes], 74 [key length], 75 [iv bytes], 76 [data bytes], 77 [data length], 78 buffer, 79 bufferSize, 80 &encryptedSize); 81 if (cryptStatus == kCCSuccess) 82 return [NSData dataWithBytesNoCopy:buffer length:encryptedSize]; 83 else 84 free(buffer); 85 86 return NULL; 87 } 88 89 @end
需要注意的是:IV 是 取KEY的前16个字符,不足的在后面补0,KEY取原KEY的前32个字符,不足的,在后面补0。
还有 PKCS7。要保证.NET里的和ios里的对应参数一至。
五,ARC
GTM 提供的代码有很多 retain,autorelease 等,对于ARC这些是不行的。一开始,我简单的把 autorelease 删了,但是 retain 不知道要怎么处理了。后来搜了一下:
在工程的 Build Phases -> Compile Sources 下选中不使用ARC的代码,双击,输入:-fno-objc-arc ,就可以了。