24/7 twenty-four seven

iOS/OS X application programing topics.

UIImageView で 'Aspect Fit (UIViewContentModeScaleAspectFit)' を指定したときの画像サイズを取得する

↓ 例えばこんなふうに UIImageView に 'Aspect Fit' を指定して表示させたときの領域を知りたいことってありますよね。


がんばって計算してもいいのですが、AVFoundation.framework の次の関数で簡単に取得できます。

CGRect AVMakeRectWithAspectRatioInsideRect(CGSize aspectRatio, CGRect boundingRect);

ドキュメントによると、ムービーを CALayer に表示するときに領域にフィットさせるのに便利ということですが、画像に使っても便利です。


上記の画像だと UIImageView の大きさは 280x508 で、画像のサイズは 2047x1199 です。
そこで下記のコードを実行すると返ってくる CGRect は {{0, 171.997}, {280, 164.006}} でピッタリ画像の領域に一致します。

CGRect frame = AVMakeRectWithAspectRatioInsideRect(image.size, self.imageView.bounds);
UIView *overlayView = [[UIView alloc] initWithFrame:frame];
overlayView.layer.borderColor = [[UIColor colorWithRed:0.0f green:0.0f blue:1.0f alpha:0.5f] CGColor];
overlayView.layer.borderWidth = 4.0f;

[self.imageView addSubview:overlayView];


別の画像 (533x800) で実行した場合は {{0, 43.8687}, {280, 420.263}} になりました。

iPhone/iPadで画像をクロッピングするライブラリを公開しました

kishikawakatsumi/PEPhotoCropEditor · GitHub

PEPhotoCropEditor は iPhone/iPad アプリに画像をクロッピングする機能を簡単に追加します。
UIは標準のPhotos.appに似ていておもしろい動きをします。

ScreenShot 1 [Movie 1

インストール

CocoaPodsでインストールできます。

pod 'PEPhotoCropEditor'

または、Lib/ ディレクトリと Resources/ ディレクトリのファイルをすべてプロジェクトにコピーして、下記のフレームワークをリンクしてください。

  • QuartzCore.framework
  • AVFoundation.framework

使い方

ビューコントローラを使う場合

(UINavigationController を使わなくても動きます。)

PECropViewController *controller = [[PECropViewController alloc] init];
controller.delegate = self;
controller.image = self.imageView.image;

UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:controller];
[self presentViewController:navigationController animated:YES completion:NULL];
ビューを直接使う場合
self.cropView = [[PECropView alloc] initWithFrame:contentView.bounds];
[self.view addSubview:self.cropView];
Cropping された画像を取り出す
デリゲートメソッドから
- (void)cropViewController:(PECropViewController *)controller didFinishCroppingImage:(UIImage *)croppedImage
{
    [controller dismissViewControllerAnimated:YES completion:NULL];
    self.imageView.image = croppedImage;
}
ビューのプロパティから
UIImage *croppedImage = self.cropView.croppedImage;

iOS/Macの両方で使えて、文字の選択やリンクのクリックに対応したテキストビューをテスト公開しました。

kishikawakatsumi/SECoreTextView · GitHub

iOS ScreenShot 1


OS X ScreenShot 1


SECoreTextView はリッチテキストの表示と文字の選択(現在はOS Xのみ)やリンクがクリック可能だったりするテキストビューです。
別のアプリでテーブルビューのセルにリンクを含むテキストを表示するのに、既存のものでMacで使えるいい感じのものが今ひとつ見つからなかったので書きました。

OS X で使うだけだとなんなので、せっかくだから iOS にも対応してみました。

UITableVIewやNSTableVIewのセルで使うと便利だと思います。

iOS のほうは半日くらいでちょちょっと書いただけなのでおかしなところが結構あると思うので見つけたら教えてください。

iPhone の画面操作を録画するライブラリを公開しました。

kishikawakatsumi/ScreenRecorder · GitHub

ScreenRecorder は iOS デバイスの画面を連続的にキャプチャして、動画に変換することで画面の操作を録画することができる機能をアプリケーションに追加します。
開発中のソフトウェアのユーザーテストなどに利用すると効果的です。

使い方

1. 以下のファイルをプロジェクトに追加します

  • Lib/SRScreenRecorder.h
  • Lib/SRScreenRecorder.m
  • Vendor/KTouchPointerWindow.h
  • Vendor/KTouchPointerWindow.m

2. 以下のフレームワークをリンクします

  • QuartzCore.framework
  • CoreVideo.framework
  • CoreMedia.framework
  • AVFoundation.framework

startRecording で録画を開始します。
デフォルトの設定は

  • バックグラウンドに入ったときに自動保存
  • 10 分ごとに自動保存、ファイルのローテート
  • 30FPSで録画
  • タッチ箇所の表示

となっています。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    [[SRScreenRecorder sharedInstance] startRecording];
    return YES;
}


いくつかの挙動は、設定で変更することができます。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    SRScreenRecorder *recorder = [SRScreenRecorder sharedInstance];
    recorder.frameInterval = 1; // 60 FPS
    recorder.autosaveDuration = 1800; // 30 minutes
    recorder.showsTouchPointer = NO; // hidden touch pointer
    recorder.filenameBlock = ^(void) {
        return @"screencast.mov";
    }; // change filename
    
    [recorder startRecording];
    
    return YES;
}


タッチ・ポインターの表示には @ さんの KTouchPointerWindow を利用しています。
iPhone/iPadの画面にタッチ位置を表示するためのソースコード KTouchPointerWindow を公開しました
itok/KTouchPointerWindow · GitHub

Windows 8 StoreアプリケーションでGoogleAnalyticsを使う

kishikawakatsumi/GoogleAnalytics-for-WinJS · GitHub

Windows 8の Store アプリケーションで GoogleAnalytics を使う場合、まだ公式の SDK はありませんが、サードパーティからすでにいくつかライブラリがリリースされているのでそれを使うのがいいと思います。
ただ JavaScript(WinJS) で書いた Metro Style アプリケーション用のものはまだ無いみたいなので Windows Runtime Component を使って WinJS から C# のライブラリを扱う方法でやってみました。


Google Analytics for WinRT - Home
NuGet Gallery | Google Analytics for WinRT (Windows 8 and 8.1) and Universal apps 2.6.0

↑ GoogleAnalytics にリクエストを送る Windows 8 用のライブラリはいくつかありますが、今回はソースコードも公開されていて実装もシンプルな Google Analytics for WinRT - Home を使いました。

Windows Runtime Component のプロジェクトをソリューションに追加する

↓ 詳しい方法は下記のドキュメントを見てください。

Walkthrough: Creating a simple component in C# or Visual Basic and calling it from JavaScript
チュートリアル : C# または Visual Basic での単純なコンポーネントの作成および JavaScript による呼び出し

  1. Solution Explorer でソリューションを右クリックして、Add > New Project... と選択します。
  2. Visual C# > Windows Runtime Component と選択して、プロジェクト名を適当につけて [OK] を押します。
  3. Solution Explorer でWindows Store アプリケーションのプロジェクトを右クリックして、Add Reference... を選択します。
  4. Solution > Projects と選択して、先ほどの Windows Runtime Component として作ったプロジェクトにチェックをつけて [OK] を押します。

↑ ここまででこんな感じになります。

GoogleAnalytics のライブラリをプロジェクトに追加する

先ほどの Windows Runtime Component のプロジェクトから利用できるように GoogleAnalytics のライブラリを追加します。

  1. Solution Explorer でプロジェクトを右クリックして、Add > New Folder と選択します。
  2. そのフォルダに 'Nascent.GoogleAnalytics.dll' をコピーします。
  3. Solution Explorer で Windows Runtime Component のプロジェクトを右クリックして、Add Reference... を選択します。
  4. [Browse...] ボタンを押して 'Nascent.GoogleAnalytics.dll' を選択します。
  5. 'Nascent.GoogleAnalytics.dll' にチェックをつけて [OK] を押します。

ライブラリの呼び出しをブリッジするクラスを書く

↓ こんな感じで。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Nascent.GoogleAnalytics;

namespace WindowsRuntimeGoogleAnalytics
{
    public sealed class Tracker
    {
        public string webPropertyId { get; set; }

        public Tracker(string id)
        {
            webPropertyId = id;
        }

        public void SetCustomVariable(int index, string name, string value)
        {
            AnalyticsTracker tracker = AnalyticsTracker.GetInstance(webPropertyId);
            tracker.SetCustomVariable(index, name, value);
        }

        public void TrackPageView(string page)
        {
            AnalyticsTracker tracker = AnalyticsTracker.GetInstance(webPropertyId);
            tracker.TrackPageView(page);
        }

        public void TrackEvent(string category, string action)
        {
            AnalyticsTracker tracker = AnalyticsTracker.GetInstance(webPropertyId);
            tracker.TrackEvent(category, action, null, null);
        }

        public void TrackEvent(string category, string action, string label)
        {
            AnalyticsTracker tracker = AnalyticsTracker.GetInstance(webPropertyId);
            tracker.TrackEvent(category, action, label, null);
        }
    }
}

JSのインターフェースを書く

↓ 直接呼んでもいいけどまとまってると何かと使いやすいのでこんな感じで。

(function () {
    "use strict";

    var Tracker = WinJS.Class.define(
        function (webPropertyId) {
            this.webPropertyId = webPropertyId;
            this.tracker = new WindowsRuntimeGoogleAnalytics.Tracker(webPropertyId);
        },

        {
            setCustomVariable: function (index, name, value) {
                var that = this;
                that.tracker.setCustomVariable(index, name, value);
            },

            trackPageView: function (page) {
                var that = this;
                that.tracker.trackPageView(page);
            },

            trackCurrentPageView: function () {
                var that = this;
                var page = Application.navigator.pageControl.uri.replace("ms-appx://" + Windows.ApplicationModel.Package.current.id.name.toLowerCase(), "");
                that.trackPageView(page);
            },

            trackEvent: function (category, action, label) {
                var that = this;
                that.tracker.trackEvent(category, action, label);
            }
        });

    WinJS.Namespace.define("GoogleAnalytics", {
        Tracker: new Tracker("UA-4291014-7")
    });
})();

WinJS から呼び出してみる

↓ たとえば、画面が表示されるたびにアクセスを記録するならこんな感じで。

ready: function (element, options) {
    GoogleAnalytics.Tracker.trackCurrentPageView();

    var listView = element.querySelector(".groupeditemslist").winControl;
    listView.groupHeaderTemplate = element.querySelector(".headertemplate");
〜(略)〜


↓ 今回のサンプルコードはこちらです。
kishikawakatsumi/GoogleAnalytics-for-WinJS · GitHub

リンクするだけで iOS 6 で Google Map が使えるようになる YAMapKit を公開しました。

kishikawakatsumi/YAMapKit · GitHub

YAMapKit は MapKit.framework と(ほぼ)互換性のある代替ライブラリです。
Google Maps Javascript API と UIWebView を利用して iOS 6 で Apple の標準地図の代わりに Google Map を使った表示ができます。
MapKit.framework と(ほぼ)互換性があるのでリンク先を差し替えるだけで動作します(たいていの場合は)。

あまりヘビーな利用には向きませんが、アプリケーションの中でちょっと MapKit を使って地図を表示したりピンを挿したりしているという場合に効果的です。

まだ未サポートの機能がたくさんあるので、手伝ってくれる方や、バグレポート、要望を歓迎します。

使い方

  1. MapKit.framework のリンクを外します。
  2. libMapKit.a をリンクします。
  3. CoreLocation.framework をリンクします。

できないこと

  • ジオコーディング(代わりに 'CLGeocoder' を使ってください)
  • カスタムビューのオーバーレイ表示(組み込みのオーバーレイ (MKPolylineView, MKCircleView など) しか使えません)
  • アノテーションのドラッグ&ドロップ
  • アノテーションのコールアウトを表示したあとで更新する

(たぶん他にもいっぱいあります)

利用例


Objective-C でサブクラスのインスタンスから任意のスーパークラスのメソッドを呼ぶ

サブクラスのインスタンスからポリモーフィズムを無視して任意のスーパークラスのメソッドを呼びます。


↓ 下のように Shape クラスと Shape クラスを継承した Path クラス、および Path クラス を継承した Circle があります。
それぞれのクラスで draw メソッドをオーバーライドしています。

////////////////////////////////////////////////////////////////////////
#pragma mark - Shape
////////////////////////////////////////////////////////////////////////
@interface Shape : NSObject
@end

@implementation Shape

- (void)draw {
    NSLog(@"%@", @"Shape.");
}

@end

////////////////////////////////////////////////////////////////////////
#pragma mark - Path
////////////////////////////////////////////////////////////////////////
@interface Path : Shape
@end

@implementation Path

- (void)draw {
    NSLog(@"%@", @"Path.");
}

@end

////////////////////////////////////////////////////////////////////////
#pragma mark - Circle
////////////////////////////////////////////////////////////////////////
@interface Circle : Path
@end

@implementation Circle

- (void)draw {
    NSLog(@"%@", @"Circle.");
}

@end


↓ Circle クラスのインスタンスから draw メソッドを呼び出すと Circle クラスの draw メソッドが実行されて "Circle." と出力されます。
たまに多態性を無視してスーパークラスや、スーパークラスのさらにスーパークラスのメソッドを実行したいということってありますよね。
そういうときは 対象メソッドの IMP (メソッドを参照する関数へのポインタ) を使います。

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    Shape *shape = [[Circle alloc] init];
    [shape draw]; // => Circle.
    
    SEL selector = @selector(draw);
    
    void(*pathFunction)(id, SEL, ...) = (void(*)(id, SEL, ...))[Path instanceMethodForSelector:selector];
    pathFunction(shape, selector); // => Path.
    
    void(*shapeFunction)(id, SEL, ...) = (void(*)(id, SEL, ...))[Shape instanceMethodForSelector:selector];
    shapeFunction(shape, selector); // => Shape.
}

@end

↑ IMP の定義のままだと ARC が戻り値を retain しようとするので、戻り値が void の関数ポインタにキャストしています。


↓ 上記のコードの出力は下記になります。

2012-10-23 13:15:20.632 Monomorphism[50033:c07] Circle.
2012-10-23 13:15:20.633 Monomorphism[50033:c07] Path.
2012-10-23 13:15:20.633 Monomorphism[50033:c07] Shape.


サブクラスのインスタンスからポリモーフィズムを無視して任意のスーパークラスのメソッドが呼べました。
↓ 試したコードの全体を載せておきます。

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController

@end

////////////////////////////////////////////////////////////////////////
#pragma mark - Shape
////////////////////////////////////////////////////////////////////////
@interface Shape : NSObject
@end

@implementation Shape

- (void)draw {
    NSLog(@"%@", @"Shape.");
}

@end

////////////////////////////////////////////////////////////////////////
#pragma mark - Path
////////////////////////////////////////////////////////////////////////
@interface Path : Shape
@end

@implementation Path

- (void)draw {
    NSLog(@"%@", @"Path.");
}

@end

////////////////////////////////////////////////////////////////////////
#pragma mark - Circle
////////////////////////////////////////////////////////////////////////
@interface Circle : Path
@end

@implementation Circle

- (void)draw {
    NSLog(@"%@", @"Circle.");
}

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    Shape *shape = [[Circle alloc] init];
    [shape draw]; // => Circle.
    
    SEL selector = @selector(draw);
    
    void(*pathFunction)(id, SEL, ...) = (void(*)(id, SEL, ...))[Path instanceMethodForSelector:selector];
    pathFunction(shape, selector); // => Path.
    
    void(*shapeFunction)(id, SEL, ...) = (void(*)(id, SEL, ...))[Shape instanceMethodForSelector:selector];
    shapeFunction(shape, selector); // => Shape.
}

@end

iOS 6.0 と iOS 5.x の両方で動作するアプリケーションをビルドする設定

iOS 4.0 と iPhone OS 3.x の両方で動作するアプリケーションをビルドする設定 - 24/7 twenty-four seven

↑ こちらも参考に
iOS 4.0 が登場したくらいのときに上の記事を書いて、仕組みは変わってないのですけど Xcode 4.x 系だと UI が変わってるので現在のやり方をまとめます。

ベース SDK と Deployment Target を設定する

プロジェクトの "Build Settings" で "Base SDK" を "Latest iOS" にします。
前にも書きましたが、ベース SDK は最新を指定したほうがいいです。


プロジェクトの "Info" で "Deployment Target" をサポートする OS の最も低いバージョンにします。
(下の場合は iOS 5.0 以降で動作する。)


今なら、Base SDK 6.0 でビルドして、Deployment Target を 5.0 または 5.1 にするのが効果的でしょうか。2世代サポートということで。

新しい Framework を Weak Link (Optional) に設定する

古い環境には含まれていない Framework をリンクしていると、Dynamic Linker がシンボルのロードに失敗してアプリケーションが起動しません。
その場合は、新しい OS にのみ存在する Framework を Weak Link に設定します(今回は Social.framework)。

↑ リンクの設定はプロジェクトではなくターゲットに対してしか行えないので、ターゲットを選択して "Build Phases" > "Link Binary With Libraries" から設定します。
リンクするフレームワークを設定して、 Required > Optional に変更します。

OS のバージョンごとに処理を分岐する

古い API には存在しないセレクタを呼び出したり、クラスを参照したりするとクラッシュしますので必要に応じて処理を分岐します。

- (void)tweet:(id)sender
{
    Class clazz = NSClassFromString(@"SLComposeViewController");
    if (clazz) {
        SLComposeViewController *controller = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeTwitter];
        [self presentViewController:controller animated:YES completion:nil];
    } else {
        TWTweetComposeViewController *controller = [[TWLandscapeTweetComposeViewController alloc] init];
        [self presentViewController:controller animated:YES completion:nil];
    }
}

Auto Layout に注意

Auto Layout は iOS 6 以降しか使えません。Auto Layout を使用していると iOS 5.x などで起動時に関連のクラスが無くてクラッシュします。StoryBoard を新しく追加したときなどにうっかり Auto Layout のチェックをつけたままにしてしまったりするので注意しましょう。

iOS 6 では Supported interface orientations の順番に注意!

最近の Xcode ではアプリケーションが対応しているデバイスの向きをターゲットの Summary 画面から GUI を用いて設定できるようになりましたが、ここから設定する場合はボタンを押す順番に注意する必要があります。

というのも、この画面で設定した内容は、Info.plist の Supported interface orientations (UISupportedInterfaceOrientations) に反映されるのですが、この項目は Array の値で順番が起動時の状態に影響するからなのです。


上記の画面の状態になるように、ボタンを左から順に押していった場合、Info.plist の UISupportedInterfaceOrientations は下記のようになります。これは新規プロジェクトを作成した場合のデフォルト値です。


今度は同じ状態になるように、ボタンを「右から」順に押していきます。すると Info.plist は下記のようになります。
値の順番が変わっています。


実はこの順番は起動時の画面の向きに関係していて、iOS 6 では一番先頭に指定されている画面の向きで起動することになります。
つまり、前の例では縦画面で起動するのですが、後の例では横向きで起動することになります。


別のキーに Initial interface orientation (UIInterfaceOrientation) というものがあって、こちらを指定すると初期状態を指定できそうですが、試したところ iOS 6 ではどうもこの値は無視されるようです。

iOS 6 ではグループスタイルのテーブルビューの背景色がこっそり非推奨になっている。


iOS 6 では上記のカラーを生成するメソッドがヘッダのコメントでひそかに deprecated になっています。

UIInterface.h

// Group style table view backgrounds can no longer be represented by a simple color.
// If you want to have a background in your own view that looks like the table view background,
// then you should create an empty table view and place it behind your content.
+ (UIColor *)groupTableViewBackgroundColor; // This method will be deprecated during the 6.0 seed program


ドキュメントの記載は変わってないのですが、実際に使ってみると今までは下記のコードでピンストライプのカラーが設定されていましたが、iOS 6 だと真っ黒になってしまいます。

self.view.backgroundColor = [UIColor groupTableViewBackgroundColor];


この背景色を使いたい場合は空のテーブルビューを設定しろということなので、iOS 6 から代替のコードは下記のようになります。

UITableView *tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStyleGrouped];
[self.view addSubview:tableView];

はやりのシンボルフォントを iOS で画像として扱える SymbolFontKit を公開しました。

kishikawakatsumi/SymbolFontKit · GitHub

ScreenShot1

↑ シミュレータに表示されている画像やツールバーのボタン、タブバーのアイコンは全てフォントです。

シンボルフォントとは要するにアイコン画像などをフォント形式にしたものです。
Webだと最近のブラウザだとWebフォントが使えるので、利用者の環境にフォントがインストールされていなくても使えるので、最近は解像度非依存ということもあっていろいろな Github や Twitter などいろいろなサイトで利用されています。

シンボルフォントについて詳しくは下記のリンク先などを見てください。
Ligature Symbols 〜ほんとにべんりなフォントのはなし〜
【完全版】Ligature Symbols フォントセットの自作方法 - くらげだらけ
Ligature Symbols

で、フォントなのでベクターデータのためどんな解像度でもキレイに表示されることや、1つのアイコンで色違いや別のサイズを表示することが簡単だったり、はやっているので様々なデザインのシンボルフォントが使いやすいライセンスで入手できるなど、iOS でも利用できたら便利だと思って作りました。

使い方

  1. SFKImage.h/m をプロジェクトにコピーします。
  2. 利用したいフォントファイルをプロジェクトにコピーします。
  3. 上記でコピーしたフォントファイルのファイル名を UIAppFonts をキーにして Info.plist に追加します。

API は UIImage 互換なので UIImage と同様の使い方ができます。(実は現在は imageNamed: 以外のインスタンス化はできません)

SFKImage *image = [SFKImage imageNamed:@"print"];


インスタンス化した SFKImage オブジェクトは UIImage のインスタンスと同様に UIButton や UIImageView、UITabBarItem などに直接設定することができます。

self.imageView1.image = [SFKImage imageNamed:@"share"];

UITabBarItem *calendarTabBarItem = [[UITabBarItem alloc] initWithTitle:@"calendar" image:[SFKImage imageNamed:@"calender"] tag:1];
UITabBarItem *globeTabBarItem = [[UITabBarItem alloc] initWithTitle:@"globe" image:[SFKImage imageNamed:@"globe"] tag:2];
_tabBar.items = @[calendarTabBarItem, globeTabBarItem];


フォントのレンダリングは実際に画面に描画されるときまで遅延されるので、UIImage と違って、1つのインスタンスを途中で色や大きさを変えたりできます。

SFKImage *image = [SFKImage imageNamed:@"compass"];
image.size = CGSizeMake(20, 20);
image.color = [UIColor redColor];
self.imageView6.image = image;
    
image.size = CGSizeMake(40, 40);
image.color = [UIColor yellowColor];
self.imageView7.image = image;
    
image.size = CGSizeMake(80, 80);
image.color = [UIColor blueColor];
self.imageView8.image = image;


UIImage として振る舞うためにちょっと無茶をしているのでそのまま AppStore の審査に通るかどうかは「?」ですが近いうちに適当なアプリを提出して調査したいと思います。


SymbolFontKit は第1回iphone_dev_jp東京 iPhone/Mac Hackathon 〜みんなが幸せになるハッカソン〜の成果物です。

Objective-C や Xcode の新しい機能のほとんどは iOS4 以前でも動く

Objective-C Feature Availability Index に一覧表が載ってるのですが、ARCのWeak Reference 以外は iOS 4 以上、ほとんどはすべての iOS バージョンで動くので、古い環境を気にせずにジャンジャン使ってしまってOKです。

新しいリテラルや Subscripting は断然コードが書きやすく読みやすくなるのでこれはうれしいですね。


Objective-C Feature Availability Index

FeatureTools versionsOS X deploymentiOS deployment
Automatic Reference Counting (ARC)Xcode 4.2
(LLVM Compiler 3.0)
Requires modern runtime
Deploys back to OS X v10.7
Deploys back to iOS 5
Automatic Reference Counting without zeroing weak reference (“ARCLite”)Xcode 4.2
(LLVM Compiler 3.0)
Requires modern runtime
Deploys back to OS X v10.6
Deploys back to iOS 4
Default synthesis of @property instance variables and accessor methodsXcode 4.4 (LLVM Compiler 4.0)Requires modern runtime
Deploys back to OS X v10.6
Deploys back to iOS 4
Instance variables in class extensionsXcode 4.2
(LLVM Compiler 3.0)
Requires modern runtimeAll iOS releases
Instance variables in @implementation blockXcode 4.2
(LLVM Compiler 3.0)
Requires modern runtimeAll iOS releases
No forward method prototypes needed in @implementation blockXcode 4.3
(LLVM Compiler 3.1)
All releasesAll iOS releases
NSNumber, NSDictionary and NSArray literalsXcode 4.4
(LLVM Compiler 4.0)
All releasesAll iOS releases
@YES and @NO literalsXcode 4.4 and OS X 10.8 or later SDK
Xcode 4.5 and iOS 6 or later SDK
(LLVM Compiler 4.0)
All releasesAll iOS releases
NSDictionary and NSArray subscriptingXcode 4.4 and OS X 10.8 or later SDK
Xcode 4.5 and iOS 6 or later SDK
(LLVM Compiler 4.0)
Requires modern runtime
Deploys back to OS X v10.6
Deploys back to iOS 4

はてなブックマークの ShareKit 拡張を書きました

ShareKit/Classes/ShareKit/Sharers/Services/Hatena at hatena · kishikawakatsumi/ShareKit · GitHub


ShareKit といういろいろな外部サービスとの連携機能を提供するライブラリがあるのですが、それのプラグイン (ShareKit では Sharer と呼びます) として「はてなブックマーク」とリンクを共有するものを書きました。
オリジナルを fork して hatena ブランチにコミットしています。利用するには clone して hatena ブランチに切り替えます。


本家にPull Request を送ったので、もしかしたらマージされるかもしれません。
(既存の Sharer に日本のサービスはなかったのでイマイチ勝手がわかりませんでした。)


↓ 発端は fladdict さんとの下記のやりとりです。


はてなブックマークへの投稿は昔書いたことがあったので、認証を OAuth にしたらあとはカンタンそうだなと思って安請け合いしました。いやまあ、特に難しいことはなかったんですけど。




↑ ちょっとだけハマったのは、付属している OAuth ライブラリ (OAuthConsumer) が不具合のあるリビジョンのもので、シグネチャを組み立てるときに追加の OAuth パラメータの部分を2重に URL エンコードしていました。
そのため、はてなの OAuth は必ずコールバック URL を oauth_callback パラメータで指定する必要があるのですが、シグネチャが invalid になって OAuth が成功しないということがありました。


↓ こちらの不具合は別の Pull Request でパッチを送ったので現在は修正されています。
Fix double URI encode for extra OAuth parameters. by kishikawakatsumi · Pull Request #515 · ShareKit/ShareKit · GitHub



↑ そんなこんなでとりあえず書いてみた割りにはちゃんと使えてるようでよかったです。
OAuth で許可するスコープに write_private を書いたら失敗する点とか気になるところはあるけど。。。



↑ Pull Request しました。↓
Add Hatena Bookmark Sharer. by kishikawakatsumi · Pull Request #521 · ShareKit/ShareKit · GitHub

第1回iphone_dev_jp東京 iPhone/Mac Hackathon 〜みんなが幸せになるハッカソン〜 を開催しました

iphone_dev_jp東京 iPhone/Mac Hackathon : ATND
iPhone_dev_jpで、みんなが幸せになるハッカソンを開催します | fladdict


前回の勉強会で深津さんが「一発もののアプリじゃなくてきちんと使われるライブラリをドキュメント込みで作るハッカソンやったらいいんじゃない?」って話をしていたので、それはすばらしいと思ったのでやってみました。


ハッカソンってやったことなくて、勝手がわからずにかなりギリギリの告知になってしまったのですが、約30名の猛者が集まってくださいました。

とはいえ、私は1日でそんなに書けるものだろうかと不安だったので実はそれまでの1週間である程度メドを立てておこうとか思ってたのですが、意外と時間がなくてぶっつけになってしまいました。
でもなんとかなるもので、やっぱり集中して書いたほうがダンゼン効率がいいんだなあとか今さらながら思いました。


こういう周辺ライブラリは必要だなあと思っててもけっこう普段の時間には書こうと思ってもなかなか筆が進まなかったりするもので、こういう集まりは小規模でもいいので定期的にやっていこうというところを確認できた一日でした。
かなりメリットのあることがわかったので近いうちにまたやると思いますのでまたみなさん集まってくれたらうれしいです。