开源 Objective-C IOS 应用开发(十二)通讯--ble

Objective-C实现iOS蓝牙BLE广播

  文章的目的为了记录使用Objective-C 进行IOS app 开发学习的经历。本职为嵌入式软件开发,公司安排开发app,临时学习,完成app的开发。开发流程和要点有些记忆模糊,赶紧记录,防止忘记。

 相关链接:

开源 Objective-C IOS 应用开发(一)macOS 的使用

开源 Objective-C IOS 应用开发(二)Xcode安装

开源 Objective-C IOS 应用开发(三)第一个iPhone的APP

开源 Objective-C IOS 应用开发(四)Xcode工程文件结构

开源 Objective-C IOS 应用开发(五)iOS操作(action)和输出口(Outlet)

开源 Objective-C IOS 应用开发(六)Objective-C 和 C语言

开源 Objective-C IOS 应用开发(七)Objective-C核心代码示例

开源 Objective-C IOS 应用开发(八)常见控件UI

开源 Objective-C IOS 应用开发(九)复杂控件-tableview

开源 Objective-C IOS 应用开发(十)数据持久化--文件

开源 Objective-C IOS 应用开发(十一)数据持久化--sqlite

开源 Objective-C IOS 应用开发(十二)通讯--ble

开源 Objective-C IOS 应用开发(十三)通讯--Http访问

开源 Objective-C IOS 应用开发(十四)传感器--陀螺仪和gps

开源 Objective-C IOS 应用开发(十五)通讯--蓝牙ble扫描

开源 Objective-C IOS 应用开发(十六)Storyboard模式下的纯代码界面

开源 Objective-C IOS 应用开发(十七)CAF音频的录制

开源 Objective-C IOS 应用开发(十八)音频的播放

开源 Objective-C IOS 应用开发(十九)视频的播放

开源 Objective-C IOS 应用开发(二十)多线程处理

开源 Objective-C IOS 应用开发(二十一)自定义控件--示波器

开源 Objective-C IOS 应用开发(二十二)自定义控件--车速仪表盘

 推荐链接:

开源 Arkts 鸿蒙应用 开发(一)工程文件分析-优快云博客

开源 Arkts 鸿蒙应用 开发(二)封装库.har制作和应用-优快云博客

开源 Arkts 鸿蒙应用 开发(三)Arkts的介绍-优快云博客

开源 Arkts 鸿蒙应用 开发(四)布局和常用控件-优快云博客

开源 Arkts 鸿蒙应用 开发(五)控件组成和复杂控件-优快云博客

开源 Arkts 鸿蒙应用 开发(六)数据持久--文件和首选项存储-优快云博客

开源 Arkts 鸿蒙应用 开发(七)数据持久--sqlite关系数据库-优快云博客

开源 Arkts 鸿蒙应用 开发(八)多媒体--相册和相机-优快云博客

开源 Arkts 鸿蒙应用 开发(九)通讯--tcp客户端-优快云博客

开源 Arkts 鸿蒙应用 开发(十)通讯--Http-优快云博客

开源 Arkts 鸿蒙应用 开发(十一)证书和包名修改-优快云博客

开源 Arkts 鸿蒙应用 开发(十二)传感器的使用-优快云博客

开源 Arkts 鸿蒙应用 开发(十三)音频--MP3播放_arkts avplayer播放音频 mp3-优快云博客

开源 Arkts 鸿蒙应用 开发(十四)线程--任务池(taskpool)-优快云博客

开源 Arkts 鸿蒙应用 开发(十五)自定义绘图控件--仪表盘-优快云博客

开源 Arkts 鸿蒙应用 开发(十六)自定义绘图控件--波形图-优快云博客

开源 Arkts 鸿蒙应用 开发(十七)通讯--http多文件下载-优快云博客

开源 Arkts 鸿蒙应用 开发(十八)通讯--Ble低功耗蓝牙服务器-优快云博客

推荐链接:

开源 java android app 开发(一)开发环境的搭建-优快云博客

开源 java android app 开发(二)工程文件结构-优快云博客

开源 java android app 开发(三)GUI界面布局和常用组件-优快云博客

开源 java android app 开发(四)GUI界面重要组件-优快云博客

开源 java android app 开发(五)文件和数据库存储-优快云博客

开源 java android app 开发(六)多媒体使用-优快云博客

开源 java android app 开发(七)通讯之Tcp和Http-优快云博客

开源 java android app 开发(八)通讯之Mqtt和Ble-优快云博客

开源 java android app 开发(九)后台之线程和服务-优快云博客

开源 java android app 开发(十)广播机制-优快云博客

开源 java android app 开发(十一)调试、发布-优快云博客

开源 java android app 开发(十二)封库.aar-优快云博客
 

本章内容主要是BLE 广播应用,文本框可以输入设备名称,点击发送进行广播。

目录:

1.手机演示

2.所有源码

3.源码分析

一、手机演示

左边为ipone app,右边为安卓的ble调试助手

二、所有源码

AppDelegate.h

#import <UIKit/UIKit.h>

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;

@end

AppDelegate.m

#import "AppDelegate.h"
#import "ViewController.h"

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    ViewController *viewController = [[ViewController alloc] init];
    self.window.rootViewController = viewController;
    [self.window makeKeyAndVisible];
    return YES;
}

@end

ViewController.h

#import <UIKit/UIKit.h>
#import <CoreBluetooth/CoreBluetooth.h>

@interface ViewController : UIViewController <CBPeripheralManagerDelegate, UITextFieldDelegate>

@property (nonatomic, strong) CBPeripheralManager *peripheralManager;
@property (nonatomic, strong) UITextField *nameTextField;
@property (nonatomic, strong) UIButton *startButton;
@property (nonatomic, strong) UIButton *stopButton;
@property (nonatomic, strong) UILabel *statusLabel;

@end

ViewController.m

#import "ViewController.h"

@interface ViewController ()

@property (nonatomic, assign) BOOL isAdvertising;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self setupUI];
    [self setupBLE];
}

- (void)setupUI {
    self.view.backgroundColor = [UIColor whiteColor];
    
    // 标题
    UILabel *titleLabel = [[UILabel alloc] init];
    titleLabel.text = @"BLE广播发送器";
    titleLabel.textAlignment = NSTextAlignmentCenter;
    titleLabel.textColor = [UIColor blackColor];
    titleLabel.font = [UIFont boldSystemFontOfSize:24];
    titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
    [self.view addSubview:titleLabel];
    
    // 设备名称标签
    UILabel *nameLabel = [[UILabel alloc] init];
    nameLabel.text = @"设备名称:";
    nameLabel.textColor = [UIColor blackColor];
    nameLabel.font = [UIFont systemFontOfSize:16];
    nameLabel.translatesAutoresizingMaskIntoConstraints = NO;
    [self.view addSubview:nameLabel];
    
    // 设备名称输入框
    self.nameTextField = [[UITextField alloc] init];
    self.nameTextField.placeholder = @"输入BLE设备名称";
    self.nameTextField.borderStyle = UITextBorderStyleRoundedRect;
    self.nameTextField.text = @"MyBLEDevice";
    self.nameTextField.delegate = self;
    self.nameTextField.translatesAutoresizingMaskIntoConstraints = NO;
    [self.view addSubview:self.nameTextField];
    
    // 状态标签
    self.statusLabel = [[UILabel alloc] init];
    self.statusLabel.text = @"准备就绪";
    self.statusLabel.textAlignment = NSTextAlignmentCenter;
    self.statusLabel.textColor = [UIColor grayColor];
    self.statusLabel.font = [UIFont systemFontOfSize:16];
    self.statusLabel.numberOfLines = 0;
    self.statusLabel.translatesAutoresizingMaskIntoConstraints = NO;
    [self.view addSubview:self.statusLabel];
    
    // 开始广播按钮
    self.startButton = [UIButton buttonWithType:UIButtonTypeSystem];
    [self.startButton setTitle:@"开始广播" forState:UIControlStateNormal];
    [self.startButton addTarget:self action:@selector(startAdvertising) forControlEvents:UIControlEventTouchUpInside];
    self.startButton.backgroundColor = [UIColor systemGreenColor];
    self.startButton.tintColor = [UIColor whiteColor];
    self.startButton.titleLabel.font = [UIFont boldSystemFontOfSize:18];
    self.startButton.layer.cornerRadius = 10;
    self.startButton.translatesAutoresizingMaskIntoConstraints = NO;
    [self.view addSubview:self.startButton];
    
    // 停止广播按钮
    self.stopButton = [UIButton buttonWithType:UIButtonTypeSystem];
    [self.stopButton setTitle:@"停止广播" forState:UIControlStateNormal];
    [self.stopButton addTarget:self action:@selector(stopAdvertising) forControlEvents:UIControlEventTouchUpInside];
    self.stopButton.backgroundColor = [UIColor systemRedColor];
    self.stopButton.tintColor = [UIColor whiteColor];
    self.stopButton.titleLabel.font = [UIFont boldSystemFontOfSize:18];
    self.stopButton.layer.cornerRadius = 10;
    self.stopButton.translatesAutoresizingMaskIntoConstraints = NO;
    self.stopButton.enabled = NO;
    self.stopButton.backgroundColor = [UIColor systemGrayColor];
    [self.view addSubview:self.stopButton];
    
    // 说明标签
    UILabel *infoLabel = [[UILabel alloc] init];
    infoLabel.text = @"模式: 不可连接广播 - 无任何服务";
    infoLabel.textAlignment = NSTextAlignmentCenter;
    infoLabel.textColor = [UIColor lightGrayColor];
    infoLabel.font = [UIFont systemFontOfSize:14];
    infoLabel.translatesAutoresizingMaskIntoConstraints = NO;
    [self.view addSubview:infoLabel];
    
    // 设置约束
    [self setupConstraintsWithTitleLabel:titleLabel nameLabel:nameLabel infoLabel:infoLabel];
    
    // 添加点击手势隐藏键盘
    UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(dismissKeyboard)];
    [self.view addGestureRecognizer:tapGesture];
}

- (void)setupConstraintsWithTitleLabel:(UILabel *)titleLabel nameLabel:(UILabel *)nameLabel infoLabel:(UILabel *)infoLabel {
    [NSLayoutConstraint activateConstraints:@[
        // 标题约束
        [titleLabel.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor constant:20],
        [titleLabel.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:20],
        [titleLabel.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-20],
        [titleLabel.heightAnchor constraintEqualToConstant:40],
        
        // 名称标签约束
        [nameLabel.topAnchor constraintEqualToAnchor:titleLabel.bottomAnchor constant:40],
        [nameLabel.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:20],
        [nameLabel.widthAnchor constraintEqualToConstant:80],
        [nameLabel.heightAnchor constraintEqualToConstant:40],
        
        // 名称输入框约束
        [self.nameTextField.topAnchor constraintEqualToAnchor:titleLabel.bottomAnchor constant:40],
        [self.nameTextField.leadingAnchor constraintEqualToAnchor:nameLabel.trailingAnchor constant:10],
        [self.nameTextField.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-20],
        [self.nameTextField.heightAnchor constraintEqualToConstant:40],
        
        // 状态标签约束
        [self.statusLabel.topAnchor constraintEqualToAnchor:self.nameTextField.bottomAnchor constant:30],
        [self.statusLabel.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:20],
        [self.statusLabel.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-20],
        [self.statusLabel.heightAnchor constraintEqualToConstant:60],
        
        // 开始按钮约束
        [self.startButton.topAnchor constraintEqualToAnchor:self.statusLabel.bottomAnchor constant:40],
        [self.startButton.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:50],
        [self.startButton.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-50],
        [self.startButton.heightAnchor constraintEqualToConstant:50],
        
        // 停止按钮约束
        [self.stopButton.topAnchor constraintEqualToAnchor:self.startButton.bottomAnchor constant:20],
        [self.stopButton.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:50],
        [self.stopButton.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-50],
        [self.stopButton.heightAnchor constraintEqualToConstant:50],
        
        // 说明标签约束
        [infoLabel.bottomAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.bottomAnchor constant:-20],
        [infoLabel.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:20],
        [infoLabel.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-20],
        [infoLabel.heightAnchor constraintEqualToConstant:30]
    ]];
}

- (void)setupBLE {
    self.peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:dispatch_get_main_queue()];
    self.isAdvertising = NO;
}

- (void)dismissKeyboard {
    [self.nameTextField resignFirstResponder];
}

#pragma mark - Button Actions

- (void)startAdvertising {
    [self dismissKeyboard];
    
    if (self.peripheralManager.state != CBManagerStatePoweredOn) {
        self.statusLabel.text = @"错误: 蓝牙未开启";
        return;
    }
    
    if (self.isAdvertising) {
        self.statusLabel.text = @"错误: 已在广播中";
        return;
    }
    
    NSString *deviceName = [self.nameTextField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
    if (deviceName.length == 0) {
        self.statusLabel.text = @"错误: 请输入设备名称";
        return;
    }
    
    [self startNonConnectableAdvertisingWithName:deviceName];
}

- (void)stopAdvertising {
    if (self.isAdvertising) {
        [self.peripheralManager stopAdvertising];
        self.isAdvertising = NO;
        self.statusLabel.text = @"广播已停止";
        self.startButton.enabled = YES;
        self.stopButton.enabled = NO;
        self.startButton.backgroundColor = [UIColor systemGreenColor];
        self.stopButton.backgroundColor = [UIColor systemGrayColor];
        NSLog(@"BLE广播已停止");
    }
}

#pragma mark - 不可连接广播实现

- (void)startNonConnectableAdvertisingWithName:(NSString *)deviceName {
    // 使用空的Service UUIDs数组来实现不可连接广播
    // 空的UUID数组表示设备没有可用的服务,因此不可连接
    NSDictionary *advertisementData = @{
        CBAdvertisementDataLocalNameKey : deviceName,
        CBAdvertisementDataServiceUUIDsKey : @[] // 空数组 - 关键!
    };
    
    [self.peripheralManager startAdvertising:advertisementData];
    self.statusLabel.text = [NSString stringWithFormat:@"正在广播: %@\n设备不可连接", deviceName];
    NSLog(@"开始不可连接广播,设备名称: %@", deviceName);
}

#pragma mark - CBPeripheralManagerDelegate

- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral {
    NSString *stateString;
    UIColor *statusColor = [UIColor grayColor];
    
    switch (peripheral.state) {
        case CBManagerStatePoweredOn:
            stateString = @"蓝牙已就绪\n请输入设备名称并开始广播";
            statusColor = [UIColor systemBlueColor];
            self.startButton.enabled = YES;
            self.startButton.backgroundColor = [UIColor systemGreenColor];
            break;
        case CBManagerStatePoweredOff:
            stateString = @"蓝牙未开启\n请打开系统蓝牙设置";
            statusColor = [UIColor systemRedColor];
            self.startButton.enabled = NO;
            self.startButton.backgroundColor = [UIColor systemGrayColor];
            break;
        case CBManagerStateUnauthorized:
            stateString = @"未授权蓝牙权限\n请在设置中允许蓝牙访问";
            statusColor = [UIColor systemOrangeColor];
            self.startButton.enabled = NO;
            self.startButton.backgroundColor = [UIColor systemGrayColor];
            break;
        case CBManagerStateUnsupported:
            stateString = @"设备不支持BLE";
            statusColor = [UIColor systemRedColor];
            self.startButton.enabled = NO;
            self.startButton.backgroundColor = [UIColor systemGrayColor];
            break;
        default:
            stateString = @"蓝牙状态未知";
            self.startButton.enabled = NO;
            self.startButton.backgroundColor = [UIColor systemGrayColor];
            break;
    }
    
    self.statusLabel.text = stateString;
    self.statusLabel.textColor = statusColor;
    NSLog(@"蓝牙状态: %@", stateString);
}

- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error {
    if (error) {
        self.statusLabel.text = [NSString stringWithFormat:@"广播启动失败\n%@", error.localizedDescription];
        self.statusLabel.textColor = [UIColor systemRedColor];
        NSLog(@"广播错误: %@", error.localizedDescription);
        
        self.isAdvertising = NO;
        self.startButton.enabled = YES;
        self.stopButton.enabled = NO;
        self.startButton.backgroundColor = [UIColor systemGreenColor];
        self.stopButton.backgroundColor = [UIColor systemGrayColor];
    } else {
        NSString *deviceName = [self.nameTextField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
        self.statusLabel.text = [NSString stringWithFormat:@"广播成功: %@\n设备不可连接", deviceName];
        self.statusLabel.textColor = [UIColor systemGreenColor];
        
        self.isAdvertising = YES;
        self.startButton.enabled = NO;
        self.stopButton.enabled = YES;
        self.startButton.backgroundColor = [UIColor systemGrayColor];
        self.stopButton.backgroundColor = [UIColor systemRedColor];
        NSLog(@"不可连接广播已成功启动: %@", deviceName);
    }
}

#pragma mark - UITextFieldDelegate

- (BOOL)textFieldShouldReturn:(UITextField *)textField {
    [textField resignFirstResponder];
    return YES;
}

- (void)textFieldDidBeginEditing:(UITextField *)textField {
    // 编辑时停止广播
    if (self.isAdvertising) {
        [self stopAdvertising];
    }
}

@end

main.m

#import <UIKit/UIKit.h>
#import "AppDelegate.h"

int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

info.plist

<key>NSBluetoothAlwaysUsageDescription</key>
<string>此应用需要蓝牙权限来发送BLE广播</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>此应用需要蓝牙权限来发送BLE广播</string>

三、源码分析

1. 项目架构概述

这是一个基于 CoreBluetooth 框架的 iOS 应用,主要功能是作为 BLE 外设发送不可连接的广播信号。

2. 核心组件分析

2.1 AppDelegate - 应用入口

objective-c

// 创建主窗口和根视图控制器
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
ViewController *viewController = [[ViewController alloc] init];
self.window.rootViewController = viewController;

  • 设置应用的主窗口

  • 初始化并设置 ViewController 为根视图控制器

  • 显示窗口

2.2 ViewController - 主控制器

2.2.1 UI 界面构建

界面元素:

  • titleLabel: 应用标题

  • nameTextField: 设备名称输入框

  • statusLabel: 状态显示标签

  • startButton/stopButton: 开始/停止广播按钮

  • infoLabel: 模式说明标签

布局特点:

  • 使用 Auto Layout 约束进行响应式布局

  • 支持安全区域适配

  • 美观的按钮样式和颜色配置

2.2.2 BLE 核心功能

初始化蓝牙:

objective-c

self.peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:dispatch_get_main_queue()];

  • 在主队列初始化外设管理器

  • 设置委托为 self

不可连接广播实现:

objective-c

NSDictionary *advertisementData = @{
    CBAdvertisementDataLocalNameKey : deviceName,
    CBAdvertisementDataServiceUUIDsKey : @[] // 空数组 - 关键!
};

关键点: 使用空的 Service UUIDs 数组使设备不可连接

2.2.3 状态管理

蓝牙状态处理:

objective-c

- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral

处理所有可能的蓝牙状态:

  • CBManagerStatePoweredOn: 蓝牙就绪

  • CBManagerStatePoweredOff: 蓝牙关闭

  • CBManagerStateUnauthorized: 未授权

  • CBManagerStateUnsupported: 不支持 BLE

广播状态反馈:

objective-c

- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error

处理广播启动成功或失败的情况

3. 功能流程分析

3.1 启动流程

  1. 应用启动 → AppDelegate 初始化窗口

  2. ViewController 加载 → 初始化 UI 和 BLE

  3. 检查蓝牙状态 → 更新界面状态

3.2 广播流程

  1. 用户输入设备名称

  2. 点击"开始广播"按钮

  3. 验证蓝牙状态和设备名称

  4. 启动不可连接广播

  5. 更新界面状态和按钮状态

3.3 停止流程

  1. 点击"停止广播"按钮

  2. 调用 stopAdvertising 方法

  3. 重置界面状态

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值