Objective-C コードで Auto Layout を設定するためのライブラリを書いた

Xcode 5 になって、InterfaceBuilder や Storyboard で Auto Layout を設定しやすくなったけど、 コードで Auto Layout 設定したいときが稀にある。 例えば自分の場合、継承して使いまわすことを前提に View や ViewController のベースクラスを作るときとか。

一度 Auto Layout の便利さを体感したら、もう Frame を設定してまわるなんてやってられなくなる。 かといって、Auto Layout をコードで記述するのもなかなか苦行。 RubyMotion の motion-layout みたいに、良い感じにラップしたライブラリがあれば、少しは楽になるのに。 そう思ったんで、Objective-C にポートしてみた。

使い方は次の通り。

@interface TNAViewController ()

@property (nonatomic) TNALayoutManager *layoutManager;

@end

@implementation TNAViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    UIButton *button1 = [UIButton buttonWithType:UIButtonTypeSystem];
    button1.backgroundColor = [UIColor blueColor];
    [button1 setTitle:@"button1" forState:UIControlStateNormal];
    [self.view addSubview:button1];

    UIButton *button2 = [UIButton buttonWithType:UIButtonTypeSystem];
    button2.backgroundColor = [UIColor yellowColor];
    [button2 setTitle:@"button2" forState:UIControlStateNormal];
    [self.view addSubview:button2];

    self.layoutManager = [[TNALayoutManager alloc] init];
    self.layoutManager.view = self.view;
    self.layoutManager.subviews = @{ @"button1": button1,
                                     @"button2": button2, };
    [self.layoutManager vertical:@"|-15-[button1]-10-[button2(==button1)]-15-|"];
    [self.layoutManager horizontal:@"|-10-[button1]-10-|"];
    [self.layoutManager horizontal:@"|-10-[button2]-10-|"];
    [self.layoutManager strain];
}

@end

motion-layout とほとんど変わらない。 実際にシミュレーターで表示してみたのがこちら。

f:id:griefworker:20140308091934p:plain

横にしても

f:id:griefworker:20140308091947p:plain

ばっちり配置されてる。

Podspec を用意したので、Podfile に

pod "TNALayoutManager", git: "https://github.com/tnakamura/TNALayoutManager.git"

って書けば CocoaPods でインストールできる。 CocoaPods のおかげで、オレオレライブラリが簡単に再利用できて開発が捗るな。

2016/10/27 追記

iOS 9 から NSAutoLayoutAnchor を使えば簡単に Auto Layout をコードで書けるようになったので、 自作ライブラリは使わなくなった。

保守するつもりもないので公開停止。

DelRaY のクレープシュゼット

天神の VIORO 5F にある DelRaY 福岡店が 3 月いっぱいで閉店するという情報を入手した。DelRay は以前行って、そのときはパフェを食べたんだけど、クレープシュゼットをどうしても食べてみたい。福岡店が無くなると、東京の銀座または表参道に行くしかない。これは事実上最後のチャンス、ってことで週末行ってみた。

最後の機会ってことで気が大きくなったのか、限定のストロベリーのクレープシュゼットを注文してしまった。その値段、なんと税込み 2730円!

f:id:griefworker:20140308134208j:plain

ストロベリーの酸味、オレンジソースの甘み、アイスクリームの冷たさ、クレープの暖かさ、そのどれもが互いに引き立て合って、相乗効果が生まれている。クレープシュゼット何度か食べたことあるけど、比べるのも失礼なくらい次元が違う。

今までで一番のクレープシュゼットだった。値段も一番。2730円って、フレンチレストランのランチが食べられる金額だぞ。普段なら絶対注文しないな。福岡店が閉店して、もう東京か海外でしか食べられなくなるのは残念だ。この店以上のクレープシュゼットには出会えない気がする。

関連ランキング:カフェ | 天神駅西鉄福岡駅(天神)天神南駅

acts-as-taggable-on と kaminari を組み合わせてハマったのでメモ

acts-as-taggable-on では

Entry.tag_counts

で Entry に付けられているすべてのタグを取得できるが、これに kaminari を組み合わせて

Entry.tag_counts.page(1).per(20).total_count

という風にページングした上で全件数を取得しようとすると

ArgumentError: wrong number of arguments (2 for 0)

というエラーが発生する。

原因は、kaminari が件数を取得するとき count に引数を渡しているけど、 acts-as-taggable-on で引数を受け取らない count を再定義しているため。 引数を受け取らないメソッドに引数を渡していて、エラーが発生してしまっていた。

英語が不自由なので、acts-as-taggable-on が count を再定義している理由は把握できなかったけど、とりあえず

module ActsAsTaggableOn::Taggable
  module Collection
    module CalculationMethods
      def count(column_name=:all, options={})
        super
      end
    end
  end
end

上記のモンキーパッチで回避した。

AutoLayout で UIImageView の幅を固定したのに横長の画像を表示すると幅が変わってしまう

  • AutoLayout で UIImageView の幅を 80 に固定
  • UIImageView の contentMode は UIViewContentModeScaleAspectFill

このとき、縦よりも横の幅がかなり長い画像を表示すると、UIImageView の幅が変わってしまった。

調べたところ、縦横のアスペクト比が違う場合、その分はみ出てしまうようだ。 UIImageView の幅が変わったんじゃなくて、画像がはみ出て描画されていたのか。 確かに、UIImageView の右端を基準に配置したラベルの位置は変わっていなかった。

UIImageView から画像がはみ出てほしくないから、

self.imageView.clipToBounds = YES;

で UIImageView の bounds のみ描画されるようにして対処。

極味や

天神パルコ地下1階にある『極味や』は、食事時いつも行列なんだけど、 会社帰りに行ったら運よく席が開いていたので滑り込んだ。

注文したのは『ハンバーグ (M サイズ) 』のセット(1380円)。

f:id:griefworker:20140226181644j:plain

ハンバーグにご飯と味噌汁とサラダ、そしてソフトクリームが付いている。 ソースを選択できるので、定番の極みやのタレにしてみた。

f:id:griefworker:20140226181706j:plain

伊万里牛を使っているのだけあって、タレをつけなくても旨い。 非常に肉肉しくて、ハンバーグを食べてる気がしない。 焼肉を食べてるような感覚。 焼石で焼いて食べるスタイルのハンバーグは何度か食べたけど、その中では現時点で1番だな。

量は M サイズでも十分なボリュームだった。 ご飯を1杯おかわりしただけで、もう満腹。 ご飯・味噌汁・サラダはおかわり自由なので、コストパフォーマンスは高い。

かなり気に入ったのでまた食べたいところだけど、 平日ランチはまず間違いなく並ぶだろうから時間的に無理だな。 行くとしたら会社帰りか休日。早めの時間帯に行くとしよう。

関連ランキング:ハンバーグ | 天神駅西鉄福岡駅(天神)天神南駅

博多ごまさば屋

刺身や寿司といった生の魚を久しく食べていなかったので、 会社帰りに福岡市中央区舞鶴にある『博多ごまさば屋』に立ち寄ってみた。

f:id:griefworker:20140224175838j:plain

注文したのは『ごまさば丼定食(780 円)』。 ごまさば屋でごまさばを食べないでどうするよ?。

f:id:griefworker:20140224180134j:plain

ごまさばとご飯、味噌汁とサラダと玉子焼き。ご飯は無料で大盛りにできる。 大盛りにしたらかなりのお得感を感じた。

f:id:griefworker:20140224180145j:plain

甘口であっさりめの醤油とごまの香ばしさが鯖にマッチしていて、ご飯が進む進む。 ごまさばの量は最初足りないかもと思ったけど、意外とそうでもなかった。 もちろん、もっと量が多くても全然かまわない。

ごまさば丼は〆にダシをかけてお茶漬けにできるんだけど、 ごまさばだけでご飯をすべてかき込んでしまった。 お茶漬けを味わい忘れたのは心残りだ。

関連ランキング:定食・食堂 | 赤坂駅天神駅西鉄福岡駅(天神)

Grape を使って Rails アプリに Web API を組み込む

Rails アプリに Web API を追加したい。 Rails のコントローラーで実装してもいいんだけど、せっかくなんで、 Web API マイクロフレームワーク『Grape』を試してみた。

Gemfile に

gem "grape"

を追加して bundle install でインストール。

RAILS_ROOT/app/apiapi.rb を作成。 RAILS_ROOT/lib に置くか迷ったけど、モデル使うし、Web API もアプリの一部と考えているから app 下に置くことにした。

# coding: utf-8

class API < Grape::API
  format :json
  default_format :json
  prefix "api"
  version "v1", using: :path

  helpers do
    def entries
      Entry.order("created_at DESC")
    end
  end

  resource "entries" do
    desc "エントリの一覧を返す"
    params do
      optional :page, type: Integer, desc: "Page number."
    end
    get do
      entries.page(params[:page])
    end

    desc "エントリを1件返す"
    params do
      requires :id, type: Integer, desc: "Entry ID."
    end
    get ":id" do
      entries.find(params[:id])
    end
  end

  resource "categories" do
    desc "すべてのカテゴリを返す"
    params do
      optional :page, type: Integer, desc: "Page number."
    end
    get do
      entries.category_counts.page(params[:page])
    end

    desc "指定したカテゴリのエントリ一覧を返す"
    params do
      requires :name, type: String, desc: "Tag name."
      optional :page, type: Integer, desc: "Page number."
    end
    get ":name" do
      entries.tagged_with(params[:name], on: :categories).page(params[:page])
    end
  end
end

kaminari を使っているんで、ページネーションも対応してみた。

この API クラスを Rails に組み込む。 API クラスは Rack アプリなので、Rails にそのままマウントできる。

# routes.rb

MyRailsApp::Application.routes.draw do
  mount API => '/'

  # ...省略...
end

これだけで、Rails に Web API を組み込むことができた。

Grape は、ある程度宣言的に記述できるので、ぱっと見で何をやる Web API なのか分かりやすい。 API クラスにまとまっているので、見通しも良い。

今回みたいにサクっとこしらえたいときにはもってこいで、 さすが Web API に特化したマイクロフレームワークなだけある。