Cocoa練習帳 -14ページ目

関東61回Cocoa勉強会のお知らせです

日時: 2013/9/7(土) 13:00-17:00
会場:新宿三丁目 新宿伊藤ビル 4F
集合:現地 会費:500円
見学申込  http://www.cocoa-study.com/mail/

[iOS]AR(位置情報)

現在地情報を取得してみる。


プロジェクトに、CoreLocation.frameworkを追加する。


CLLocationManagerDelegateプロトコルを採用する。


#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
 
@interface ViewController : UIViewController <CLLocationManagerDelegate>
 
@property (weak, nonatomic) IBOutlet UIView *augmentedRealityView;
 
@end

CLLocationManagerクラスのインスタンスを生成して、現在地の更新を開始する。


    self.locationManager = [[CLLocationManager alloc] init];
    self.locationManager.delegate = self;
    [self.locationManager startUpdatingLocation];

現在地の更新を受け取る。


- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
    CLLocation  *newLocation = [locations lastObject];
    CLLocationCoordinate2D  newCoordinate = newLocation.coordinate;
    DBGMSG(@"new latitude=%f, longitude=%f", newCoordinate.latitude, newCoordinate.longitude);
}

デバイスの情報も取得してみよう。


電子コンパスが仕様可能な場合は、測定を開始する。


    if ([CLLocationManager headingAvailable]) {
        [self.locationManager startUpdatingHeading];
    }

方位の更新を受け取る。


- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading
{
    DBGMSG(@"trueHeading %f, magneticHeading %f", newHeading.trueHeading, newHeading.magneticHeading);
}

ソースコード
GitHubからどうぞ。

関連情報

【Cocoa練習帳】
http://ameblo.jp/bitz/(ミラー・サイト)
Ios Sensor Programming: Iphone and Ipad Apps Wi.../Oreilly & Associates Inc
¥4,163
Amazon.co.jp

[iOS]AR(カメラのライブプレビュー表示)

話題となっているAR(AugmentedReality)やビーコン(Beacon)について実験だ!

iOS Sensor Programming/Oreilly & Associates Inc
¥4,072
Amazon.co.jp

とりあえず、プロジェクトに、AVFoundation.frameworkとCoreMedia.framework、CoreVideo.frameworkを追加しておく。

プロジェクトのInfo.plistのRequired device capabilitiesに、still-cameraとauto-focus-camera、front-facing-camera、camera-flash、video-cameraを追加しておく。

ViewControllerにプレビューを表示するビューを用意する。

@interface ViewController : UIViewController
 
@property (weak, nonatomic) IBOutlet UIView *augmentedRealityView;
 
@end

デバイスモードの設定。フラッシュ(トーチ)を自動に設定。

    /* Set torch and flash mode to auto */
    if ([[self _backFacingCamera] hasFlash]) {
		
			
				
			
			
		
	
	
		
			
				
			
			
		
	

カメラデバイスの初期化。背面カメラを利用する事にした。

- (AVCaptureDevice *)_cameraWithPosition:(AVCaptureDevicePosition)position
{
    NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
    for (AVCaptureDevice *device in devices) {
        if ([device position] == position) {
            return device;
        }
    }
    return nil;
}
 
- (AVCaptureDevice *)_backFacingCamera
{
    return [self _cameraWithPosition:AVCaptureDevicePositionBack];
}
 
	
    /* Init the device inputs */
    self.videoInput = [[AVCaptureDeviceInput alloc] initWithDevice:[self _backFacingCamera]
                                                             error:nil];
	

動画取り込みのセッションを生成して、入力源として背面カメラを設定する。

    /* Create session (use default AVCaptureSessionPresetHigh) */
    self.capureSession = [[AVCaptureSession alloc] init];
    
    /* Add inputs and output to the capture session */
    if ([self.capureSession canAddInput:self.videoInput]) {
        [self.capureSession addInput:self.videoInput];
    }

ライブプレビューのレイヤーを生成して、それをプレビューを表示するビューに追加する。

    /* Create video preview layer and add it to the UI */
    self.captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.capureSession];
    CALayer *viewLayer = [self.augmentedRealityView layer];
    [viewLayer setMasksToBounds:YES];
    
    CGRect bounds = [self.augmentedRealityView bounds];
    [self.captureVideoPreviewLayer setFrame:bounds];
    
    if ([self.captureVideoPreviewLayer isOrientationSupported]) {
        [self.captureVideoPreviewLayer setOrientation:AVCaptureVideoOrientationPortrait];
    }
    
    [self.captureVideoPreviewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
    
    [viewLayer insertSublayer:self.captureVideoPreviewLayer below:[[viewLayer sublayers] objectAtIndex:0]];

動画取り込みを開始!

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [self.capureSession startRunning];
    });

上記のコードは、ViewControllerの-viewDidLoadに記述したのだが、表示されない!色々調べて分かったのだが、プレビューを表示するビューのサイズが0だ!Storyboardを使っているのだが何故だ!

なんとかして原因は分かった。古いOSをサポートする場合が多いため、StoryboardのAutolayoutを使ってこなかったのだが、Autolayoutを使う場合、-viewDidLoadが呼ばれたタイミングでは、サブ・ビューのサイズが決まっていないというのが理由のようだ。

-viewDidLayoutSubviews:というメソッドがあるので、そこでライブプレビューのレイヤのサイズを設定すればいい。

- (void)viewWillLayoutSubviews
{
    [super viewWillLayoutSubviews];
    
    CGRect bounds = [self.augmentedRealityView bounds];
    [self.captureVideoPreviewLayer setFrame:bounds];
}

AR


ソースコード GitHubからどうぞ。
https://github.com/murakami/workbook/tree/master/ios/AugmentedReality - GitHub
関連情報 Augmented Reality in iOS: Building Apps With Sensors and Computer Vision
iOS Sensor Programming: iPhone and iPad Apps With Arduino, Augmented Reality, and Geolocation
【Cocoa練習帳】 http://www.bitz.co.jp/weblog/
http://ameblo.jp/bitz/(ミラー・サイト)
Augmented Reality in Ios: Building Apps With Se.../Oreilly & Associates Inc
¥2,024
Amazon.co.jp

関東第60回Cocoa勉強会

今回は、新宿三丁目で開催。
Bluetooth LEや、IO Kitドライバ、暗号と復号、検出関連のサービスの紹介、圧縮と解凍について発表があった。

関東第60回Cocoa勉強会


【Cocoa練習帳】
http://www.bitz.co.jp/weblog/

http://ameblo.jp/bitz/(ミラー・サイト)

[OSX]ドライバについて(driver)(その2)

ドライバーがロードされたかどうかが分かっても面白くないということで、対象のUSB機器が抜き差しされたら、ログが出力される事を確認することにした。




以前紹介した、DarwinのソースコードのIOUSBFamily-(バージョン番号).tar.gzのExamplesにあるVendorSpecific Driverのプロジェクトを開いて、Info.plistを確認する。




VendorSpecificDriver.plist




IOProviderClassをIOUSBDeviceにして、bcdDeviceとiDProduct、idVendorを対象機器の値にすれば、抜き差しに反応しそうだ!




以前の開発環境に含まれていたUSB Proberを持っていたら、それを使って、SmartScrollの情報を確認する。







VendorIDが0x056A、ProductIDが0x0050、DeviceVersionNumberが0x0007なので、SmartScrollDriver-Info.plistのIOKitPersonalitiesのSmartScrollDriverにbcdDeviceとidProduct、idVendorを追加して、7と80、1386を入力する。




SmartScrollDriver-Info.plist




この内容でビルドしてドライバーを前回の方法でロードする。




$ sudo cp -R SmartScrollDriver.kext /tmp
$ sudo kextutil -v /tmp/SmartScrollDriver.kext
Notice: /tmp/SmartScrollDriver.kext has debug properties set.
/tmp/SmartScrollDriver.kext appears to be loadable (not including linkage for on-disk libraries).
Loading /tmp/SmartScrollDriver.kext.
/tmp/SmartScrollDriver.kext successfully loaded (or already loaded).



kernel.logをtailし、USB機器を差し込んで、抜く。




$ tail -f /var/log/kernel.log
Jul  1 22:30:56 mbc2d kernel[0]: [SmartScrollDriver]Initializing
Jul  1 22:30:56 mbc2d kernel[0]: [SmartScrollDriver]Probing
Jul  1 22:30:56 mbc2d kernel[0]: No interval found for . Using 8000000
Jul  1 22:30:56 mbc2d kernel[0]: [SmartScrollDriver]Starting
Jul  1 22:31:14 mbc2d kernel[0]: No interval found for . Using 8000000
Jul  1 22:31:14 mbc2d kernel[0]: [SmartScrollDriver]Stopping
Jul  1 22:31:14 mbc2d kernel[0]: [SmartScrollDriver]Freeing



アンロード。




$ sudo kextunload -v /tmp/SmartScrollDriver.kext
jp.co.bitz.driver.SmartScrollDriver unloaded and personalities removed.



抜き差しした際に、ログが出力されている事が分かるはずだ。




関連情報
Mac OS X 10.7.5 - Source

Kernel Extension Programming Topics



【Cocoa練習帳】
http://www.bitz.co.jp/weblog/

http://ameblo.jp/bitz/(ミラー・サイト)

[OSX]ドライバについて(driver)

今回は、IOKit driver templateで生成したプロジェクトを使って開発だ。




新規プロジェクトで、IOKit driver templateを選択する。




新規プロジェクトdriver




プロジェクト名は、前回の流れからSmartScrollDriverとした。




今回も、前回と同様にTARGETSのArchitecturesのBuild Active Architecture Onlyは、NOを選択する。




SmartScrollDriver-Info.plistのCFBundleIdentifierをcom.MyCompany.driver.${PRODUCT_NAME:rfc1034identifier}に変更する。著者は、MyCompanyに自分の会社のものにしている。




SmartScrollDriver-Info.plistのIOKitPersonalitiesにSmartScrollDriver辞書型を追加して、以下の内容に設定する。


























IOKitPersonalities




IOClassは、このドライバの起点となるクラス名の様だ。ようするに、この名前のクラスを実装する事になる。


IOProviderClassは、ドライバがぶら下がるnubクラス名。




テンプレートのSmartScrollDriver.hとSmartScrollDriver.cppは空なので中身を実装する。メソッドが呼ばれたらにデバッグ出力するだけだ。




#include <IOKit/IOService.h>
class jp_co_bitz_driver_SmartScrollDriver : public IOService {
    OSDeclareDefaultStructors(jp_co_bitz_driver_SmartScrollDriver)
public:
    virtual bool init(OSDictionary *dictionary = 0);
    virtual void free(void);
    virtual IOService *probe(IOService *provider, SInt32 *score);
    virtual bool start(IOService *provider);
    virtual void stop(IOService *provider);
};



#include <IOKit/IOLib.h>
#include "SmartScrollDriver.h"
 
OSDefineMetaClassAndStructors(jp_co_bitz_driver_SmartScrollDriver, IOService)
 
#define super IOService
 
bool jp_co_bitz_driver_SmartScrollDriver::init(OSDictionary *dict)
{
    bool result = super::init(dict);
    IOLog("Initializing\n");
    return result;
}
 
void jp_co_bitz_driver_SmartScrollDriver::free(void)
{
    IOLog("Freeing\n");
    super::free();
}
 
IOService *jp_co_bitz_driver_SmartScrollDriver::probe(IOService *provider,
                                                SInt32 *score)
{
    IOService *result = super::probe(provider, score);
    IOLog("Probing\n");
    return result;
}
 
bool jp_co_bitz_driver_SmartScrollDriver::start(IOService *provider)
{
    bool result = super::start(provider);
    IOLog("Starting\n");
    return result;
}
 
void jp_co_bitz_driver_SmartScrollDriver::stop(IOService *provider)
{
    IOLog("Stopping\n");
    super::stop(provider);
}



一度、プロジェクトをビルドする。Show the Log NavigatorからSmartScrollDriver.kextの出力先を調べて、ターミナル.appで、そのディレクトリに移動する。




cd Build/Products/Debug



そこで以下のコマンドを実行する。




$ kextlibs -xml SmartScrollDriver.kext










この内容をSmartScrollKext-Info.plistのOSBundleLibrariesに設定する。




再度、ビルドし、生成されたSmartScrollDriver.kextを/tmpにコピーする。




$ sudo cp -R SmartScrollDriver.kext /tmp



以下のコマンドで、問題がないか確認する。




$ kextutil -n -t /tmp/SmartScrollDriver.kext
No kernel file specified; using running kernel for linking.
Notice: /tmp/SmartScrollDriver.kext has debug properties set.
/tmp/SmartScrollDriver.kext appears to be loadable (including linkage for on-disk libraries).



ロードしてみる。




$ sudo kextutil -v /tmp/SmartScrollDriver.kext
Password:
Notice: /tmp/SmartScrollDriver.kext has debug properties set.
/tmp/SmartScrollDriver.kext appears to be loadable (not including linkage for on-disk libraries).
Loading /tmp/SmartScrollDriver.kext.
/tmp/SmartScrollDriver.kext successfully loaded (or already loaded).



確認する。




$ kextstat | grep jp.co.bitz
  162    0 0xb97000   0x4000     0x3000     jp.co.bitz.driver.SmartScrollDriver (1) <4 3>



アンロード。




$ sudo kextunload -v /tmp/SmartScrollDriver.kext
jp.co.bitz.driver.SmartScrollDriver unloaded and personalities removed.



関連情報
Mac OS X 10.7.5 - Source

Kernel Extension Programming Topics



【Cocoa練習帳】
http://www.bitz.co.jp/weblog/

http://ameblo.jp/bitz/(ミラー・サイト)

[OSX]ドライバについて(kext)

Apple開発者のサイトでDarwinのソースコードが公開されているが、ドライバ関連では、文書とサンプルコードも付属しているので、開発したいものに近いサンプルコードを基に拡張していく方法もあるが、今回は、Xcodeの新規プロジェクトから製作してみようと思う。




Open Source - Releases

Mac OS X 10.7.5 - Source



Xcodeでドライバの新規プロジェクトを生成する場合、雛形としては、Generic Kernel ExtensionとIOKit Driverの二種類があるが、その違いについてまとめてみる。




















それでは、kext(generic kernel extension)を作ってみよう。




新規プロジェクトで、Generic kernel extension templateを選択する。




新規プロジェクトkext




プロジェクト名は、Smart Scrollという製品に対するプログラムを作成しようと思ったので、サンプルでは、SmartScrollKextとした。




TARGETSのArchitecturesのBuild Active Architecture Onlyは、NOを選択する。これを設定しておかないと、32bitカーネルで動作している開発機で、64bitドライバを生成し、テストに失敗してしまうなどのトラブルに遭遇する可能性がある。




アーキテクチャ




SmartScrollKext.cにデバッグ出力を追加する。




#include <sys/systm.h>
#include <mach/mach_types.h>
 
kern_return_t SmartScrollKext_start(kmod_info_t * ki, void *d);
kern_return_t SmartScrollKext_stop(kmod_info_t *ki, void *d);
 
kern_return_t SmartScrollKext_start(kmod_info_t * ki, void *d)
{
    printf("SmartScrollKext has started.\n");
    return KERN_SUCCESS;
}
 
kern_return_t SmartScrollKext_stop(kmod_info_t *ki, void *d)
{
    printf("SmartScrollKext has stopped.\n");
    return KERN_SUCCESS;
}



SmartScrollKext-Info.plistのCFBundleIdentifierをcom.MyCompany.kext.${PRODUCT_NAME:rfc1034identifier}に変更する。下記の例では、MyCompanyは著者の会社のものにしている。




CFBundleIdentifier




一度、プロジェクトをビルドする。Show the Log NavigatorからSmartScrollKext.kextの出力先を調べて、ターミナル.appで、そのディレクトリに移動する。




cd Build/Products/Debug



そこで以下のコマンドを実行する。




$ kextlibs -xml SmartScrollKext.kext








この内容をSmartScrollKext-Info.plistのOSBundleLibrariesに設定する。




OSBundleLibraries




再度、ビルドし、生成されたSmartScrollKext.kextを/tmpにコピーする。




$ sudo cp -R SmartScrollKext.kext /tmp



以下のコマンドで、問題がないか確認する。




$ kextutil -n -print-diagnostics /tmp/SmartScrollKext.kext
No kernel file specified; using running kernel for linking.
/tmp/SmartScrollKext.kext appears to be loadable (including linkage for on-disk libraries).



ログ確認の準備を行う。




$ cd /var/log
$ tail -f kernel.log



ロードする。




$ sudo kextload /tmp/SmartScrollKext.kext



アンロードする。




$ sudo kextunload /tmp/SmartScrollKext.kext



ログにデバッグ出力が印字されている事を確認する。




Jun 24 00:31:39 マシン名 kernel[0]: SmartScrollKext has started.
Jun 24 00:33:08 マシン名 kernel[0]: SmartScrollKext has stopped.



関連情報
Mac OS X 10.7.5 - Source

Kernel Extension Programming Topics



【Cocoa練習帳】
http://www.bitz.co.jp/weblog/

http://ameblo.jp/bitz/(ミラー・サイト)

[Android]ネット上の画像の全画面表示

AndroidManifest.xmlで、通信の利用を有効にする。




<uses-permission android:name="android.permission.INTERNET" />



例では、主アクティビティで、画像を一個、表示するようにしている。




<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <ImageView
        android:id="@+id/imageview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="fitStart" />

</RelativeLayout>



activity_vertical_marginとactivity_horizontal_marginは0に設定している。scaleTypeはfitStartを指定しているので、画面上部に表示される。




AsyncTaskを使って、画像をダンロードして、表示した。




public class MainActivity extends Activity {











































}



どうしても、気持ち悪いので、インスタンスの初期値としてnullを設定してしまう。




AsyncTaskは、全体で一度に一個の処理しか実行しないので、何でもかんでもAsyncTaskで非同期処理すると、著者のように困った事象に遭遇してしまうので気をつけた方がいい。




関連情報
Android Developers

初めてのAndroid



【Cocoa練習帳】
http://www.bitz.co.jp/weblog/

http://ameblo.jp/bitz/(ミラー・サイト)
初めてのAndroid 第3版/オライリージャパン
¥2,415
Amazon.co.jp

[Android]タイトルバーの背景色

Androidで、テーマとして「Theme.Light」を使用している場合、タイトルバーの背景色を変更するには、どうすればいいか?




res/values/colors.xmlに指定したい色を定義する。




<resources>
    <color name="custom_theme_color">#FFFFFF00</color>
</resources>



res/values/styles.xmlに、独自のテーマを定義する。




<resources>
    <style name="MyFirstAppTheme" parent="android:Theme.Light">
        <item name="android:windowContentOverlay">@null</item>
        <item name="android:windowTitleBackgroundStyle">@style/WindowTitleBackground    </style>
    <style name="WindowTitleBackground">
        <item name="android:background">@color/custom_theme_color</item>
    </style>
</resources>



windowContentOverlayはタイトルバーンの影で非表示にした。




AndroidManifest.xmlで、定義してテーマを背景色を変更したいアクティビティに設定する。




        <activity
            android:name="demo.myfirstapp.MainActivity"
            android:label="@string/app_name"
            android:theme="@style/MyFirstAppTheme" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>



この例では、タイトルバーの背景色は黄色になる。




関連情報
Android Developers

初めてのAndroid



【Cocoa練習帳】
http://www.bitz.co.jp/weblog/

http://ameblo.jp/bitz/(ミラー・サイト)

初めてのAndroid 第3版/オライリージャパン
¥2,415
Amazon.co.jp

[Android]アクティビティの表示

iOSアプリケーションは伝統的なUnixアプリケーションだが、Androidは独特だ。




アクティビティを簡単に説明すると画面という事になるのだが、システムのアクティビティマネージャによって開かれた画面はスタック構造で管理され、WebブラウザのBackボタンのように、前のアクティビティに戻る動きをする。

Linuxのプロセスとアプリケーションのライフサイクルは等号でなく、プロセスはアクティビティを入れる容器にすぎない。




アクティビティには、launchModeという開かれた際の挙動を定義する項目があり、これには以下の4つの種類がある。
























アクティビティが呼び出し元タスクに属する事に驚いたが、同一アプリケーション内部の場合に限定すると、あるアクティビティを開くインテントを実行した際に、とにかく開く要求が発生したら、その度に、ある画面を出したい場合はstandardを、そのアクティビティが開かれていた場合は新規に画面を開きたくない場合は、simgleTopをというのが良さそうだ。




実は、あるアプリケーションを作っていて、それはアプリ内のサービスから、通知経由で、そのアプリをフロントに移動させるという動きをさせたいのだが、フロントに移動する度にアクティビティが追加されて困っていた為、調査していた。




この問題そのものについて、Android関連のMLで答えを教えてもらったので、それも紹介しよう。




同一アプリで、あるアクティビティを開く場合は、以下のようにインテントを記述すると思うが、




Intent intent = new Intent(this, アクティビティ名.class);
startActivity(intent);



この時に以下の設定を追加すればいい。




intent.setAction(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);



質問の場を設けてくれた日本Androidの会の皆さん、ありがとうございます。助かりました!




関連情報
Android Developers

初めてのAndroid

Y.A.Mの雑記帳『Android launchMode の違い』

rokuta96のAndroidアプリ開発『Activityの属性「launchMode」について』



【Cocoa練習帳】
http://www.bitz.co.jp/weblog/

http://ameblo.jp/bitz/(ミラー・サイト)