精华内容
下载资源
问答
  • 二维条码/二维码(2-dimensional bar code)是用某种特定的几何图形按一定规律在平面(二维方向上)分布的黑白相间的图形记录数据符号信息的;在代码编制上巧妙地利用构成计算机内部逻辑基础的“0”、“1”比特流的...

    二维条码/二维码(2-dimensional bar code)是用某种特定的几何图形按一定规律在平面(二维方向上)分布的黑白相间的图形记录数据符号信息的;在代码编制上巧妙地利用构成计算机内部逻辑基础的“0”、“1”比特流的概念,使用若干个与二进制相对应的几何形体来表示文字数值信息,通过图象输入设备或光电扫描设备自动识读以实现信息自动处理:它具有条码技术的一些共性:每种码制有其特定的字符集;每个字符占有一定的宽度;具有一定的校验功能等。同时还具有对不同行的信息自动识别功能、及处理图形旋转变化点。
    二维码矩阵

    尽管二维码应用渐趋广泛,但与日韩等国相比,我国的二维码发展还远远不够。制约因素除了运营商的支持度外,还有技术、终端适配、盈利模式等方面。炒得很火热的是二维码与O2O(Online To Offline)模式的结合,即利用二维码的读取将线上的用户引流给线下的商家。腾讯很看好这个模式,马化腾称”二维码是线上线下的一个关键入口”。尽管有些人不看好二维码的应用,但无可否认,只要培养了足够多的用户群,再结合良好的商业模式,二维码将成为桥接现实与虚拟最得力的工具之一。

    总结:二维码就是通过编排上的0,1等方式,给用户了一个可实现的脚本,这样其实就是一个脚本而已,通过代码完成了一部分的工作。

    但是可能会被别有用心的人利用,造成代码隐患~~

    展开全文
  • 说起二维码、条形码和RFID,你可能不知道那是什么玩意,当你在超市结账时,售货员扫描你商品上的一排黑白竖线,那个叫做条形码;...科普时间,准备拿好小本本记一下。条形码是由黑白相间的条纹组成的图案,黑色部分称
  • 最近这段时间,团队在做的业务主要和二维码相关,在做的过程中,发现不管是自己还是团队里其他同学,都对二维码本身了解甚少,因此自己调研了一番,在团队内部做了一次分享,在这里整理出来。 有意思的是之前在做...

    作者:孙辉,美团金融前端团队成员。15年毕业加入美团,相信技术,更相信技术只是大千世界里知识的一种,个人博客: sunyuhui.com

    最近这段时间,团队在做的业务主要和二维码相关,在做的过程中,发现不管是自己还是团队里其他同学,都对二维码本身了解甚少,因此自己调研了一番,在团队内部做了一次分享,在这里整理出来。

    有意思的是之前在做业务时,QA跑过来问我一个问题:

    我两次进入同一个页面,发现图片不一样,是不是有问题啊?

    我当时很庆幸自己在做项目之前了解了一些二维码相关的东西,所以我问他:

    你两次扫码的结果是一样的吗?

    备注:本文涉及到的二维码原理相对粗浅,只能当做二维码的科普文,若有心对二维码生成和扫码原理有深入研究,请出门左转~

    二维码的生成

    二维码的结构

    为了让大家先直观了解二维码的结构,建议大家先去QR Code Generater,在左侧的地址栏里持续输入不同的内容(随便敲,使劲敲),在二维码图片的变更过程中,仔细观察图片,看图片的变化是不是有什么规律。

    这时候你心里应该大致有谱了,二维码对你来说不再只是混沌的黑白图片,现在我们来介绍下二维码的结构



    整体分为功能区编码区,功能区主要用于定位,编码区则是真正存储数据的。

    有几个地方需要注意:

    1. 二维码所有的模块中,储存的并不都是我们需要的信息,甚至只有一小部分才是。在上面的图中,只有数据和纠错码字中的数据才是我们实际想存储的数据。
    2. 版本信息:二维码一共有40个版本,不同版本的区别主要是存储数据的模块的多少(每一块黑/白块就是一个模块),模块数量遵循: moduleCount = 21 + (n-1)×4,比如:版本1的模块数量是21*21,版本40的模块数量为: 21 + (40-1)×4 = 177。

    二维码是如何生成的?

    那上面的二维码到底是怎么一步步生成的呢?



    上面的图中,详细标注出了二维码的各个结构生成的顺序。左边的流程图是绘制的顺序,右边的图表示绘制到这个流程时已经绘制出来的图形。比如,绘制到【定位图形】这一步时,整个二维码就已经有了左上角、右上角、左小角、连接这三者的横竖两条定位标志以及右下方的校正标志

    添加【掩码图案】这个阶段时,整个二维码其实已经绘制完成了,但是如果只是按照实际的信息来绘制,可能会导致页面上有一大片黑色或者一大片白色,而不是我们现在看到的黑白交叉的图形,这样会导致扫码时识别困难。所以在这一步会再做一步操作:从已有的模板中(模板的黑白是交叉的)选一个,来和生成的二维码图形做异或操作,其中异或操作就是JavaScript中的^运算符

    其实在整个生成过程中,我最好奇的是这些黑白色是怎么生成的(恩,我相信你也是这样的 ^-^ )。

    现在我们以 jquery-qrcode 为例,看下源码

    options    = $.extend( {}, {
        render                : "canvas",
        width                : 256,
        height                : 256,
        typeNumber        : -1,
        correctLevel        : QRErrorCorrectLevel.H,
        background      : "#ffffff",
        foreground      : "#000000"
    }, options);复制代码

    这里是生成二维码时的一些参数,可以看到参数里有个background属性和foreground属性,分别为白色黑色,咦?这么巧?

    继续看

    // draw in the canvas
    for( var row = 0; row < qrcode.getModuleCount(); row++ ){
        for( var col = 0; col < qrcode.getModuleCount(); col++ ){
            ctx.fillStyle = qrcode.isDark(row, col) ? options.foreground : options.background;
            var w = (Math.ceil((col+1)*tileW) - Math.floor(col*tileW));
            var h = (Math.ceil((row+1)*tileW) - Math.floor(row*tileW));
            ctx.fillRect(Math.round(col*tileW),Math.round(row*tileH), w, h);  
        }    
    }复制代码

    这里就是二层循环,用来绘制二维码的内容(二维码是矩形,所以两层循环),其中有行代码鹤立鸡群,立马引起了我的注意:

    ctx.fillStyle = qrcode.isDark(row, col) ? options.foreground : options.background;

    isDark函数是干啥的?我们去 源码 瞅瞅,然后就能发现,这个函数其实返回的就是二维码里这个位置上的二进制数据,从 这篇文章 里我们知道二维码生成过程中,会把所有信息都转换成二进制,所以二维码内容上的数据不是0就是1,那么在isDark函数里,我们发现,如果位置上的数据是1,就使用白色,如果数据是0,就使用黑色

    恩, 这时候就知道为啥二维码是黑白的了吧。

    生成二维码时的几个问题

    1. 二维码只能是黑白的吗?

    源码在手,天下我有

    所以让我们来尝试着生成彩色的二维码吧!

    我们把生成二维码的foreground参数和background参数修改一下,

    options = $.extend( {}, {
    render : "canvas",
    width : 256,
    height : 256,
    typeNumber : -1,
    correctLevel : QRErrorCorrectLevel.H,
    background : "#f00", //改成红色
    foreground : "#0f0" //改成绿色
    }, options);

    恩,是的,我们生成了一个大红大绿的二维码。。。。。绿色对应原来的白色,红色对应原来的黑色。(我打赌,你之前肯定没有见过这么艳的二维码 ~



    2. 同样的内容生成的二维码是不同的吗?

    不是相同的,就如同我们开篇QA提到的那样,不过扫码后得到的结果是一样的。不同的原因是由于在具体生成黑白块时,是按照一定规律随机放置在图片中的。

    3. 既然可以生成彩色的,为啥我们平常见到的二维码都是黑白的?

    嗯,好问题,这个问题我们稍后解答~

    二维码的[扫码]

    大家平常在用各种APP扫码的时候,主要是付款、取(电影)票、添加微信好友等一些场景

    我们能明显感受到的只是那的一声,那么在扫码的时候究竟发生了什么呢?我最近在做的业务中就有一环是【扫码】,所以我研究了一下。整体流程图是这样的



    总的来说,扫码的流程就是两步:

    1. 获取二维码图片信息
    2. 对图片信息做相应处理

    这时候你再回想下自己在微信、美团、点评等APP里使用扫码功能时出现的不同情况,恩,你应该明白了。。

    也许你已经习惯了每次扫码都会跳转到一个新页面,又或者你平常根本没注意这种事,但并不总是 扫码后跳转到一个链接 这么简单,而是对扫码返回的信息做不同处理。跳转到一个链接 只是其中一种方式。

    在生成二维码时,我们提过到,二维码的生成过程,就是一个对信息加密的过程,那么【扫码】这个过程呢?其实就是一个解密的过程。具体怎么去解密的,可以去读一下Zxing的源码

    扫码时的几个问题

    拍照用的摄像头和扫码用的摄像头有什么区别?

    其实都是调用的手机的摄像头,但是扫码的摄像头由APP控制,具备解析二维码内容的能力。

    为啥大家见到的二维码都是黑白的?

    即便是从颜值上考虑,彩色的也会更好看,在如今颜值即正义的时代里,不可能有人想不到用彩色的二维码来代替黑白的。

    原因是由于:黑白二维码扫码速度更快,对于手机摄像头来说,要读取信息,就要获取图片中的两种二进制信息(1和0),在前文已经说过,1代表白色,0代表黑色,黑色的色值是#000000,白色的色值是#ffffff,在所有颜色中,只有黑色和白色的色值差是最大的,对手机来说,也就更容易区分了,但是如果我们用其他的两种颜色来代替黑色和白色呢?其实我已经在前文里把彩色二维码给大家展现了,大家可以分别扫一下彩色的二维码和黑白的二维码,应该能明显感觉到黑白的更快。

    扫码时的速度取决于什么?

    这个问题可能是很多做二维码相关业务的人关注的事情了,因素也有多方面的。

    从二维码的角度:

    1. 二维码的平整度(二维码都放在一个壳子里)
    2. 二维码内容的辨识度(能看到的基本都是黑白的)
    3. 存续的信息量大小(以现在手机的处理能力,这个因素影响很小)

    从手机的角度

    1. 不同的APP针对扫码做的优化措施不一样,比如微信就做了很多优化措施。
    2. 摄像头硬件配置

    其中二维码的平整度(二维码都放在一个壳子里)这个因素,大家平常在外面吃东西扫二维码付款时,应该都可以看到二维码是放在一个白色的壳子里,我不知道这是不是各大公司有意识的做这件事,也许他们只是想延长二维码的使用寿命,但这个措施确实做的非常好。

    ok,到这里就差不多了,希望看完这篇文章之后,大家对二维码应该有了一些了解,不会像以前一样,二维码对大家来说只是一张黑白图片。

    Done.

    最后,团队为了招聘方便,整了个公众号,主要是一些招聘信息,团队信息,所有的技术文章在公众号里也可以看到,对了,如果你想去美团其他团队,我们也可以帮你内推哦 ~

    二维码

    参考:

    1. 二维码的生成细节和原理
    展开全文
  • 前言 二维码现在很常见, App中有许多都配置了该功能, 网上也有很多对iOS二维码的讲解, 但是对配置扫码范围这个问题好像都没怎么讲清晰. 作者今天就写一下二维码....科普二维码是什么原理?这个小视频, 简单介绍了...

    前言

    二维码现在很常见, App中有许多都配置了该功能, 网上也有很多对iOS二维码的讲解, 但是对配置扫码范围这个问题好像都没怎么讲清晰. 作者今天就写一下二维码. 

    一 二维码介绍


    二维码

    三个回形大方块, 是为了给相机定位的; 黑白块, 黑块代表1, 白块代表0, 八个一组, 组成二进制信息.  科普:二维码是什么原理? 这个小视频, 简单介绍了下二维码.


    原理图

    二 iOS 扫码二维码

    • 一.工程中General -> Linked Frameworks and Libraries -> 引入 AVFoundation.framework

    • 二. 代码部分解析

    1) 头文件

    //引入头文件
    #import <AVFoundation/AVFoundation.h>
    // 作者自定义的View视图, 继承UIView
    #import "ShadowView.h"
    
    #define kWidth [UIScreen mainScreen].bounds.size.width
    #define kHeight [UIScreen mainScreen].bounds.size.height
    #define customShowSize CGSizeMake(200, 200);

    2) 定义属性

    // ScanCodeViewController是作者创建的VC , 用Navi推出, 写入协议 (UIImagePickerControllerDelegate, UINavigationControllerDelegate 是为了 可以直接扫码图库中的二维码, 在Navi右上角创建button)
    @interface ScanCodeViewController ()<AVCaptureMetadataOutputObjectsDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate>
    
    /** 输入数据源 */
    @property (nonatomic, strong) AVCaptureDeviceInput *input;
    /** 输出数据源 */
    @property (nonatomic, strong) AVCaptureMetadataOutput *output;
    /** 输入输出的中间桥梁 负责把捕获的音视频数据输出到输出设备中 */
    @property (nonatomic, strong) AVCaptureSession *session;
    /** 相机拍摄预览图层 */
    @property (nonatomic, strong) AVCaptureVideoPreviewLayer *layerView;
    /** 预览图层尺寸 */
    @property (nonatomic, assign) CGSize layerViewSize;
    /** 有效扫码范围 */
    @property (nonatomic, assign) CGSize showSize;
    /** 作者自定义的View视图 */
    @property (nonatomic, strong) ShadowView *shadowView;
    
    @end

    3) 创建二维码扫码

    -(void)creatScanQR{
    
    /** 创建输入数据源 */
            AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];  //获取摄像设备
            self.input = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil];  //创建输出流
    
    /** 创建输出数据源 */
            self.output = [[AVCaptureMetadataOutput alloc] init];
            [self.output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];  //设置代理 在主线程里刷新
    
    /** Session设置 */
            self.session = [[AVCaptureSession alloc] init];
            [self.session setSessionPreset:AVCaptureSessionPresetHigh];   //高质量采集
            [self.session addInput:self.input];
            [self.session addOutput:self.output];
            //设置扫码支持的编码格式
            self.output.metadataObjectTypes = @[AVMetadataObjectTypeQRCode,
                                            AVMetadataObjectTypeEAN13Code,
                                            AVMetadataObjectTypeEAN8Code,
                                            AVMetadataObjectTypeCode128Code];
    /** 扫码视图 */
             //扫描框的位置和大小
            self.layerView = [AVCaptureVideoPreviewLayer layerWithSession:self.session];
            self.layerView.videoGravity = AVLayerVideoGravityResizeAspectFill;
            self.layerView.frame = CGRectMake(0, 64, self.view.frame.size.width, self.view.frame.size.height - 64);
            // 将扫描框大小定义为属行, 下面会有调用
            self.layerViewSize = CGSizeMake(_layerView.frame.size.width, _layerView.frame.size.height);
    
    }
    
    #pragma mark - 实现代理方法, 完成二维码扫描
    -(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection{
    
        if (metadataObjects.count > 0) {
    
             // 停止动画, 看完全篇记得打开注释, 不然扫描条会一直有动画效果
            //[self.shadowView stopTimer];
    
            //停止扫描
            [self.session stopRunning];
    
            AVMetadataMachineReadableCodeObject * metadataObject = [metadataObjects objectAtIndex : 0 ];
            //输出扫描字符串
            NSLog(@"%@",metadataObject.stringValue);
            UIAlertView * alert = [[UIAlertView alloc]initWithTitle:@"提示" message:[NSString stringWithFormat:@"%@", metadataObject.stringValue] delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil];
            [alert show];
        }
    }

    4) 调用方法

    - (void)viewDidLoad {
        [super viewDidLoad];
    
        self.view.backgroundColor = [UIColor whiteColor];
    
        //调用
        [self creatScanQR];
        //添加拍摄图层
        [self.view.layer addSublayer:self.layerView];
        //开始二维码
        [self.session startRunning];
    
        // Do any additional setup after loading the view.
    }

    至此 二维码扫码完成 , 你会发现 整个屏幕都可以扫码二维码, 与微信的二维码扫码 差的太多. 下面开始个性化设置

    5) 自定义阴影视图层

    #import <UIKit/UIKit.h>
    
    @interface ShadowView : UIView
    
    @property (nonatomic, assign) CGSize showSize;
    - (void)stopTimer;
    
    @end
    #import "ShadowView.h"
    
    @interface ShadowView ()
    
    @property (nonatomic, strong) UIImageView *lineView;
    @property (nonatomic, strong) NSTimer *timer;
    
    @end
    
    @implementation ShadowView
    
    - (instancetype)initWithFrame:(CGRect)frame
    {
        self = [super initWithFrame:frame];
        if (self) {
    
            self.backgroundColor = [UIColor clearColor];
            // 图片下方附上
            self.lineView  = [[UIImageView alloc] init];
            self.lineView.image = [UIImage imageNamed:@"line"];
            [self addSubview:self.lineView];
    
        }
        return self;
    }
    
    -(void)playAnimation{
    
        [UIView animateWithDuration:2.4 delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
    
            self.lineView .frame = CGRectMake((self.frame.size.width - self.showSize.width) / 2, (self.frame.size.height + self.showSize.height) / 2, self.showSize.width, 2);
    
        } completion:^(BOOL finished) {
            self.lineView .frame = CGRectMake((self.frame.size.width - self.showSize.width) / 2, (self.frame.size.height - self.showSize.height) / 2, self.showSize.width, 2);
        }];
    }
    
    - (void)stopTimer
    {
        [_timer invalidate];
        _timer = nil;    
    }
    
    -(void)layoutSubviews{
    
        [super layoutSubviews];
    
        self.lineView .frame = CGRectMake((self.frame.size.width - self.showSize.width) / 2, (self.frame.size.height - self.showSize.height) / 2, self.showSize.width, 2);
    
    
        if (!_timer) {
    
            [self playAnimation];
    
            /* 自动播放 */
            self.timer = [NSTimer scheduledTimerWithTimeInterval:2.5 target:self selector:@selector(playAnimation) userInfo:nil repeats:YES];
    
        }
    
    }
    
    -(void)drawRect:(CGRect)rect{
    
        [super drawRect:rect];
    
        CGContextRef ctx = UIGraphicsGetCurrentContext();
    
        // 整体颜色
        CGContextSetRGBFillColor(ctx, 0.15, 0.15, 0.15, 0.6);
        CGContextFillRect(ctx, rect);   //draw the transparent layer
    
        //中间清空矩形框
        CGRect clearDrawRect = CGRectMake((rect.size.width - self.showSize.width) / 2, (rect.size.height - self.showSize.height) / 2, self.showSize.width, self.showSize.height);
        CGContextClearRect(ctx, clearDrawRect);
    
        //边框
        CGContextStrokeRect(ctx, clearDrawRect);
        CGContextSetRGBStrokeColor(ctx, 1, 1, 1, 1);  //颜色
        CGContextSetLineWidth(ctx, 0.5);             //线宽
        CGContextAddRect(ctx, clearDrawRect);       //矩形
        CGContextStrokePath(ctx);
    
        [self addCornerLineWithContext:ctx rect:clearDrawRect];
    
    }
    
    - (void)addCornerLineWithContext:(CGContextRef)ctx rect:(CGRect)rect{
    
        float cornerWidth = 4.0;
    
        float cornerLong = 16.0;
    
        //画四个边角 线宽
        CGContextSetLineWidth(ctx, cornerWidth);
    
        //颜色
        CGContextSetRGBStrokeColor(ctx, 83 /255.0, 239/255.0, 111/255.0, 1);//绿色
    
        //左上角
        CGPoint poinsTopLeftA[] = {CGPointMake(rect.origin.x + cornerWidth/2, rect.origin.y),
                                   CGPointMake(rect.origin.x + cornerWidth/2, rect.origin.y + cornerLong)};
    
        CGPoint poinsTopLeftB[] = {CGPointMake(rect.origin.x, rect.origin.y + cornerWidth/2),
                                   CGPointMake(rect.origin.x + cornerLong, rect.origin.y + cornerWidth/2)};
    
        [self addLine:poinsTopLeftA pointB:poinsTopLeftB ctx:ctx];
    
    
        //左下角
        CGPoint poinsBottomLeftA[] = {CGPointMake(rect.origin.x + cornerWidth/2, rect.origin.y + rect.size.height - cornerLong),
                                      CGPointMake(rect.origin.x + cornerWidth/2, rect.origin.y + rect.size.height)};
    
        CGPoint poinsBottomLeftB[] = {CGPointMake(rect.origin.x, rect.origin.y + rect.size.height - cornerWidth/2),
                                      CGPointMake(rect.origin.x + cornerLong, rect.origin.y + rect.size.height - cornerWidth/2)};
    
        [self addLine:poinsBottomLeftA pointB:poinsBottomLeftB ctx:ctx];
    
    
        //右上角
        CGPoint poinsTopRightA[] = {CGPointMake(rect.origin.x+ rect.size.width - cornerLong, rect.origin.y + cornerWidth/2),
                                    CGPointMake(rect.origin.x + rect.size.width, rect.origin.y + cornerWidth/2 )};
    
        CGPoint poinsTopRightB[] = {CGPointMake(rect.origin.x+ rect.size.width - cornerWidth/2, rect.origin.y),
                                    CGPointMake(rect.origin.x + rect.size.width- cornerWidth/2, rect.origin.y + cornerLong)};
    
        [self addLine:poinsTopRightA pointB:poinsTopRightB ctx:ctx];
    
        //右下角
        CGPoint poinsBottomRightA[] = {CGPointMake(rect.origin.x+ rect.size.width - cornerWidth/2, rect.origin.y+rect.size.height - cornerLong),
                                       CGPointMake(rect.origin.x- cornerWidth/2 + rect.size.width, rect.origin.y +rect.size.height )};
    
        CGPoint poinsBottomRightB[] = {CGPointMake(rect.origin.x+ rect.size.width - cornerLong, rect.origin.y + rect.size.height - cornerWidth/2),
                                       CGPointMake(rect.origin.x + rect.size.width, rect.origin.y + rect.size.height - cornerWidth/2 )};
    
        [self addLine:poinsBottomRightA pointB:poinsBottomRightB ctx:ctx];
    
    
        CGContextStrokePath(ctx);
    }
    
    - (void)addLine:(CGPoint[])pointA pointB:(CGPoint[])pointB ctx:(CGContextRef)ctx {
    
        CGContextAddLines(ctx, pointA, 2);
        CGContextAddLines(ctx, pointB, 2);
    }
    
    
    @end

    line.png

    注意 : 如果你对应写demo了, 并且真机测试的系统在ios7 以上, 会发现也许扫描线的初始位置会出现问题. 给个小提示 layoutSubviews 被执行了两次. 留个小尾巴. 让大家思考.

    传送门 : 上述边角画图方法, 作者引用 Raul7777 并附上链接 , 作者进行了一下小改进 , 并添加注释. 如果对你有帮助, 请给他点个 Star

    补充 : 扫描线动画, 用NSTimer, 偶尔手机处理卡顿下, 就会被坑. 作者看到有用 CABasicAnimation 写的, 觉得挺好. 所以 提供下, 该方法

    -(void)addAnimationAboutScan{
    
        self.lineView.hidden = NO;
        CABasicAnimation *animation = [ShadowView moveYTime:2.5 fromY:[NSNumber numberWithFloat:0] toY:[NSNumber numberWithFloat:(self.showSize.height-1)] rep:OPEN_MAX];
        [self.lineView.layer addAnimation:animation forKey:@"LineAnimation"];       
    }
    
    + (CABasicAnimation *)moveYTime:(float)time fromY:(NSNumber *)fromY toY:(NSNumber *)toY rep:(int)rep{
    
        CABasicAnimation *animationMove = [CABasicAnimation animationWithKeyPath:@"transform.translation.y"];
        [animationMove setFromValue:fromY];
        [animationMove setToValue:toY];
        animationMove.duration = time;
        animationMove.delegate = self;
        animationMove.repeatCount  = rep;
        animationMove.fillMode = kCAFillModeForwards;
        animationMove.removedOnCompletion = NO;
        animationMove.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
        return animationMove;
    }
    
    - (void)removeAnimationAboutScan{
    
        [self.lineView.layer removeAnimationForKey:@"LineAnimation"];
        self.lineView.hidden = YES;
    }

    6) 回到ScanCodeViewController 配置扫码范围

    重点: 这是作者最想讲的 . 附上官方介绍
    rectOfInterest  Property
    A rectangle of interest for limiting the search area for visual metadata.
    Discussion
    The value of this property is a CGRect value that determines the object’s rectangle of interest for each frame of video.
    The rectangle's origin is top left and is relative to the coordinate space of the device providing the metadata.
    Specifying a rectangle of interest may improve detection performance for certain types of metadata. Metadata objects whose bounds do not intersect with the rectOfInterest will not be returned.
    The default value of this property is a rectangle of (0.0, 0.0, 1.0, 1.0).

    说明 :
    看到矩形的原点是左上角, 但是真正测试 你会发现却是在右上角, 因为扫码默认是 横屏, 所以原右上角变成左上角, 原宽变成高, 原高变成宽. 取值是按照 摄像头分辨率 来取的比例 而不是屏幕的宽高比例.

    屏幕宽高比例:
    iPhone4 : [320 480] ; iPhone5: [320 568 ] ; iPhone 6 : [375 667] ; iPhone 6plus : [414 736]. 

    作者设置 AVCaptureSessionPresetHigh 所以机型分辨率均为 1920×1080. 所以除了iPhone4 基本上 屏幕宽高比 符合 分辨率的比例. 会有些许误差, 但影响不大. 如需支持包含iPhone4的所以机型 需要将 屏幕宽高与分辨率统一. 方法如下. 这样便将 ShadowView 中间清空的矩形框 与 有效扫码范围 对应上了.

    /** 配置扫码范围 */
    -(void)allowScanRect{
    
    
        /** 扫描是默认是横屏, 原点在[右上角]
         *  rectOfInterest = CGRectMake(0, 0, 1, 1);
         *  AVCaptureSessionPresetHigh = 1920×1080   摄像头分辨率
         *  需要转换坐标 将屏幕与 分辨率统一
         */
    
        //剪切出需要的大小位置
        CGRect shearRect = CGRectMake((self.layerViewSize.width - self.showSize.width) / 2,
                                      (self.layerViewSize.height - self.showSize.height) / 2,
                                      self.showSize.height,
                                      self.showSize.height);
    
    
        CGFloat deviceProportion = 1920.0 / 1080.0;
        CGFloat screenProportion = self.layerViewSize.height / self.layerViewSize.width;
    
        //分辨率比> 屏幕比 ( 相当于屏幕的高不够)
        if (deviceProportion > screenProportion) {
            //换算出 分辨率比 对应的 屏幕高
            CGFloat finalHeight = self.layerViewSize.width * deviceProportion;
            // 得到 偏差值
            CGFloat addNum = (finalHeight - self.layerViewSize.height) / 2;
    
                                                  // (对应的实际位置 + 偏差值)  /  换算后的屏幕高
            self.output.rectOfInterest = CGRectMake((shearRect.origin.y + addNum) / finalHeight,
                                                     shearRect.origin.x / self.layerViewSize.width,
                                                     shearRect.size.height/ finalHeight,
                                                     shearRect.size.width/ self.layerViewSize.width);
    
        }else{
    
            CGFloat finalWidth = self.layerViewSize.height / deviceProportion;
    
            CGFloat addNum = (finalWidth - self.layerViewSize.width) / 2;
    
            self.output.rectOfInterest = CGRectMake(shearRect.origin.y / self.layerViewSize.height,
                                                    (shearRect.origin.x + addNum) / finalWidth,
                                                    shearRect.size.height / self.layerViewSize.height,
                                                    shearRect.size.width / finalWidth);
        }
    
    }

    7) 读取相册中二维码

    注意事项 : ios8 以上系统才开放读取相册二维码功能(CIDetectorTypeQRCode), 所以如iPhone4 该功能实现不了, 需要有判断.

    #pragma mark - 相册中读取二维码
    /* navi按钮实现 */
    -(void)takeQRCodeFromPic:(UIBarButtonItem *)leftBar{
    
    
    
        if ([[[UIDevice currentDevice] systemVersion] doubleValue] < 8) {
    
            UIAlertView * alert = [[UIAlertView alloc]initWithTitle:@"提示" message:@"请更新系统至8.0以上!" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil];
            [alert show];
    
        }else{
    
            if([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]){
    
                UIImagePickerController *pickerC = [[UIImagePickerController alloc] init];
                pickerC.delegate = self;
    
                pickerC.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;  //来自相册
    
                [self presentViewController:pickerC animated:YES completion:NULL];
    
            }else{
    
                UIAlertView * alert = [[UIAlertView alloc]initWithTitle:@"提示" message:@"设备不支持访问相册,请在设置->隐私->照片中进行设置!" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil];
                [alert show];
            }
    
        }
    
    
    }
    
    - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
    {
        //1.获取选择的图片
        UIImage *image = info[UIImagePickerControllerEditedImage];
    
        if (!image) {
            image = info[UIImagePickerControllerOriginalImage];
        }
        //2.初始化一个监测器
        CIDetector*detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:nil options:@{ CIDetectorAccuracy : CIDetectorAccuracyHigh }];
    
        [picker dismissViewControllerAnimated:YES completion:^{
    
            //监测到的结果数组  放置识别完之后的数据
            NSArray *features = [detector featuresInImage:[CIImage imageWithCGImage:image.CGImage]];
            //判断是否有数据(即是否是二维码)
            if (features.count >=1) {
                /**结果对象 */
                CIQRCodeFeature *feature = [features objectAtIndex:0];
                NSString *scannedResult = feature.messageString;
    
                UIAlertView * alertView = [[UIAlertView alloc]initWithTitle:@"提示" message:scannedResult delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil];
                [alertView show];
    
            }
            else{
                UIAlertView * alertView = [[UIAlertView alloc]initWithTitle:@"提示" message:@"该图片没有包含二维码!" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil];
                [alertView show];
    
            }
        }];
    }

    8) 重写调用

    - (void)viewDidLoad {
        [super viewDidLoad];
    
        self.view.backgroundColor = [UIColor whiteColor];
    
        //显示范围
         self.showSize = customShowSize;
        //调用
        [self creatScanQR];
        //添加拍摄图层
        [self.view.layer addSublayer:self.layerView];
        //开始二维码
        [self.session startRunning];
        //设置可用扫码范围
        [self allowScanRect];
    
       //添加上层阴影视图
        self.shadowView = [[ShadowView alloc] initWithFrame:CGRectMake(0, 64, kWidth, kHeight - 64)];
        [self.view addSubview:self.shadowView];
        self.shadowView.showSize = self.showSize;
    
    
        //添加扫码相册按钮
         self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"相册中选" style:UIBarButtonItemStylePlain target:self action:@selector(takeQRCodeFromPic:)];
    
        // Do any additional setup after loading the view.
    }

    8) 权限问题

    权限问题 使用相机需要获取相应权限, 如用户未开启, 可以设置提醒, 留个小尾巴, 读者自行设置.

    三 iOS 二维码生成

    天听云道的 [ iOS ] 生成二维码 系统方法 这篇文章写了, 挺简单, 生成的是高清二维码, 但是 在ios7系统上, 还是不清晰, 也就是说如果 你的程序支持到ios7 该方法有些问题.

    船长_的 iOS的生成二维码(彩色+阴影) 这篇文章写了, 支持ios7 系统, 高清二维码. 

    所以对二维码生成问题 作者就不赘述了. 对iOS中二维码的讲解完成. 

    四 三方二维码链接

    补充下三方传送门 : https://github.com/MxABC/LBXScan
    https://github.com/TheLevelUp/ZXingObjC

    文章转载自 http://www.jianshu.com/p/2180e940a589

    以上 !

    demo地址 :https://github.com/OC888/scanQRcode.git

    转载于:https://www.cnblogs.com/OC888/p/6652274.html

    展开全文
  • 很多想要开发小程序的新手会误以为,开发小程序就需要下载安装... 以上就是关于微信小程序在线制作的科普了,对于不懂技术的小白来说,用模板在线制作小程序是非常简单的,而且后续还能随时修改,基本没什么制作难度。

    很多想要开发小程序的新手会误以为,开发小程序就需要下载安装开发者工具,然后自己写代码开发。其实不是这样,不懂代码你也可以开发小程序,而且无需下载安装任何工具,直接在线就能制作。如何实现呢?

    在线小程序制作会需要用到小程序在线制作平台。通常这种平台会提供多套做好的小程序模板,新手只需选一个自己需要的模板,再添加自己需要的功能版块、商品、图片即可,这样制作小程序的方法比较方便,而且不需要你懂任何技术,因此近年来很受小白欢迎。

    bd960c75e72e73e35d4a24cacc1d2e5d.png

    本展示页所提供的模板及元素仅供展示功能效果,未经授权不得应用于其他用途

    这种小程序在线制作平台都有什么优势呢?主要在于这4个方面:

    (1)简单。毕竟是基于现场的模板开发,不需要做任何代码上的操作,对小白非常友好。以「上线了」小程序制作平台为例,开发过程主要由选择模板、可视化修改模板、发布上线3步构成。其中,难度相对较大的就是修改模板这一步,但也就是添加功能版块、上传商品、设置小程序信息等基础操作,会打字就能做好,都是可视化操作,如下图所示。

    70432d9bbee5e7e98b5a3b1e21ac6ecd.png

    (2)省时。在线制作小程序时非常快的,东西不多的话几分钟就能搭建好一个小程序页面,而如果自己写代码制作的话,可能大半天都做不好一个顺畅无BUG的页面。

    (3)美观度有保证。以「上线了」为例,小程序模板都是经过专业设计师设计好的,比较符合现在的审美,而如果你自己制作的话,就未必有这样好的效果了。

    862acd239def97756e0d7b577b691fb5.png

    上线了小程序案例,禁止转载

    (4)售后方便。通常知名的小程序平台都有比价完善的售后系统,以便给客户及时解答问题,提供制作指导,让你在制作小程序时无后顾之忧。

    当然模板在线制作也有缺陷,就是个性化施展空间有限,你只能做基于模板给出的功能,另外无法有代码归属权。不过总体来说,这还是非常适合新手的一种小程序制作方法了。

    以上就是关于微信小程序在线制作的科普了,对于不懂技术的小白来说,用模板在线制作小程序是非常简单的,而且后续还能随时修改,基本没什么制作难度。

    展开全文
  • 以认证服务号为例,后台支持下载的二维码,并没有详细的数据统计功能。 如果你在AB二个地方都了一场活动,想知道哪个地方的效果最好(带来的粉丝最多)?...一、渠道二维码规则科普 ① 渠道的...
  • 科普网址: H5如何生成安卓组件对象 H5调用安卓本地摄像头api 在线二维码图片生成器 二维码扫描:(使用的是mui的框架,下面是html代码) <!doctype html> <html> <head> <meta charset="UTF-8">...
  • 今天主管给了我个需求,说要用混合开发,用H5调用本地摄像头进行扫描二维码,我之前有做过原生安卓的二维码扫一扫,主要是...科普网址: H5如何生成安卓组件对象 H5调用安卓本地摄像头api 在线二维码图片生成器二维码
  • 科普一下,微信开放平台提供了两种二维码的展示方式,一种是跳转页面,还有一种是内嵌在自己的网站中,可以直接在网站内就能完成登录,无需跳转到微信域下登录后再返回,提升微信登录的流畅性与成功率,详细可看...
  • 给不知道二维码存储信息原理的同学科普一下: 制作原理:二维码其实bai就是由很多0、1组成的du数字矩阵。二zhi维条码/二维码是用某种特定的dao几何图形按一定规律在平面分布的黑白相间的图形记录数据符号信息的。 ...
  • 下面九州集团小编和大家普科普一些关于刷脸支付的知识。你知道刷脸支付和二维码智慧之间的距离吗?刷脸支付的安全性如何? 刷脸支付操作其实很简单,初次体验刷脸支付,在刷脸支付设备上输入手机号后四位,即可刷脸...
  • 5G小科普(漫画版)

    千次阅读 2019-08-13 08:42:00
    本文内容来源于:昨天的发文《一文带你秒懂5G黑科技》漫画作者:易小宝微博:小宝漫画杂记想知道更多?扫描下面的二维码关注我相关推荐:《科普 | 明星公司之Netflix》《...
  • 面对区块链的众多安全问题,慢雾特推出区块链安全入门笔记系列,向大家介绍十篇区块链安全相关名词,让新手们更快适应区块链危机四伏的安全攻防世界,同时欢迎添加文章末尾二维码催更! 共识Consensus 共识算法...
  • 面对区块链的众多安全问题,慢雾特推出区块链安全入门笔记系列,向大家介绍十篇区块链安全相关名词,让新手们更快适应区块链危机四伏的安全攻防世界,同时欢迎添加文章末尾二维码催更! 多签 (Multi-sig) 多签...
  • 什么是区块链?一分钟科普小动画给你解惑。想了解更多区块链入门小动画,欢迎长按二维码,关注白话区块链创始人马丹的视频号@猫姐Joyce。以下为文字稿内容:有一个与世隔绝的小村落,村民们经常...
  • 比特币是骗局吗?一分钟科普小动画给你解惑。想了解更多区块链入门小动画,欢迎长按二维码,关注白话区块链创始人马丹的视频号@猫姐Joyce。丨更多区块链入门内容推荐矿池和矿场有何区别?比特币...
  • 区块链企业求贤若渴,百万年薪依然人才难觅。高考落幕,来看看这份来自区块链行业的高考志愿填报指南吧。一分钟科普小动画给你解惑。想了解更多区块链入门小动画,欢迎长按二维码,关注白话区块链...
  • 麻将是最早的区块链?会打麻将你就明白什么是区块链了。一分钟科普小动画给你解惑。想了解更多区块链入门小动画,欢迎长按二维码,关注白话区块链创始人马丹的视频号@猫姐Joyce。丨更多区块链入...
  • 区块链为啥防伪,防篡改?一分钟科普小动画给你解惑。想了解更多区块链入门小动画,欢迎长按二维码,关注白话区块链创始人马丹的视频号@猫姐Joyce。丨更多区块链入门内容推荐被称为“区块链2...
  • 比特币为什么那么值钱?一分钟科普小动画给你解惑。想了解更多区块链入门小动画,欢迎长按二维码,关注我们的视频号@白话区块链丨更多区块链入门内容推荐号称“区块链灵魂”的共识机制,要发生巨大演...

空空如也

空空如也

1 2 3 4 5 ... 8
收藏数 144
精华内容 57
关键字:

二维码科普