iOSでiBeaconを探す、AndroidでiBeaconを探す
ちょっとメモなので親切で無いのはご勘弁を。
AndroidとiOSの両方で、iBeaconのアドバタイズ・パケットを受信して領域検知を行うアプリを作るとする。
肝心要の領域検知を開始する開始の仕方。
iOSの場合。
var myLocationManager:CLLocationManager = CLLocationManager() var myBeaconRegion:CLBeaconRegion! myBeaconRegion = CLBeaconRegion(proximityUUID: uuid) myLocationManager.startMonitoringForRegion(myBeaconRegion)
Androidの場合。
APIだけで申し訳ないが、ちょっと時間が無いのでご勘弁を。
public boolean startLeScan(UUID[] serviceUuids, LeScanCallback callback)
ここで注目したいのが、UUID。
iOSの場合、CLBeaconRegionの引数のUUIDは、proximity UUID
。
実際のアドバタイズ・パケットに含まれているもの。
一方、Androidの場合、startLeScanの引数のUUIDはService UUID
。
GATTサーバーに登録済みのプロファイルを引っ掛けるもの……だと思う、たぶん。
もし特定のperipheralのみを探す場合, 代わりにstartLeScan(UUID[], BluetoothAdapter.LeScanCallback)を使うことができる. アプリケーションがサポートしているGATT serviceのUUIDのarrayを提供すればよい.
Yukiの枝折
同じ128bitのUUIDであっても、2つのUUIDのもたらす効果は全く別ってわけ。
まあ本家本元だけあってiOS
の方が断然使いやすくできているのは仕方ないとして、Android
の紛らわしさったら無い。
private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() { @Override public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) { ... } }; . . . mBluetoothAdapter.startLeScan(mLeScanCallback);
この様に、Androidでは、引数1つのstartLeScan()
を用いてBLEの検出をコールバックで待ち受けて、scanRecord
を解析するまでそれがiBeacon
形式のアドバタイズ・パケットかどうかはわからない。
……っていうの、本当かなあって疑ってる。
今ひとつBLEとかGATTのことがよく理解できていないのであれなんだけども。
サービスがこうで、
Bluetooth Developer Portal サービス
proximityのプロファイルがこうなわけでしょ。
Home > GATT Specifications > Profiles > Profile Viewer
<?xml version="1.0" encoding="UTF-8"?> <!-- Copyright 2011 Bluetooth SIG, Inc. All rights reserved. --> <Profile xsi:noNamespaceSchemaLocation="http://schemas.bluetooth.org/Documents/profile.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" type="org.bluetooth.profile.proximity" name="Proximity"> <InformativeText> <Abstract> The Proximity profile enables proximity monitoring between two devices. </Abstract> <Summary> The Proximity profile defines the behavior when a device moves away from a peer device so that the connection is dropped or the path loss increases above a preset level, causing an immediate alert. This alert can be used to notify the user that the devices have become separated. As a consequence of this alert, a device may take further action, for example to lock one of the devices so that it is no longer usable. <p> The Proximity profile can also be used to define the behavior when the two devices come closer together such that a connection is made or the path loss decreases below a preset level. </p> </Summary> </InformativeText> <Role name="Proximity Reporter"> <Service type="org.bluetooth.service.link_loss"> <Declaration>Primary</Declaration> <Requirement>Mandatory</Requirement> </Service> <Service type="org.bluetooth.service.immediate_alert"> <Declaration>Primary</Declaration> <Requirement>Optional</Requirement> </Service> <Service type="org.bluetooth.service.tx_power"> <Declaration>Primary</Declaration> <Requirement>Optional</Requirement> </Service> <Conditional condition="if_any_supported_all_must_be_supported"> <Type>org.bluetooth.service.immediate_alert</Type> <Type>org.bluetooth.service.tx_power</Type> </Conditional> </Role> <Role name="Proximity Monitor"> <Client type="org.bluetooth.service.link_loss"> <Requirement>Mandatory</Requirement> </Client> <Client type="org.bluetooth.service.immediate_alert"> <Requirement>Mandatory</Requirement> </Client> <Client type="org.bluetooth.service.tx_power"> <Requirement>Mandatory</Requirement> </Client> </Role> </Profile>
ビーコン側がたぶんProximity Reporter
だと思うんだけど、0000XXXX-0000-1000-8000-00805f9b34fb
のXXXXに、
- org.bluetooth.service.link_loss……0x1803
入れてやってUUID.fromString(String)
したのをstartLeScan()
に渡してやれば、GATTサーバに登録済みのサービス(BLE)を無制限にscanするんじゃなくて、iBeacon(proximity)だけ拾うんじゃ無いかなあ……と思う次第。
追記
ダメでした。
そっかー、proximityじゃないのか……。
噂のAndroid用iBeaconライブラリAltBeaconのソースを確認してみたけど、やっぱり引数1つのstartLeScan()
を使っている。
※ソース
こういう質問も出てるけど、今のところ回答なし。
Service UUIDはわかりませーん。
【今度こそ】カスタムセルに設置したUISwitchのindexPathを取得する
恥ずかしい恥ずかしい恥ずかしい!
昨日書いたarticle
が「カスタムセル uiswitch superview」でググると一番最初に表示されますが、解決方法が間違っています……orz
techmeganeyamada.hatenadiary.com
ちょっと言葉を変えてきちんと検索すると、正しい答えをグーグル先生は教えてくれました。
func handleTouchSwitch(sender: UISwitch) { let point: CGPoint = sender.layer.position let indexPath = self.tableView.indexPathForRowAtPoint(point) let object = objects[indexPath!.row] as! NSDate println(object) }
上記のやり方では、何番目のセルを選択したか、わかりません。
取得できるCGPoint
は何番目のセルを選択しても同じ座標を返してきます。
↑ 参考サイト1
昨日のarticle
ではUISwitch
のsuperview
を用いた方式だと上手くいかなかった、と書きました。
参考サイト1にも示されていますが、どうやらiOS
のバージョンによってセルの階層構造が変化したことが原因だったようです。
要するに、最初に参考にしたサイトが古いiOS
について書かれたものだったようですね。
参考サイト1にはiOS7
での階層構造が示されていますが、いつまたこれが変更になるか、わかりません。
次のiOS9
でまた変わってしまうかも。
となると、手動でカスタムセルのクラスにたどり着く方法はあまり賢明とは言えません。
↑ 参考サイト2
ああ、先人は偉大ですねえ。
参考サイト2ほど高度な解決策を今回は求めていないので、必要な箇所だけ採用させて頂きます。
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! CustomCell let object = objects[indexPath.row] as! NSDate cell.textLabel!.text = object.description cell.customSwitch.addTarget(self, action: "hundleSwitch:", forControlEvents: UIControlEvents.ValueChanged) return cell }
func hundleSwitch(sender: UISwitch) { var hoge = sender.superview while(hoge!.isKindOfClass(CustomCell) == false) { hoge = hoge!.superview } let cell = hoge as! CustomCell // touchIndexは選択したセルが何番目かを記録しておくプロパティ touchIndex = self.tableView.indexPathForCell(cell) }
こうしておけば、セルの階層構造がiOS
のバージョンアップで変化があったとしても対応できます。
ああ、赤っ恥……。
カスタムセルに設置したUISwitchのindexPathを取得する
シンプルなカスタムセルの作り方とセル内のボタンへのイベント設定方法 | Technology-Gym
↑参考サイト
な・ん・で !
ボタンだと簡単に何番目のセルかわかるのにスイッチだとわからんかな!
テーブルビューでカスタムセルにスイッチをくっつけた。
上記参考サイトの真似をしてon/off
切り替わったセルが何番目なのか知ろうとしたらXcodeがストライキ始めた。
addTarget
でUISwitch
にaction
メソッド設定する時に、引数にUIEvent
取るようにしても、実際にメソッド呼ばれた時にはevent
の中身が空っぽなので、何番目のセルに触っているかわかりませーん。
ってことらしい。
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! CustomCell let object = objects[indexPath.row] as! NSDate cell.textLabel!.text = object.description cell.customSwitch.addTarget(self, action: Selector("handleTouchSwitch:event:"), forControlEvents: UIControlEvents.ValueChanged) return cell }
extension TableViewController { func handleTouchSwitch(sender: UISwitch, event: UIEvent){ let indexPath = self.indexPathForControlEvent(event) let object = objects[indexPath.row] as! NSDate println(object) } func indexPathForControlEvent(event: UIEvent) -> NSIndexPath { let touch = event.allTouches()?.first as! UITouch let point = touch.locationInView(self.tableView) let indexPath = self.tableView.indexPathForRowAtPoint(point) return indexPath! } }
Stack Overflow
とか見ると、sender.superview
がセルで、cell.superview
がテーブルだから、っていう手法が紹介されてるんだけど、上手くいかない。
superview
をキャストしたところでUIView
はセルになれないから!
ハハッ!
とXcodeに言われた(ような気がする)。
仕方ないのでUISwitch
について調べたら、いいプロパティ発見。
これで何番目のセルかわかる。
追記:
わかりませんでした……orz
下記ソースでは何番目のセルを選んだかわかりません。
何番目のセルを選んでも同じ座標を示すCGPoint
を取得してしまいます。
ちゃんと何番目のセルを選んだかがわかるソースはこちらの記事で。
techmeganeyamada.hatenadiary.com
追記:下記ソースは間違い
func handleTouchSwitch(sender: UISwitch) { let point: CGPoint = sender.layer.position let indexPath = self.tableView.indexPathForRowAtPoint(point) let object = objects[indexPath!.row] as! NSDate println(object) }
たまにはググるより、ctrl
+cmd
+j
の方が役立つこともある。
……てかたぶんだけど、ボタンもUIView
だから同じ手法でうまくいくと思うよ?
以上。