Monthly Archives: October 2011

iOSで行間調整、カーニングするメモ

UILabelの行間、カーニング調整できないクソ仕様に悶絶したので代替手段としてCoreTextを調べてみたメモ。

・もろもろパラメータ設定したNSAttributedStringを用意する。指定できるパラメータはCoreText.framework内CTStringAttributes.hを参照。

・CTFramesetterCreateWithAttributedString(CFAttributedString*)でCTFramesetterRef取得する。

・CoreFoundationとObjective-Cのデータ型はだいたい互換性があるらしく、引数がCF~のとこに、同名のNS~を(CF~)NS~みたいにキャストしてつっこめれる。

・CGPathCreateMutable()でCGMutablePathRef取得する。

・CGPathAddRect(CGMutablePathRef, NULL, self.bounds)で描画範囲を設定。

・取得したCTFramesetterRefとCGMutablePathRefからCTFramesetterCreateFrame(CTFramesetterRef, CFRange, CGPathRef, NULL)を使ってCTFrameRefを作る。

・UIViewのdrawRect:(CGRect)rect内でUIGraphicsGetCurrentContext()使ってCGContextRef取得。

・CTFrameDraw(CTFrameRef, CGContextRef)でテキスト描画。

・UIViewとCoreGraphicsで座標系が違うので、
CGContextTranslateCTM(CGContextRef, 0, self.bounds.size.height);
CGContextScaleCTM(CGContextRef, 1, -1);
で上下反転させておく。

CoreFoundationとかガチのC言語な上、手続きがめんどすぎるのでよっぽどのことがない限りUIWebViewで描画した方がよい。
あとCoreTextの日本語資料少な過ぎで死ぬ。

参考:
Core Text Programming Guide: Introduction
Text Layout With Core Text
CoreText.framework – iPhone Development Wiki

動的に生成されるURLに対して固有のOGPを生成する方法メモ

http://hoge.com/#1000 みたいな、ハッシュ以下の値に対してコンテンツを書き換えるサイトで、それぞれのハッシュに応じて異なるOpen Graph Protocol(OGP)を吐き出したい時の対処法。

フェイスブック、ミクシィ、グリーで使われている OGP (Open Graph Protocol) とは何か
http://d.hatena.ne.jp/amachang/20110117/1295233078

1. まず同一ドメイン内に、OGP吐き出し用のPHPを用意する。

ex) http://hoge.com/share.php

2. 元のhtmlと同一のハッシュを受け取ってその値に応じたOGPを吐くようにする。

ex) http://hoge.com/share.php?id=1000

3. html内に設置したShareボタン、Likeボタンの対象URLを上記のshare.phpにする。

 <div data-href="http://hoge.com/share.php?id=1000" data-send=false data-layout="button_count" data-width=450 data-show-faces=false></div>

4. このままだとウォールにshare.phpのURLが投稿されてしまい、ウォールからアクセスしたユーザーがshare.phpに飛んでしまうので、share.php内でfacebookのクローラー以外を元のhtmlにリダイレクトする。
facebookのクローラーはUserAgant内の“facebookexternalhit”の有無で判定する。

Best Practices – Facebook開発者
http://developers.facebook.com/docs/best-practices/

5. 以下share.phpの概要

<?php
$id = $_GET["id"];
$ua = $_SERVER['HTTP_USER_AGENT'];
 
//facebook crowlerじゃなかったらリダイレクト
if (!preg_match('/facebookexternalhit/i', $ua))
{
    header("HTTP/1.1 301 Moved Permanently");
    header("Location: http://hoge.com/#" . $id);
    exit;
}
 
//idに応じて情報を取得する
$api = "http://hoge.com/api?id=" . $id;
$json = file_get_contents($api);
$user_data = json_decode($json);
$username = htmlspecialchars($user_data->result->username);
$img_path = htmlspecialchars($user_data->result->img_path);
 
?><!DOCTYPE html> 
<html lang="en" xmlns:og="http://ogp.me/ns#"> 
    <head>
        <meta charset="utf-8"> 
        <meta property="og:title" content="<?=$username?>'s page">
        <meta property="og:description" content="SITE DESCRIPTION">
        <meta property="og:url" content="http://hoge.com/#<?=$id?>">
        <meta property="og:image" content="<?=$img_path?>">
        <link rel="image_src" href="<?=$img_path?>">
        <title><?=$username?>'s page</title>
    </head>
    <body>
        <script type="text/javascript">location.replace("http://hoge.com/#<?=$id?>");</script>
    </body>
</html>

ActionScript 3 SDK for Facebook Platform API ハマリどころメモ

ActionScript 3 SDK for Facebook Platform APIを使う時に、attributes.nameを指定せずattributes.idだけを指定してswfobject.embedSWFすると、SDK内のExternalInterface.objectIDを参照している箇所でnullが返ってjsの方でエラーが出るブラウザがある。

このままだとjsの処理が止まってFB.loginとかFB.apiでコールバックが呼ばれなくなってしまうので、attributes.idと同じ名前をattributes.nameで指定してExternalInterface.objectIDを参照できるようにする。

var flashvars = {
};
 
var params = {
    base:'.',
    menu:'false',
    wmode: 'window',
    allowFullScreen:'true',
    allowscriptaccess:'always'
};
 
var attributes = {
    id:"hoge", //←これと
    name:"hoge" //←これを指定する
};
 
swfobject.embedSWF('main.swf', 'flashcontent', '100%', '100%', '10.0.0', '', flashvars, params, attributes);

Objective-C を強引に AS3 で例えるメモ

全部雰囲気で理解してるので間違ってる可能性大です。

変数宣言

プリミティブ型
int num → var num:int

クラス型
MyClass *myClass → var myClass:MyClass

※プリミティブ型以外の型は変数宣言で*付ける

ディスプレイオブジェクト的な概念

UIView → DisplayObject
UIResponder → InteractiveObject
addSubView → addChild
CGContextRef → Graphics
UIImage → BitmapData
UIImageView → Bitmap

座標指定
view.center = CGPointMake(100, 200)

view.x = 100;
view.y = 200;

リサイズ
view.size = CGSizeMake(100, 200)

view.width = 100;
view.height = 200;

メソッド

[instance method] → instance.method()
method:は:までがメソッド名

イベントハンドラ

[obj addTarget:self action:@selector(nullEventHandler:) forControlEvents:NullEventNull]

obj.addEventListener(NullEvent.NULL, nullEventHandler)

ヌル

nil → null

タイマー

NSTimer → Timer

文字列

@”This is text” → “This is text”

関数の定義

– (void)func: (NSString)arg1 argument2:(NSString)arg2 argument3:(NSString)arg3
{
}
※argument2とargument3は引数名(ラベル)で省略可。

private function func(arg1:String, arg2:String, arg3:String):void
{
}

関数の呼び出し

[obj func:@”引数1″ argument2:@”引数2″ argument3:@”引数3″];

obj.func(“引数1″, “引数2″, “引数3″);

コンストラクタ呼び出し

[[Class alloc] init] → new Class()

用語

プロトコル → インターフェイス

デリゲート

//インターフェイス(プロトコル)で通知用の関数定義
interface IHoge
{
function onHoge():void;
}

//デリゲートされるクラス
class Hoge implements IHoge
{
public function onHoge():void
{
trace(“called Hoge”);
}
}

/*
イベント(この場合は初期化)が起こったらインターフェイス(プロトコル)に対して通知関数を呼ぶ

デリゲート?
*/
class Main()
{
public Main(hoge:IHoge)
{
hoge.onHoge();
}
}

実行順序

アプリ起動

main()実行

UIApplicationMain()実行

アプリケーションデリゲートオブジェクトを呼び出し
– アプリケーションデリゲートオブジェクト = UIApplicationDelegateプロトコルを採用(継承)したオブジェクト
– didFinishLaunchingWithOptions:

イベントループ
– ユーザーの操作等々
– アプリケーションデリゲートオブジェクト経由で各メソッドを呼び出す

アプリ終了準備 → applicationWillResignActive:

アプリ終了 → applicationDidEnterBackground:

参照:
http://developer.apple.com/jp/devcenter/ios/library/documentation/iPhoneAppProgrammingGuide.pdf

FlashからOAuth用のポップアップ開くメモ

ポップアップしたウインドウ(子)とFlashがのっかってる元のウインドウ(親)はLocalConnection経由でやりとりする。

流れとしては、

元FlashでOAuthするボタンを押す
 ↓
子ウインドウがポップアップする
 ↓
TwitterのOAuthページが表示される。
 ↓
OAuth完了する
 ↓
元Flashに完了した旨が通知される。
 ↓
ポップアップウインドウが閉じる
 ↓
Twitterに各種情報をとりに行く

という感じ。

ここで問題になるのがLocalConnectionをする時に同じConnect IDを使い回すと、同じページを開いた場合にconnect対象が複数になってしまい接続が失敗する。

これを回避するためにconnect用のIDにタイムスタンプを使う。

var connectID:String = String((new Date()).getTime());

Flashから子ウインドウを開く時に、

ExternalInterface.call('window.open("' + url + '", "' + connectID + '")');

もしくは、

navigateToURL(new URLRequest(url) , connectID);

のようにwindow.nameとして生成したIDを子ウインドウに渡す。

子ウインドウはtwitterのOAuth画面が開き、
リダイレクトでもどってきたページでwindow.nameを参照し、
flashvars経由でsend用のswf(通知側)にこのIDを渡す。
send用のswfはこのIDを使ってsendしOAuthが終了したことを親ウインドウに知らせる。

connectが完了したら子ウインドウは自分でwindow.closeして閉じる。

さらに問題になるのがブラウザごとのポップアップブロックの条件。
IEはnavigateToURLがブロックされ、
MacのSafariはポップアップしたページに

タグがあるとwindow.closeできなくなるので、
ExternalInterfaceからwindow.openを呼びウインドウを開く。

ExternalInterface.call('window.open("' + url + '", "' + connectID + '")');

それ以外のブラウザはnavigateToURLで開く。

さらにさらに、Firefox4でwindow.nameを指定してウインドウを開くと、
window.closeした時に他のタブ(親ページ)も巻き込んで閉じてしまうので、
connectIDの頭に”_blank”をつけて開く。

var connectID:String = "_blank" + connectID;

こんな感じ。

Flash、wmode=gpuのバグ?

最近Flashのサイトを制作した際にハマったバグを共有しておきます。

バグの内容は、FireFox、ChromeでFlashサイトを開くと画面が点滅するというものです。
今の段階の結論を言うと、swfをエンベットする時にwmodeをgpu以外のものにするとこの現象は起こらなくなります。

この現象はブラウザ、OS、Flash Playerのバージョンが同じ環境でも異なる機種のMacでは起こらなかったりとハードウェアに依存する部分もあるようです。

検証では以下の環境で現象が確認できました。

黒く点滅し続ける

Mac 10.6.7
Firefox 3.6.17
FlashPlayer 10 debug

Mac 10.4.11
Firefox 3.6.17
FlashPlayer 10.2

画面が真っ白になる

Mac 10.6.7
Firefox 3.6.17
FlashPlayer 10.1

Mac 10.6.7
Firefox 3.5.17
FlashPlayer 10.1.102.64

読み込み時一瞬黒くなる

Windows7
Chrome 11
FlashPlayer 10.2.159.1 debug

Windows7
Chrome 11
FlashPlayer 10.3.181.14 debug

これらの現象が発生する共通する条件は以下の2点です。
・wmodeがgpu
・Flash Playerの設定内「ハードウェアアクセラレーションを有効化」にチェックが入っている
※この項目はデフォルトでチェックが入っています。

また、Flashのパブリッシュ設定内の「ハードウェアアクセラレーション」に関してはこの現象に関係ないようです。

より詳しい情報がわかれば教えて頂けると助かります。
よろしくお願いします。