RubyMotion 用 gem の Teacup は、CSS っぽくビューのデザインを記述できる、
厳密にはビューのプロパティを設定するための内部 DSL。
なんと AutoLayout もサポートしていたりする。
Teacup の AutoLayout を使って、カスタム UITableViewCell をレイアウトしようとして、
README に書かれていないことで結構苦労したのでメモしておく。
まずスタイルを記述する。
Teacup::Stylesheet.new :item_cell do
style :title_label,
constraints: [
constrain(:left).equals(:superview, :left),
constrain(:top).equals(:superview, :top),
constrain(:bottom).equals(:superview, :bottom),
]
style :price_label,
constraints: [
constrain(:right).equals(:superview, :right),
constrain(:top).equals(:superview, :top),
constrain(:bottom).equals(:superview, :bottom),
]
style :comment_label,
constraints: [
constrain(:left).equals(:title_label, :right),
constrain(:right).equals(:price_label, :left),
constrain(:top).equals(:superview, :top),
constrain(:bottom).equals(:superview, :bottom),
]
end
constraints の部分が AutoLayout。ここ記述ミスしやすい上に、ミスっていてもなかなか気づかない。今回は1時間ほどハマった。要注意。
次にスタイルを適用するセルを作る。
UIViewController は Teacup が拡張していて stylesheet メソッドが使えるけど、
UIView は拡張されていない。
Teacup::Layout を incude する必要がある。
class ItemCell < UITableViewCell
CELL_ID = "item-cell"
include Teacup::Layout
stylesheet :item_cell
def self.cellForItem(item, inTableView:tableView)
cell = tableView.dequeueReusableCellWithIdentifier(CELL_ID)
unless cell
cell = self.alloc.initWithStyle(
UITableViewCellStyleDefault,
reuseIdentifier:CELL_ID
)
end
cell.setupItem(item)
cell
end
def initWithStyle(style, reuseIdentifier:reuseIdentifier)
super
layout self.contentView do
@titleLabel = subview(UILabel, :title_label)
@commentLabel = subview(UILabel, :comment_label)
@priceLabel = subview(UILabel, :price_label)
end
self
end
def setupItem(item)
@titleLabel.text = item[:title]
@commentLabel.text = item[:comment]
@priceLabel.text = "#{item[:price]} 円"
end
end
セルを使うコントローラー。
class MainViewController < UITableViewController
def self.controllerWithNavigation
UINavigationController.alloc.initWithRootViewController(self.alloc.init)
end
def init
initWithStyle(UITableViewStylePlain)
@items = [
{ title: "foo", comment: "aaaaa", price: 100 },
{ title: "bar", comment: "bbbbb", price: 200 },
{ title: "hoge", comment: "ccccc", price: 300 },
{ title: "fuga", comment: "eeeee", price: 400 },
]
self
end
def viewDidLoad
super
self.title = "Sample"
end
def tableView(tableView, numberOfRowsInSection:section)
@items.size
end
def tableView(tableView, cellForRowAtIndexPath:indexPath)
item = @items[indexPath.row]
cell = ItemCell.cellForItem(item, inTableView:tableView)
cell.restyle!
cell.apply_constraints
cell
end
end
cell の restyle! と apply_constrains を呼び出さないと、スタイルに記述した制約が適用されない。
README に書いてなくて、これまたハマった。最終的にはソースコードまで読むことに。
最後に、今回作成したサンプルを載せておく。
Teacup::Stylesheet.new :item_cell do
style :title_label,
constraints: [
constrain(:left).equals(:superview, :left),
constrain(:top).equals(:superview, :top),
constrain(:bottom).equals(:superview, :bottom),
]
style :price_label,
constraints: [
constrain(:right).equals(:superview, :right),
constrain(:top).equals(:superview, :top),
constrain(:bottom).equals(:superview, :bottom),
]
style :comment_label,
constraints: [
constrain(:left).equals(:title_label, :right),
constrain(:right).equals(:price_label, :left),
constrain(:top).equals(:superview, :top),
constrain(:bottom).equals(:superview, :bottom),
]
end
class MainViewController < UITableViewController
def self.controllerWithNavigation
UINavigationController.alloc.initWithRootViewController(self.alloc.init)
end
def init
initWithStyle(UITableViewStylePlain)
@items = [
{ title: "foo", comment: "aaaaa", price: 100 },
{ title: "bar", comment: "bbbbb", price: 200 },
{ title: "hoge", comment: "ccccc", price: 300 },
{ title: "fuga", comment: "eeeee", price: 400 },
]
self
end
def viewDidLoad
super
self.title = "Sample"
end
def tableView(tableView, numberOfRowsInSection:section)
@items.size
end
def tableView(tableView, cellForRowAtIndexPath:indexPath)
item = @items[indexPath.row]
cell = ItemCell.cellForItem(item, inTableView:tableView)
cell.restyle!
cell.apply_constraints
cell
end
end
class ItemCell < UITableViewCell
CELL_ID = "item-cell"
include Teacup::Layout
stylesheet :item_cell
def self.cellForItem(item, inTableView:tableView)
cell = tableView.dequeueReusableCellWithIdentifier(CELL_ID)
unless cell
cell = self.alloc.initWithStyle(
UITableViewCellStyleDefault,
reuseIdentifier:CELL_ID
)
end
cell.setupItem(item)
cell
end
def initWithStyle(style, reuseIdentifier:reuseIdentifier)
super
layout self.contentView do
@titleLabel = subview(UILabel, :title_label)
@commentLabel = subview(UILabel, :comment_label)
@priceLabel = subview(UILabel, :price_label)
end
self
end
def setupItem(item)
@titleLabel.text = item[:title]
@commentLabel.text = item[:comment]
@priceLabel.text = "#{item[:price]} 円"
end
end
class AppDelegate
def application(application, didFinishLaunchingWithOptions:launchOptions)
@window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
@window.rootViewController = MainViewController.controllerWithNavigation
@window.makeKeyAndVisible
true
end
end