bear-zyy

NSResponseder

自定义左滑cell删除样式

自定义左滑cell删除样式

1、首先实现左滑删除功能

实现tableview的代理事件,不然什么都是免谈

1
2
3
4
5
6
7
8
9
10
-(UITableViewCellEditingStyle )tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath{
return UITableViewCellEditingStyleDelete;//这个是删除
}
-(BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath{
return YES;
}
-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath{
//在这个方法里面实现删除操作
}

2、改变左滑cell删除按钮

这里就要用到一点黑科技了

首先来看看左滑后,UI图层

logo
logo

看到这个图层后,就可以操作了

UITableViewCellDeleteConfirmationView这个view在代码中是关联不出来的,不像contentview,但是我们可以获取到它。并且改变它。

在cell中的代码如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-(void)layoutSubviews{
[super layoutSubviews];
for (UIView * view in self.subviews) {
if ([view isKindOfClass:NSClassFromString(@"UITableViewCellDeleteConfirmationView")]) {
CGRect cRect = view.frame;
cRect.size.height = self.contentView.frame.size.height - 20;
cRect.origin.y = self.contentView.frame.origin.y + 10;
view.frame = cRect;
UIView * confirmView = (UIView *)[view.subviews firstObject];
confirmView.backgroundColor = [UIColor redColor];
}
}
}

3、实现微信那样左滑出现两个按钮

在控制器中实现这个代理事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-(NSArray<UITableViewRowAction *>*)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewRowAction *rowAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleNormal title:@"删除" handler:^(UITableViewRowAction * _Nonnull action, NSIndexPath * _Nonnull indexPath) {
//实现点击删除按钮的操作
}];
rowAction.backgroundColor = [UIColor redColor];
UITableViewRowAction *rowaction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleNormal title:@"置顶" handler:^(UITableViewRowAction * _Nonnull action, NSIndexPath * _Nonnull indexPath) {
//实现点击置顶按钮的操作
}];
rowaction.backgroundColor = [UIColor grayColor];
return @[rowAction , rowaction];
}

权限检测

ios开发中检测相机、麦克风、通知权限

这个是获取到权限后的枚举:

1
2
3
4
AVAuthorizationStatusNotDetermined = 0,//未询问过用户是否授权
AVAuthorizationStatusRestricted, //未授权,例如家长控制
AVAuthorizationStatusDenied, //未授权,用户曾选择过拒绝授权
AVAuthorizationStatusAuthorized //已经授权

相机权限检测

1
2
3
4
5
6
7
8
9
10
AVAuthorizationStatus atatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
if (atatus == AVAuthorizationStatusDenied || atatus == AVAuthorizationStatusRestricted) {
UIAlertController *alert =[UIAlertController alertControllerWithTitle:[NSString stringWithFormat:@"请在手机的”设置-隐私-相机“中允许访问您的相机。"] message:@"" preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
}]];
[self presentViewController:alert animated:NO completion:nil];
return NO;
}

麦克风权限检测

1
2
3
4
5
6
7
8
9
10
AVAuthorizationStatus AVatatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];//麦克风
if (AVatatus == AVAuthorizationStatusDenied || AVatatus == AVAuthorizationStatusRestricted) {
UIAlertController *alert =[UIAlertController alertControllerWithTitle:[NSString stringWithFormat:@"请在手机的”设置-隐私-麦克风“中允许访问您的麦克风。"] message:@"" preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
}]];
[self presentViewController:alert animated:NO completion:nil];
return NO;
}

通知权限检测

注本文只提供ios8以后

1
2
3
4
5
6
7
UIUserNotificationSettings * setting = [[UIApplication sharedApplication] currentUserNotificationSettings];
if (setting.types == UIUserNotificationTypeNone) {//这个是关闭了推送
UIAlertController *alert =[UIAlertController alertControllerWithTitle:[NSString stringWithFormat:@"您关闭了推送通知"] message:@"" preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
}]];
[self presentViewController:alert animated:NO completion:nil];
}

ios开发中保存照片和视频到本地相册

保存照片和视频到本地相册

1、保存视频文件到相册

1在工程中添加这个框架:

1
2
3
4
5
6
7
8
9
10
11
#import <AssetsLibrary/AssetsLibrary.h>
2、
//fileURL :为文件目录
ALAssetsLibrary * library = [[ALAssetsLibrary alloc]init];
[library writeVideoAtPathToSavedPhotosAlbum:fileURL completionBlock:^(NSURL *assetURL, NSError *error) {
if (error) {
[Uility showWindowToastWithText:@"视频保存至相册失败"];
} else {
[Uility showWindowToastWithText:@"录制完成,视频已保存至相册"];
}
}];

2、保存图片到相册

保存图片:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
UIImageWriteToSavedPhotosAlbum(image, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);
// 保存图片成功或失败的回调方法:
- (void) image: (UIImage*)image didFinishSavingWithError: (NSError*) error contextInfo: (void*)contextInfo
{
//NSLog(@"save result");
if (error != nil) {
//show error message
NSLog(@"take picture failed");
}else {
//show message image successfully saved
NSLog(@"save success");
}
}

状态栏状态改变

iOS 开发之状态栏状态变化

首先状态栏状态是两个枚举


状态栏的字体为黑色:UIStatusBarStyleDefault
状态栏的字体为白色:UIStatusBarStyleLightContent


改变状态栏状态可以分为两大块:

一、在info.plist中,将View controller-based status bar appearance设为NO

状态栏字体的颜色只由下面的属性设定,默认为黑色:


[UIApplication sharedApplication].statusBarStyle
// default is UIStatusBarStyleDefault


解决个别vc中状态栏字体颜色不同的办法

1、在info.plist中,将View controller-based status bar appearance设为NO.

2、在app delegate中:

1
[UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleLightContent;

3、在个别状态栏字体颜色不一样的vc中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-(void)viewWillAppear:(BOOL)animated{
[UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleDefault;
}
-(void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleLightContent;
}

二、在info.plist中,将View controller-based status bar appearance设为YES,或者没有设置。

View controller-based status bar appearance的默认值就是YES。

如果View controller-based status bar appearance为YES。

则[UIApplication sharedApplication].statusBarStyle 无效。


用下面的方法:

1、在vc中重写vc的preferredStatusBarStyle方法。

1
2
3
4
5
6
7
-(UIStatusBarStyle)preferredStatusBarStyle
{
return UIStatusBarStyleDefault;
}

2、在viewDidload中调用:

1
[self setNeedsStatusBarAppearanceUpdate];

但是,当vc在nav中时,上面方法没用,vc中的preferredStatusBarStyle方法根本不用被调用。

原因是,[self setNeedsStatusBarAppearanceUpdate]发出后,

只会调用navigation controller中的preferredStatusBarStyle方法,

vc中的preferredStatusBarStyley方法跟本不会被调用。

解决办法有两个:

方法一:

设置navbar的barStyle 属性会影响status bar 的字体和背景色。如下。

//status bar的字体为白色

//导航栏的背景色是黑色。

self.navigationController.navigationBar.barStyle = UIBarStyleBlack;

//status bar的字体为黑色

//导航栏的背景色是白色,状态栏的背景色也是白色。

//self.navigationController.navigationBar.barStyle = UIBarStyleDefault;

方法二:

自定义一个nav bar的子类,在这个子类中重写preferredStatusBarStyle方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
MyNav* nav = [[MyNav alloc] initWithRootViewController:vc];
self.window.rootViewController = nav;
@implementation MyNav
- (UIStatusBarStyle)preferredStatusBarStyle
{
UIViewController* topVC = self.topViewController;
return [topVC preferredStatusBarStyle];
}

oc和swift语言混合使用

在oc代码和swift代码混合使用

在oc项目中使用swift文件

1 、创建swift文件

======

创建swift文件

logo
logo

这个时候会自动弹出时候创建bridgehead.h文件,点击Create就行

logo
logo

(注:bridgehead.h也可以自己创建,自己创建后,需要在Bulid Settings -> swift Compiler 中可以设置)

logo
logo


2 、 使用swift文件

======

用swift怎么写代码就不讲了。。。。 这里默认你swift代码写好了

创建swift文件时,xcode会产生一个可以叫做中间文件的家伙 “项目名+Swift.h”

在你需要使用swift代码的类导入”项目名+Swift.h”,就可以使用了

logo
logo

总结:

1、 创建了swift文件后,确保有Birdging header文件。

注:birdging文件可以在 build setting 中 swift compiler 中,可以替换成自己的

2、确保Build Settings中 packaging中 product module name 是项目名 $(TARGET_NAME) 通配符

3、这些配置好了后,就可以在项目中使用了。 所有的swift文件都会被编译成 以“项目名-Swift.h”为头文件的文件,在使用的类中,导入就行了

在swift项目中使用oc文件

1、创建oc文件

===

如果是第一次在swift项目中创建oc文件,也会创建一个bridging header文件

logo
logo

同上,也可以don’t create,自己去创建briding head文件,不细说了。。

2、使用oc代码

===

把需要使用的oc类的头文件导入到briding head 文件中,这个时候就可以使用oc类了。

RMQClient iOS的集成和使用

RMQClient iOS的集成和使用

一、介绍

QoS(Quality of Service,服务质量)

logo

RETAIN(保持)

1:表示发送的消息需要一直持久保存(不受服务器重启影响),不但要发送给当前的订阅者,并且以后新来的订阅了此Topic name的订阅者会马上得到推送。
备注:新来乍到的订阅者,只会取出最新的一个RETAIN flag = 1的消息推送。
0:仅仅为当前订阅者推送此消息。
假如服务器收到一个空消息体(zero-length payload)、RETAIN = 1、已存在Topic name的PUBLISH消息,服务器可以删除掉对应的已被持久化的PUBLISH消息。

心跳

1、客户端会在一个心跳周期内发送一条pingreq消息到服务器。

2、心跳平率在connect可变头部“keep alive timer”中定义时间,单位为秒。

3、服务器收到pingreq请求后,会立即响应一个两个字节固定格式的pingresp消息。

4、服务器一般在1.5倍的心跳周期内接收不到客服端发送的pingreq,可考虑关闭客服端的连接描述,但对客服端的订阅不会产生影响。

5、若客服端发送pingreq之后的一个心跳周期内接收不到pingresp消息,可考虑关闭TCP/IP套接字连接

二、集成

1、用cocopods

pod ‘RMQClient’


2、用Carthage

github “rabbitmq/rabbitmq-objc-client” ~> 0.10.0


3、也可以直接添加RMQClienk框架,这里自己去找框架了


操作完成后,目录是这个样子的

logo

三、集成

1、导入头文件

import “RMQClient.h”

注意:如果是用Carthage导入的话,导入头文件

import < RMQClient/RMQClient.h >

2、连接RabbitMQ服务器

1
2
3
4
self.connection = [[RMQConnection alloc] initWithUri:self.uri delegate:[RMQConnectionDelegateLogger new]];//初始化一个连接
[self.connection start];

3、创建一个通道

1
2
3
4
5
6
7
8
9
10
id<RMQChannel> ch = [self.connection createChannel];
self.channel = ch;
RMQExchange * ex = [[RMQExchange alloc] initWithName:@"amq.topic" type:@"topic" options:RMQExchangeDeclareDurable channel:ch];
/*
exchange有四种type,分别是topic direct , headers,fanout
*/
self.exchange = ex;
self.queue = [ch queue:@"" options:RMQQueueDeclareDurable];
[self.queue bind:ex routingKey:@"你的项目topic"];//订阅的主题名字

4、接收消息

1
2
3
4
5
6
7
8
9
[self.queue subscribe:^(RMQMessage * _Nonnull message)
NSLog(@"====%@",message);
NSString * string = [[NSString alloc] initWithData:message.body encoding:NSUTF8StringEncoding];
NSData * jsonData = [string dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary * dict = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:nil];
NSLog(@"%@",dict);
}];

5、发送消息

1
2
[self.exchange publish:[@"hello world" dataUsingEncoding:NSUTF8StringEncoding] routingKey:@"robot.1000896.status@sub"];

参考资料:http://www.rabbitmq.com/tutorials/tutorial-one-objectivec.html

HTTPS证书链

HTTPS证书链

对发送端:HTTPS把应用层的数据放到SSL/TSL层加密后,在下放到TCP/IP层组成IP数据报传输,以此保证数据的安全,对接收端:先把IP数据报在SSL/TSL层解密之后,将数据传给应用层,协议层。

SSL/TSL包括四次握手,主要交换三个信息:
1、数字证书;
2、三个随机数;
3、加密通讯协议。

logo
logo

数字证书

该证书一般是由服务器发给客户端,接收方通过验证这个证书是不是由信赖的CA签发,或者与本地的证书相对比,来判断证书是否可信;假如需要双向验证,则服务器和客户端都需要发送数字证书给对方验证。
数字证书的生成是分层级的,从叶节点证书往根证书层层验证(有效期、签名等等),遇到根证书时,发现作为可信锚点的它存在与可信证书列表中,那么验证就通过。例如在钥匙串中可以看到某券商公司申请的证书没有在系统可信任的证书列表中。

三个随机数

这三个随机数构成了后续通信过程中用来对数据进行对称加密解密的“对话密钥”。首先客户端先发第一个随机数n1,然后服务器返回第二个随机数n2(这个过程同时把数字证书发给客户端),这两个随机数都是明文的;而第三个随机数n3,客户端用数字证书的公钥进行非对称加密,发给服务器;而服务器用只有自己知道的私钥来解密,获取第三个随机数。服务端和客户端都有了三个随机数n1+n2+n3后,两端就使用这三个随机数来生成“对话密钥”,在此之后的通信就使用这个“对话密钥”来进行对称加解密。

加密通信协议

双方商量具体使用哪一种加密方式,假如两者支持的加密方式不匹配,则无法进行通信。因为这个过程中,服务端的私钥只用来解密第三个随机数,从来没有在网络中传输过,只要私钥没有被泄露,那么数据就是安全的。

iOS开发黑科技之runtime

iOS 开发之黑科技—runtime

runtime其实就是oc底层的一套C语音的API

调用方法的本质就是发消息,

1、动态交换两个方法的实现(特别是交换系统自动的方法)

2、动态添加对象的成员变量和成员方法

3、获得某个类的所有成员方法、所有成员变量

注意:

对于一般OC代码的method swizzling, 在load方法中执行即可. 而Swift没有load, 所以要在initialize中执行.

应用

1、block的实现原理

2、拦截系统自带的方法调用

1
2
3
4
5
6
7
8
9
10
+ (void)load {
// 获取两个类的类方法
Method m1 = class_getClassMethod([UIImage class], @selector(imageNamed:));
Method m2 = class_getClassMethod([UIImage class], @selector(xh_imageNamed:));
// 开始交换方法实现
method_exchangeImplementations(m1, m2);
}
+ (UIImage *)xh_imageNamed:(NSString *)name {
return [UIImage xh_imageNamed:name];
}

3、实现类别也可以增加属性

1
2
3
4
5
6
7
-(NSString *)name{
return objc_getAssociatedObject(self, @"nameKey");
}
-(void)setName:(NSString *)name{
objc_setAssociatedObject(self, @"nameKey", name , OBJC_ASSOCIATION_COPY_NONATOMIC);
}

4、实现nscodeing的自动归档和自动接档

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
- (void)decode:(NSCoder *)aDecoder {
// 一层层父类往上查找,对父类的属性执行归解档方法
Class c = self.class;
while (c &&c != [NSObject class]) {
unsigned int outCount = 0;
Ivar *ivars = class_copyIvarList(c, &outCount);
for (int i = 0; i < outCount; i++) {
Ivar ivar = ivars[i];
NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
id value = [aDecoder decodeObjectForKey:key];
[self setValue:value forKey:key];
}
}
}
- (void)encode:(NSCoder *)aCoder {
// 一层层父类往上查找,对父类的属性执行归解档方法
Class c = self.class;
while (c &&c != [NSObject class]) {
unsigned int outCount = 0;
Ivar *ivars = class_copyIvarList([self class], &outCount);
for (int i = 0; i < outCount; i++) {
Ivar ivar = ivars[i];
NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
// 如果有实现该方法再去调用
id value = [self valueForKeyPath:key];
[aCoder encodeObject:value forKey:key];
}
}
}

5、实现字典和模型的自动转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
- (void)setDict:(NSDictionary *)dict {
Class c = self.class;
while (c &&c != [NSObject class]) {
unsigned int outCount = 0;
Ivar *ivars = class_copyIvarList(c, &outCount);
for (int i = 0; i < outCount; i++) {
Ivar ivar = ivars[i];
NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
// 成员变量名转为属性名(去掉下划线 _ )
key = [key substringFromIndex:1];
// 取出字典的值
id value = dict[key];
// 如果模型属性数量大于字典键值对数理,模型属性会被赋值为nil而报错
if (value == nil) continue;
// 获得成员变量的类型
NSString *type = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
// 如果属性是对象类型
NSRange range = [type rangeOfString:@"@"];
if (range.location != NSNotFound) {
// 那么截取对象的名字(比如@"Dog",截取为Dog)
type = [type substringWithRange:NSMakeRange(2, type.length - 3)];
// 排除系统的对象类型
if (![type hasPrefix:@"NS"]) {
// 将对象名转换为对象的类型,将新的对象字典转模型(递归)
Class class = NSClassFromString(type);
value = [class objectWithDict:value];
}else if ([type isEqualToString:@"NSArray"]) {
// 如果是数组类型,将数组中的每个模型进行字典转模型,先创建一个临时数组存放模型
NSArray *array = (NSArray *)value;
NSMutableArray *mArray = [NSMutableArray array];
// 获取到每个模型的类型
id class ;
if ([self respondsToSelector:@selector(arrayObjectClass)]) {
NSString *classStr = [self arrayObjectClass];
class = NSClassFromString(classStr);
}else {
NSLog(@"数组内模型是未知类型");
return;
}
// 将数组中的所有模型进行字典转模型
for (int i = 0; i < array.count; i++) {
[mArray addObject:[class objectWithDict:value[i]]];
}
value = mArray;
}
}
// 将字典中的值设置到模型上
[self setValue:value forKeyPath:key];
}
free(ivars);
c = [c superclass];
}
}
+ (instancetype )objectWithDict:(NSDictionary *)dict {
NSObject *obj = [[self alloc]init];
[obj setDict:dict];
return obj;
}

这两个都是

Ivar * ivars = class_copyIvarList(self.class, &outCount);
就不用一个一个属性写了。
再说一下归档解档

如果不是系统的类,要进行归档解档要遵守协议,和实现协议中的方法

1
2
3
4
5
6
7
8
9
-(void)encodeWithCoder:(NSCoder *)aCoder{
[aCoder encodeObject:self.name forKey:@"name"];
}
-(id)initWithCoder:(NSCoder *)aDecoder{
if (self = [super init]) {
self.name = [aDecoder decodeObjectForKey:@"name"];
}

iOS蓝牙

ios开发之蓝牙

一、作为外围设备开发

1、开启蓝牙会调用这个方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//peripheralManager状态改变
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral{
switch (peripheral.state) {
//在这里判断蓝牙设别的状态 当开启了则可调用 setUp方法(自定义)
case CBPeripheralManagerStatePoweredOn:
NSLog(@"powered on");
[self setUp];
break;
case CBPeripheralManagerStatePoweredOff:
NSLog(@"powered off");
break;
default:
break;
}
}

2、打开peripheral manager ,设置它的委托

1
peripheralManager = [[CBPeripheralManager alloc]initWithDelegate:self queue:nil];

3、创建characteristics,创建service,把characteristics 添加到service中,把service添加到peripheral manager中,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
-(void)setUp{
//characteristics字段描述
CBUUID *CBUUIDCharacteristicUserDescriptionStringUUID = [CBUUID UUIDWithString:CBUUIDCharacteristicUserDescriptionString];
/*
可以通知的Characteristic
properties:CBCharacteristicPropertyNotify
permissions CBAttributePermissionsReadable
*/
CBMutableCharacteristic *notiyCharacteristic = [[CBMutableCharacteristic alloc]initWithType:[CBUUID UUIDWithString:notiyCharacteristicUUID] properties:CBCharacteristicPropertyNotify value:nil permissions:CBAttributePermissionsReadable];
/*
可读写的characteristics
properties:CBCharacteristicPropertyWrite | CBCharacteristicPropertyRead
permissions CBAttributePermissionsReadable | CBAttributePermissionsWriteable
*/
CBMutableCharacteristic *readwriteCharacteristic = [[CBMutableCharacteristic alloc]initWithType:[CBUUID UUIDWithString:readwriteCharacteristicUUID] properties:CBCharacteristicPropertyWrite | CBCharacteristicPropertyRead value:nil permissions:CBAttributePermissionsReadable | CBAttributePermissionsWriteable];
//设置description
CBMutableDescriptor *readwriteCharacteristicDescription1 = [[CBMutableDescriptor alloc]initWithType: CBUUIDCharacteristicUserDescriptionStringUUID value:@"bear-zyy"];
[readwriteCharacteristic setDescriptors:@[readwriteCharacteristicDescription1]];
/*
只读的Characteristic
properties:CBCharacteristicPropertyRead
permissions CBAttributePermissionsReadable
*/
CBMutableCharacteristic *readCharacteristic = [[CBMutableCharacteristic alloc]initWithType:[CBUUID UUIDWithString:readCharacteristicUUID] properties:CBCharacteristicPropertyRead value:nil permissions:CBAttributePermissionsReadable];
//service1初始化并加入两个characteristics
CBMutableService *service1 = [[CBMutableService alloc]initWithType:[CBUUID UUIDWithString:ServiceUUID1] primary:YES];
NSLog(@"%@",service1.UUID);
[service1 setCharacteristics:@[notiyCharacteristic,readwriteCharacteristic]];
// [service1 setCharacteristics:@[readwriteCharacteristic]];
//service2初始化并加入一个characteristics
CBMutableService *service2 = [[CBMutableService alloc]initWithType:[CBUUID UUIDWithString:ServiceUUID2] primary:YES];
[service2 setCharacteristics:@[readCharacteristic]];
//添加后就会调用代理的- (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error
[peripheralManager addService:service1];
[peripheralManager addService:service2];
}

4、开启广播advertising

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//perihpheral添加了service
- (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error{
// if (error == nil) {
// serviceNum++;
// }
// //因为我们添加了2个服务,所以想两次都添加完成后才去发送广播
//// if (serviceNum==2) {
// //添加服务后可以在此向外界发出通告 调用完这个方法后会调用代理的
// //(void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error
[peripheralManager startAdvertising:@{
CBAdvertisementDataServiceUUIDsKey : @[[CBUUID UUIDWithString:ServiceUUID1],[CBUUID UUIDWithString:ServiceUUID2]],
CBAdvertisementDataLocalNameKey : @"bear-zyy"
}
];
// }
}
//peripheral开始发送advertising
- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error{
NSLog(@"in peripheralManagerDidStartAdvertisiong");
}

5、对central的操作进行响应
5.1 读characteristics请求

1
2
3
4
5
6
7
8
9
10
11
12
13
//读characteristics请求
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request{
NSLog(@"didReceiveReadRequest");
//判断是否有读数据的权限
if (request.characteristic.properties & CBCharacteristicPropertyRead) {
NSData *data = request.characteristic.value;
[request setValue:data];
//对请求作出成功响应
[peripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
}else{
[peripheralManager respondToRequest:request withResult:CBATTErrorWriteNotPermitted];
}
}

5.2写characteristics请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//写characteristics请求
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray *)requests{
NSLog(@"didReceiveWriteRequests");
CBATTRequest *request = requests[0];
//判断是否有写数据的权限
if (request.characteristic.properties & CBCharacteristicPropertyWrite) {
//需要转换成CBMutableCharacteristic对象才能进行写值
CBMutableCharacteristic *c =(CBMutableCharacteristic *)request.characteristic;
c.value = request.value;
[peripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
}else{
[peripheralManager respondToRequest:request withResult:CBATTErrorWriteNotPermitted];
}
}

5.3订阅和取消订阅characteristics

1
2
3
4
5
6
7
8
9
10
11
12
//订阅characteristics 当中心设备订阅了这个特征会回调这个方法
-(void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic{
NSLog(@"订阅了 %@的数据",characteristic.UUID);
//每秒执行一次给主设备发送一个当前时间的秒数
// timer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(sendData:) userInfo:characteristic repeats:YES];
}
//取消订阅characteristics
-(void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic{
NSLog(@"取消订阅 %@的数据",characteristic.UUID);
}

二、作为中心设备

1、打开中心设备,设置它的委托

1
2
manager = [[CBCentralManager alloc]initWithDelegate:self queue:dispatch_get_main_queue()];

2、扫描外围设备

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
-(void)centralManagerDidUpdateState:(CBCentralManager *)central{
switch (central.state) {
case CBCentralManagerStatePoweredOn:
NSLog(@">>>CBCentralManagerStatePoweredOn");
//开始扫描周围的外设
/*
第一个参数nil就是扫描周围所有的外设,扫描到外设后会进入
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI;
*/
[central scanForPeripheralsWithServices:nil options:nil];
break;
default:
break;
}
}

3、把外围设备加入到中心设备中,连接外围设备

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//扫描到设备会进入方法
-(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI{
NSLog(@"当扫描到设备:%@",peripheral.name);
//接下连接我们的测试设备,如果你没有设备,可以下载一个app叫lightbule的app去模拟一个设备
//这里自己去设置下连接规则,我设置的是P开头的设备
// if ([peripheral.name hasPrefix:@"张"]){
/*
一个主设备最多能连7个外设,每个外设最多只能给一个主设备连接,连接成功,失败,断开会进入各自的委托
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral;//连接外设成功的委托
- (void)centra`lManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;//外设连接失败的委托
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;//断开外设的委托
*/
//找到的设备必须持有它,否则CBCentralManager中也不会保存peripheral,那么CBPeripheralDelegate中的方法也不会被调用!!
if ([peripheral.name isEqualToString:@"外围设备名"]) {
[discoverPeripherals addObject:peripheral];
[central connectPeripheral:peripheral options:nil];
}
}

4、设置的peripheral委托CBPeripheralDelegate 扫描外设服务

1
2
3
4
5
6
7
8
9
10
11
12
13
//连接到Peripherals-成功
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
NSLog(@">>>连接到名称为(%@)的设备-成功",peripheral.name);
//设置的peripheral委托CBPeripheralDelegate
//@interface ViewController : UIViewController<CBCentralManagerDelegate,CBPeripheralDelegate>
[peripheral setDelegate:self];
//扫描外设Services,成功后会进入方法:-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
[peripheral discoverServices:nil];
}

5、扫描到service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
// NSLog(@">>>扫描到服务:%@",peripheral.services);
if (error)
{
NSLog(@">>>Discovered services for %@ with error: %@", peripheral.name, [error localizedDescription]);
return;
}
CBUUID * serviceUUID = [CBUUID UUIDWithString:@"服务码"];
//写
CBUUID *characteristicUUID1=[CBUUID UUIDWithString:@"特征码"];
//通知 读
CBUUID *characteristicUUID2=[CBUUID UUIDWithString:@"特征码"];
//写
CBUUID * characteristicUU = [CBUUID UUIDWithString:@"特征码"];
for (CBService *service in peripheral.services) {
[peripheral discoverCharacteristics:nil forService:service];
if ([service.UUID isEqual:serviceUUID]) {
[peripheral discoverCharacteristics:@[characteristicUUID1,characteristicUUID2,characteristicUU] forService:service];
}
//扫描每个service的Characteristics,扫描到后会进入方法: -(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
}
}

6、发现特征

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//扫描到Characteristics
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error{
if (error)
{
NSLog(@"error Discovered characteristics for %@ with error: %@", service.UUID, [error localizedDescription]);
return;
}
//获取Characteristic的值,读到数据会进入方法:-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
for (CBCharacteristic *characteristic in service.characteristics){
{
[peripheral readValueForCharacteristic:characteristic];
//
[peripheral setNotifyValue:YES forCharacteristic:characteristic];
// if ([characteristic.UUID.UUIDString isEqualToString:@"2A60"]) {
// NSLog(@"嘿嘿");
// [peripheral writeValue:[@"hahha" dataUsingEncoding:NSUTF8StringEncoding] forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
// }
// [peripheral writeValue:[@"看看" dataUsingEncoding:NSUTF8StringEncoding] forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
}
}
//搜索Characteristic的Descriptors,读到数据会进入方法:-(void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
for (CBCharacteristic *characteristic in service.characteristics){
[peripheral discoverDescriptorsForCharacteristic:characteristic];
}
}

7、注册读、写、通知

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#pragma mark 搜索到Characteristic的properties
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
if (characteristic.properties & CBCharacteristicPropertyRead) {
// if ([characteristic.UUID.UUIDString isEqualToString:@"FFE1"]) {
[peripheral readValueForCharacteristic:characteristic];
if (characteristic.value) {
NSString * value=[[NSString alloc]initWithData:characteristic.value encoding:NSUTF8StringEncoding];
NSLog(@"读取到特征值:%@",value);
// label.text = [NSString stringWithFormat:@"%@",value];
}
// }
}
//情景二:通知
if (characteristic.properties & CBCharacteristicPropertyNotify) {
// if ([characteristic.UUID.UUIDString isEqualToString:@"FFF1"] || [characteristic.UUID.UUIDString isEqualToString:@"FFF2"]) {
// _curCharacter = characteristic;
// label.text = @"通知";
[peripheral setNotifyValue:YES forCharacteristic:characteristic];
}
// }
// //情景二:写数据
if (characteristic.properties & CBCharacteristicPropertyWrite) {
NSLog(@"characteristic.UUID.UUIDString===========%@",characteristic.UUID.UUIDString);
if ([characteristic.UUID.UUIDString isEqualToString:@"2A60"]) {
_sendPeripheral = peripheral;
_sendCharacteristic = characteristic;
[peripheral writeValue:[@"2A60" dataUsingEncoding:NSUTF8StringEncoding] forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
}
////// //新加的
if ([characteristic.UUID.UUIDString isEqualToString:@"2A62"]) {
[peripheral writeValue:[@"2A62" dataUsingEncoding:NSUTF8StringEncoding] forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
}
}
}
📖 more posts 📖