だいたい47度

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

PageTop

All life is an experiment

さいきん、視線に関するテクノロジに興味があって色々見ているのですが、
ダイドードリンコの実験で面白いものがありました。

前提知識として、アイトラッキングというものがあります。
人がどこを見ているのかというデータを収集・可視化する技術です。
こんな感じにサーモグラフィ様に描画されます。

ダイドードリンコは、アイトラッキングを使って自動販売機を調査しました。
その結果、いままでの常識が覆されたのです。

【それまでの常識】
ユーザが自動販売機を見るときは、Zの字のように目を走らせる。
だから一番売りたい製品を左上、その後は上段の右に並べるべき。
Z字理論は、広告やチラシ、スーパー・コンビニの陳列でも明らかに有効だ。

【実験結果】
ユーザは自動販売機の左下をみて、後は左右に目線が流れる。
よって、一番売りたい製品は左下に設置すべきだ。
人の視線はウソがつけない!ダイドードリンコ“売れる自販機”の秘密



常識を疑ってみることは、こんなレベルでもまだまだ有効です。
もちろん常識を疑っても大抵「あぁ本当だ」という結論に至りますが、
あるときふと面白い発見につながったりします。

僕はよく頭だけで考えて結論を出してしまうのですが、
常に「本当かな、実験ができないかな」と考えなきゃですね。
面倒くさいけれど、思い込みは一番の敵かもしれません。



余談ですが、最近興味があるのはAR(拡張現実)。
遊ぶ前に前提知識をまるっと入れようと思ったのですが、なかなかよい本がないですね。

とりあえず以下の本は浅く広くという感じですが、早く読めてよかったです。
ARについての事象が写真やURLつきでたくさん示されており、
全体観をつかむのには悪くないかもしれません。

AR入門―身近になった拡張現実 (I・O BOOKS)AR入門―身近になった拡張現実 (I・O BOOKS)
(2010/11)
佐野 彰

商品詳細を見る

PageTop

【Objective-C】Restkitなどでのアカウントのログアウトでハマった

アカウント登録型のiOSアプリを作っていたのですが、ログアウト周りでハマりました。

Aさんでログインしたあと、ログアウトし、Bさんでログインして情報をサーバから受け取ろうとすると、なぜかAさんの結果が帰って来てしまうというバグがどうしても取れなかったのです。なお通信はBASIC認証を使って行なっています。

まずサーバ側に問題がないか確認しました。コマンドラインからサーバ問い合わせをすると、アカウントごとにちゃんと違う結果が返ってきます。またwiresharkを使って通信をみていると、どうもアプリから発信している要求がおかしいことがわかりました。

しかし、アプリ側でログ出力させてみても、きちんと違うクエリを発行しているように見えます。AFNetworkingを使用していたのですが、setAuthorizationHeaderWithUsername:password:がきちんと呼ばれていますし、その後の交信でも使われていそうです。

すわキャッシュか!と思い、ログアウトのときに大いにキャッシュ削除を記述してみましたが、うまくいきません。以下のように念入りにCookieも消していますが、うまくいきません。

NSDictionary *credentialsDict = [[NSURLCredentialStorage sharedCredentialStorage] allCredentials];

if ([credentialsDict count] > 0) {
NSEnumerator *protectionSpaceEnumerator = [credentialsDict keyEnumerator];
id urlProtectionSpace;

while (urlProtectionSpace = [protectionSpaceEnumerator nextObject]) {
NSEnumerator *userNameEnumerator = [[credentialsDict objectForKey:urlProtectionSpace] keyEnumerator];
id userName;

while (userName = [userNameEnumerator nextObject]) {
NSURLCredential *cred = [[credentialsDict objectForKey:urlProtectionSpace] objectForKey:userName];
[[NSURLCredentialStorage sharedCredentialStorage] removeCredential:cred forProtectionSpace:urlProtectionSpace];
}
}
}

[[NSURLCache sharedURLCache] removeAllCachedResponses];

NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
NSArray *cookies = [cookieStorage cookies];

id cookie;
for (cookie in cookies) {
[cookieStorage deleteCookie:cookie];
}


他にもセッションのAuthorizationを書き換えてみたり、と色々やってみましたが全然ダメでした。

困り果てた結果、以下のブログに行き着きました。
HTTP Basic Authentication "Logout" with NSURLConnection
http://www.springenwerk.com/2008/11/i-am-currently-building-iphone.html

なんとURLの一番最後に#をつけることでキャッシュさせなくするという方法。

[NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://localhost:3000/something.json"]]

[NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://localhost:3000/something.json#"]]


で、これでやってみると確かにうまくいく!

2日ほどここで困っていたのですが解消しました。特にRestkitとCoreDataの導入を試しでやっていたときだったので、問題がNSURLConnectionにあるという切り分けにも手間取りました。なお、パラメータをつけるときは、URLの一番最後に#がくるようにうまく調整しないといけないので注意してください。

PageTop

人と物語と脳噛ネウロ

トロイの木馬を知っていますか?

【トロイの木馬とは】
ギリシャが堅固なトロイ城を攻め落とした戦法。
1 大きな木馬を作り、その中にギリシャ軍の兵士を潜ませる。
2 戦いにわざと負けて、木馬をおいて逃げる。
3 トロイ軍は戦利品として木馬を持ち帰る
4 敵陣中枢でギリシャ兵が中から出てきてトロイは大混乱!

コンピュータへの悪意のあるソフトウェアとしても有名なので
知っている人も多いかもしれません。



さてこの話、鵜呑みにしていたのですが、明らかにおかしいところがあります。

今まで戦争していた相手のものを、ろくに調べもせずに自陣に引きこむ点です。
だって明らかにアヤシイじゃないですか。唐突に巨大な木馬。

どうしてトロイ軍はそんなことをしちゃったのでしょう?


この問題提起は、「魔人探偵脳噛ネウロ 」という漫画の一幕で出て来ました。
そして作中ではこんな解釈をしています。

「だから俺が思うにその木馬は
そんな事考えられねー位に 魅力的なデザインだったと思うのさ 
思わず独り占めにしたくなってよ 細かいことがどーでもよくなってよ」

この一幕は、ただ数ページ挿入されているだけの話なのですが、僕には衝撃的でした。
それまで僕は「トロイの木馬」というお話を知っていると思い込んでいました。
でも、僕は「トロイの木馬」という事実を慣用句的に知っていただけで、
物語としては考えてもいなかったのです。

そこには人が居たことを頭の片隅にも置いていなかったのです。

事実に人が絡むと物語になります。
複雑になって要点が隠れていってしまう一方、味わい深くなります。

ロジカル・シンプルな思考を追求していきながら、
でも事実から物語を読み込める能力もつけていきたいものだと思うのです。



ネウロの文庫版最終巻が週明けにでることもあり、好きな漫画の紹介でした。

魔人探偵脳噛ネウロ 12 (集英社文庫 ま 24-12)魔人探偵脳噛ネウロ 12 (集英社文庫 ま 24-12)
(2013/06/18)
松井 優征

商品詳細を見る

PageTop

盲点と脳と言い訳

盲点という言葉があります。
「その発想は盲点だった!」のアレです。

慣用句的に使われますが、盲点というものは実際に存在します。
僕達の目には見えていないのに見えているように感じてしまっている場所があるのです。

左目を閉じて、下の図の☓に集中してください。
その状態で画面に顔を近づけると、だいたい画面から30 cmくらいのところで●が消えます。



☓                ●



盲点は物理的に存在します。
眼球内には視細胞がびっしりと配置されていますが、
視神経が眼球内に入り込むところだけ、視細胞を配置できません。
そのため、そこに当たる部分が、視界の中にあるのに見えない部分、盲点となるのです。

僕達は生きているときに盲点を感じません。
それは両目があることも大きいでしょう。
しかし、片目をつぶっても「あ!ここは見えない領域だぞ」と思うことはありません。
さっきの●も、そこが見えないのではなく、●がなくなったように見えたでしょう。

それは、盲点の領域を脳が勝手に補完しているからです。
●は物理的に見えないのですが、「周りが白いからおそらく見えないところも白だろう」と脳が勝手に判断して補完しています。

この補完に関しては、どこまでやってくれるかを調べてみるとなかなか面白いです。
例えば、途切れている直線の間隙に、盲点をあわせると直線がつながってみえたりします。
曲線を補完できるのか、四角形の角を補完できるのか、人の顔は?



斯様に脳は僕たちを騙しています。
で本題。

脳卒中で半身不随になったにも関わらず、その半身が普通だと思い込む人がいるそうです。
病態失認といいます。
彼らに「あなたの左手は動きますか?」と聞くと「もちろん」と答えます。
「では、その左手でこのコップを取ってください」というと彼らはなんと答えるでしょう。

「いまちょっと手が痛いので嫌です」
「みんなが見ている前でやるのは恥ずかしいです」
といってやらないそうです。

彼らに嘘をついている感覚はありません。
本当にそう思っていて、だからやらないのです。

その結果、リハビリをしないので半身不随は治りません。
自分が半身不随だと理解でれば、リハビリをしたり、他の暮らし方を模索することができるのに。自分の状況を理解できないので、いや、理解していると思い込んでいるので、前進できないのです。



彼らは脳卒中になったから、そんな風になってしまったのでしょうか。
いいえ。盲点をはじめ僕たちは脳に騙され続けているのですから、きっと僕達もこうなんです。

自分が「できない」と思い込んでいるものには、実は全然違う理由があるのかもしれません。
誰かが馬鹿みたいな理由で「できない」と言っていてイイワケに聞こえたとしても、本人は本気でそういっているのかもしれません。

この話を読んで、「人間はそういったもんだ」というバッファをもって自分にも他人にも接するといいのかな、と思ったのでした。

脳のなかの幽霊 (角川文庫)脳のなかの幽霊 (角川文庫)
(2011/03/25)
V・S・ラマチャンドラン、サンドラ・ブレイクスリー 他

商品詳細を見る



関係ないけど、最近読んで面白かった漫画。

九井諒子作品集 竜のかわいい七つの子 (ビームコミックス)九井諒子作品集 竜のかわいい七つの子 (ビームコミックス)
(2012/10/15)
九井諒子

商品詳細を見る

PageTop

無限にスクロールできるUIScrollView

結構簡単に作れました。

ページャブルなUIScrollViewに3ページだけスペースを用意し、ページがめくれると順次ページを繰り上げる。それを繰り返すことで無限にスクロールできるようにしました。

新しいページはEndlessScrollViewDelegateプロトコルを作成し、- (UIView *)viewOfPage:(int)pageでページ数を指定して供給します。ページの左右端を指定するときは、viewOfPageでnilを返してあげると、普通にスクロールしてbounceするようになります。
endlessScroll

//EndlessScrollViewDelegate.h
@protocol EndlessScrollViewDelegate
- (UIView *)viewOfPage:(int)page;
@end

//EndlessScrollView.h
@interface EndlessScrollView : UIScrollView

@property (nonatomic, strong) UIView *pageView1;
@property (nonatomic, strong) UIView *pageView2;
@property (nonatomic, strong) UIView *pageView3;

@property (nonatomic, weak) id endscrollDelegate;

- (void)createFirst3Pages;

@end

//EndlessScrollView.m
#import "EndlessScrollView.h"

@implementation EndlessScrollView
{
  int _centerPageNum;
  BOOL _isLoadableScrollView;
}

- (id)initWithFrame:(CGRect)frame
{
  self = [super initWithFrame:frame];
  if (self) {
   self.contentSize = CGSizeMake(frame.size.width * 3, frame.size.height);
   self.pagingEnabled = YES;
   self.showsHorizontalScrollIndicator = NO;
   self.scrollsToTop = NO; // ステータスバーのタップによるトップ移動禁止
   self.delegate = self;
  }
  return self;
}

- (void)createFirst3Pages
{
  self.pageView1 = [self.endscrollDelegate viewOfPage:1];
  self.pageView2 = [self.endscrollDelegate viewOfPage:2];
  self.pageView3 = [self.endscrollDelegate viewOfPage:3];
  self.pageView1.frame = [self correctRectOfPageView:1];
  self.pageView2.frame = [self correctRectOfPageView:2];
  self.pageView3.frame = [self correctRectOfPageView:3];
  [self addSubview:self.pageView1];
  [self addSubview:self.pageView2];
  [self addSubview:self.pageView3];

  if (self.pageView2 == nil) {
   _isLoadableScrollView = NO;
   self.contentSize = CGSizeMake(self.frame.size.width * 1 + 1,//bounceさせる1
    self.frame.size.height);
  } else if (self.pageView3 == nil) {
   _isLoadableScrollView = NO;
   self.contentSize = CGSizeMake(self.frame.size.width * 2, self.frame.size.height);
  }else {
   _isLoadableScrollView = YES;
  }

  _centerPageNum = 2;
}

- (CGRect)correctRectOfPageView:(int)pageViewNum
{
  float x = self.frame.size.width * (pageViewNum - 1);
  return CGRectMake(x, 0, self.frame.size.width, self.frame.size.height);
}

#pragma mark - UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
  if (_isLoadableScrollView == NO) {
   return;
  }

  CGFloat position = scrollView.contentOffset.x / scrollView.bounds.size.width;
  if (position >= 2.0f) {
   int nextPage = _centerPageNum + 1;
   UIView *nextView = [self.endscrollDelegate viewOfPage:nextPage + 1];

   if (nextView) {
    [self addSubview:nextView];

    nextView.frame = self.pageView3.frame;
    self.pageView3.frame = self.pageView2.frame;
    self.pageView2.frame = self.pageView1.frame;

    [self.pageView1 removeFromSuperview];
    self.pageView1 = self.pageView2;
    self.pageView2 = self.pageView3;
    self.pageView3 = nextView;
    self.contentOffset = CGPointMake(self.bounds.size.width, 0);

    _centerPageNum = nextPage;
   }
  } else if (position <= 0.0) {
   int nextPage = _centerPageNum - 1;
   UIView *nextView = [self.endscrollDelegate viewOfPage:nextPage - 1];

   if (nextView) {
    [self addSubview:nextView];

    nextView.frame = self.pageView1.frame;
    self.pageView1.frame = self.pageView2.frame;
    self.pageView2.frame = self.pageView3.frame;

    [self.pageView3 removeFromSuperview];
    self.pageView3 = self.pageView2;
    self.pageView2 = self.pageView1;
    self.pageView1 = nextView;
    self.contentOffset = CGPointMake(self.bounds.size.width, 0);

    _centerPageNum = nextPage;
   }
  }
}

@end


- (void)scrollViewDidScroll:(UIScrollView *)scrollViewで制御しちゃっているので、- (UIView *)viewOfPage:(int)pageが呼び出された時にnilを返す処理が重いときついです。didEnd系はページを複数枚一気にめくろうとするときに対処出来ません。頑張ればいろいろできるけど、とりあえずこれで目的は達成。

使う方は、普通にEndlessScrollViewをaddしてあげてcreateFirst3Pagesを呼び出すだけでOK。もちろんEndlessScrollViewDelegateのviewOfPageの実装も忘れずに。

PageTop
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。