开源 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 鸿蒙应用 开发(一)工程文件分析-CSDN博客

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

推荐链接:

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

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

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

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

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

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

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

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

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

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

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

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

本章内容主要是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. 重置界面状态

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值