2012年7月4日水曜日

iOSでQRコードを読み込む(ZXing 2.0)

ちょっと前に Android で Zxing を使ってQRコードを読み込んでました。
http://teru2-bo2.blogspot.jp/2012/06/androidqrzxing.html

ZXing はiOS用のライブラリも持っているので今回もZXingを使ってQRコードを読み込んでみました。
READMEもサンプルもあるんでそれをそのまま動かせばなんとなく動きはわかると思いますが自分も忘れないように。

とりあえずライブラリをダウンロードして解凍しておきます。
http://code.google.com/p/zxing/downloads/list

Xcodeを起動してプロジェクトを作成しましょう。(ここではZxingTestとしました)
「zxing-2.0/iphone/README」があるんでそれを抜粋しながらZXingを使用する準備をします。

1. Locate the "ZXingWidget.xcodeproj" file under "`zxing/iphone/ZXingWidget/`". Drag ZXingWidget.xcodeproj and drop it onto the root of your Xcode project's "Groups and Files" sidebar. A dialog will appear -- make sure "Copy items" is unchecked and "Reference Type" is "Relative to Project" before clicking "Add". Alternatively you can right-click on you project navigator and select 'Add files to "MyProject"'

「zxing-2.0/iphone/ZXingWidget/ZXingWidget.xcodeproj」を作成したプロジェクトにドラッグ&ドロップします。

2. Now you need to link the ZXingWidget static library to your project. To do that,
 a. select you project file in the project navigator
 b. In the second column, select your _target_ and not the project itself
 c. Go to the 'build phases' tab, expand the 'link binary with libraries' section,
 d. Click the add button A dialog will appear and you should see libZXingWidget.a in the very first possibilities

作成したプロジェクト >> TARGETS(ZXingTest) >> Build Phases >> Link Binary With Libraries に libZXingWidget.a を追加します。

3. Now you need to add ZXingWidget as a dependency of your project, so Xcode compiles it whenever you compile your project.
 a. like in substep c. of previous step, you nedd to do that in the 'build phases' tab of your target
 b. Expand the 'Target Dependencies' section
 c. Click the add Button and a dialog will appear select ZXingWidget target

先ほどの画面上にある Target Dependencies に ZXingWidget を追加します。

4. Headers search path 1: you need to tell your project where to find the ZXingWidget headers. Select your project in the project navigator, and the select your target and go to the "Build Settings" tab. Look for "Header Search Paths" and double-click it. Add the relative path from your project's directory to the "zxing/iphone/ZXingWidget/Classes" directory. Make sure you click the checkbox "recursive path" !

5. Headers search path 2: You need to add zxing cpp headers to your headers search path, do this similarly as previous step to point the path to cpp/core/src/ where the 'zxing' directory is. Do not check the "recursive path" option for this path.

作成したプロジェクト >> PROJECT(ZXingTest) >> Build Settings >> Search Paths >> Header Search Paths に 「zxing-2.0/cpp/core/src」と「zxing-2.0/iphone/ZXingWidget/Classes」を追加します。

6. Import the following iOS frameworks:
 a. AVFoundation
 b. AudioToolbox
 c. CoreVideo
 d. CoreMedia
 e. libiconv
 f. AddressBook
 g. AddressBookUI

 This must be done by adding them in the 'Link Libraries with Binary' just like step 2.c.

これはそのままですね。上のフレームワークを追加します。

これで前準備はOK。
なかなか面倒です。

次は画面ですが特に説明することもないので Assistant Editor と合わせて以下のようになります。 

次にコードです。

ViewController.h
#import <UIKit/UIKit.h>
#import "ZXingWidgetController.h"

@interface ViewController : UIViewController
<
    ZXingDelegate
>

- (IBAction)scanPressed:(id)sender;

@property (retain, nonatomic) IBOutlet UITextView *resultsView;
@property (copy, nonatomic) NSString *resultsString;

@end

ViewController.mm
#import "ViewController.h"
#import "QRCodeReader.h"

@interface ViewController ()

@end

@implementation ViewController
@synthesize resultsView;
@synthesize resultsString;

- (void)viewDidLoad
{
    [super viewDidLoad];
 // Do any additional setup after loading the view, typically from a nib.
}

- (void)viewDidUnload
{
    [self setResultsString:nil];
    [self setResultsView:nil];
    [super viewDidUnload];
    // Release any retained subviews of the main view.
}
- (void)dealloc {
    [resultsString release];
    [resultsView release];
    [super dealloc];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}

- (IBAction)scanPressed:(id)sender {
    ZXingWidgetController *zwc = [[ZXingWidgetController alloc] initWithDelegate:self showCancel:YES OneDMode:NO];
    QRCodeReader *qrcodeReader = [[QRCodeReader alloc] init];
    NSSet *readers = [[NSSet alloc] initWithObjects:qrcodeReader, nil];
    zwc.readers = readers;
    [self presentModalViewController:zwc animated:YES];
    
    [qrcodeReader release];
    [readers release];
    [zwc release];
}

- (void)zxingController:(ZXingWidgetController *)controller didScanResult:(NSString *)result
{
    self.resultsString= result;
    if (self.isViewLoaded)
    {
        [resultsView setText:resultsString];
        [resultsView setNeedsDisplay];
    }
    
    [self dismissModalViewControllerAnimated:YES];
}

- (void)zxingControllerDidCancel:(ZXingWidgetController *)controller
{
    [self dismissModalViewControllerAnimated:YES];
}

@end

コード自体は難しいものではないので問題ないかと思います。
ZXingWidgetController がいいかんじに何でもやってくれます。

一点注意する点はコードのファイル名。
プロジェクトを作成した段階では「ViewContrller.m」ですが、ここでは「ViewController.mm」となります。
.m のままビルドすると「iostream file not found」とかいうエラーが出ると思います。

.m ではなく .mm にするのは簡単に言うと Objective-C と C++ を混在させて動作させることができるようにするため。
ZXing がC++ とかでラップされてるんでしょうね。きっと。

これで動作すると思います。
実機で試してみてください。

SCAN をタップすると以下の画面が立ち上がります。この白い枠内にQRコードをも

認識すると以下のように内容が表示されると思います。





2012年7月3日火曜日

Sassでメディアクエリを使ってみたけど変数が使えない?

Sass でメディアクエリを記述してみました。

普通に書くと
#header {
    background-color: #ccc;

    h1 {
        float: left;

        @media screen and (max-width: 320px) {
            float: none;
        }
    }
}

コンパイル後は
#header {
  background-color: #ccc;
}
#header h1 {
  float: left;
}
@media screen and (max-width: 320px) {
  #header h1 {
    float: none;
  }
}

こんなかんじで至って問題ありません。
まぁ確かにこれでいいんですが max-width の部分を変数にしようと思ったんですがコンパイルエラーになりました。
/* 変数宣言 */
$mobile-width:320px;
#header {
    background-color: #ccc;

    h1 {
        float: left;

        /* 変数を使用 */
        @media screen and (max-width: $mobile-width) {
            float: none;
        }
    }
}
Syntax error: Invalid CSS after "...nd (max-width: ": expected expression (e.g. 1px, bold), was "$mobile-width) {")

んーよくわかりません。
普通に変数を使う分は問題ないんだけど。。。単純にこういうところに変数使ったらだめなんかな。。。

誰か詳しいひと教えてください!!!



ユーザエージェントを判別する(FuelPHP)

これも最近少しずつさわってるFuelPHPですが
ドキュメント見てたら Agent とあったんで気になってさわってみました。

PHPでユーザエージェントを判別する場合
$agent = $_SERVER['HTTP_USER_AGENT'];

これでユーザエージェントを取得することができます。
しかし、これから各ブラウザ、またデバイスを仕分けるのはなかなか面倒な作業だったと思いますが、これが一発で判別できます。
// 従来のユーザエージェント取得
$data['ua'] = $_SERVER['HTTP_USER_AGENT'];

// ブラウザ名
$data['browser'] = Agent::browser();
// バージョン
$data['version'] = Agent::version();
// プラットフォーム名
$data['platform'] = Agent::platform();
// モバイル判別
$data['mobile'] = 'false';
if (Agent::is_mobiledevice())
{
    $data['mobile'] = 'true';
}

// クローラ判別
$data['robot'] = 'false';
if (Agent::is_robot())
{
    $data['robot'] = 'true';
}

これをViewに渡して表示すると
PC画面

 iPod touch

iPad

上のコードではそれぞれ個別にデータを情報を取得しましたが一発ですべてのデータも取得できます。
// ブラウザのすべてのプロパティを取得
$agent = Agent::properties();

$data['browser'] = $agent['browser'];
$data['version'] = $agent['version'];
$data['platform'] = $agent['platform'];
$data['mobile'] = 'false';
if ($agent['ismobiledevice'])
{
    $data['mobile'] = 'true';
}
$data['robot'] = 'false';
if ($agent['crawler'])
{
    $data['robot'] = 'true';
}

これでしても結果は同じです。
ほかにもあるのでその辺りはマニュアルを参照してください。
http://docs.fuelphp.com/classes/agent/usage.html

※2012.7.4
iPadって mobile ?て聞かれたんで試してみました。
iPadは mobile です!


Sassの基本的な使い方

Sassのインストールについては先ほど書きましたが
まず簡単な使い方。GUIツールとかもあるみたいですがまずはコマンドを使って試してみます。

ターミナル(Windowsならコマンドプロンプト)を起動して任意のディレクトリに移動して以下のコマンドを実行します。
$ sass --watch .:.
>>> Sass is watching for changes. Press Ctrl-C to stop.

これでOK。
あとはそのディレクトリにscssファイルを生成してコーディングすると自動的にコンパイルしcssファイルに変換してくれます。

試しに以下のような sample.scss を作成してみます。
#container {
    width: 100%;
    height: 50px;

    h1 {
        font-size: 1.5em;
    }
}

そうすると自動的に sample.css ができたことがわかると思います。
#container {
  width: 100%;
  height: 50px; }
  #container h1 {
    font-size: 1.5em; }

これが sass です。

cssのコーディングをしているとセレクタの指定はほぼ必須。
同じものを毎回記述するのはけっこう面倒なことです。
sass では親セレクタにネストすることでこの面倒な手間を省略することができます。
これだけでもかなりいいです。
慣れてしまえば今までの css より直感的に操作できるのではないかと思います。


次によく使うであろうアンカーなどの:hoverや:activeなどの疑似クラス。
これも簡単です。
sample.scss
a {
    font-size: .9em;
    &:hover {
        text-decoration: underline;
    }
    &:visited {
        text-decoration: none;
    }
}

sample.css
a {
  font-size: .9em; }
  a:hover {
    text-decoration: underline; }
  a:visited {
    text-decoration: none; }

scss の特殊文字 & を使うと親セレクタと & を置換してくれます。
簡単です。

ほかにも sass には変数や関数を作ることもできますし、@mixinやsingleton といった機能も備えています。
このあたりは追々。


sass の コマンドについてちょっとメモ。

< --watch >
フォルダの監視
--watch input/stylesheets:output/stylesheets
ファイルの監視
--watch input.scss:output.css

< --style >
nested / compact / compressed / expanded

--watch と --style は組み合わせて使うことができます。
時間があればそれぞれ試してみてください。
$ sass --style compact --watch .:.
#container { width: 100%; height: 50px; }
#container h1 { font-size: 1.5em; }

a { color: #888; font-size: .9em; }
a:hover { text-decoration: underline; }
a:visited { text-decoration: none; }



Sassを試してみました(インストール)

Sass
CSSを拡張したメタ言語。CSSのコーディング規約とかを作っててもかんじてた部分ではありますが、ほかの言語に比べ再利用とか難しいしいくら規約を作ってもどうしても煩雑になってしまいがち。
Sassを使って効率的にできないかとさわってみました。

Sassは、scssファイルをコンパイルすることでcssファイルに変換してくれます。
コンパイルするにはRubyがまず必要。
MacにはRubyが入ってるんで問題ありませんがWindowsではまずRubyをインストールします。

http://rubyinstaller.org/
ここからインストーラをダウンロードします。
(現時点での最新はrubyinstaller-1.9.3-p194.exe)
注意するところはパスを追加するくらいですがインストーラを使用すればこのあたりも自動的にやってくれます。
(以下、Add Ruby/executables to your PATHにチェックをつけます)


完了後、バージョンを確認します。
ruby -v

次にSassのインストール
gem install sass

これOK。
これもバージョンを確認します。
sass -v

使い方とかも追々書いていきます。


Sassについて参考にしたページ



2012年7月2日月曜日

FuelPHPでファイルアップロード

ファイルアップロードを試してみました。
まず設定ファイルをCore/config/upload.phpからApp/configにコピーします。
<?php
return array(
    /**
     * global configuration
    */

    // if true, the $_FILES array will be processed when the class is loaded
    'auto_process' => true,

    /**
     * file validation settings
    */

    // maximum size of the uploaded file in bytes. 0 = no maximum
    'max_size' => 0,

    // list of file extensions that a user is allowed to upload
    'ext_whitelist' => array(),

    // list of file extensions that a user is NOT allowed to upload
    'ext_blacklist' => array(),

    // list of file types that a user is allowed to upload
    // ( type is the part of the mime-type, before the slash )
    'type_whitelist' => array(),

    // list of file types that a user is NOT allowed to upload
    'type_blacklist' => array(),

    // list of file mime-types that a user is allowed to upload
    'mime_whitelist' => array(),

    // list of file mime-types that a user is NOT allowed to upload
    'mime_blacklist' => array(),

    /**
     * file save settings
    */

    // prefix given to every file when saved
    'prefix' => '',

    // suffix given to every file when saved
    'suffix' => '',

    // replace the extension of the uploaded file by this extension
    'extension' => '',

    // default path the uploaded files will be saved to
    'path' => DOCROOT.'assets/upload',

    // create the path if it doesn't exist
    'create_path' => true,

    // permissions to be set on the path after creation
    'path_chmod' => 0777,

    // permissions to be set on the uploaded file after being saved
    'file_chmod' => 0666,

    // if true, add a number suffix to the file if the file already exists
    'auto_rename' => true,

    // if true, overwrite the file if it already exists (only if auto_rename = false)
    'overwrite' => false,

    // if true, generate a random filename for the file being saved
    'randomize' => false,

    // if true, normalize the filename (convert to ASCII, replace spaces by underscores)
    'normalize' => false,

    // valid values are 'upper', 'lower', and false. case will be changed after all other transformations
    'change_case' => false,

    // maximum lengh of the filename, after all name modifications have been made. 0 = no maximum
    'max_length' => 0
);

変えたところは path くらいです。

次にコントローラ。
<?php

class Controller_Upload extends Controller_Template
{
    public function action_index()
    {
        $data = array();

        $errors = array();
        $files = array();

        // POSTを確認
        if (Input::method() == 'POST')
        {
            Upload::process();
            if (Upload::is_valid())
            {
                // アップロード
                Upload::save();

                // エラーメッセージを取得する
                foreach (Upload::get_errors() as $file)
                {
                    foreach ($file['errors'] as $error)
                    {
                        $errors[] = $error['message'];
                    }
                }
            }
        }

        // config/upload.phpの読み込み
        Config::load('upload', true);
        $upload_config = Config::get('upload');

        // ファイル一覧を取得する
        $files = File::read_dir($upload_config['path']);
  
        $data['errors'] = $errors;
        $data['files'] = $files;

        $this->template->title = 'ファイルアップロード';
        $this->template->content = View::forge('upload/form', $data);
    }
}

やっていることとしてはファイルのアップロードとアップロードディレクトリからファイル一覧を取得してるくらいです。
といいながらもファイル一覧で結構悩みました。。。

最後にViewです。
<-- errors -->
<?php if (count($errors) > 0) : ?>
<ul>
<?php foreach ($errors as $error) : ?>
    <li><?php echo $error; ?></li>
<?php endforeach; ?>
</ul>
<?php endif; ?>

<?php echo Form::open(array('class'=>'form-stacked', 'enctype'=>'multipart/form-data')); ?>
<div class="clearfix">
    <?php echo Form::label('画像ファイル', 'upload-file'); ?>
    <div class="field">
        <?php echo Form::file('upload-file'); ?>
    </div>
</div>
<div class="actions">
    <?php echo Form::submit('submit', 'アップロード', array('class' => 'btn primary')); ?>
</div>
<?php echo Form::close(); ?>

<-- files -->
<ul>
<?php foreach ($files as $file) : ?>
<li><?php echo $file; ?></li>
<?php endforeach; ?>
</ul>

真ん中にフォームが上と下にそれぞれエラーメッセージとファイル一覧を表示するようにしています。
form タグの指定が通常とは違うんで注意するところはそれぐらいかなと思います。





XCode4のブレークポイント

先日ある勉強会に参加させて頂きました。
そのなかでこれ使えるなって思った機能をひとつ。

件名の通りブレークポイントなんですが通常は実行中の処理をとめてブレークポイントを指定した地点での変数情報や動作状態を確認するために使用すると思います。

<設定した場合>

<有効/無効の切替>
有効時

無効時

ブレークポイントを削除する場合は、どこか適当な場所にドラッグする。


これ使えるなって思った機能ですが、ブレークポイントのマークをControl+クリックすることでいろんな設定ができるみたいです。

<右クリック>

実際にControl+クリックすると上のような画面が表示されます。
Condition
ブレークポイントが実行される条件(入力しなくてもOK)
Ignore
無視する回数
Action
ブレークポイントが実行されたときの動作
Options
Automatically continue after evaluatingをONにするとブレークポイントは実行されますがそこで動作が一時停止せず処理を続行する
Actionは複数設定できるんでいろんな組み合わせでデバッグができそうです。
いままでNSLogをコード上に記述していた部分も少なくなるのではないしょうか。

<実際に設定した画面>


こんなかんじで「Log Message」を設定した場合は上のようなイメージで出力されます。
是非。