ScanUI - novars-jp/MaBeeeiOSSDK GitHub Wiki

概要

  • MaBeeeDeviceのスキャン・接続をするサンプルプロジェクトです。
  • samples配下のScanUI(Objective-C), ScanUISwift(Swift)がそれになります。言語は違いますが内容は全く同じです。

ライブラリのインポート

Embedded Binaries

  • ViewControllerにインポート宣言を追加します。

  • Objective-C

@import MaBeeeSDK;
  • Swift
import MaBeeeSDK

UIの作成

1. Storyboard

  1. ScanボタンとSliderがある画面を作成します。
  2. Navigation Controllerを追加します。サンプルではRoot View ControllerのタイトルをScan View Controllerに変更してあります。
  3. ScanTableViewControllerクラスを作成して、Storyboard上のViewControllerに対応させます。

ScanUI

ScanTableViewController

2. Scanボタン

  • 実行してScanボタンを押すと、Navigation ControllerがModalで表示されるようにします。
  • ScanボタンのTriggered Seguesのacionと、追加したNavigation Controllerを接続して、Present Modallyを選択します。

3. Slider

  • こちらはFirstMaBeeeと同じようにViewControllerクラスに接続して、ソースを記述します。

Objective-C

- (IBAction)sliderValueChanged:(UISlider *)slider {
  for (MaBeeeDevice *device in MaBeeeApp.instance.devices) {
    device.pwmDuty = (int)(slider.value * 100);
  }
}

Swift

@IBAction func sliderValueChanged(slider: UISlider) {
  for device in MaBeeeApp.instance().devices() {
    device.pwmDuty = Int32(slider.value * 100)
  }
}

ScanTableViewController

  • 作成してStoryboardに紐付けた新しいクラスです。このクラスでスキャン・接続などの説明をします。

1. Doneボタンの設置

  • 本題に入る前に、Doneボタンを作成してScanTableViewControllerを閉じれるようにします。

Objective-C

- (void)viewDidLoad {
  [super viewDidLoad];
  self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc]
                                           initWithBarButtonSystemItem:UIBarButtonSystemItemDone
                                           target:self
                                           action:@selector(doneButtonPressed:)];
}

- (void)doneButtonPressed:(UIBarButtonItem *)doneButton {
  [self dismissViewControllerAnimated:YES completion:NULL];
}

Swift

override func viewDidLoad() {
  super.viewDidLoad()
  self.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .Done,
                                                          target: self,
                                                          action: #selector(doneButtonPressed))
}

func doneButtonPressed() {
  self.dismissViewControllerAnimated(true, completion: nil)
}

スキャンの開始と停止

  • スキャンの開始はMaBeeeAppクラスのstartScan関数で行ないます。コールバックを指定するとスキャンにヒットしたデバイスを引数に呼ばれます。
  • スキャンの停止はMaBeeeAppクラスのstopScan関数で行ないます。
  • ここではviewDidAppearでスキャン開始、viewWillDisappearでスキャンを停止します。
  • 単純にヒットしたMaBeeeDeviceをコンソールに出力しています。

Objective-C

- (void)viewDidAppear:(BOOL)animated {
  [super viewDidAppear:animated];
  [MaBeeeApp.instance startScan:^(NSArray<MaBeeeDevice *> *devices) {
    NSLog(@"%@", devices);
  }];
}

- (void)viewWillDisappear:(BOOL)animated {
  [super viewWillDisappear:animated];
  [MaBeeeApp.instance stopScan];
}

Swift

override func viewDidAppear(animated: Bool) {
  super.viewDidAppear(animated)
  MaBeeeApp.instance().startScan({(devices: [MaBeeeDevice]?) in
    print(devices)
  })
}

override func viewWillDisappear(animated: Bool) {
  super.viewDidAppear(animated)
  MaBeeeApp.instance().stopScan()
}

実行

  • この状態でビルド、実行してみます。
  • iOSのBluetoothが有効になっているか確認してください。
  • MaBeeeをセットしたおもちゃ等の電源がONになっているか確認してください。
  • Scanボタンを押すと、作成したScanTableViewControllerが表示されます。
  • コンソールに以下のようなログが出力されます。この例では2台スキャンにヒットしています。
// Objective-C
2016-08-22 16:58:28.653 ScanUI[2674:549912] (
    "<MaBeeeDevice: 0x16d483d0>",
    "<MaBeeeDevice: 0x16e5ba80>",
)

// Swift
[<MaBeeeDevice: 0x155b8240>, <MaBeeeDevice: 0x156b4b20>]

テーブルビューへの表示

  • スキャンにヒットしたMaBeeeDeviceをテーブルビューに表示します。

1. メンバ変数の追加

  • セルに表示するためにクラスのメンバ変数として、MaBeeeDeviceの配列を宣言します。

Objective-C

@interface ScanTableViewController ()
@property (nonatomic, strong) NSArray<MaBeeeDevice *> *devices;
@end

Swift

var maBeeeDevices:[MaBeeeDevice]?

2. MaBeeeDeviceの配列の保持

  • viewDidAppearのstartScanのコールバックに、デバイスの保持とテーブルビューのリロードを追記します。

Objective-C

[MaBeoeeApp.instance startScan:^(NSArray<MaBeeeDevice *> *devices) {
  NSLog(@"%@", devices);
  self.devices = devices;
  [self.tableView reloadData];
}];

Swift

MaBeeeApp.instance().startScan({(devices: [MaBeeeDevice]!) in
  print(devices)
  self.maBeeeDevices = devices
  self.tableView.reloadData()
})

3. セルの表示

  • numberOfRowsInSection, cellForRowAtIndexPathの2つの関数をoverrideします。
  • numberOfRowsInSectionでは、保持しているMaBeeeDeviceの配列の要素数を返します。
  • cellForRowAtIndexPathでは、MaBeeeDeviceを取得して、name, rssi, stateを整形してセルに表示しています。
  • stateを文字列にするためにstateStringという関数を準備しました。

Objective-C

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
  return self.devices.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MaBeeeDeviceCell"];
  MaBeeeDevice *device = self.devices[indexPath.row];
  NSString *state = [self stateString:device];
  cell.textLabel.text = [NSString stringWithFormat:@"%@, %d, %@", device.name, device.rssi, state];
  return cell;
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  if let devices = maBeeeDevices {
    return devices.count;
  }
  return 0;
}

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
  let cell = tableView.dequeueReusableCellWithIdentifier("MaBeeeDeviceCell", forIndexPath: indexPath)
  if let device = maBeeeDevices?[indexPath.row] {
    cell.textLabel?.text = String(format: "%@, %d, %@",
                                  device.name,
                                  device.rssi,
                                  self.stateString(device.state))
  }
  return cell;
}

4. 実行

  • ビルドして実行すると、以下のような画面が表示されます。

scan_table_view

接続・切断・仕上げ

1. 接続

  • セルをタップして、選択されたデバイスに接続できるようにします。
  • didSelectRowAtIndexPath関数をoverrideします。
  • MaBeeeDeviceを取得して、stateがDisconnectedの場合は接続、それ以外は切断します。

Objctive-C

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
  [tableView deselectRowAtIndexPath:indexPath animated:YES];
  MaBeeeDevice *device = self.devices[indexPath.row];
  switch (device.state) {
    case MaBeeeDeviceStateDisconnected:
      [MaBeeeApp.instance connect:device];
      break;
    case MaBeeeDeviceStateConnecting:
      [MaBeeeApp.instance disconnect:device];
      break;
    case MaBeeeDeviceStateConnected:
      [MaBeeeApp.instance disconnect:device];
      break;
  }
}

Swift

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
  tableView.deselectRowAtIndexPath(indexPath, animated: true)
  if let device = maBeeeDevices?[indexPath.row] {
    switch (device.state) {
    case .Disconnected:
      MaBeeeApp.instance().connect(device)
    case .Connecting:
      MaBeeeApp.instance().disconnect(device)
    case .Connected:
      MaBeeeApp.instance().disconnect(device)
    }
  }
}

2. 仕上げ・通知の設定

  • この状態でも動作しますが、接続の状態をすぐに反映するために、通知を受け取ってテーブルビューを更新するようにします。
  • viewDidAppearでaddObserver、viewWillDisappearでremoveObserverを呼びます。
  • この例では通知を受けた場合、無条件にテーブルビューをリロードしています。

Objective-C

- (void)viewDidAppear:(BOOL)animated {
  [super viewDidAppear:animated];
  [MaBeeeApp.instance addObserver:self selector:@selector(receiveNotification:)];
  [MaBeeeApp.instance startScan:^(NSArray<MaBeeeDevice *> *devices) {
    NSLog(@"%@", devices);
    self.devices = devices;
    [self.tableView reloadData];
  }];
}

- (void)viewWillDisappear:(BOOL)animated {
  [super viewWillDisappear:animated];
  [MaBeeeApp.instance removeObserver:self];
  [MaBeeeApp.instance stopScan];
}

- (void)receiveNotification:(NSNotification *)notification {
  [self.tableView reloadData];
}

Swift

override func viewDidAppear(animated: Bool) {
  super.viewDidAppear(animated)
  MaBeeeApp.instance().addObserver(self, selector: #selector(receiveNotification(_:)))
  MaBeeeApp.instance().startScan({(devices: [MaBeeeDevice]!) in
    print(devices)
    self.maBeeeDevices = devices
    self.tableView.reloadData()
  })
}

override func viewWillDisappear(animated: Bool) {
  super.viewDidAppear(animated)
  MaBeeeApp.instance().removeObserver(self)
  MaBeeeApp.instance().stopScan()
}

func receiveNotification(notification: NSNotification) {
  self.tableView.reloadData()
}

3. 実行

  • 実行して、セルをタップすると接続して、以下のようにConnectedという表示になります。
  • この状態でScanTableViewControllerを閉じて、スライダーを操作するとMaBeeeDeviceの出力が変更できます。

scan_table_view_connected

stateString関数

  • MaBeeeDeviceをセルに表示するときに、MaBeeeDeviceのstateを文字列にする関数です。
  • 必須ではありませんが、わかりやすくするために関数としました。
  • あまり本題とは関係ないです。

Objective-C

- (NSString *)stateString:(MaBeeeDevice *)device {
  NSString *stateString;
  switch (device.state) {
    case MaBeeeDeviceStateDisconnected:
      stateString = @"Disconnected";
      break;
    case MaBeeeDeviceStateConnecting:
      stateString = @"Connecting";
      break;
    case MaBeeeDeviceStateConnected:
      stateString = @"Connected";
      break;
  }
  return stateString;
}

Swift

func stateString(state: MaBeeeDeviceState) -> String {
  var stateString: String
  switch (state) {
  case .Disconnected:
    stateString = "Disconnected"
  case .Connecting:
    stateString = "Connecting"
  case .Connected:
    stateString = "Connected"
  }
  return stateString
}