[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

 
 
これで

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

[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'

FMDBでlike検索

Standard

iOSでSQLite3を使うときの定番wrapper FMDB。クエリパラメータのバインドもやってくれるので楽なわけですが、like検索するときのバインドがうまくいかなくてハマった。変な使い方するみたいなのでメモ。FMDBっていうか、sqlite3_bind_textっていう関数の使い方っぽい。
 
 
普通に条件つきのSQLを使うときはこんな感じにクエリを発行する。

NSString *query = @"select * from ocha_master where name = ?";
FMResultSet *rs = [db_ executeQuery:query,@"ジャスミン"];
while ([rs next]) {
    //カーソルから取り出してサムシングエルス
}
[rs close];

 
で、このままlike検索しようとすると、こうなる

NSString *query = @"select * from ocha_master where name like '%?%'";
FMResultSet *rs = [db_ executeQuery:query,@"ジャスミン"];
while ([rs next]) {
    //カーソルから取り出してサムシングエルス
}
[rs close];

 
んだけど、これだと1件もヒットしない。
where name like '%?%'

where name like '%%?%%'
に変えたりとかいろいろやったけどうまくいかなくて、google x 20ぐらいでたどり着いた結論が

NSString *query = @"select * from ocha_master where name like ?001";
FMResultSet *rs = [db_ executeQuery:query,[NSString stringWithFormat:@"%%%@%%",@"ジャスミン"]];
while ([rs next]) {
    //カーソルから取り出してサムシングエルス
}
[rs close];

 
 
 
sqlite3_bind_textはなんか、泥臭いことしてそ。。