PythonでのiPhoneアプリ-GroupedTableViewサンプル

adonishi2008-08-26


前に書いた、Pythonで作るiPhoneアプリのサンプルを見ていたのだけれど、アドレスブックの内容(名前のみ)の一覧をざーっとテーブルにするだけで、あまり面白くない。(sqliteとの連携が簡単って所はいいのだけど。)

もっとよくある感じの画面に沢山ボタン等が出てる感じのサンプルを作ってみた。

実はこれもテーブル(TableView)の一種で、TableViewを作るときにstyleを「UITableViewStyleGrouped」に指定すると、こんな感じのを作ることができる。


簡単にこのボタン等を変えられるように、プログラム中で、、、

self.sections_ = [
{"header": "header 1", "items": [{"title": "title 1", "action": lambda p: self.callback(p)}]},
{"header": "header 2", "items": [
    {"title": "title 2-1", "action": None},
    {"title": "title 2-2", "action": None}]
}
]

の、ように設定すると、自動的にボタンが表示されるようにしてみた。(「title 1」のボタンを押すと、とりあえずアラートが表示される。)


これを作るのにやっぱり一番苦労したのは、Objective-C Type Encodingsで、@objc.signature("i@:@")見たいな奴。これがPythonのメソッドと合わないとエラーになったり、Python側でとんでもない値が渡って来たりする。

ちょっと簡単に解説すると、、、

@objc.signature("i@:@")
def numberOfSectionsInTableView_(self, table):

これ「i@:@」は、この関数(メソッド)の戻り値や引数を表している。

  • 最初の文字は戻り値の型で、この場合の「i」はInteger/NSInteger
  • 2文字目は「self」を表していて、この場合の「@」はオブジェクト一般
  • 「:」はメソッドセレクタ
  • その後は引数で、この場合はオブジェクト(「@」)一個

#文字種は「i」「@」ぐらいしか使わない。文字列も大抵NSStringと言うオブジェクトを使うので、出てくるのはほとんど「i」「@」で、たまに「v」(void)を見る程度。詳しくはType Encodingsに載っているのを参照。

ちょっと分かりづらいのは、Objective-Cではメソッド名=パラメーター識別名で、メソッド名なのか、パラメーター名なのか分かりづらい。「numberOfRowsInSection」メソッドを例に取ると、、、

  • (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

これが、これになる、、、

@objc.signature("i@:@i")
def tableView_numberOfRowsInSection_(self, table, section):

要は、「numberOfRowsInSection」がメソッド名と思わずに、メソッド名は、「tableView」+「numberOfRowsInSection」と考えるわけ。理解するのに時間がかかりました。



参考リンク:

  1. Table View Programming Guide for iPhone OS
  2. UITableView Class Reference
  3. The Objective-C 2.0 Programming Language - Type Encodings


Grouped TableViewサンプルプログラム

import sys

import objc
from _uicaboodle import UIApplicationMain
from objc import YES, NO, NULL

objc.loadBundle("UIKit", globals(), "/System/Library/Frameworks/UIKit.framework")

class PYApplication(UIApplication):

    @objc.signature("i@:@")
    def numberOfSectionsInTableView_(self, table):
        return len(self.sections_)

    @objc.signature("i@:@i")
    def tableView_numberOfRowsInSection_(self, table, section):
        return len(self.sections_[section]["items"])

    @objc.signature("@@:@@")
    def tableView_cellForRowAtIndexPath_(self, table,  path):
        cell = table.dequeueReusableCellWithIdentifier_("PYApplication")
        if cell == None:
            zrect = ((0, 0), (0, 0))
            cell =  UITableViewCell.alloc().initWithFrame_reuseIdentifier_(zrect, "PYApplication")
        cell.setText_(self.sections_[path.section()]["items"][path.row()]["title"])
        return cell

    @objc.signature("@@:@i")
    def tableView_titleForHeaderInSection_(self, table, section):
        return self.sections_[section]["header"]

    @objc.signature("v@:@@")
    def tableView_didSelectRowAtIndexPath_(self, table, path):
        action = self.sections_[path.section()]["items"][path.row()]["action"]
        if action != None:
            action(path)

    def callback(self, path):
        UIAlertView.alloc().initWithTitle_message_delegate_cancelButtonTitle_otherButtonTitles_("", "You cliked on a TableView", None, "Ok", None).show()

    @objc.signature("v@:@")
    def applicationDidFinishLaunching_(self, unused):
        self.sections_ = [{"header": "header 1", "items": [{"title": "title 1", "action": lambda p: self.callback(p)}]},
                          {"header": "header 2", "items": [{"title": "title 2-1", "action": None},
                                                           {"title": "title 2-2", "action": None}]
                          }
                         ]

        # init window.
        outer = UIHardware.fullScreenApplicationContentRect()
        self.window = UIWindow.alloc().initWithFrame_(outer)

        self.window.orderFront_(self)
        self.window.makeKey_(self)
        self.window.setHidden_(NO)

        inner = self.window.bounds()
        navsize = UINavigationBar.defaultSize()
        navrect = ((0, 0), (inner[1][0], navsize[1]))

        # init UIView
        self.view = UIView.alloc().initWithFrame_(self.window.bounds())
        self.window.setContentView_(self.view)

        # init Navi-bar
        self.navbar = UINavigationBar.alloc().initWithFrame_(navrect)
        self.view.addSubview_(self.navbar)

        self.navbar.setBarStyle_(1)
        navitem = UINavigationItem.alloc().initWithTitle_("Grouped TableView")
        self.navbar.pushNavigationItem_(navitem)

        # init Table View
        lower = ((0, navsize[1]), (inner[1][0], inner[1][1] - navsize[1]))
        table = UITableView.alloc().initWithFrame_style_(lower, 1) # grouped TableView 
        self.table = table
        self.view.addSubview_(table)

        table.setSeparatorStyle_(1)
        table.setDelegate_(self)
        table.setDataSource_(self)
        table.reloadData()

UIApplicationMain(sys.argv, PYApplication)