條形碼掃描

閑暇時間做了個條形碼掃描的功能:包括界面的搭建(只能在矩形框內掃描出結果,并且中間清晰 周邊模糊效果)、功能的實現。

界面展示.jpg
一、利用AVFoundation框架屬性設置(5個重要的變量):

<記錄變量>
// 輸入輸出的中間橋梁
@property(nonatomic, strong) AVCaptureSession *session;
// layer
@property(nonatomic, strong) AVCaptureVideoPreviewLayer *layer;
@property(nonatomic, strong) AVCaptureMetadataOutput *output;

1、 初始化設備為視頻設備 AVCaptureDevice *device
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
2、初始化輸入設備是攝像頭視頻設備 AVCaptureDeviceInput *input
NSError *error = nil;
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];

// 如果沒有攝像頭,或者攝像頭不可用就調用代理方法傳出錯誤信息
if (error) {
    if ([self.delegate respondsToSelector:@selector(openFailureWithError:)]) {
        [self.delegate openFailureWithError:error];
        NSLog(@"您的手機不支持條形碼掃描");
    }
    return;
}
3、初始化輸出設備 AVCaptureMetadataOutput *output
    AVCaptureMetadataOutput *output = [[AVCaptureMetadataOutput alloc] init];
    // 設置輸出設備的代理 為當前控制器 并且設置隊列為主隊列
    [output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
    self.output  = output;
4、初始化會話連接對象 AVCaptureSession *session
    self.session = [[AVCaptureSession alloc] init];
    // 高質量采集率
    [_session setSessionPreset:AVCaptureSessionPresetHigh];
    [_session addInput:input];
    [_session addOutput:output];
    
    // 設置掃碼支持的編碼格式(如下設置為全部條形碼的可能)
    output.metadataObjectTypes = @[AVMetadataObjectTypeEAN13Code,
                                   AVMetadataObjectTypeEAN8Code,
                                   AVMetadataObjectTypeUPCECode,
                                   AVMetadataObjectTypeCode39Code,
                                   AVMetadataObjectTypeCode39Mod43Code,
                                   AVMetadataObjectTypeCode93Code,
                                   AVMetadataObjectTypeCode128Code,
                                   AVMetadataObjectTypePDF417Code];
5、用會話初始化一個能拍照掃描的AVCaptureVideoPreviewLayer *layer 大小是導航條除外屏幕的大小--size是傳入的參數代表著想照射的范圍,本例設置為屏幕的大小
    AVCaptureVideoPreviewLayer *layer = [AVCaptureVideoPreviewLayer layerWithSession:_session];
    layer.videoGravity = AVLayerVideoGravityResizeAspectFill;
    // 設置layer的frame
    layer.frame = CGRectMake((SCREENWIDTH - size.width) * 0.5, 64, size.width, size.height - 64);
    // 把拍攝的layer添加到主視圖的layer
    [self.view.layer  addSublayer:layer];
    self.layer = layer;
    
    // 開始捕獲圖像
    [_session startRunning];
二、界面的搭建(內清外模糊,并且只在矩形框內才能掃描出結果----抽取自定義視圖)

<保存自定義視圖>
@property(nonatomic, weak) PLScanView *scanView; // 掃描視圖
// 視圖布局
PLScanView *scanView = [[PLScanView alloc] initScanView];
scanView.frame = CGRectMake(0, 64, SCREENWIDTH, SCREENHEIGHT - 64);
[self.view addSubview:scanView];
self.scanView = scanView;
在自定的PLScanView(繼承自UIView)的文件中,寫了一個其他文件可以調用的實例方法:
- (instancetype)initScanView;
在這個方法的實現中,添加必要的視圖 并且

視圖背景色必須設置為clearColor,是為了可以看到攝像頭照射的內容。
1、添加視圖
   // 1、添加描述信息
    UILabel *messageLbl = [[UILabel alloc] init];
    messageLbl.text = @"請將條碼置于取景器內進行掃描,距離大約10cm,盡量避免反光,開啟閃光燈等有助于弱光條件下識別";
    messageLbl.numberOfLines = 0;
    messageLbl.font = [UIFont systemFontOfSize:12.0];
    messageLbl.textColor = [UIColor whiteColor];
    [self addSubview:messageLbl];
    self.messageLbl = messageLbl;
    
    // 2、添加imageView與滾動線
    // 2.1 設置中間掃描區(qū)域的背景圖片
    UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"pick_bg.png"]];
    //添加到視圖上
    [self addSubview:imageView];
    self.imageView = imageView;
    
    // 2.2 初始化二維碼的掃描線
    UIImageView *lineImageView = [[UIImageView alloc] init];
    lineImageView.image = [UIImage imageNamed:@"line.png"];
    [imageView addSubview:lineImageView];
    self.lineImageView = lineImageView;
    
    //開啟定時器
    [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(animation) userInfo:nil repeats:YES];
2、在layoutSubviews中設置布局--設置矩形寬250 高150:
  - (void)layoutSubviews
 {
     [super layoutSubviews];
    //描述的label
    self.messageLbl.frame = CGRectMake(10, 20, self.width - 20, 30);
    //設置位置到界面的中間
    self.imageView.frame = CGRectMake(self.bounds.size.width * 0.5 - 250 * 0.5, self.bounds.size.height * 0.5 - 150 * 0.5, 250, 150);
  // 開始位置
    self.lineImageView.frame  = CGRectMake(10, 10, 230, 2);
}
3、定時器的實現:
   - (void)animation
{
     [UIView animateWithDuration:1.8 delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
    // 最低位置
    self.lineImageView.frame = CGRectMake(10, 130, 230, 2);
    
     } completion:^(BOOL finished) {
    // 開始位置
        self.lineImageView.frame = CGRectMake(10, 10, 230, 2);
    }];
}
4、中間清晰 周圍模糊的界面實現,在drawRect中繪制,代碼如下:
// 1 獲得圖形上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 2 畫整個屏幕的視圖顏色
CGContextSetRGBFillColor(ctx, 40/255.0, 40/255.0, 40/255.0, 0.5);
CGContextFillRect(ctx, CGRectMake(0, 0, self.width, self.height));

// 3 畫中間透明視圖 clearRect
CGRect clearRect = CGRectMake((self.width - 250) * 0.5, (self.height - 150) * 0.5, 250, 150);
CGContextClearRect(ctx, clearRect);
5、在控制器的viewWillAppear中,如果該控制器被pop之后,還要再次可以掃描,那么AVFoundation的5個變量,要依然存在,所以只需要重新調用上文“一”的代碼即可:
  if (!self.session.running) {
      // 調用代碼一...
   }
6、只能在中間區(qū)域內掃描出功能實現rectOfInterest屬性 -- 修正掃描區(qū)域 右上角為坐標原點,但是傳入的值依然按右上角坐標原點的Y/X/H/W進行理解(右上角為原點下的YXHW)
CGFloat scaleY = (SCREENHEIGHT - 150) * 0.5 / SCREENHEIGHT;
CGFloat scaleX = (SCREENWIDTH - 250) * 0.5 / SCREENWIDTH;
CGFloat scaleH = 150 / SCREENHEIGHT;
CGFloat scaleW = 250 / SCREENWIDTH;
self.output.rectOfInterest = CGRectMake(scaleY, scaleX, scaleH, scaleW);
注:關于self.output.rectOfInterest方法,使用解析如下

//這個CGRectMake(Y,X,H,W) 1代表最大值 原點是導航右上角 為起始點
[output setRectOfInterest:CGRectMake(0, 0.5, 0.5, 0.5)];//左上角 1/4 屏幕
[output setRectOfInterest:CGRectMake(0.5, 0.5, 0.5, 0.5)];//左下角 1/4 屏幕
[output setRectOfInterest:CGRectMake(0.5, 0, 0.5, 0.5)]; //右下角 1/4 屏幕
[output setRectOfInterest:CGRectMake(0, 0, 0.5, 0.5)]; //右上角 1/4 屏幕
[output setRectOfInterest:CGRectMake(0.25,0.25, 0.5, 0.5)]; //貌似 中間的感覺?。?!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容