在上一篇中介紹了打印小票所須要的命令,這一篇介紹Bluetooth鏈接藍牙和打印小票的全過程。git
由於CoreBluetooth中的代理太多,而每一次操做又比較依賴上一次操做的結果,方法又比較零散,因此我作了粗略封裝,把代理改爲了block方式回調。github
HLBLEManager *manager = [HLBLEManager sharedInstance];
__weak HLBLEManager *weakManager = manager;
manager.stateUpdateBlock = ^(CBCentralManager *central) {
NSString *info = nil;
switch (central.state) {
case CBCentralManagerStatePoweredOn:
info = @"藍牙已打開,而且可用";
//三種種方式
// 方式1
[weakManager scanForPeripheralsWithServiceUUIDs:nil options:nil];
// 方式2
[central scanForPeripheralsWithServices:nil options:nil];
// 方式3
[weakManager scanForPeripheralsWithServiceUUIDs:nil options:nil didDiscoverPeripheral:^(CBCentralManager *central, CBPeripheral *peripheral, NSDictionary *advertisementData, NSNumber *RSSI) {
}];
break;
case CBCentralManagerStatePoweredOff:
info = @"藍牙可用,未打開";
break;
case CBCentralManagerStateUnsupported:
info = @"SDK不支持";
break;
case CBCentralManagerStateUnauthorized:
info = @"程序未受權";
break;
case CBCentralManagerStateResetting:
info = @"CBCentralManagerStateResetting";
break;
case CBCentralManagerStateUnknown:
info = @"CBCentralManagerStateUnknown";
break;
}
[SVProgressHUD setDefaultStyle:SVProgressHUDStyleDark];
[SVProgressHUD showInfoWithStatus:info ];
};
複製代碼
由於CBCentralManager一建立,就會在代理中返回藍牙模塊的狀態,因此及時設置狀態返回的回調,以便在搜索附近可用的藍牙外設。web
// 方式1
[weakManager scanForPeripheralsWithServiceUUIDs:nil options:nil];
// 方式2
[central scanForPeripheralsWithServices:nil options:nil];
// 方式3
[weakManager scanForPeripheralsWithServiceUUIDs:nil options:nil didDiscoverPeripheral:^(CBCentralManager *central, CBPeripheral *peripheral, NSDictionary *advertisementData, NSNumber *RSSI) {
}];
複製代碼
這裏給出了三種方式,前兩種方式都須要先設置好搜索到藍牙外設以後的回調, 即:bash
manager.discoverPeripheralBlcok = ^(CBCentralManager *central, CBPeripheral *peripheral, NSDictionary *advertisementData, NSNumber *RSSI) {
if (peripheral.name.length <= 0) {
return ;
}
if (self.deviceArray.count == 0) {
NSDictionary *dict = @{@"peripheral":peripheral, @"RSSI":RSSI};
[self.deviceArray addObject:dict];
} else {
BOOL isExist = NO;
for (int i = 0; i < self.deviceArray.count; i++) {
NSDictionary *dict = [self.deviceArray objectAtIndex:i];
CBPeripheral *per = dict[@"peripheral"];
if ([per.identifier.UUIDString isEqualToString:peripheral.identifier.UUIDString]) {
isExist = YES;
NSDictionary *dict = @{@"peripheral":peripheral, @"RSSI":RSSI};
[_deviceArray replaceObjectAtIndex:i withObject:dict];
}
}
if (!isExist) {
NSDictionary *dict = @{@"peripheral":peripheral, @"RSSI":RSSI};
[self.deviceArray addObject:dict];
}
}
[self.tableView reloadData];
};
}
複製代碼
第三種方式,則附帶一個block,便於直接處理。網絡
HLBLEManager *manager = [HLBLEManager sharedInstance];
[manager connectPeripheral:_perpheral
connectOptions:@{CBConnectPeripheralOptionNotifyOnDisconnectionKey:@(YES)}
stopScanAfterConnected:YES
servicesOptions:nil
characteristicsOptions:nil
completeBlock:^(HLOptionStage stage, CBPeripheral *peripheral, CBService *service, CBCharacteristic *character, NSError *error) {
switch (stage) {
case HLOptionStageConnection:
{
if (error) {
[SVProgressHUD showErrorWithStatus:@"鏈接失敗"];
} else {
[SVProgressHUD showSuccessWithStatus:@"鏈接成功"];
}
break;
}
case HLOptionStageSeekServices:
{
if (error) {
[SVProgressHUD showSuccessWithStatus:@"查找服務失敗"];
} else {
[SVProgressHUD showSuccessWithStatus:@"查找服務成功"];
[_infos addObjectsFromArray:peripheral.services];
[_tableView reloadData];
}
break;
}
case HLOptionStageSeekCharacteristics:
{
// 該block會返回屢次,每個服務返回一次
if (error) {
NSLog(@"查找特性失敗");
} else {
NSLog(@"查找特性成功");
[_tableView reloadData];
}
break;
}
case HLOptionStageSeekdescriptors:
{
// 該block會返回屢次,每個特性返回一次
if (error) {
NSLog(@"查找特性的描述失敗");
} else {
NSLog(@"查找特性的描述成功");
}
break;
}
default:
break;
}
}];
複製代碼
由於鏈接藍牙外設--->掃描藍牙外設服務--->掃描藍牙外設服務特性--->掃描特性描述app
這些操做都是有階段性的,而且依賴上一步的結果。ide
這裏我也給出了兩種方式:佈局
方式一(推薦):如上面代碼同樣,設置最後一個參數block,而後在block中判斷當前是哪一個階段的回調。測試
方式二:提早設置好每一階段的block,而後設置方法中最後一個參數的block爲nil。字體
/** 鏈接外設完成的回調 */
@property (copy, nonatomic) HLConnectCompletionBlock connectCompleteBlock;
/** 發現服務的回調 */
@property (copy, nonatomic) HLDiscoveredServicesBlock discoverServicesBlock;
/** 發現服務中的特性的回調 */
@property (copy, nonatomic) HLDiscoverCharacteristicsBlock discoverCharacteristicsBlock;
/** 發現特性的描述的回調 */
@property (copy, nonatomic) HLDiscoverDescriptorsBlock discoverDescriptorsBlock;
複製代碼
記錄下特性中的可寫服務以便,往這個藍牙外設中寫入數據。
CBCharacteristic *character = [service.characteristics objectAtIndex:indexPath.row];
CBCharacteristicProperties properties = character.properties;
if (properties & CBCharacteristicPropertyWriteWithoutResponse) {
self.chatacter = character;
}
複製代碼
NSString *title = @"測試電商";
NSString *str1 = @"測試電商服務中心(銷售單)";
HLPrinter *printer = [[HLPrinter alloc] init];
[printer appendText:title alignment:HLTextAlignmentCenter fontSize:HLFontSizeTitleBig];
[printer appendText:str1 alignment:HLTextAlignmentCenter];
[printer appendBarCodeWithInfo:@"RN3456789012"];
[printer appendSeperatorLine];
[printer appendTitle:@"時間:" value:@"2016-04-27 10:01:50" valueOffset:150];
[printer appendTitle:@"訂單:" value:@"4000020160427100150" valueOffset:150];
[printer appendText:@"地址:深圳市南山區學府路東深大店" alignment:HLTextAlignmentLeft];
[printer appendSeperatorLine];
[printer appendLeftText:@"商品" middleText:@"數量" rightText:@"單價" isTitle:YES];
CGFloat total = 0.0;
for (NSDictionary *dict in goodsArray) {
[printer appendLeftText:dict[@"name"] middleText:dict[@"amount"] rightText:dict[@"price"] isTitle:NO];
total += [dict[@"price"] floatValue] * [dict[@"amount"] intValue];
}
[printer appendSeperatorLine];
NSString *totalStr = [NSString stringWithFormat:@"%.2f",total];
[printer appendTitle:@"總計:" value:totalStr];
[printer appendTitle:@"實收:" value:@"100.00"];
NSString *leftStr = [NSString stringWithFormat:@"%.2f",100.00 - total];
[printer appendTitle:@"找零:" value:leftStr];
[printer appendFooter:nil];
[printer appendImage:[UIImage imageNamed:@"ico180"] alignment:HLTextAlignmentCenter maxWidth:300];
NSData *mainData = [printer getFinalData];
複製代碼
HLBLEManager *bleManager = [HLBLEManager sharedInstance];
[bleManager writeValue:mainData forCharacteristic:self.chatacter type:CBCharacteristicWriteWithoutResponse];
複製代碼
寫入數據後,藍牙打印機就會開始打印小票。
HLPrinter *printer = [[HLPrinter alloc] init];
複製代碼
在建立這個打印機操做對象時,內部作了不少預設置:
- (instancetype)init
{
self = [super init];
if (self) {
[self defaultSetting];
}
return self;
}
- (void)defaultSetting
{
_printerData = [[NSMutableData alloc] init];
// 1.初始化打印機
Byte initBytes[] = {0x1B,0x40};
[_printerData appendBytes:initBytes length:sizeof(initBytes)];
// 2.設置行間距爲1/6英寸,約34個點
// 另外一種設置行間距的方法看這個 @link{-setLineSpace:}
Byte lineSpace[] = {0x1B,0x32};
[_printerData appendBytes:lineSpace length:sizeof(lineSpace)];
// 3.設置字體:標準0x00,壓縮0x01;
Byte fontBytes[] = {0x1B,0x4D,0x00};
[_printerData appendBytes:fontBytes length:sizeof(fontBytes)];
}
複製代碼
能夠打印的內容包括:文字、二維碼、條形碼、圖片。 而對這些內容的處理已經作了封裝,只須要簡單調用某些API便可。
/**
* 添加單行標題,默認字號是小號字體
*
* @param title 標題名稱
* @param alignment 標題對齊方式
*/
- (void)appendText:(NSString *)text alignment:(HLTextAlignment)alignment;
/**
* 添加單行標題
*
* @param title 標題名稱
* @param alignment 標題對齊方式
* @param fontSize 標題字號
*/
- (void)appendText:(NSString *)text alignment:(HLTextAlignment)alignment fontSize:(HLFontSize)fontSize;
複製代碼
/**
* 添加單行信息,左邊名稱(左對齊),右邊實際值(右對齊)。
* @param title 名稱
* @param value 實際值
* @param fontSize 字號大小
* 警告:因字號和字體與iOS中字體不一致,計算出來有偏差
*/
- (void)appendTitle:(NSString *)title value:(NSString *)value fontSize:(HLFontSize)fontSize;
/**
* 設置單行信息,左標題,右實際值
*
* @param title 標題
* @param value 實際值
* @param offset 實際值偏移量
*/
- (void)appendTitle:(NSString *)title value:(NSString *)value valueOffset:(NSInteger)offset;
/**
* 設置單行信息,左標題,右實際值
*
* @param title 標題
* @param value 實際值
* @param offset 實際值偏移量
* @param fontSize 字號
*/
- (void)appendTitle:(NSString *)title value:(NSString *)value valueOffset:(NSInteger)offset fontSize:(HLFontSize)fontSize;
複製代碼
/**
* 添加選購商品信息標題,通常是三列,名稱、數量、單價
*
* @param LeftText 左標題
* @param middleText 中間標題
* @param rightText 右標題
*/
- (void)appendLeftText:(NSString *)left middleText:(NSString *)middle rightText:(NSString *)right isTitle:(BOOL)isTitle;
複製代碼
/**
* 添加條形碼圖片
*
* @param info 條形碼中包含的信息,默認居中顯示,最大寬度爲300。若是大於300,會等比縮放。
*/
- (void)appendBarCodeWithInfo:(NSString *)info;
/**
* 添加條形碼圖片
*
* @param info 條形碼中的信息
* @param alignment 圖片對齊方式
* @param maxWidth 圖片最大寬度
*/
- (void)appendBarCodeWithInfo:(NSString *)info alignment:(HLTextAlignment)alignment maxWidth:(CGFloat)maxWidth;
複製代碼
/**
* 添加二維碼圖片
*
* @param info 二維碼中的信息
*/
- (void)appendQRCodeWithInfo:(NSString *)info;
/**
* 添加二維碼圖片
*
* @param info 二維碼中的信息
* @param centerImage 二維碼中間的圖片
* @param alignment 對齊方式
* @param maxWidth 二維碼的最大寬度
*/
- (void)appendQRCodeWithInfo:(NSString *)info centerImage:(UIImage *)centerImage alignment:(HLTextAlignment)alignment maxWidth:(CGFloat )maxWidth;
複製代碼
/**
* 添加圖片,通常是添加二維碼或者條形碼
*
* @param image 圖片
* @param alignment 圖片對齊方式
* @param maxWidth 圖片的最大寬度,若是圖片過大,會等比縮放
*/
- (void)appendImage:(UIImage *)image alignment:(HLTextAlignment)alignment maxWidth:(CGFloat)maxWidth;
複製代碼
/**
* 添加一條分割線,like this:---------------------------
*/
- (void)appendSeperatorLine;
複製代碼
/**
* 添加底部信息
*
* @param footerInfo 不填默認爲 謝謝惠顧,歡迎下次光臨!
*/
- (void)appendFooter:(NSString *)footerInfo;
複製代碼
/**
* 獲取最終的data
*
* @return 最終的data
*/
- (NSData *)getFinalData;
複製代碼
而HLPrinter內部實際有一些私有方法,都是對上一篇內容中打印機命令的封裝,做爲基礎操做 例如:
/**
* 換行
*/
- (void)appendNewLine
{
Byte nextRowBytes[] = {0x0A};
[_printerData appendBytes:nextRowBytes length:sizeof(nextRowBytes)];
}
/**
* 回車
*/
- (void)appendReturn
{
Byte returnBytes[] = {0x0D};
[_printerData appendBytes:returnBytes length:sizeof(returnBytes)];
}
/**
* 設置對齊方式
*
* @param alignment 對齊方式:居左、居中、居右
*/
- (void)setAlignment:(HLTextAlignment)alignment
{
Byte alignBytes[] = {0x1B,0x61,alignment};
[_printerData appendBytes:alignBytes length:sizeof(alignBytes)];
}
/**
* 設置字體大小
*
* @param fontSize 字號
*/
- (void)setFontSize:(HLFontSize)fontSize
{
Byte fontSizeBytes[] = {0x1D,0x21,fontSize};
[_printerData appendBytes:fontSizeBytes length:sizeof(fontSizeBytes)];
}
複製代碼
在UIImage+Bitmap
中,主要是對圖片操做的兩個Category,一個是建立二維碼、條形碼圖片。
另外一個是將圖片轉換爲點陣圖數據。
可能對於小票的樣式不只僅侷限於封裝的幾種,有人提到左邊二維碼圖片,右邊居中顯示一些文字的佈局方式,這樣用原來的指令集組合的方式就很難實現。
對於一些不太好弄的佈局樣式,咱們能夠曲線救國,這裏有一些新的場景和解決方案:
用UIWebView打印的方式,還能夠在線修改訂單的樣式和佈局,就是比較浪費墨,沒有指令集組合的方式打印出來的清晰。 如下是利用UIWebView,而後獲取WebView快照打印出來的小票:
獲取UIWebView的完整內容截圖的方法:
/**
* 獲取當前加載的網頁的截圖
* 獲取當前WebView的size,而後一屏一屏的截圖後,再拼接成一張完整的圖片
*
* @return
*/
- (UIImage *)imageForWebView
{
// 1.獲取WebView的寬高
CGSize boundsSize = self.bounds.size;
CGFloat boundsWidth = boundsSize.width;
CGFloat boundsHeight = boundsSize.height;
// 2.獲取contentSize
CGSize contentSize = self.scrollView.contentSize;
CGFloat contentHeight = contentSize.height;
// 3.保存原始偏移量,便於截圖後復位
CGPoint offset = self.scrollView.contentOffset;
// 4.設置最初的偏移量爲(0,0);
[self.scrollView setContentOffset:CGPointMake(0, 0)];
NSMutableArray *images = [NSMutableArray array];
while (contentHeight > 0) {
// 5.獲取CGContext 5.獲取CGContext
UIGraphicsBeginImageContextWithOptions(boundsSize, NO, 0.0);
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 6.渲染要截取的區域
[self.layer renderInContext:ctx];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// 7.截取的圖片保存起來
[images addObject:image];
CGFloat offsetY = self.scrollView.contentOffset.y;
[self.scrollView setContentOffset:CGPointMake(0, offsetY + boundsHeight)];
contentHeight -= boundsHeight;
}
// 8 webView 恢復到以前的顯示區域
[self.scrollView setContentOffset:offset];
CGFloat scale = [UIScreen mainScreen].scale;
CGSize imageSize = CGSizeMake(contentSize.width * scale,
contentSize.height * scale);
// 9.根據設備的分辨率從新繪製、拼接成完整清晰圖片
UIGraphicsBeginImageContext(imageSize);
[images enumerateObjectsUsingBlock:^(UIImage *image, NSUInteger idx, BOOL *stop) {
[image drawInRect:CGRectMake(0,
scale * boundsHeight * idx,
scale * boundsWidth,
scale * boundsHeight)];
}];
UIImage *fullImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return fullImage;
}
複製代碼
想要體驗這種方式的能夠在BLEDetailViewController
的viewDidLoad
方法中,將導航欄右按鈕的註釋修改下:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.title = @"藍牙詳情";
// UIBarButtonItem *rightItem = [[UIBarButtonItem alloc] initWithTitle:@"商品" style:UIBarButtonItemStylePlain target:self action:@selector(goToShopping)];
UIBarButtonItem *rightItem = [[UIBarButtonItem alloc] initWithTitle:@"網絡訂單" style:UIBarButtonItemStylePlain target:self action:@selector(goToOrder)];
self.navigationItem.rightBarButtonItem = rightItem;
_infos = [[NSMutableArray alloc] init];
_tableView.rowHeight = 60;
//鏈接藍牙並展現詳情
[self loadBLEInfo];
}
複製代碼
補充一些參數: 據佳博的一技術人員提供的一些參數:
完整的庫和Demo地址:github地址
若是你只關注iOS 打印小票部分,不想太多操做藍牙鏈接和處理,看這裏:藍牙打印小票
首先,肯定你使用的是標籤打印機仍是通常的小票打印機。
我寫的Demo不支持標籤打印機,你能夠仿照個人例子,本身封裝一下指令(咱們並無採購標籤打印機,也沒辦法測試,抱歉了)。
若是你鏈接成功,可是發出打印指令後,打印機沒反應,頗有多是由於你的打印機一次發送的數據長度小於146,你把146改的更小一點試試看。
我測試的兩臺佳博打印機,一臺沒有長度限制,一臺最多每次只能發送146個字節,不然會出現打印沒反應的狀況,須要重啓打印機。
不一樣的打印機,可能對長度的限制不太同樣,據羣友反應有的打印機只能支持一次發送20個字節,因此你須要將宏裏面的146改的更小一點。