iOS开发实用技术之二维码

一. 二维码生成和扫描

1. 二维码概念

    二维码, 是用某种特定的几何图形按一定规律在平面(二维方向上)分布的黑白相间的图形记录数据符号信息的;
    

2. 二维码的使用场景

    ● 信息获取(名片、地图、WIFI密码、资料)
    ● 网站跳转(跳转到微博、手机网站、网站)
    ● 广告推送(用户扫码,直接浏览商家推送的视频、音频广告)
    ● 手机电商(用户扫码、手机直接购物下单)
    ● 防伪溯源(用户扫码、即可查看生产地;同时后台可以获取最终消费地)
    ● 优惠促销(用户扫码,下载电子优惠券,抽奖)
    ● 会员管理(用户手机上获取电子会员信息、VIP服务)
    ● 手机支付(扫描商品二维码,通过银行或第三方支付提供的手机端通道完成支付)
    

3. 二维码生成方式

    > 从iOS7开始集成了二维码的生成和读取功能
    > 此前被广泛使用的zbarsdk目前不支持64位处理器

4. 生成二维码步骤

  1. 导入CoreImage框架

     #import <CoreImage/CoreImage.h>
  2. 通过滤镜CIFilter生成二维码

     > 1. 实例化二维码滤镜
         CIFilter *filter = [CIFilter filterWithName:@"CIQRCodeGenerator"];
    
     > 2. 恢复滤镜的默认属性
         [filter setDefaults];
    
     > 3. 将字符串转换成NSData
         NSData *data = [@"小码哥" dataUsingEncoding:NSUTF8StringEncoding];
    
     > 4. 通过KVC设置滤镜inputMessage数据
         [filter setValue:data forKey:@"inputMessage"];
    
     > 5. 获得滤镜输出的图像
         CIImage *outputImage = [filter outputImage];
    
     > 6. 将CIImage转换成UIImage,并放大显示
         return [UIImage imageWithCIImage:outputImage scale:20.0 orientation:UIImageOrientationUp];

5. 读取二维码

读取二维码需要导入AVFoundation框架

利用摄像头识别二维码中的内容(模拟器不行)

  1. 实例化拍摄设备

     AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
  2. 设置输入设备

     AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil];
  3. 设置元数据输出

     3.1 实例化拍摄元数据输出
         AVCaptureMetadataOutput *output = [[AVCaptureMetadataOutput alloc] init];
    
     3.2 设置输出数据代理
         [output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
    
     3.3 设置扫描取景范围(rectOfInterest 都是按照横屏来计算的 所以当竖屏的情况下 x轴和y轴要交换一下)
     CGFloat screenW = [UIScreen mainScreen].bounds.size.width;
     CGFloat screenH = [UIScreen mainScreen].bounds.size.height;
     CGFloat scanW = screenW * 0.6;
     CGRect scanRect = CGRectMake((screenW - scanW) * 0.5, (screenH - scanW) * 0.5, scanW, scanW);
     output.rectOfInterest = CGRectMake(scanRect.origin.y / screenH, scanRect.origin.x / screenW, scanRect.size.height / screenH, scanRect.size.width / screenW);
    
     3.4 设置扫描区域的边框
     UIView *scanV = [[UIView alloc] initWithFrame:scanRect];
     [self.view addSubview:scanV];
     scanV.layer.borderWidth = 2;
     scanV.layer.borderColor = [UIColor redColor].CGColor;
  4. 添加拍摄会话

     4.1 实例化拍摄会话
         AVCaptureSession *session = [[AVCaptureSession alloc] init];
    
     4.2 添加会话输入
         [session addInput:input];
    
     4.3 添加会话输出
         [session addOutput:output];
    
     4.3 设置输出数据类型,需要将元数据输出添加到会话后,才能指定元数据类型,否则会报错
         [output setMetadataObjectTypes:@[AVMetadataObjectTypeQRCode]];
  5. 视频预览图层

     5.1 实例化预览图层
         AVCaptureVideoPreviewLayer *preview = [AVCaptureVideoPreviewLayer layerWithSession:_session];
         preview.videoGravity = AVLayerVideoGravityResizeAspectFill;
         preview.frame = self.view.bounds;
    
     5.2 将图层插入当前视图
         [self.view.layer addSublayer:preview];
         self.previewLayer = preview;
  6. 启动会话

     [_session startRunning];

二. 内存分析

主要目的就是为了检测程序是否存在内存泄露

1. 静态内存分析(Analyze)

作用: 
    > 逻辑错误:访问未初始化的变量, 野指针等;
    > 声明错误:从未使用过的对象;
    > 内存管理错误:如内存泄漏等;
分析方法:
    > 静态内存分析是不运行程序,直接对代码进行分析.
    > 根据代码的上下文的语法结构,来分析是否有内存泄露
缺点:
    > 不一定准确,但是如果发现有提示,那么去结合上下文看一下,这里的代码是否有问题

场景演练:
    > MRC 下桥接 - Foundation 和 CoreFoundation框架的数据类型转换
    > ARC 下桥接 - Foundation 和 CoreFoundation框架的数据类型转换

2. 内存分配

作用:
    > 查看是内存的分配情况
    > 查看内存是否有释放
场景演示:
    > UIImage 的两种创建方法测试
    > imageNamed:
    > imageWithContentOfFile:
    

3. 动态内存分析

作用:
    > 检测程序在运行过程中是否存在内存泄露
场景演示:
    > 模拟循环引用, 测试内存泄露

三. 通讯录获取

1. 通讯录应用场景

    1. 最常见的是一些即时通讯APP, 关联联系人;
    

2. 通讯录获取方案

  1. AddressBookUI.framework 框架

         > 提供了联系人列表界面、联系人详情界面、添加联系人界面等
         > 一般用于选择联系人
  2. AddressBook.framework 框架

         > 纯C语言的API,仅仅是获得联系人数据
         > 没有提供UI界面展示,需要自己搭建联系人展示界面
         > 里面的数据类型大部分基于Core Foundation框架,使用起来极其蛋疼
  3. ContactsUI.framework (iOS9.0最新框架)

         > 方案1/方案2 的替代品
  4. 第三方框架 RHAddressBook

         > 对AddressBook.framework框架的封装       

3. 获取通讯录-AddressBookUI

1. 实现步骤
  1. 创建选择联系人的控制器
  2. 设置代理(用来接收用户选择的联系人信息)
  3. 弹出联系人控制器
  4. 实现代理
  5. 在对应的代理方法中获取联系人信息
2. 具体代码实现
  1. 创建选择联系人的控制器

     ABPeoplePickerNavigationController *ppnc = [[ABPeoplePickerNavigationController alloc] init];
  2. 设置代理(用来接收用户选择的联系人信息)

     ppnc.peoplePickerDelegate = self;
  3. 弹出联系人控制器

     [self presentViewController:ppnc animated:YES completion:nil];
  4. 实现代理

     // 1. 选中某个联系人时调用
     - (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person
     {
    
     }
    
     // 2. 选中某个联系人某个属性时调用
     - (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier
     {
    
     }
    
     // 3. 点击了取消按钮会执行该方法
     - (void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker
     {
    
     }
  5. 在对应的代理方法中获取联系人信息

     // 1. 获取选中联系人的姓名(姓lastname和名firstname)
     CFStringRef firstname = ABRecordCopyValue(person, kABPersonFirstNameProperty);
     CFStringRef lastname = ABRecordCopyValue(person, kABPersonLastNameProperty);
     NSString *firstName = (__bridge_transfer NSString *)(firstname);
     NSString *lastName = (__bridge_transfer NSString *)(lastname);
     NSLog(@"%@ %@", firstName, lastName);
    
     // 2. 获取联系人的电话号码
     ABMultiValueRef phones = ABRecordCopyValue(person, kABPersonPhoneProperty);
     CFIndex count = ABMultiValueGetCount(phones);
     for (CFIndex i = 0; i < count; i++) {
         NSString *phoneLabel = (__bridge_transfer NSString *)ABMultiValueCopyLabelAtIndex(phones, i);
         NSString *phoneValue = (__bridge_transfer NSString *)ABMultiValueCopyValueAtIndex(phones, i);
         NSLog(@"label : %@ value : %@", phoneLabel, phoneValue);
     }
    
     // 3. 释放不再使用的对象
     CFRelease(phones);

4. 获取通讯录-AddressBook

1. 实现步骤
  1. 请求授权
  2. 判断授权状态, 如果已授权, 则继续; 未授权, 则提示用户, 并返回;
  3. 创建通讯录对象
  4. 从通信录对象中, 获取所有的联系人
  5. 遍历所有的联系人
  6. 释放不再使用的对象
2. 代码实现
  1. 请求授权

     // 1.获取授权的状态
     ABAuthorizationStatus status = ABAddressBookGetAuthorizationStatus();
    
     // 2.判断授权状态,如果是未决定状态,才需要请求
     if (status == kABAuthorizationStatusNotDetermined) {
         // 2.1.创建通信录对象
         ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, NULL);
    
         // 2.2.请求授权
         ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {
         if (granted) {
             NSLog(@"授权成功");
         } else {
             NSLog(@"授权失败");
         }
     });
     }
  2. 判断授权状态, 如果已授权, 则继续; 未授权, 则提示用户, 并返回;

     // 1.获取授权的状态
     ABAuthorizationStatus status = ABAddressBookGetAuthorizationStatus();
    
     // 2.如果用户已经授权
     if (status != kABAuthorizationStatusAuthorized) return;
  3. 创建通讯录对象

     ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, NULL);
  4. 从通信录对象中, 获取所有的联系人

     CFArrayRef peopleArray = ABAddressBookCopyArrayOfAllPeople(addressBook);
  5. 遍历所有的联系人

     // 5.遍历所有的联系人(每一个联系人都是一条记录)
     CFIndex peopleCount = CFArrayGetCount(peopleArray);
     for (CFIndex i = 0; i < peopleCount; i++) {
    
     // 6.获取到联系人
     ABRecordRef person = CFArrayGetValueAtIndex(peopleArray, i);
    
     // 7.获取姓名
     NSString *lastname = (__bridge_transfer NSString *)ABRecordCopyValue(person, kABPersonLastNameProperty);
     NSString *firstName = (__bridge_transfer NSString *)ABRecordCopyValue(person, kABPersonFirstNameProperty);
     NSLog(@"%@ %@", lastname, firstName);
     }
  6. 释放不再使用的对象

     CFRelease(peopleArray);
     CFRelease(addressBook);

5. 获取通讯录-第三方框架RHAddressBook

1. 实现步骤
  1. 集成框架
  2. 使用框架获取所有联系人信息
2. 具体实现
  1. 集成框架

     > 1. 将整个工程拖入项目
     > 2. 添加工程依赖
         Build Phases -> Target Dependencies -> + 
     > 3. 添加链接项
         Build Settings -> Other Linker Flags -> -ObjC -all_load
     > 4. 导入框架头文件
         #import <RHAddressBook/AddressBoook.h>
  2. 使用框架获取所有联系人信息

     > 请求授权    
         // 1. 获取授权状态
         RHAuthorizationStatus status = [RHAddressBook authorizationStatus];
    
         if (status == RHAuthorizationStatusNotDetermined)
         {
             // 2. 创建通讯录对象
             RHAddressBook *addressBook = [[RHAddressBook alloc] init];
    
             // 3. 请求授权
             [addressBook requestAuthorizationWithCompletion:^(bool granted, NSError *error) {
             if (granted)
             {
                 NSLog(@"授权成功!");
             }
             else
             {
                 NSLog(@"授权失败");
             }
             }];
         }
    
     > 获取联系人信息
    
         // 1. 判断当前授权状态
         RHAuthorizationStatus status = [RHAddressBook authorizationStatus];
         if (status != RHAuthorizationStatusAuthorized) {
             return;
         }
    
         // 2. 创建通讯录对象
         RHAddressBook *addressBook = [[RHAddressBook alloc] init];
    
         // 3. 获取所有联系人
         NSArray *peoples = addressBook.people;
    
         // 4. 遍历所有联系人
         for (RHPerson *person in peoples)
         {
             // 4.1 获取联系人姓名
             NSString *firstName = person.firstName;
             NSString *lastName = person.lastName;
             NSLog(@"%@---%@", firstName, lastName);
    
             // 4.2 获取联系人电话
             RHMultiStringValue *mv = person.phoneNumbers;
             for (int i = 0; i < mv.count; i ++)
             {
                 // 4.2.1 获取电话标签
                 NSString *label = [mv labelAtIndex:i];
    
                 // 4.2.2 获取电话号码
                 NSString *phone = [mv valueAtIndex:i];
                 NSLog(@"%@--%@", label, phone);
    
             }
         }

四. 换肤

1. 换肤的应用场景?

一般应用在某些APP , 在节假日更换主题, 或者切换白天或者夜间模式时使用.

2. 换肤的实现方案

1. 方案1
1. 将图片以及颜色按照主题命名,使用虚拟文件夹, 直接在各个控制器针对不同主题单独设置
    缺点 : 
        1. 扩展性差, 增加一个主题非常复杂
        2. 代码分散, 可维护性差
        3. 使用虚拟文件夹保存资源, 容易与其他资源容易产生冲突
2. 方案2
2. 抽取公共的切换主题类, 使用物理文件夹, 并将图片按照应用场景命名, 不按照主题命名, 但是按照主题划分文件夹
优点:
    1. 扩展性好, 增加主题简单
    2. 功能代码集中, 方便维护
    3. 资源包单独管理, 不会产生资源冲突
演练步骤
1. 实现基本的换肤功能
2. 使用用户偏好记录当前皮肤主题
3. 抽取公共的皮肤管理类, 简化控制器逻辑
4. 验证多处加载主题情景
5. 加载主题文字颜色功能实现

五. 硬件信息的获取

1. 应用场景

例如
    QQ空间APP 发说说时, 出现的什么什么型号的手机;
    迅雷APP 下载文件时提示剩余空间,已用空间

2. 实现方案

直接通过第三方工具类(UIDevice的分类), 进行获取对应信息
原因: 自己写起来比较复杂, 很多C语言的东西, 而且没有必要;

3. 框架完善

框架存在问题: 该第三方框架从2012年就停止更新了,意味着12年之后的手机型号都没有, 需要手动添加, 修改框架
解决方案: 找到对应的实现方法, 使用真机进行测试, 手动新增手机型号

转载于:https://www.cnblogs.com/Xfsrn/p/5001479.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在iOS开发中获取图片中二维码的定位,可以使用CoreImage框架。具体步骤如下: 1. 使用CIImage加载图片: ``` CIImage *image = [CIImage imageWithCGImage:image.CGImage]; ``` 2. 创建CIDetector并设置识别类型: ``` CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:nil options:@{CIDetectorAccuracy:CIDetectorAccuracyHigh}]; ``` 3. 识别二维码: ``` NSArray *features = [detector featuresInImage:image]; ``` 4. 遍历识别结果,获取二维码定位: ``` for (CIQRCodeFeature *feature in features) { NSArray *corners = feature.corners; // 获取定位四个角的坐标 // 在图像上绘制定位 UIGraphicsBeginImageContext(imageSize); CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSetStrokeColorWithColor(context, [UIColor greenColor].CGColor); CGContextSetLineWidth(context, 2.0); CGContextMoveToPoint(context, corners[0].x, corners[0].y); CGContextAddLineToPoint(context, corners[1].x, corners[1].y); CGContextAddLineToPoint(context, corners[2].x, corners[2].y); CGContextAddLineToPoint(context, corners[3].x, corners[3].y); CGContextAddLineToPoint(context, corners[0].x, corners[0].y); CGContextStrokePath(context); UIImage *resultImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); } ``` 这样就可以在原图上绘制出二维码的定位了。需要注意的是,CIDetector只能识别二维码,如果要识别其他类型的码,需要设置不同的detector类型。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值