[iOS] Facebook SDKのSSOボタンをローカライズする

Standard

ドキュメントを見つけられなくてハマッてしまったのでメモ。さっさとSDK付属のサンプルアプリを見ればよかった。。
 
やりたいのは単純に↓みたいにボタンのテキストを日本語にしたいだけ。
20130819-01

20130819-01

 
 
Podfile

platform :ios, '6.1'
pod 'Facebook-iOS-SDK'

pod install

した後に、Info.plistにFacebookAppIDを追加。 
20130819-02

20130819-02

 
で、
ViewController.h

//
//  ViewController.h
//  FBSample
//

#import <UIKit/UIKit.h>
#import <FacebookSDK/FacebookSDK.h>

@interface ViewController : UIViewController <FBLoginViewDelegate>

@end

 
ViewController.m

//
//  ViewController.m
//  FBSample
//

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    FBLoginView *loginBtn = [[FBLoginView alloc] initWithFrame:CGRectMake(
                                                                          self.view.frame.size.width / 2 - 154,
                                                                          self.view.frame.size.height / 2 - 22,
                                                                          308,
                                                                          44)
                             ];
    loginBtn.readPermissions = @[@"basic_info"];
    loginBtn.delegate = self;
    [self.view addSubview:loginBtn];
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
}

@end

 
のようにFBLoginViewを追加すると、シングルサインオンボタン(SSOボタン)が表示される。英語テキストで。
 
20130819-03

20130819-03

 
英語テキストの方がかっこいいのでこのままでいいんだけど、まー仕事だとそうもいかないので、これを日本語テキストに変更する。

[loginBtn.label setText:@"Facebookログイン"];

とかできてもよさそうなのに、そんなメソッドは用意されていない。Settings Bundleを追加してそこで上書かないといけない。
 
Facebookのサンプルアプリ ScrumptiousからFacebookSDKOverrides.bundleをコピーしてInfo.plistを編集。FacebookBundleNameFacebookSDKOverridesを設定する。
 
20130819-04

20130819-04

 
コンソールで

cp -r FacebookSDKOverrides.bundle/en.lproj FacebookSDKOverrides.bundle/ja.lproj

 
あとはFacebookSDKOverrides.bundle/ja.lproj/Localizable.stringsを以下のように編集すれば晴れて日本語化される。

/* FBLoginView (aka FBLV) */
"FBLV:LogOutButton" = "ログアウト";
"FBLV:LogInButton" = "Facebookでログイン";
"FBLV:LoggedInAs" = "%@ でログイン中";
"FBLV:LoggedInUsingFacebook" = "Facebookでログインしました";
"FBLV:LogOutAction" = "ログアウト";
"FBLV:CancelAction" = "キャンセル";

 
Localizationsの追加もお忘れなく。

20130819-05

20130819-05

[iOS] iOSでカルーセルを簡単に実装できるiCarousel

Standard

xtoneのVersion 1.0.2が公開されました。このバージョンでアルバム選択部分につかっているiCarouselが超絶便利だったのでメモ。

映像だとわかりにくいんですが、フリックジェスチャーでアルバムアートワークを切替・選択しています。UIScrollViewにdatasourceが実装されたようなイメージで、こういう動きを簡単に実装できる代物です。
 
iCarousel – https://github.com/nicklockwood/iCarousel
 
 
使い方は簡単で、iCarouselDataSourceとiCarouselDelegateのメソッドを実装するだけ。以下は導入方法とサンプルコード。
 

インストール

cocoapodsでインストール。
Podfile

platform :ios, '6.1'
pod 'iCarousel'

podコマンドを実行

pod install

別途QuartzCoreが必要なのでプロジェクトに追加しておく。
 
ViewController.h

//
//  ViewController.h
//  iCarouselIsFine
//

#import <UIKit/UIKit.h>
#import "iCarousel.h"

@interface ViewController : UIViewController <iCarouselDataSource, iCarouselDelegate>

@property iCarousel *carousel_;
@property NSMutableArray *items_;

@end

 
ViewController.m

//
//  ViewController.m
//  iCarouselIsFine
//

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    // set scrollable items
    _items_ = [@[] mutableCopy];
    NSDictionary *item;
    int cnt = 1;
    int i = 0;
    while (i < 3) {
        CGFloat j = 0.0f;
        while (j <= 1.0f) {
            UIColor *color;
            if (i == 0) color = [UIColor colorWithRed:j green:0 blue:0 alpha:1];
            if (i == 1) color = [UIColor colorWithRed:0 green:j blue:0 alpha:1];
            if (i == 2) color = [UIColor colorWithRed:0 green:0 blue:j alpha:1];
            item = @{
                     @"text": [NSString stringWithFormat:@"item-%d", cnt],
                     @"color": color
                     };
            [_items_ addObject:item];
            j += 0.05f;
            cnt++;
        }
        i++;
    }

    // set up iCarousel
    _carousel_ = [[iCarousel alloc] initWithFrame:CGRectMake(
                                                             0,
                                                             self.view.frame.size.height / 2 - 50,
                                                             self.view.frame.size.width,
                                                             100
                                                             )];
    [_carousel_ setBackgroundColor:[UIColor clearColor]];
    _carousel_.dataSource = self;
    _carousel_.delegate = self;
//    iCarouselTypeLinear
//    iCarouselTypeRotary
//    iCarouselTypeInvertedRotary
//    iCarouselTypeCylinder
//    iCarouselTypeInvertedCylinder
//    iCarouselTypeWheel
//    iCarouselTypeInvertedWheel
//    iCarouselTypeCoverFlow
//    iCarouselTypeCoverFlow2
//    iCarouselTypeTimeMachine
//    iCarouselTypeInvertedTimeMachine
    _carousel_.type = iCarouselTypeCylinder;
    [self.view addSubview:_carousel_];
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
}

#pragma mark -
#pragma mark iCarousel methods

- (NSUInteger)numberOfItemsInCarousel:(iCarousel *)carousel
{
    return [_items_ count];
}

- (UIView *)carousel:(iCarousel *)carousel viewForItemAtIndex:(NSUInteger)index reusingView:(UIView *)view
{
    UILabel *label = nil;

    //create new view if no view is available for recycling
    if (view == nil) {
        //don't do anything specific to the index within
        //this `if (view == nil) {...}` statement because the view will be
        //recycled and used with other index values later
        view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
        label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
        [label setBackgroundColor:[UIColor clearColor]];
        [label setFont:[UIFont boldSystemFontOfSize:18]];
        [label setTextColor:[UIColor whiteColor]];
        [label setTextAlignment:NSTextAlignmentCenter];
        label.tag = 1;
        [view addSubview:label];
    } else {
        //get a reference to the label in the recycled view
        label = (UILabel *)[view viewWithTag:1];
    }
    [view setBackgroundColor:_items_[index][@"color"]];
    [label setText:_items_[index][@"text"]];
    return view;
}

- (CGFloat)carousel:(iCarousel *)carousel valueForOption:(iCarouselOption)option withDefault:(CGFloat)value
{
    if (option == iCarouselOptionSpacing) {
        return value * 1.25f;
    }
    return value;
}

@end

 
 
これで

こうなる。エフェクトも豊富でいい感じです!

[xtone] iPhoneアプリを公開しました

Standard

7月から兼業フリーランスという働き方をしています。これについてはいろいろ思うところがあるので別にエントリーを書こうと思います。
@k13style こと三登さんとxtoneというiPhoneアプリを作ったので今日はその宣伝をします。
 
 

次世代音楽サジェストアプリ xtone

http://x-tone.org/
読み方は「クロストーン」です。「エックストーン」でも「ペケトン」でもありません。

iPhoneに入っているアルバムを2つミックスして、あなたにピッタリな音楽をおすすめします。
操作はアルバムをフリックするだけ。
xtoneがあなたの好きな音楽を分析して、新たな曲を見つけてくれます。

自分の持っているアルバムを2枚選ぶと、おすすめのアルバムを3枚教えてくれます。iTunes Storeにそのアルバムがあればアプリ内で試聴が可能です。みんなのミックス履歴も見れるので、そこから自分の知らない音楽に出会うこともできます。あと、スタッフ(三登とボク)が今聴いている曲もムダに教えてくれますw
 
三登さんがコンセプトとUIデザイン + フロントエンドの開発、ボクがバックエンド + フロントエンドの開発を担当しました。UIに関しては、2人でカッコいいと思った動きをふんだんに取り込んでいます。フロントは9割ぐらいHTML5とCSS3で書けました。仕事後にボクらのカフェ・Miyamaに通いつめて、だいたい1ヶ月ぐらいで1.0.0をリリースしたので、敷居下がったよなってのをすごく感じました。2年前に仕事でがっつりiOSアプリ作ったときはすごく辛かったのに。
CocoaPodscocoa CONTROLSの存在は偉大です。
 
 

おすすめのアルゴリズム

次世代音楽サジェストアプリと謳ってるのですが、おすすめを選ぶときに何を基準に選んでるかっていう話です。

xtoneのおすすめアルゴリズムは音楽のジャンルをベースにしたものではありません。
「音楽を勧める」ことに向き合い「似ている音楽とは何か」を考えました。
xtoneがピックアップするのは音楽ではなく、「ユーザー」です。
この独自アルゴリズムにより、ユーザーが求めている思いがけない音楽を発見できるようにつくりました。

ちょっとカッコよく書いてるんですが、実はすごくシンプルで、「あなたが選んだアルバムをすごい聴いてる人が聴いてる他のアルバムって、あなたも好きかもしれないよね」っていう考えがベースです。いろんな人のアルバムの情報が集まれば集まるほど、面白いおすすめが提供される可能性も上がってくっていう仕組みが作れたので、しばらくはアルバムデータベースの成長を見守りながらアルゴリズムを調整していこうと思います。
 
 

名刺代わりのアプリ

受託メインで仕事をしていると、業界が違う人から「どんな仕事してるの?」って聞かれたときに地味に困ったりします。今までもいろんなWEBサービスとかアプリを作り捨てしてきたんですが、今回は長く成長させていけそうなプロダクトができたので、「これ作ってます」って言ってこうと思います。上でも書きましたが、フリーランスとして仕事も受けていくことにしたので、名刺代わりにステッカーでも配ったらいいかなと。。w
 
 

まだまだやることたくさん

メモリを大量に消費してしまったり、おすすめのされるアルバムが同じものばっかりだったり、改良すべき点が多いです。。当面の目標は、アルバム情報10万枚と、アプリレビューで★5つを獲得することです。(ちなみに今は★1つの酷評が1つだけ)
たまーにアプリを立ち上げて、ボロクソに酷評したフィードバックとか送ってもらえるとうれしいです。
 
 

P.S.

アプリの審査通ったタイミングで三登さんとボクで同時にFacebookで告知したんですけど、コミュ力格差社会っていうのを垣間見た気がしたね。ボクの友達のほとんどは食べ物にしか反応してくれないw

[iOS]アルバムアートワークをUIWebViewから参照する

Standard

UIWebViewにiPhone内にある音楽データのアルバム情報を表示ようとしようとしたら、アルバムアートワークを表示する部分で悩んだのでメモ。
 
アルバム情報のJSONを引数にとり、HTMLで描画するようなメソッドをJavascriptに用意しておいて、UIWevView#stringByEvaluatingJavaScriptFromStringでそれをコールする感じで実装した。
MPMediaQueryでアルバムの情報は取得できる。が、アルバムアートワーク(MPMediaItemPropertyArtwork)はUIImageクラスで取得される。ローカルのパスが取得できればいいんだけど、できないっぽかったので「アプリ内にアートワーク画像を保存して、そのパスをWebViewから参照する方法」と「アートワーク画像をBase64の文字列にする」との2パターンを考えた。
前者はストレージを圧迫するし、後者はメモリ食うので他にいい方法あったら教えてください。。imageDataToFilePathが前者、imageDataToBase64Stringが後者。
 
 

 
NSDataからBase64の文字列を生成するには、NSData-Base64を使っている。
 
Podfileに以下を記述して pod install

pod 'NSData+Base64'

スケジュール調整を10倍早く
Cu-hacker

Standard

ここ数ヶ月、焼肉おごってもらったりしながらちょこっとお手伝いしていたCu-hacker(クゥハッカー)がベータ版としてリリースされたそうです。
https://cu-hacker.com/
 
おめでとうございます。
プロジェクトに参加した当初のやけにグラデーション強めなデザインを見たときはどうなることかと思いましたが(笑)、機能もUIもいい感じになってのリリースです。これからたくさんのフィードバックをもらって、ぜひぜひ磨きをかけてって欲しいです。「PHPで書かれてた参加当時のコードをこき下ろしたせいで、Rubyでイチから書き直し」なんてことになってすみませんでした。。w
がんばれイケメン集団!