2012年6月23日土曜日

HTML5+JavaScriptによるパノラマ写真実装例



Manifest+LocalStorageによるオフラインアプリ作成(5)

360°パノラマ写真をスワイプで回転させるサンプルアプリです。
水平方向のみの対応です。
デスクトップではマウスドラッグで回転します。IEでもヘッダのレイアウトが崩れますが、回転動作はします。

このアプリではManifestは設定していますが、LocalStorageは使用していません。

このサンプルアプリのパノラマ写真回転に関するコードは以下のとおりです。

メニュー画面で[Panorama]タップ時に表示するDIV
    <div id="divPic" class="pic"></div>

スタイルシート
     div.pic {
width:100%;
         height:220px;
background-position-x:0px;
    }

JavaSctipt(簡略化のためiOS対応の場合のみ抽出)
    //Picオブジェクト作成
    var pic = new Pic(divPic, "Images/001.jpg");
    //Picクラス
    Pic = function(div, url) {
        this.div = div;
        this.url = url;
        //background-imageセット
        div.style.backgroundImage = 'url("' + url + '")';
        //swipeまたはdrag操作のためのイベントハンドラ
        div.addEventListener("touchstart", Pic.touchStart, false);
        Pic.dict[div.id] = this;
    }
    //DIVのidをkey、valueをPicオブジェクトとするArray
    Pic.dict = new Array();
    //移動量計算のため直前のtouch位置をセット. 初期値=Number.MIN_VALUE
    Pic.prevX = Number.MIN_VALUE;
    //イベントハンドラから対応するPicオブジェクトを探す.
    Pic.getInstance = function(id) {
        return Pic.dict[id];
    }
    //touch 開始
    Pic.touchStart = function(event) {
        Pic.prevX = Number.MIN_VALUE;
        var x = event.srcElement.style.backgroundPositionX;
        var div = event.srcElement;
        //イベントハンドラ設定
        div.addEventListener("touchmove", Pic.touchMove, false);
        div.addEventListener("touchend", Pic.touchEnd, false);
    }
    //swipe
    Pic.touchMove = function(event) {
        var x = event.pageX;
        if (Pic.prevX == Number.MIN_VALUE) {
            //一回目はprevXをセット
            Pic.prevX = x;
        } else {
            //二回目以降
            var pic = Pic.getInstance(event.srcElement.id);
            //x位置の末尾の"px"を取り、数値化.
            var px =    pic.div.style.backgroundPositionX;
            var p = new Number(px.substr(0, px.length-2));
            p += (x - Pic.prevX);
            //swipe量を加減した位置にbackgroundImageを移動
            pic.div.style.backgroundPositionX = p + "px";
            Pic.prevX = x;
        }
    }
    //touch 終了
    Pic.touchEnd = function(event) {
        Pic.prevX = Number.MIN_VALUE;
        var div = event.srcElement;
        div.removeEventListener("touchmove", Pic.touchMove);
        div.removeEventListener("touchend", Pic.touchEnd);
    }

HTML5+JavaScriptによるパノラマ写真実装例

2012年6月18日月曜日

残りメモリ容量表示



残りメモリ容量表示

メモリ使用状況はInstrumentsでチェックすることができますが、処理が重く、またiPhoneデバイス単体でのテストが行えません。そこで、残りメモリ容量を画面に表示させるクラスを作りました。UILabelなどを使って画面の空きスペースに表示させることができます。

.hファイル

#import <Foundation/Foundation.h>

@interface MemMonitor : NSObject

@property (nonatomic, readonly) long freeMemory;
@property (nonatomic, readonly) NSString *freeMemoryAsFormattedString;

@end

.mファイル
#import "MemMonitor.h"
#import <mach/mach.h>
#import <mach/mach_host.h>

@implementation MemMonitor
{
    mach_msg_type_number_t vm_info_count;
    mach_port_t mach_port;
    vm_size_t pagesize;
    NSNumberFormatter *formatter;
}
//初期化
- (id)init
{
    self = [super init];
    if (self) {
        vm_info_count = HOST_VM_INFO_COUNT;
        mach_port = mach_host_self();
        host_page_size(mach_port, &pagesize);
        formatter = [[NSNumberFormatter alloc] init];
        [formatter setPositiveFormat:@"#,##0"];
    }
    return self;
}
//メモリ残り容量をlongで返す。
- (long)freeMemory 
{
    struct vm_statistics vm_stat;
    kern_return_t ret = host_statistics(mach_port, HOST_VM_INFO, (host_info_t)&vm_stat ,&vm_info_count);
    if (ret == KERN_SUCCESS) {
        return (NSInteger)vm_stat.free_count * pagesize;
    } else {
        return -1;
    }
}
//メモリ残り容量を3桁カンマ区切りでフォーマットしたNSStringで返す。
- (NSString *)freeMemoryAsFormattedString
{
    NSNumber *num = [NSNumber numberWithInteger:self.freeMemory];
    return [formatter stringFromNumber:num];
}
@end

使用例:
UILabel *memMonitorLabel;

MemMonitor *memMonitor = [[MemMonitor alloc] init];
memMonitorLabel.text = [memMonitor freeMemoryAsFormattedString];

2012年6月17日日曜日

DOT.NETエラー集



ASP.NETエラー集

あまりお目にかからないエラーに出会ったのをきっかけに、ASP.NET関連のエラーを集めてみます。

  • 「オブジェクトの現在の状態に問題があるため、操作は有効ではありません。」
    データ量が増えると発生するようであれば、コントロール数の上限を超えたために発生しました。Web.configの設定で回避できます。
    <appSettings>
    <add key="aspnet:MaxHttpCollectionKeys" value="5001" />
    </appSettings>

    参考:~ 開発者の憂鬱 ~
          
  • 型 'MyProject.MyMembershipProvider' を読み込めませんでした
    MembershipProviderまたはRollProviderのサブクラスをaspxのあるプロジェクトとは別の(クラスライブラリなどの)別のプロジェクトに置いた場合に発生しました。
    aspxのあるプロジェクト内でMyMembershipProviderをコードから呼ばない場合、aspxのあるプロジェクトに(この例の場合は)MyProjectを追加しなくてもコンパイルは通りますが、実行時にWeb.configの設定に従ってMyMembershipProviderを使用するときにクラスが見るからず、エラーとなります。

    aspxのあるプロジェクトの参照設定に"MyProject"を追加することで解消されます。

  • Provider name cannot be null or empty.
    MembershipProviderまたはRollProviderのサブクラスをaspxのあるプロジェクトとは別の(クラスライブラリなどの)別のプロジェクトに置いた場合に発生しました。

    親クラスのMembershipProviderのNameプロパティ―が空文字を返すために発生します。

    同じプロジェクト内であればエラーとならないのはなぜかわかりませんが、次のようなコードを追加すると回避できます。

        override public string Name
    {
        get { return "MyMembershipProvider"; }
    }

    参考:Problem with custom Role Provider

  • 'System.Xml.Linq.XElement' に 'XPathSelectElement' の定義が含まれておらず、型 'System.Xml.Linq.XElement' の最初の引数を受け付ける拡張メソッドが見つかりませんでした。using ディレクティブまたはアセンブリ参照が不足しています。
    LINQ to XMLでXPathSelectElementを使おうとしたら発生。”using System.Xml.XPath;"を追加すると解決する。

    参考: Is there something wrong with my System.Xml.Linq library?
  • 無効なポストバックまたはコールバック引数です。イベントの検証は、構成の <pages enableEventValidation="true"/>、またはページの <%@ Page EnableEventValidation="true" %> を使用して有効にされます。...

    次のようなDataGridの中にButtonなどを組み込んだ場合に発生する場合があります。

        <asp:GridView ID="GridView1" runat="server">
            <Columns>
                <asp:TemplateField HeaderText="">
                    <ItemTemplate>
                        <asp:Button ID="Button1" runat="server" Text="Test"
                                OnCommand="button1_Clicked"
                                CommandArgument=CommandArgument='<%# Eval("ID") %>'
                                autoPsotBack="true" />

                    </ItemTemplate> </asp:TemplateField>

    これはViewStateが有効なときに、 Page_LoadのPostBackの処理でDataGridを再バインドするのが原因です。

    もし
    Page_Loadでの再バインドが必要でViewStateを無効にしてもよい場合は、PageあるいはDataGridのViewStateModeを"Disabled"にすればエラーが解消します。

    ViewStateを有効にしておく必要がある場合は次の要領でPostBack以外のときだけDataGridのバインドを行います。

        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack
            {
               //この時だけDataGridのバインドを行う。
            }
        }

    DataGridの再バインドが必要な場合は、上記の例の場合はコマンドイベントで行います。

    public void button1_Clicked(object sender, EventArgs e)
        {
           //ここでDataGridのバインドを行う。
        }
    

    参考: Invalid postback or callback argument in ImageButton

  • コンパイル エラー メッセージ: CS1040: プリプロセッサ ディレクティブは行でスペース以外の最初の文字でなければなりません。

    DataGridの中のCommandArgument設定で、次のような文字結合を行うと発生します。CommandArgument='<%# Eval("item1") %>&<%# Eval("item2") %>'

    この例の場合は次のように変更すると文字結合を行えます。
    CommandArgument='<%# Eval("item1") + "&" + Eval("item2") %>'

    エラーそのものは、不適切なコードのために二番目の"#"がコンパイラのプリプロセッセのディレクティブとみなされ、これが行の途中に出現しているために発生しているようです。

2012年6月16日土曜日

iPod Library表示



Playerが表示するiPod Library画面の呼び出し方法です。
SimulatorではiPod Libraryにアクセスできないため、実機を接続する必要があります。
iPhone simulatorで実行すると次のエラーでクラッシュします。
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Unable to load iPodUI.framework'

MediaPlayer.framework追加


.hファイル
#import <MediaPlayer/MediaPlayer.h>

@interface MyViewController : UIViewController <MPMediaPickerControllerDelegate>
- (IBAction)someMethod:(id)sender;
@end

.mファイル
@implementation MyViewController

//MPMediaPickerController呼び出し.
- (IBAction)showLibrary:(id)sender {
    MPMediaPickerController *picker = [[MPMediaPickerController alloc] initWithMediaTypes: MPMediaTypeAnyAudio];
    [picker setDelegate: self];
    //複数選択を許す場合YES、一曲のみ選択の場合NO
    [picker setAllowsPickingMultipleItems: YES];
    [self presentModalViewController:picker animated: YES];
}

//Pickerの[Cancel]ボタンタップ. 
- (void)mediaPickerDidCancel: (MPMediaPickerController *)mediaPicker
{
    // モーダルビュー解除.
    [self dismissModalViewControllerAnimatedYES];
}

//Pickerの[Done]ボタンタップ.
- (void)mediaPicker:(MPMediaPickerController *)mediaPicker
 didPickMediaItems:(MPMediaItemCollection *)collection
{
    [self dismissModalViewControllerAnimated: YES];
    [self doSomethingWithCollection: collection];
}

//collectionを使った処理
- (void)doSomethingWithCollection:(MPMediaItemCollection *)collection
{
    //MusicPlayer作成
    MPMusicPlayerController* musicPlayer = [MPMusicPlayerController iPodMusicPlayer];
    //collectionをセット
    [musicPlayer setQueueWithItemCollection: collection];
    //演奏開始
    [musicPlayer play];
}

//曲名等の取得
MPMediaItem * mediaItem = [[collection items] objectAtIndex:index];
[mediaItem valueForProperty:MPMediaItemPropertyArtist];
[mediaItem valueForProperty:MPMediaItemPropertyAlbumTitle];
[mediaItem valueForProperty:MPMediaItemPropertyTitle];

//歌詞の取得
[mediaItem valueForProperty: MPMediaItemPropertyLyrics];

ただし、MPMediaItemの場合はPlayerで一度再生した曲でないと歌詞データが取得できない(Xcode 4.3.2)。AVAssetからは取得できる。
AVFoundation.framework追加

#import <AVFoundation/AVFoundation.h>

NSURL *url = [mediaItem valueForProperty:MPMediaItemPropertyAssetURL];
AVAsset *asset = [AVAsset assetWithURL:url];
NSString *lyrics = asset.lyrics;  

参考:iPodライブラリアクセスプログラミングガイド

this class is not key value coding-compliant for the key someKey.



reason: '[<MyClass 0x1346b0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key someKey.'

someKeyメソッド, あるいはReadOnlyでなければsetSomeKeyメソッドがない場合に発生します。
次の場合に発生しました。
  • StoryboardでActionを追加する。
  • .hにメソッド宣言、.mに実装が追加される。
  • その後に.h、.mから宣言、実装を削除したが、.storyboardに接続情報が残っている.
次のいずれかの方法で解消する。
  • Storyboardを開き当該接続がある部品のペインを表示、当該接続を解除する。

  • Storyboardをソースで開き、当該Actionの記述を削除する。
    <connections>
       <action selector="someMethod:" destination="2"
                eventType="touchUpInside" id="kdU-9Q-rW1"/>
    </connections>