二维码, 是用某种特定的几何图形按必定规律在平面(二维方向上)分布的黑白相间的图形记录数据符号信息的;
● 信息获取(名片、地图、WIFI密码、资料) ● 网站跳转(跳转到微博、手机网站、网站) ● 广告推送(用户扫码,直接浏览商家推送的视频、音频广告) ● 手机电商(用户扫码、手机直接购物下单) ● 防伪溯源(用户扫码、便可查看生产地;同时后台能够获取最终消费地) ● 优惠促销(用户扫码,下载电子优惠券,抽奖) ● 会员管理(用户手机上获取电子会员信息、VIP服务) ● 手机支付(扫描商品二维码,经过银行或第三方支付提供的手机端通道完成支付)
> 从iOS7开始集成了二维码的生成和读取功能 > 此前被普遍使用的zbarsdk目前不支持64位处理器
导入CoreImage框架session
#import <CoreImage/CoreImage.h>
经过滤镜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];
读取二维码须要导入AVFoundation框架ide
利用摄像头识别二维码中的内容(模拟器不行)工具
实例化拍摄设备测试
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
设置输入设备网站
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil];
设置元数据输出ui
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;
添加拍摄会话spa
4.1 实例化拍摄会话 AVCaptureSession *session = [[AVCaptureSession alloc] init]; 4.2 添加会话输入 [session addInput:input]; 4.3 添加会话输出 [session addOutput:output]; 4.3 设置输出数据类型,须要将元数据输出添加到会话后,才能指定元数据类型,不然会报错 [output setMetadataObjectTypes:@[AVMetadataObjectTypeQRCode]];
视频预览图层代理
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;
启动会话指针
[_session startRunning];
做用: > 逻辑错误:访问未初始化的变量, 野指针等; > 声明错误:从未使用过的对象; > 内存管理错误:如内存泄漏等; 分析方法: > 静态内存分析是不运行程序,直接对代码进行分析. > 根据代码的上下文的语法结构,来分析是否有内存泄露 缺点: > 不必定准确,可是若是发现有提示,那么去结合上下文看一下,这里的代码是否有问题 场景演练: > MRC 下桥接 - Foundation 和 CoreFoundation框架的数据类型转换 > ARC 下桥接 - Foundation 和 CoreFoundation框架的数据类型转换
做用: > 查看是内存的分配状况 > 查看内存是否有释放 场景演示: > UIImage 的两种建立方法测试 > imageNamed: > imageWithContentOfFile:
做用: > 检测程序在运行过程当中是否存在内存泄露 场景演示: > 模拟循环引用, 测试内存泄露
1. 最多见的是一些即时通信APP, 关联联系人;
AddressBookUI.framework 框架
> 提供了联系人列表界面、联系人详情界面、添加联系人界面等 > 通常用于选择联系人
AddressBook.framework 框架
> 纯C语言的API,仅仅是得到联系人数据 > 没有提供UI界面展现,须要本身搭建联系人展现界面 > 里面的数据类型大部分基于Core Foundation框架,使用起来极其蛋疼
ContactsUI.framework (iOS9.0最新框架)
> 方案1/方案2 的替代品
第三方框架 RHAddressBook
> 对AddressBook.framework框架的封装
建立选择联系人的控制器
ABPeoplePickerNavigationController *ppnc = [[ABPeoplePickerNavigationController alloc] init];
设置代理(用来接收用户选择的联系人信息)
ppnc.peoplePickerDelegate = self;
弹出联系人控制器
[self presentViewController:ppnc animated:YES completion:nil];
实现代理
// 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 { }
在对应的代理方法中获取联系人信息
// 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);
请求受权
// 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(@"受权失败"); } }); }
判断受权状态, 若是已受权, 则继续; 未受权, 则提示用户, 并返回;
// 1.获取受权的状态 ABAuthorizationStatus status = ABAddressBookGetAuthorizationStatus(); // 2.若是用户已经受权 if (status != kABAuthorizationStatusAuthorized) return;
建立通信录对象
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, NULL);
从通讯录对象中, 获取全部的联系人
CFArrayRef peopleArray = ABAddressBookCopyArrayOfAllPeople(addressBook);
遍历全部的联系人
// 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); }
释放再也不使用的对象
CFRelease(peopleArray); CFRelease(addressBook);
集成框架
> 1. 将整个工程拖入项目 > 2. 添加工程依赖 Build Phases -> Target Dependencies -> + > 3. 添加连接项 Build Settings -> Other Linker Flags -> -ObjC -all_load > 4. 导入框架头文件 #import <RHAddressBook/AddressBoook.h>
使用框架获取全部联系人信息
> 请求受权 // 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); } }
通常应用在某些APP , 在节假日更换主题, 或者切换白天或者夜间模式时使用.
1. 将图片以及颜色按照主题命名,使用虚拟文件夹, 直接在各个控制器针对不一样主题单独设置 缺点 : 1. 扩展性差, 增长一个主题很是复杂 2. 代码分散, 可维护性差 3. 使用虚拟文件夹保存资源, 容易与其余资源容易产生冲突
2. 抽取公共的切换主题类, 使用物理文件夹, 并将图片按照应用场景命名, 不按照主题命名, 可是按照主题划分文件夹 优势: 1. 扩展性好, 增长主题简单 2. 功能代码集中, 方便维护 3. 资源包单独管理, 不会产生资源冲突
1. 实现基本的换肤功能 2. 使用用户偏好记录当前皮肤主题 3. 抽取公共的皮肤管理类, 简化控制器逻辑 4. 验证多处加载主题情景 5. 加载主题文字颜色功能实现
例如 QQ空间APP 发说说时, 出现的什么什么型号的手机; 迅雷APP 下载文件时提示剩余空间,已用空间
直接经过第三方工具类(UIDevice的分类), 进行获取对应信息 缘由: 本身写起来比较复杂, 不少C语言的东西, 并且没有必要;
框架存在问题: 该第三方框架从2012年就中止更新了,意味着12年以后的手机型号都没有, 须要手动添加, 修改框架 解决方案: 找到对应的实现方法, 使用真机进行测试, 手动新增手机型号